🟥 Stackful cooperative task runtime
SaneCppFibers.h is a stackful fiber runtime in Sane C++ style: explicit storage, stable objects, cooperative suspension, and no hidden dynamic allocation.
Fibers is a CPU/tasking runtime for code that wants synchronous-looking control flow without blocking an OS thread. A fiber owns a stack, can call normal C++ functions, and can cooperatively suspend with FiberScheduler::yield() or by waiting on fiber primitives. Later it can resume on the same worker or on another worker.
The intended long-term shape is a small no-allocation runtime for micro-tasking workloads: many short jobs over time, bounded pools of reusable FiberTask objects and stacks, work stealing between worker threads, and explicit memory budgets chosen by the caller.
Use Fibers when you want:
FiberScheduler owns the logical scheduling state, but not the storage of the things it schedules. A task is made from:
FiberTask;FiberStack, or a slot acquired through FiberTaskPool;FiberTask::Procedure returning plain Result;FiberTaskSpawnOptions.The scheduler runs ready tasks until they complete, yield, or wait. When a task yields or a wait is satisfied, it is queued back as ready using intrusive links already present in FiberTask; normal yield/wake publication does not allocate.
FiberTaskPool is the ergonomic way to run many bounded tasks without manually pairing each task with a stack. The pool does not grow: if all slots are active, producers can wait for capacity and try again.
This is still ordinary C++ control flow. The call to yield() cooperatively gives another ready fiber a chance to run, but no OS thread is blocked waiting for preemption.
When producing more work than the pool can hold at once, capacity pressure is explicit. From inside a fiber, waitForSpawnCapacity() suspends cooperatively until at least one pool slot is available.
Result is still reserved for real errors or cancellation. Capacity is observable through hasAvailableTask() and availableCount(), while waiting is modeled as an explicit scheduling operation.
FiberWorkerPool runs one FiberScheduler on caller-provided OS thread storage. Workers can steal ready fibers from each other, and optional allocator-backed worker deques avoid placing worker queue storage on the heap.
For higher-throughput scheduling, provide explicit deque storage through FiberAllocator:
The worker pool owns OS threads while running, but the memory for workers, thread handles, deques, tasks, and stacks is still selected by the caller.
For simple examples, FiberTaskPool(Span<FiberTask>, Span<char>, stackSize) is often enough. For larger systems, the draft API also has explicit classes for reusable task records and virtual-memory-backed stack slots:
This keeps the public memory budget explicit while preparing the runtime for large numbers of tasks over time.
Fiber primitives suspend the current fiber instead of blocking the OS thread:
FiberCounter waits for a counted set of operations to complete.FiberEvent wakes all waiters when signaled.FiberAutoResetEvent wakes one waiter per signal.FiberSemaphore controls access to a fixed number of logical slots.FiberMutex protects cooperative fiber critical sections and diagnoses recursive or wrong-owner use.FiberTaskGroup spawns child tasks and collects errors without dynamic allocation.Example using a semaphore as a cooperative concurrency limit:
Cancellation is cooperative. FiberCancellationTokenSource can request cancellation for a group of spawned tasks, and the scheduler wakes interruptible waits so tasks can return an error Result.
Tasks should still return plain Result; there is no exception dependency and no hidden cancellation object allocation.
FiberTask execution is not pinned to the OS thread that first started the task. A task may yield, become ready again, and later resume on a different worker thread, for example after work stealing or when another thread drives the same FiberScheduler.
Do not use C++ thread_local variables or platform TLS to store logical fiber task state. Those values belong to the current OS thread, not to the fiber, so a resumed task may observe a different value than it wrote before suspension. Use explicit task state instead, such as captured state in the task procedure, caller-owned objects, or FiberTask::userData().
Fibers follows the Sane C++ allocation rules:
FiberAllocator when enabled;FiberAllocator::createMalloc() exists only as an explicit opt-in mode;This means the runtime can apply backpressure instead of silently growing. Producers either provide enough capacity, wait for capacity, or receive a normal Result error when setup/allocation fails.
Async is the low-level callback I/O library. Await is a C++20 coroutine wrapper over Async. Fibers is different: it is stackful, does not require C++20 coroutines, and can suspend through ordinary nested function calls because each fiber has an explicit stack.
I/O integration is intentionally not part of Fibers; it lives in FibersAsync, which depends on both Fibers and Async.
| Fibers API | Description |
|---|---|
| FiberStack | Caller-owned stack storage used by fiber contexts. |
| FiberVirtualStack | Virtual-memory-backed stack storage with an optional guard page. |
| FiberStackClass | Fixed-size virtual stack slot class with explicit capacity. |
| FiberTask | Caller-owned task object scheduled by FiberScheduler. |
| FiberTaskClass | Allocator-backed fixed-capacity storage for reusable FiberTask objects. |
| FiberTaskPool | Caller-owned pool pairing task objects with stack slots. |
| FiberTaskGroup | Helper for spawning child tasks and waiting for completion/errors. |
| FiberCounter | Counter used to suspend fibers until work completes. |
| FiberEvent | Manual-reset event that wakes waiting fibers when signaled. |
| FiberAutoResetEvent | Auto-reset event that wakes one waiting fiber per signal. |
| FiberSemaphore | Counting semaphore for cooperative fibers. |
| FiberMutex | Cooperative mutex for fibers running on one scheduler. |
| FiberAllocator | Explicit allocator for scheduler/deque and scalable runtime storage. |
| FiberWorker | Caller-owned execution agent for running ready fibers on an OS thread. |
| FiberWorkerPool | No-allocation OS-thread-owning worker pool using caller-provided storage. |
| FiberScheduler | Cooperative scheduler with explicit workers and caller-owned storage. |
Examples/FibersDemo shows a tiny CPU fiber workload, FibersAsync sleeps, and worker-pool I/O.Examples/FibersBenchmark contains explicit benchmark-style workloads for yield/resume and sustained micro-tasking.Tests/Libraries/Fibers/FibersTest.cpp is the best source of focused examples for cancellation, primitives, task pools, worker pools, work stealing, diagnostics, virtual stacks, and allocator-backed storage.🟥 Draft
Current support includes:
FiberStack memory;FiberStackClass pools;FiberTask objects and allocator-backed FiberTaskClass storage;FiberTaskPool;FiberScheduler spawn, run, yield, and no-progress detection;FiberCounter wait from both fibers and the root caller;FiberTaskGroup convenience spawning, wait-all result reporting, cancel-on-error, and pool-backed bounded fan-out;FiberEvent, FiberAutoResetEvent, FiberSemaphore, and FiberMutex primitives;SCTest coverage for the raw context switch layer, scheduler primitives, worker pools, and storage classes.