Thread
Concept
Photon thread is what we usually call coroutine/fiber. The reason why we use thread naming instead of the latter two is because we hope that users will not notice the difference between the Photon program and traditional multi-threaded programs.
A thread is essentially a function, and an execution unit.
A thread must reside in one vCPU. Threads can be migrated between vCPUs.

Namespace
photon::
Headers
<photon/thread/thread.h>
Types
photon::thread → The Photon's coroutine/fiber entity.
photon::join_handle → Join handle. Used to join a thread.
photon::vcpu_base → A base class for the vCPU entity. By far there is no way to modify a vCPU. It's immutable.
Thread API
thread_create
photon::thread* photon::thread_create(thread_entry start, void* arg,
uint64_t stack_size = DEFAULT_STACK_SIZE,
uint16_t reserved_space = 0);
Description
Create a new thread, put it into the RunQueue. Leave it to the scheduler to decide when to run.
No thread yield in this call, which means the caller will continue its execution.
Parameters
startThread function's entry point.thread_entryis a type ofvoid* (*)(void*)argThread function's sole argument.stack_sizeDefault stack size is 8MB.reserved_spaceCan be used to passed large arguments to the new thread. Default is 0.
Please make sure that the parameters you pass in have the same life cycle as the function and are not destroyed before the function starts executing. This may cause invalid memory access.
If this cannot be guaranteed, you can use thread_create11 below,
which can copy all parameters to the function stack of the new coroutine.
stack_size refers to the size of the coroutine stack, default is 8MB, same as Linux default function size.
Be careful not to cause a stack overflow. Linux will report a stack overflow error at runtime. However, since Photon simulates the stack in user mode, it will not detect a stack overflow error immediately. Continuous access might cause the memory to be trampled. Normally, you should avoid write recursive functions.
Although we can use kernel's mprotect to guard this memory region, it will affect performance, so it is up to the user to control it.
Every time a new coroutine is created, malloc is called to apply for memory. Therefore, it is recommended to use
the thread-pool or pooled_stack_allocator for frequent creation.
In terms of the actual memory used by the OS, it does not add 8MB immediately for every malloc, but increases dynamically based on the mapping of the actual written page.
int main() {
photon::init();
for (int i = 0; i < 2000; ++i) {
photon::thread_create11([]{
photon::thread_usleep(-1UL);
});
}
photon::thread_usleep(-1UL);
}
In the above example, we created 2000 sleeping coroutines, the process shows that it has 16GB virtual memory, but only consumed 24MB physical memory.

Return
Pointer to the new thread.
thread_create11
template<typename F, typename... ARGUMENTS>
photon::thread* photon::thread_create11(F f, ARGUMENTS&& ...args); // (1)
template<typename FUNCTOR, typename... ARGUMENTS>
photon::thread* photon::thread_create11(FUNCTOR&& f, ARGUMENTS&& ...args); // (2)
template<typename CLASS, typename F, typename... ARGUMENTS>
photon::thread* photon::thread_create11(F f, CLASS* obj, ARGUMENTS&& ...args); // (3)
Description
C++11 syntax wrapper for thread_create. Multiple arguments is allowed.
Headers
<photon/thread/thread11.h>
Usage
- F is a global function.
double func(int a, char b);
auto th = photon::thread_create11(func, 1, '2');
- FUNCTOR is a function object.
int a = 1; char b = '2';
auto th = photon::thread_create11([&] {
// Access a and b
});
- CLASS is a type, and F is its member function
class A {
void func(int a, char b);
void run() {
photon::thread_create11(&A::func, this, 1, '2');
}
};
Arguments are forwarded to the new thread by value. If needed to pass by reference, it has to be wrapped by std::ref or std::cref.
Return
Pointer to the new thread.
thread_enable_join
photon::join_handle* photon::thread_enable_join(photon::thread* th, bool flag = true);
Description
Join is disabled by default.
Once join is enabled, the thread will remain existing until being joined. Failing to do so will cause resource leak.
Threads are join-able only through their
join_handle.
Parameters
thThread pointer.flagEnable or disable. Defaults to true.
Return
Pointer of a join_handle.
thread_join
void photon::thread_join(photon::join_handle* jh);
Description
Join a thread.
You can't join an exited or non-existent thread. It will cause core dump.
Parameters
jhPointer of ajoin_handle. Get fromthread_enable_join.
Return
None
thread_yield
void photon::thread_yield();
Description
Switching to other threads, without going into the SleepQueue.
Parameters
None
Return
None
thread_usleep
int photon::thread_usleep(uint64_t useconds);
Description
Suspend CURRENT thread for specified time duration, insert self to the SleepQueue, and switch control to other running threads.
Threads in the SleepQueue might have a chance to be resumed then.
Parameters
usecondsSleep time in microseconds. -1UL means forever sleep.
Return
- Returns 0 if slept well (at least
usecondstime). - Returns -1 if been interrupted, and
errnois set by the caller who invokesthread_interrupt.
thread_interrupt
void photon::thread_interrupt(photon::thread* th, int error_number = EINTR);
Description
Interrupt the target thread. Awaken it from sleep.
You can't interrupt an exited or non-existent thread. It will cause core dump.
Parameters
thTarget thread.error_numberSet theerrnoof the target thread. Default isEINTR.
Return
None.
Note
The target thread and the caller might NOT belong to the same vCPU. In this situation, the StandbyQueue will be used.
thread_shutdown
int photon::thread_shutdown(photon::thread* th, bool flag = true);
Description
Set the shutdown flag to disable further scheduling of the target thread, then fire an interrupt.
Parameters
thTarget thread.flagSet the shutdown flag or not.
Return
None.
thread_migrate
int photon::thread_migrate(photon::thread* th, photon::vcpu_base* vcpu);
Description
Migrate a READY state thread to another vCPU.
Although Photon is of M:1 model, we can still migrate the coroutine function to another thread.
Run-to-completion model is prefer. Once created, do not schedule the coroutine to other threads unless you encounter a CPU bottleneck in this thread.
Parameters
thTarget thread.vcpuTarget vCPU.
Return
Returns 0 on success, returns -1 on error.
get_vcpu
vcpu_base* get_vcpu(thread* th = CURRENT);
Description
Get the vCPU of the target thread.
Parameters
thTarget thread. Defaults toCURRENTthread.
Return
Returns a pointer of vcpu_base.
Thread Pool
Additional Header
<photon/thread/thread-pool.h>
Description
The thread-pool's implementation is based on identity-pool.h. It supports both static allocation (on stack) and dynamic allocation (on heap).
// on heap
auto p1 = photon::ThreadPoolBase::new_thread_pool(100);
auto th1 = p1->thread_create(&func, nullptr);
// on stack
photon::ThreadPool<400> p2;
auto th2 = p2.thread_create(&func, nullptr);
Timer
Additional Header
<photon/thread/thread-pool.h>
Description
Create a timer object with default_timedout in usec, callback function on_timer,
and callback argument arg. The timer object is implemented as a special thread, so
it has a stack_size, and the on_timer is invoked within the thread's context.
The timer object is deleted automatically after it is finished.
A non-repeating is basically equal to creating a new thread and run thread_usleep.
Timer(uint64_t default_timeout, Entry on_timer,
bool repeating = true, uint64_t stack_size = 1024 * 64);
int Timer::cancel();
int Timer::reset(uint64_t new_timeout = -1);
int Timer::stop();