|
Serene Runtime 1.0.0
C runtime for the Serene programming language
|
Data Fields | |
| srn_engine_t * | engine |
| srn_mutex_t | lock |
| Global lock. | |
| srn_fiber_t * | ready_head |
| Global / overflow queue. | |
| srn_fiber_t * | ready_tail |
| srn_fiber_t * | registry |
Registry: head of the doubly-linked list (through reg_prev/reg_next) of every live fiber, on a different axis from the run queues. | |
| srn_cond_t | work |
| Worker coordination. | |
| atomic_int | idle |
| atomic_int | runnable |
| int | nworkers |
| _Atomic srn_sched_state_t | state |
| srn_worker_t * | workers |
srn_sched_run allocates these two arrays and srn_sched_shutdown frees them. | |
| srn_thread_t * | os_threads |
| _Atomic bool | run_active |
True for the duration of an srn_sched_run call. | |
| bool | destroyed |
Set once srn_sched_shutdown has torn the scheduler down. | |
Definition at line 131 of file scheduler.c.
| bool srn_scheduler_t::destroyed |
Set once srn_sched_shutdown has torn the scheduler down.
The scheduler is not usable afterwards, a further run panics, and a further shutdown is a no-op.
Definition at line 201 of file scheduler.c.
| srn_engine_t* srn_scheduler_t::engine |
Definition at line 132 of file scheduler.c.
| atomic_int srn_scheduler_t::idle |
Definition at line 175 of file scheduler.c.
| srn_mutex_t srn_scheduler_t::lock |
Global lock.
Guards the global/overflow queue, the registry, and the worker coordination fields below. It does NOT guard the per-worker local queues, which carry their own locks. Lock order is global-before-local: a worker may hold this while taking a local lock (only the park scan does), but a local lock is never held while taking this.
Definition at line 138 of file scheduler.c.
| int srn_scheduler_t::nworkers |
Definition at line 177 of file scheduler.c.
| srn_thread_t* srn_scheduler_t::os_threads |
Definition at line 192 of file scheduler.c.
| srn_fiber_t* srn_scheduler_t::ready_head |
Global / overflow queue.
Holds fibers enqueued with no current worker – an external waker, or the initial fibers made before the run. A worker drains its own local queue first, then this. FIFO through the intrusive link.
Definition at line 142 of file scheduler.c.
| srn_fiber_t* srn_scheduler_t::ready_tail |
Definition at line 143 of file scheduler.c.
| srn_fiber_t* srn_scheduler_t::registry |
Registry: head of the doubly-linked list (through reg_prev/reg_next) of every live fiber, on a different axis from the run queues.
A fiber joins at srn_fiber_make and leaves when reaped, so the list is every fiber the scheduler is still responsible for, including SUSPENDED ones that sit on no run queue. It is how the scheduler accounts for and cleans them up.
Definition at line 149 of file scheduler.c.
| _Atomic bool srn_scheduler_t::run_active |
True for the duration of an srn_sched_run call.
srn_sched_shutdown reads it to reject being called while a run is still in flight (it must run after run has returned). Atomic because shutdown may read it from a different thread than the one inside run.
Definition at line 197 of file scheduler.c.
| atomic_int srn_scheduler_t::runnable |
Definition at line 176 of file scheduler.c.
| _Atomic srn_sched_state_t srn_scheduler_t::state |
Definition at line 178 of file scheduler.c.
| srn_cond_t srn_scheduler_t::work |
Worker coordination.
Parked os threads wait on work. idle counts parked os threads and runnable counts fibers waiting in ANY queue (local or global). Both are atomic because a push reads idle, and the park path reads runnable, without holding the lock the other side updates them under. The "idle++ then read runnable" park ordering against the "runnable++ then read idle" push ordering is what makes a lost wakeup impossible.
WARNING: that pairing is correct ONLY because all four of those operations are seq_cst (the default for atomic_fetch_add and atomic_load). announce_work does its runnable++ and its idle read with NO lock held, so the single seq_cst total order is the only thing tying it to the park path. Do NOT weaken these to acq_rel or relaxed for "speed". Weaken them and a push and a park can each fail to see the other, so an os thread sleeps on work forever while a runnable fiber sits in a queue. That is a lost wakeup, a hang. If these ever must be relaxed, the whole runnable/idle handshake has to move under the lock first, the way global_enqueue already does it, so the lock supplies the ordering the weaker atomics would not.
nworkers is fixed for a run. state drives termination. An os thread stops once it observes SRN_SCHED_STOPPING, set at quiescence (idle == nworkers and runnable == 0) or by srn_sched_stop.
Definition at line 174 of file scheduler.c.
| srn_worker_t* srn_scheduler_t::workers |
srn_sched_run allocates these two arrays and srn_sched_shutdown frees them.
They live until shutdown, so shutdown can join the threads and reap. The scheduler struct itself is immortal, but these arrays are not.
workers holds all nworkers workers in one array, so each worker can find the others to steal from. os_threads holds the OS threads the scheduler started. There is not one thread per worker. The caller's own thread runs worker 0, so the scheduler never starts a thread for it. Only workers 1 through nworkers-1 get a thread, so slot 0 of os_threads is unused and shutdown joins slots 1 through nworkers-1. A thread belongs here, not in srn_worker_t, because it marks a thread the scheduler started and must join, which is not the same as being a worker.
Definition at line 191 of file scheduler.c.