5#include "../Common/CompilerMacrosExport.h"
6#ifndef SC_EXPORT_LIBRARY_FIBERS
7#define SC_EXPORT_LIBRARY_FIBERS 0
9#define SC_FIBERS_EXPORT SC_COMPILER_LIBRARY_EXPORT(SC_EXPORT_LIBRARY_FIBERS)
11#include "../Common/AlignedStorage.h"
12#include "../Common/Assert.h"
13#include "../Common/Function.h"
14#include "../Common/OpaqueObject.h"
15#include "../Common/PlatformMacrosInstructionSet.h"
16#include "../Common/PlatformMacrosType.h"
17#include "../Common/PrimitiveDefinitions.h"
18#include "../Common/Result.h"
19#include "../Common/Span.h"
28SC_DECLARE_ASSERT_PROVIDER(FibersAssert, SC_FIBERS_EXPORT);
30#define SC_FIBERS_ASSERT_RELEASE(e) SC_ASSERT_PROVIDER_RELEASE(SC::FibersAssert, e)
31#define SC_FIBERS_ASSERT_DEBUG(e) SC_ASSERT_PROVIDER_DEBUG(SC::FibersAssert, e)
32#define SC_FIBERS_TRUST_RESULT(expression) SC_FIBERS_ASSERT_RELEASE(expression)
35struct FiberAutoResetEvent;
43struct FiberTaskClassDiagnostics;
44struct FiberTaskClassInternal;
45struct FiberTaskClassOptions;
47struct FiberTaskPoolDiagnostics;
49struct FiberVirtualStack;
50struct FiberVirtualStackInternal;
51struct FiberVirtualStackOptions;
52struct FiberStackClass;
53struct FiberStackClassDiagnostics;
54struct FiberStackClassInternal;
55struct FiberStackClassOptions;
56struct FiberSchedulerDiagnostics;
57struct FiberTraceEvent;
58struct FiberTraceHooks;
60struct FiberAllocatorInterface;
61struct FiberAllocatorVirtualOptions;
63struct FiberWorkerPoolWakeEvent;
64struct FiberWorkerPool;
65struct FiberWorkerPoolOptions;
66struct FiberWorkerPoolThreadEntry;
67struct FiberWorkerThread;
69#if SC_PLATFORM_WINDOWS
70static constexpr int FiberContextStorageSize = 2048;
71#elif SC_PLATFORM_ARM64
72static constexpr int FiberContextStorageSize = 224;
74static constexpr int FiberContextStorageSize = 128;
76static constexpr int FiberContextStorageAlignment = 16;
77static constexpr int FiberStackAlignment = 16;
78static constexpr int FiberStackMinimumSize = 4096;
85 [[nodiscard]] Span<char> memory()
const;
86 [[nodiscard]]
size_t sizeInBytes()
const;
87 [[nodiscard]]
size_t usableSizeInBytes()
const;
88 [[nodiscard]]
size_t alignmentWasteInBytes()
const;
89 [[nodiscard]]
bool isUsable()
const;
90 void fillHighWaterMark();
91 [[nodiscard]]
size_t highWaterUsedBytes()
const;
92 [[nodiscard]]
size_t highWaterUnusedBytes()
const;
100 Span<char> stackMemory;
101 void* stackOwner =
nullptr;
106 static constexpr int Windows = 64;
107 static constexpr int Apple = 64;
108 static constexpr int Linux = 64;
109 static constexpr int Default = 64;
110 static constexpr size_t Alignment =
alignof(
void*);
112 using Object = FiberVirtualStackInternal;
114using FiberVirtualStackOpaque = OpaqueObject<FiberVirtualStackDefinition>;
119 size_t usableSizeInBytes = 64 * 1024;
120 bool guardPage =
true;
125 static constexpr int Windows = 160;
126 static constexpr int Apple = 160;
127 static constexpr int Linux = 160;
128 static constexpr int Default = 160;
129 static constexpr size_t Alignment =
alignof(
void*);
131 using Object = FiberStackClassInternal;
133using FiberStackClassOpaque = OpaqueObject<FiberStackClassDefinition>;
137 size_t stackSizeInBytes = 64 * 1024;
138 size_t maxStacks = 0;
139 bool guardPage =
true;
145 size_t activeStacks = 0;
146 size_t peakActiveStacks = 0;
147 size_t stackSizeInBytes = 0;
148 size_t guardSizeInBytes = 0;
149 size_t reservedSizeBytes = 0;
150 size_t committedSizeBytes = 0;
151 size_t peakCommittedBytes = 0;
152 size_t highWaterUsedBytes = 0;
167 [[nodiscard]] Result acquire(
FiberStack& outStack);
168 [[nodiscard]] Result release(
FiberStack& stack);
169 [[nodiscard]] Result waitForAvailableSlot(
FiberScheduler& scheduler);
171 void fillHighWaterMarks();
174 [[nodiscard]]
bool isReserved()
const;
175 [[nodiscard]]
bool owns(
const FiberStack& stack)
const;
176 [[nodiscard]]
size_t capacity()
const;
177 [[nodiscard]]
size_t activeCount()
const;
182 FiberStackClassOpaque internal;
185enum class FiberAllocatorMode : uint8_t
196 size_t numAllocations = 0;
197 size_t numReleases = 0;
199 size_t requestedBytesAllocated = 0;
200 size_t requestedBytesReleased = 0;
202 size_t bytesInUse = 0;
203 size_t peakBytesInUse = 0;
205 size_t numAllocationFailures = 0;
206 size_t lastFailedAllocationSize = 0;
207 size_t largestFailedAllocationSize = 0;
212 size_t reserveBytes = 0;
213 size_t initialCommitBytes = 0;
218 virtual void* allocateImpl(
const void* owner,
size_t numBytes,
size_t alignment) = 0;
219 virtual void releaseImpl(
void* memory) = 0;
233 [[nodiscard]] Result createFixed(Span<char> storage);
235 [[nodiscard]] Result createMalloc();
237 [[nodiscard]] Result validateClose()
const;
238 [[nodiscard]] Result close();
240 [[nodiscard]]
void* allocate(
const void* owner,
size_t numBytes,
size_t alignment);
241 void release(
void* memory);
242 static void releaseFromAnyAllocator(
void* memory);
244 [[nodiscard]] FiberAllocatorMode mode()
const;
246 [[nodiscard]]
bool isOpen()
const;
248 [[nodiscard]]
size_t used()
const;
249 [[nodiscard]]
size_t capacity()
const;
250 [[nodiscard]]
size_t peakUsed()
const;
251 [[nodiscard]]
size_t failedAllocationSize()
const;
252 [[nodiscard]]
size_t reservedBytes()
const;
253 [[nodiscard]]
size_t committedBytes()
const;
258 Result initializeFixedStorage(Span<char> storage);
259 void* allocateFromBlocks(
const void* owner,
size_t numBytes,
size_t alignment);
260 void releaseBlock(BlockHeader& header);
261 bool ensureCommitted(
size_t sizeInBytes);
262 void releaseVirtualMemory();
263 void recordAllocationFailure(
size_t numBytes);
266 FiberAllocatorMode currentMode = FiberAllocatorMode::None;
268 Span<char> fixedStorage;
269 BlockHeader* firstBlock =
nullptr;
272 void* virtualMemory =
nullptr;
273 size_t virtualReservedBytes = 0;
274 size_t virtualCommittedBytes = 0;
292 [[nodiscard]] Span<char> memory()
const;
293 [[nodiscard]]
size_t usableSizeInBytes()
const;
294 [[nodiscard]]
size_t reservedSizeInBytes()
const;
295 [[nodiscard]]
size_t guardSizeInBytes()
const;
296 [[nodiscard]]
bool isReserved()
const;
299 FiberVirtualStackOpaque internal;
303enum class FiberTaskStatus
313enum class FiberTaskSuspendAction
320struct FiberCancellationToken;
331 [[nodiscard]]
bool isActive()
const;
335 [[nodiscard]]
const FiberTask* runningTask()
const;
351 size_t localReadyFibers = 0;
352 size_t localDequeCapacity = 0;
353 size_t localDequeHead = 0;
354 volatile size_t localDequeTop = 0;
355 volatile size_t localDequeBottom = 0;
356 size_t localReadyPeakFibers = 0;
357 size_t localSpilledFibers = 0;
358 size_t stealAttempts = 0;
359 size_t stolenFibers = 0;
360 size_t failedSteals = 0;
361 size_t runAttempts = 0;
362 size_t idlePolls = 0;
363 size_t executedFibers = 0;
364 size_t completedFibers = 0;
365 size_t yieldedFibers = 0;
366 size_t waitingFibers = 0;
367 bool workerActive =
false;
368 bool localSchedulingActive =
false;
370 AlignedStorage<FiberContextStorageSize, FiberContextStorageAlignment> rootContextStorage;
372 [[nodiscard]] FiberContext& rootContext();
375#if SC_PLATFORM_WINDOWS
376static constexpr int FiberWorkerThreadStorageSize =
sizeof(
void*);
378static constexpr int FiberWorkerThreadStorageSize =
sizeof(
void*) * 2;
380static constexpr int FiberWorkerThreadStorageAlignment =
alignof(
void*);
391 [[nodiscard]]
bool wasStarted()
const;
392 [[nodiscard]] Result result()
const;
396 friend struct FiberWorkerPoolThreadEntry;
398 AlignedStorage<FiberWorkerThreadStorageSize, FiberWorkerThreadStorageAlignment> threadStorage;
401 size_t workerIndex = 0;
402 uint64_t affinityMask = 0;
403 uint8_t priority = 0;
404 Result threadResult = Result(
true);
405 bool started =
false;
407 Result startThread();
409 Result runThreadEntry();
410 Result applyThreadPolicy();
413enum class FiberWorkerThreadPriority : uint8_t
424 size_t dequeCapacityPerWorker = 0;
425 Span<const uint64_t> affinityMasks;
426 FiberWorkerThreadPriority threadPriority = FiberWorkerThreadPriority::Default;
431 size_t readyFibers = 0;
432 size_t readyPeakFibers = 0;
433 size_t dequeCapacity = 0;
434 size_t spilledFibers = 0;
435 size_t stealAttempts = 0;
436 size_t stolenFibers = 0;
437 size_t failedSteals = 0;
438 size_t runAttempts = 0;
439 size_t idlePolls = 0;
440 size_t executedFibers = 0;
441 size_t completedFibers = 0;
442 size_t yieldedFibers = 0;
443 size_t waitingFibers = 0;
448 size_t readyFibers = 0;
449 size_t activeFibers = 0;
450 size_t lockAcquisitions = 0;
451 size_t lockContentions = 0;
452 size_t lockSpinRetries = 0;
453 size_t lockPeakSpinRetries = 0;
456enum class FiberTraceEventType : uint8_t
466 FiberTraceEventType type = FiberTraceEventType::TaskStarted;
475 using Callback = void (*)(
void* userData,
const FiberTraceEvent& event);
477 Callback callback =
nullptr;
478 void* userData =
nullptr;
490 Result start(
FiberScheduler& scheduler, Span<FiberWorker> workerStorage, Span<FiberWorkerThread> threadStorage);
491 Result start(
FiberScheduler& scheduler, Span<FiberWorker> workerStorage, Span<FiberWorkerThread> threadStorage,
493 Result requestStop();
497 [[nodiscard]]
bool isRunning()
const;
498 [[nodiscard]]
size_t workerCount()
const;
502 static constexpr int Windows =
sizeof(
void*) * 16;
503 static constexpr int Apple =
sizeof(
void*) * 16;
504 static constexpr int Linux =
sizeof(
void*) * 16;
505 static constexpr int Default = Linux;
507 static constexpr size_t Alignment =
alignof(
void*);
509 using Object = FiberWorkerPoolWakeEvent;
512 using WakeEventOpaque = OpaqueObject<WakeEventDefinition>;
519 Span<FiberWorker> workers;
520 Span<FiberWorkerThread> threads;
521 WakeEventOpaque wakeEvent;
522 mutable volatile int32_t stopRequested = 0;
523 mutable volatile int32_t running = 0;
524 bool localDequesCreated =
false;
527 void waitForWork(uint32_t observedGeneration);
528 [[nodiscard]] uint32_t wakeGeneration()
const;
530 Result workerMain(
size_t workerIndex);
541 void requestCancel();
543 Result check()
const;
545 [[nodiscard]]
bool isCancellationRequested()
const;
552 mutable volatile int32_t requested = 0;
560 Result check()
const;
562 [[nodiscard]]
bool isValid()
const;
563 [[nodiscard]]
bool isCancellationRequested()
const;
579 void* userData =
nullptr;
580 bool setUserData =
false;
591#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
593#pragma warning(disable : 4324)
609 [[nodiscard]]
bool isValid()
const;
610 [[nodiscard]]
bool isStarted()
const;
611 [[nodiscard]]
bool isCompleted()
const;
612 [[nodiscard]]
bool isActive()
const;
613 [[nodiscard]]
bool isCancellationRequested()
const;
614 [[nodiscard]] FiberTaskStatus status()
const;
615 [[nodiscard]] Result result()
const;
616 void setUserData(
void* data);
617 [[nodiscard]]
void* userData()
const;
623 AlignedStorage<FiberContextStorageSize, FiberContextStorageAlignment> contextStorage;
639 Span<char> originStackMemory;
641 void* runningWorker =
nullptr;
642 void* stackOwner =
nullptr;
643 void* taskUserData =
nullptr;
644 Result taskResult = Result(
true);
645 FiberTaskStatus taskStatus = FiberTaskStatus::Invalid;
646 FiberTaskSuspendAction suspendAction = FiberTaskSuspendAction::None;
647 bool cancelRequested =
false;
648 bool suspendInterruptible =
false;
650 [[nodiscard]] FiberContext& context();
651 [[nodiscard]]
const FiberContext& context()
const;
656 static constexpr int Windows = 96;
657 static constexpr int Apple = 96;
658 static constexpr int Linux = 96;
659 static constexpr int Default = 96;
660 static constexpr size_t Alignment =
alignof(
void*);
662 using Object = FiberTaskClassInternal;
664using FiberTaskClassOpaque = OpaqueObject<FiberTaskClassDefinition>;
674 size_t activeTasks = 0;
675 size_t availableTasks = 0;
676 size_t peakActiveTasks = 0;
691 [[nodiscard]] Result acquire(
FiberTask*& outTask);
692 [[nodiscard]] Result release(
FiberTask& task);
693 [[nodiscard]] Result waitForAvailableSlot(
FiberScheduler& scheduler);
694 [[nodiscard]] Result validateClose()
const;
695 [[nodiscard]] Result close();
698 [[nodiscard]]
bool isOpen()
const;
699 [[nodiscard]]
bool owns(
const FiberTask& task)
const;
700 [[nodiscard]]
size_t capacity()
const;
701 [[nodiscard]]
size_t activeCount()
const;
702 [[nodiscard]]
size_t availableCount()
const;
707 FiberTaskClassOpaque internal;
719 [[nodiscard]]
size_t value()
const;
724 size_t counterValue = 0;
733 Result result = Result(
true);
755 Result waitAll(Result* outFirstError =
nullptr);
756 Result waitAllCancelOnParentCancel(Result* outFirstError =
nullptr);
757 Result waitCancelOnError(Result* outFirstError =
nullptr);
760 [[nodiscard]]
size_t pending()
const;
761 [[nodiscard]]
size_t countErrors()
const;
762 Result collectErrors(Span<FiberTaskGroupError> errors,
size_t& outErrors)
const;
770 Result findFirstError(Result* outFirstError)
const;
777 size_t activeTasks = 0;
778 size_t availableTasks = 0;
779 bool classBacked =
false;
788 FiberTaskPool(Span<FiberTask> taskStorage, Span<char> stackStorage,
size_t stackSize);
804 [[nodiscard]]
size_t capacity()
const;
805 [[nodiscard]]
size_t activeCount()
const;
806 [[nodiscard]]
size_t availableCount()
const;
807 [[nodiscard]]
bool hasAvailableTask()
const;
811 [[nodiscard]]
size_t stackSizeInBytes()
const;
812 void fillHighWaterMarks();
813 Result stackHighWaterUsedBytes(
size_t stackIndex,
size_t& outBytes)
const;
814 Result stackHighWaterUnusedBytes(
size_t stackIndex,
size_t& outBytes)
const;
822 WaitNode* next =
nullptr;
823 bool notified =
false;
826 Span<FiberTask> tasks;
828 size_t stackSize = 0;
834 WaitNode* availabilityWaitHead =
nullptr;
835 WaitNode* availabilityWaitTail =
nullptr;
837 mutable volatile int32_t primitiveLock = 0;
839 Result stackAt(
size_t stackIndex,
FiberStack& outStack)
const;
840 void queueAvailabilityWaiter(WaitNode& node);
842 bool removeAvailabilityWaiter(WaitNode& node);
858 [[nodiscard]]
bool isSignaled()
const;
864 WaitNode* next =
nullptr;
865 bool notified =
false;
868 WaitNode* waitHead =
nullptr;
869 WaitNode* waitTail =
nullptr;
870 bool signaled =
false;
871 mutable volatile int32_t primitiveLock = 0;
873 void queueWaiter(WaitNode& node);
874 bool removeWaiter(WaitNode& node);
890 [[nodiscard]]
bool isSignaled()
const;
896 WaitNode* next =
nullptr;
897 bool notified =
false;
900 WaitNode* waitHead =
nullptr;
901 WaitNode* waitTail =
nullptr;
902 bool signaled =
false;
903 mutable volatile int32_t primitiveLock = 0;
905 void queueWaiter(WaitNode& node);
906 bool popWaiter(WaitNode*& node);
907 bool removeWaiter(WaitNode& node);
922 [[nodiscard]]
size_t available()
const;
928 WaitNode* next =
nullptr;
929 bool notified =
false;
932 WaitNode* waitHead =
nullptr;
933 WaitNode* waitTail =
nullptr;
934 size_t availableCount = 0;
935 mutable volatile int32_t primitiveLock = 0;
937 void queueWaiter(WaitNode& node);
938 WaitNode* popWaiter();
939 bool removeWaiter(WaitNode& node);
954 [[nodiscard]]
bool isLocked()
const;
955 [[nodiscard]]
bool isOwnedByCurrentTask(
FiberScheduler& scheduler)
const;
961 WaitNode* next =
nullptr;
962 bool notified =
false;
965 WaitNode* waitHead =
nullptr;
966 WaitNode* waitTail =
nullptr;
969 mutable volatile int32_t primitiveLock = 0;
971 void queueWaiter(WaitNode& node);
972 WaitNode* popWaiter();
973 bool removeWaiter(WaitNode& node);
993 Result runOnce(
FiberWorker& worker, Span<FiberWorker> workerGroup);
996 Result runNoWait(
FiberWorker& worker, Span<FiberWorker> workerGroup);
997 Result runReadyFibers();
999 Result runReadyFibers(
FiberWorker& worker, Span<FiberWorker> workerGroup);
1002 Result run(
FiberWorker& worker, Span<FiberWorker> workerGroup);
1003 Result createWorkerDeques(
FiberAllocator& allocator, Span<FiberWorker> workers,
size_t capacityPerWorker);
1004 void releaseWorkerDeques(Span<FiberWorker> workers);
1008 Result shutdown(
FiberWorker& worker, Span<FiberWorker> workerGroup);
1012 Result requestCancelAll();
1020 void clearTraceHooks();
1023 [[nodiscard]]
const FiberTask* currentTask()
const;
1024 [[nodiscard]]
bool isCurrentTaskCancellationRequested()
const;
1026 [[nodiscard]]
bool hasReadyFibers()
const;
1027 [[nodiscard]]
bool hasActiveFibers()
const;
1028 [[nodiscard]]
size_t readyFiberCount()
const;
1029 [[nodiscard]]
size_t readyFiberCount(
const FiberWorker& worker)
const;
1030 [[nodiscard]]
size_t stolenFiberCount(
const FiberWorker& worker)
const;
1031 [[nodiscard]]
size_t stolenFiberCount(Span<FiberWorker> workers)
const;
1032 [[nodiscard]]
size_t activeFiberCount()
const;
1034 void resetSchedulerDiagnostics();
1039 void resetWorkerDiagnostics(Span<FiberWorker> workers);
1050 volatile size_t readyFibers = 0;
1051 size_t activeFibers = 0;
1053 mutable volatile int32_t schedulerLock = 0;
1055 mutable size_t schedulerLockAcquisitions = 0;
1056 mutable size_t schedulerLockContentions = 0;
1057 mutable size_t schedulerLockSpinRetries = 0;
1058 mutable size_t schedulerLockPeakSpinRetries = 0;
1065 void unlock()
const;
1066 void trace(FiberTraceEventType type,
FiberTask* task,
FiberWorker* worker,
size_t value = 0)
const;
1069 void notifyReadyWorkUnlocked();
1070 void pushReadyUnlocked(
FiberTask& task);
1075 [[nodiscard]]
FiberTask* popReadyUnlocked();
1076 [[nodiscard]]
FiberTask* popReadyUnlocked(
FiberWorker& worker, Span<FiberWorker> stealWorkers);
1079 [[nodiscard]]
FiberTask* stealReadyUnlocked(
FiberWorker& worker, Span<FiberWorker> stealWorkers);
1082 [[nodiscard]]
bool preparePreferredWorkerReadyPublishUnlocked(
FiberTask& task,
FiberWorker& worker,
1084 void publishSuspensionUnlocked(
FiberTask& task);
1085 void finishCurrentTask(
FiberTask& task, Result result);
1086 Result cancelTaskUnlocked(
FiberTask& task);
1087 void linkActiveUnlocked(
FiberTask& task);
1088 void unlinkActiveUnlocked(
FiberTask& task);
1090 Result waitImpl(
FiberCounter& counter,
bool interruptible);
1092 void wakeCounterWaitersUnlocked(
FiberCounter& counter);
1094 static void taskEntry(
void* userData);
1097#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
Explicit allocator for future fiber scheduler storage.
Definition Fibers.h:225
Auto-reset event that wakes one waiting fiber per signal.
Definition Fibers.h:879
Caller-owned cancellation source shared by one or more fiber tasks.
Definition Fibers.h:535
Lightweight cancellation token copied into spawned fiber tasks.
Definition Fibers.h:557
Counter used to suspend fibers until a group of operations completes.
Definition Fibers.h:712
Manual-reset event that wakes waiting fibers when signaled.
Definition Fibers.h:847
Cooperative mutex for fibers running on one FiberScheduler.
Definition Fibers.h:944
Cooperative fiber scheduler with explicit workers and caller-owned storage.
Definition Fibers.h:978
Counting semaphore for cooperative fibers.
Definition Fibers.h:912
Virtual-memory-backed fixed-size stack slots with caller-controlled capacity.
Definition Fibers.h:157
Caller-owned stack storage used by fiber contexts.
Definition Fibers.h:82
Allocator-backed fixed-capacity storage for reusable FiberTask objects.
Definition Fibers.h:681
One failed task collected from a FiberTaskGroup.
Definition Fibers.h:731
Convenience helper for spawning a group of child tasks and waiting on their completion.
Definition Fibers.h:738
Caller-owned pool that pairs FiberTask objects with fixed-size stack slots.
Definition Fibers.h:775
Optional scheduling inputs used when the short spawn overloads are not expressive enough.
Definition Fibers.h:576
Caller-owned task object scheduled by FiberScheduler.
Definition Fibers.h:598
Options for reserving a virtual-memory-backed fiber stack.
Definition Fibers.h:118
Virtual-memory-backed stack storage with an optional no-access guard page below the stack.
Definition Fibers.h:279
No-allocation OS-thread-owning worker pool using caller-provided worker and thread storage.
Definition Fibers.h:483
Caller-owned OS thread storage used by FiberWorkerPool.
Definition Fibers.h:384
Caller-owned execution agent for running ready fibers on the current OS thread.
Definition Fibers.h:324