#### Schedulers & Fibers

# Schedulers

The `DexScheduler` is responsible for running work items on a thread. This is performed by integrating with the threads `GMainContext`.

The main thread of your application will have a `DexMainScheduler` as the assigned scheduler.

The scheduler manages callbacks such as work items created with `dex_scheduler_push()`.

You can get the default scheduler for the application’s main thread using `dex_scheduler_get_default()`.

The thread’s scheduler can be retrieved with `dex_scheduler_ref_thread_default()`.

# Thread Pool Scheduling

Libdex manages a thread pool which may be retrieved using `dex_thread_pool_scheduler_get_default()`.

The thread pool scheduler will manage a number of threads that is deemed useful based on the number of CPU available.

Work items created from outside of the thread pool are placed into a global queue. Thread pool workers will take items from the global queue when they have no more items to process.

All thread pool workers have a local `DexScheduler` so use of timeouts and other `GSource` features continue to work.

If you need to interact with long-blocking API calls it is better to use `dex_thread_spawn()` rather than a thread pool thread.

Thread pool workers use a work-stealing wait-free queue which allows the worker to push work items onto one side of the queue quickly. Doing so also helps improve cacheline effectiveness.

Workers sleep in their main context when they have no work. When a worker publishes enough local work for another worker to help, it sends a directed request to one peer. Requests to a peer are coalesced, and a successful steal may wake one additional peer while useful backlog remains. This allows the pool to increase parallelism gradually without waking every worker for one item. An already-busy peer may retain a request until it drains its own local work, and a thief continues while any victim observed later in its scan has backlog.

# Fibers

Fibers are a type of stackful co-routine. A new stack is created and a trampoline is performed onto the stack from the current thread.

Use `dex_scheduler_spawn()` to create a new fiber.

When a fiber calls a `dex_await()`, similar function, or returns the fiber is suspended and execution returns to the scheduler.

By default, fibers have a 128-kb stack with a guard page at the end. Fiber stacks are pooled so that they may be reused during heavy use.

Fibers are a `DexFuture` which means you can await the completion of a fiber just like any other future.

Note that fibers are pinned to a scheduler. They will not be migrated between schedulers even when a thread pool is in use.

# Stackless Coroutines

Coroutines are a stackless alternative that are still futures managed by the same `DexScheduler`. Use `dex_scheduler_spawn_coroutine()` to create them.

A coroutine function is given a `DexCoroutineContext` and is expected to return a `DexFuture` or suspend by returning `NULL`.

Stackless coroutines generally have lower memory overhead than fibers when you are mainly waiting on futures between steps.

Use `DEX_COROUTINE_BEGIN` and `DEX_COROUTINE_END` when writing coroutines. Suspend with one of the `DEX_COROUTINE_SUSPEND_*` helpers. See Coroutines for more guidance.

## Cancellation

Fibers may be cancelled if the fiber has been discarded by all futures awaiting completion. Fibers will always exit through a natural exit point such as a pending “await”. All attempts to await will reject with error once a fiber has been cancelled.

If you want to ignore cancellation of fibers, use `dex_future_disown()` on the fiber after creation.