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;
40struct FiberJobClassDiagnostics;
41struct FiberJobClassInternal;
42struct FiberJobClassOptions;
43struct FiberJobContext;
46struct FiberJobScheduler;
48struct FiberJobWorkerPool;
49struct FiberJobWorkerPoolThreadEntry;
50struct FiberJobWorkerThread;
56struct FiberTaskClassDiagnostics;
57struct FiberTaskClassInternal;
58struct FiberTaskClassOptions;
60struct FiberTaskPoolDiagnostics;
62struct FiberVirtualStack;
63struct FiberVirtualStackInternal;
64struct FiberVirtualStackOptions;
65struct FiberStackClass;
66struct FiberStackClassDiagnostics;
67struct FiberStackClassInternal;
68struct FiberStackClassOptions;
69struct FiberStackGrowthRuntime;
70struct FiberStackGrowthRuntimeInternal;
71struct FiberStackGrowthThread;
72struct FiberStackGrowthThreadInternal;
73struct FiberSchedulerDiagnostics;
74struct FiberTraceEvent;
75struct FiberTraceHooks;
77struct FiberAllocatorInterface;
78struct FiberAllocatorVirtualOptions;
80struct FiberWorkerPoolWakeEvent;
81struct FiberWorkerPool;
82struct FiberWorkerPoolOptions;
83struct FiberWorkerPoolThreadEntry;
84struct FiberWorkerThread;
86#if SC_PLATFORM_WINDOWS
87static constexpr int FiberContextStorageSize = 2048;
88#elif SC_PLATFORM_ARM64
89static constexpr int FiberContextStorageSize = 224;
91static constexpr int FiberContextStorageSize = 128;
93static constexpr int FiberContextStorageAlignment = 16;
94static constexpr int FiberStackAlignment = 16;
95static constexpr int FiberStackMinimumSize = 4096;
97static constexpr int FiberStackGrowthSignalStackSize = 128 * 1024;
99static constexpr int FiberInjectionSlotStorageSize =
sizeof(
void*) +
sizeof(
size_t);
105 static constexpr size_t FourKiB = 4 * 1024;
106 static constexpr size_t EightKiB = 8 * 1024;
107 static constexpr size_t ThirtyTwoKiB = 32 * 1024;
108 static constexpr size_t SixtyFourKiB = 64 * 1024;
113 static constexpr int Windows = 64;
114 static constexpr int Apple = 128;
115 static constexpr int Linux = 384;
116 static constexpr int Default = Linux;
117 static constexpr size_t Alignment =
alignof(
void*);
119 using Object = FiberStackGrowthRuntimeInternal;
121using FiberStackGrowthRuntimeOpaque = OpaqueObject<FiberStackGrowthRuntimeDefinition>;
132 [[nodiscard]] Result create();
133 [[nodiscard]] Result close();
135 [[nodiscard]]
static bool isSupported();
136 [[nodiscard]]
bool isOpen()
const;
137 [[nodiscard]]
size_t registeredThreadCount()
const;
141 friend struct FiberStackGrowthThreadInternal;
143 FiberStackGrowthRuntimeOpaque internal;
148 static constexpr int Windows = 64;
149 static constexpr int Apple = 128;
150 static constexpr int Linux = 128;
151 static constexpr int Default = Linux;
152 static constexpr size_t Alignment =
alignof(
void*);
154 using Object = FiberStackGrowthThreadInternal;
156using FiberStackGrowthThreadOpaque = OpaqueObject<FiberStackGrowthThreadDefinition>;
169 [[nodiscard]] Result close();
171 [[nodiscard]]
bool isOpen()
const;
174 FiberStackGrowthThreadOpaque internal;
182 [[nodiscard]] Span<char> memory()
const;
183 [[nodiscard]]
size_t sizeInBytes()
const;
184 [[nodiscard]]
size_t usableSizeInBytes()
const;
185 [[nodiscard]]
size_t alignmentWasteInBytes()
const;
186 [[nodiscard]]
bool isUsable()
const;
187 void fillHighWaterMark();
188 [[nodiscard]]
size_t highWaterUsedBytes()
const;
189 [[nodiscard]]
size_t highWaterUnusedBytes()
const;
197 Span<char> stackMemory;
198 void* stackOwner =
nullptr;
203 static constexpr int Windows = 64;
204 static constexpr int Apple = 64;
205 static constexpr int Linux = 64;
206 static constexpr int Default = 64;
207 static constexpr size_t Alignment =
alignof(
void*);
209 using Object = FiberVirtualStackInternal;
211using FiberVirtualStackOpaque = OpaqueObject<FiberVirtualStackDefinition>;
216 size_t usableSizeInBytes = FiberStackSize::SixtyFourKiB;
217 bool guardPage =
true;
222 static constexpr int Windows = 224;
223 static constexpr int Apple = 224;
224 static constexpr int Linux = 224;
225 static constexpr int Default = 224;
226 static constexpr size_t Alignment =
alignof(
void*);
228 using Object = FiberStackClassInternal;
230using FiberStackClassOpaque = OpaqueObject<FiberStackClassDefinition>;
232enum class FiberStackCommitMode : uint8_t
240 size_t stackSizeInBytes = FiberStackSize::SixtyFourKiB;
241 size_t maxStacks = 0;
242 bool guardPage =
true;
244 FiberStackCommitMode commitMode = FiberStackCommitMode::Full;
245 size_t initialCommitSizeInBytes = 0;
247 size_t growthCommitSizeInBytes = 0;
253 size_t activeStacks = 0;
254 size_t peakActiveStacks = 0;
255 size_t stackSizeInBytes = 0;
256 size_t guardSizeInBytes = 0;
257 size_t reservedSizeBytes = 0;
258 size_t committedSizeBytes = 0;
259 size_t peakCommittedBytes = 0;
260 size_t highWaterUsedBytes = 0;
261 size_t initialCommitSizeInBytes = 0;
262 size_t growthCommitSizeInBytes = 0;
264 FiberStackCommitMode commitMode = FiberStackCommitMode::Full;
279 [[nodiscard]] Result acquire(
FiberStack& outStack);
280 [[nodiscard]] Result release(
FiberStack& stack);
281 [[nodiscard]] Result waitForAvailableSlot(
FiberScheduler& scheduler);
283 void fillHighWaterMarks();
286 [[nodiscard]]
bool isReserved()
const;
287 [[nodiscard]]
bool owns(
const FiberStack& stack)
const;
288 [[nodiscard]]
size_t capacity()
const;
289 [[nodiscard]]
size_t activeCount()
const;
295 FiberStackClassOpaque internal;
298enum class FiberAllocatorMode : uint8_t
309 size_t numAllocations = 0;
310 size_t numReleases = 0;
312 size_t requestedBytesAllocated = 0;
313 size_t requestedBytesReleased = 0;
315 size_t bytesInUse = 0;
316 size_t peakBytesInUse = 0;
318 size_t numAllocationFailures = 0;
319 size_t lastFailedAllocationSize = 0;
320 size_t largestFailedAllocationSize = 0;
325 size_t reserveBytes = 0;
326 size_t initialCommitBytes = 0;
331 virtual void* allocateImpl(
const void* owner,
size_t numBytes,
size_t alignment) = 0;
332 virtual void releaseImpl(
void* memory) = 0;
346 [[nodiscard]] Result createFixed(Span<char> storage);
348 [[nodiscard]] Result createMalloc();
350 [[nodiscard]] Result validateClose()
const;
351 [[nodiscard]] Result close();
353 [[nodiscard]]
void* allocate(
const void* owner,
size_t numBytes,
size_t alignment);
354 void release(
void* memory);
355 static void releaseFromAnyAllocator(
void* memory);
357 [[nodiscard]] FiberAllocatorMode mode()
const;
359 [[nodiscard]]
bool isOpen()
const;
361 [[nodiscard]]
size_t used()
const;
362 [[nodiscard]]
size_t capacity()
const;
363 [[nodiscard]]
size_t peakUsed()
const;
364 [[nodiscard]]
size_t failedAllocationSize()
const;
365 [[nodiscard]]
size_t reservedBytes()
const;
366 [[nodiscard]]
size_t committedBytes()
const;
371 Result initializeFixedStorage(Span<char> storage);
372 void* allocateFromBlocks(
const void* owner,
size_t numBytes,
size_t alignment);
373 void releaseBlock(BlockHeader& header);
374 bool ensureCommitted(
size_t sizeInBytes);
375 void releaseVirtualMemory();
376 void recordAllocationFailure(
size_t numBytes);
379 FiberAllocatorMode currentMode = FiberAllocatorMode::None;
381 Span<char> fixedStorage;
382 BlockHeader* firstBlock =
nullptr;
385 void* virtualMemory =
nullptr;
386 size_t virtualReservedBytes = 0;
387 size_t virtualCommittedBytes = 0;
405 [[nodiscard]] Span<char> memory()
const;
406 [[nodiscard]]
size_t usableSizeInBytes()
const;
407 [[nodiscard]]
size_t reservedSizeInBytes()
const;
408 [[nodiscard]]
size_t guardSizeInBytes()
const;
409 [[nodiscard]]
bool isReserved()
const;
412 FiberVirtualStackOpaque internal;
416enum class FiberTaskStatus
426enum class FiberTaskSuspendAction
433struct FiberCancellationToken;
444 [[nodiscard]]
bool isActive()
const;
448 [[nodiscard]]
const FiberTask* runningTask()
const;
465 size_t localReadyFibers = 0;
466 size_t localDequeCapacity = 0;
467 size_t localDequeHead = 0;
468 volatile size_t localDequeTop = 0;
469 volatile size_t localDequeBottom = 0;
470 size_t localReadyPeakFibers = 0;
471 size_t localSpilledFibers = 0;
472 size_t stealAttempts = 0;
473 size_t stealVictimProbes = 0;
474 size_t stolenFibers = 0;
475 size_t stolenBatches = 0;
476 size_t stolenBatchPeak = 0;
477 size_t failedSteals = 0;
478 size_t stealCursor = 0;
479 volatile size_t runAttempts = 0;
480 volatile size_t idlePolls = 0;
481 volatile size_t idleSpinIterations = 0;
482 volatile size_t parkAttempts = 0;
483 volatile size_t parkedWakeups = 0;
484 volatile size_t executedFibers = 0;
485 size_t completedFibers = 0;
486 volatile size_t yieldedFibers = 0;
487 size_t waitingFibers = 0;
488 volatile int32_t activeRegistryLock = 0;
489 bool workerActive =
false;
490 bool localSchedulingActive =
false;
491 bool stealCursorInitialized =
false;
493 AlignedStorage<FiberContextStorageSize, FiberContextStorageAlignment> rootContextStorage;
495 [[nodiscard]] FiberContext& rootContext();
498#if SC_PLATFORM_WINDOWS
499static constexpr int FiberWorkerThreadStorageSize =
sizeof(
void*);
501static constexpr int FiberWorkerThreadStorageSize =
sizeof(
void*) * 2;
503static constexpr int FiberWorkerThreadStorageAlignment =
alignof(
void*);
514 [[nodiscard]]
bool wasStarted()
const;
515 [[nodiscard]] Result result()
const;
519 friend struct FiberWorkerPoolThreadEntry;
521 AlignedStorage<FiberWorkerThreadStorageSize, FiberWorkerThreadStorageAlignment> threadStorage;
524 size_t workerIndex = 0;
525 uint64_t affinityMask = 0;
526 uint8_t priority = 0;
527 Result threadResult = Result(
true);
528 bool started =
false;
532 Span<char> stackGrowthSignalStackStorage;
534 Result startThread();
536 Result runThreadEntry();
537 Result applyThreadPolicy();
540enum class FiberWorkerThreadPriority : uint8_t
551 size_t dequeCapacityPerWorker = 0;
553 size_t injectionCapacity = 0;
554 size_t idleSpinAttempts = 32;
555 Span<const uint64_t> affinityMasks;
556 FiberWorkerThreadPriority threadPriority = FiberWorkerThreadPriority::Default;
565 size_t readyFibers = 0;
566 size_t readyPeakFibers = 0;
567 size_t dequeCapacity = 0;
568 size_t spilledFibers = 0;
569 size_t stealAttempts = 0;
570 size_t stealVictimProbes = 0;
571 size_t stolenFibers = 0;
572 size_t stolenBatches = 0;
573 size_t stolenBatchPeak = 0;
574 size_t failedSteals = 0;
575 size_t runAttempts = 0;
576 size_t idlePolls = 0;
577 size_t idleSpinIterations = 0;
578 size_t parkAttempts = 0;
579 size_t parkedWakeups = 0;
580 size_t executedFibers = 0;
581 size_t completedFibers = 0;
582 size_t yieldedFibers = 0;
583 size_t waitingFibers = 0;
589 size_t wakeNotifications = 0;
591 size_t wakeSignals = 0;
596 size_t readyFibers = 0;
597 size_t globalReadyFibers = 0;
598 size_t activeFibers = 0;
599 size_t injectionCapacity = 0;
600 size_t injectionReady = 0;
601 size_t injectionPublishing = 0;
602 size_t injectionPeak = 0;
603 size_t injectionSpills = 0;
604 size_t injectionClaimBatchPeak = 0;
605 size_t injectionLockAcquisitions = 0;
606 size_t injectionLockContentions = 0;
607 size_t injectionLockSpinRetries = 0;
608 size_t injectionLockPeakSpinRetries = 0;
610 size_t lockAcquisitions = 0;
611 size_t lockContentions = 0;
612 size_t lockSpinRetries = 0;
613 size_t lockPeakSpinRetries = 0;
614 size_t lockSpawn = 0;
615 size_t lockReady = 0;
616 size_t lockSynchronization = 0;
617 size_t lockCompletion = 0;
618 size_t lockControl = 0;
621enum class FiberTraceEventType : uint8_t
631 FiberTraceEventType type = FiberTraceEventType::TaskStarted;
640 using Callback = void (*)(
void* userData,
const FiberTraceEvent& event);
642 Callback callback =
nullptr;
643 void* userData =
nullptr;
655 Result start(
FiberScheduler& scheduler, Span<FiberWorker> workerStorage, Span<FiberWorkerThread> threadStorage);
656 Result start(
FiberScheduler& scheduler, Span<FiberWorker> workerStorage, Span<FiberWorkerThread> threadStorage,
658 Result requestStop();
662 [[nodiscard]]
bool isRunning()
const;
663 [[nodiscard]]
size_t workerCount()
const;
671 static constexpr int Windows =
sizeof(
void*) * 16;
672 static constexpr int Apple =
sizeof(
void*) * 16;
673 static constexpr int Linux =
sizeof(
void*) * 16;
674 static constexpr int Default = Linux;
676 static constexpr size_t Alignment =
alignof(
void*);
678 using Object = FiberWorkerPoolWakeEvent;
681 using WakeEventOpaque = OpaqueObject<WakeEventDefinition>;
688 Span<FiberWorker> workers;
689 Span<FiberWorkerThread> threads;
690 WakeEventOpaque wakeEvent;
691 mutable volatile int32_t stopRequested = 0;
692 mutable volatile int32_t running = 0;
693 size_t idleSpinAttempts = 0;
694 bool localDequesCreated =
false;
695 bool injectionCreated =
false;
697 void wakeOneWorker();
698 void wakeAllWorkers();
699 void resetWakeDiagnostics();
700 [[nodiscard]]
bool waitForWork(uint32_t observedGeneration);
701 [[nodiscard]] uint32_t wakeGeneration()
const;
703 Result workerMain(
size_t workerIndex);
714 void requestCancel();
716 Result check()
const;
718 [[nodiscard]]
bool isCancellationRequested()
const;
726 mutable volatile int32_t requested = 0;
734 Result check()
const;
736 [[nodiscard]]
bool isValid()
const;
737 [[nodiscard]]
bool isCancellationRequested()
const;
750enum class FiberJobStatus
762 [[nodiscard]]
FiberJob& job()
const;
763 [[nodiscard]]
bool isCancellationRequested()
const;
764 Result checkCancellation()
const;
788 [[nodiscard]]
bool isActive()
const;
789 [[nodiscard]]
bool isCompleted()
const;
790 [[nodiscard]]
bool isCancellationRequested()
const;
791 [[nodiscard]] FiberJobStatus status()
const;
792 [[nodiscard]] Result result()
const;
808 Result jobResult = Result(
true);
809 volatile int32_t jobStatus =
static_cast<int32_t
>(FiberJobStatus::Invalid);
810 uint32_t cancelGeneration = 0;
811 volatile bool cancelRequested =
false;
812 bool poolRetained =
false;
817 size_t readyJobs = 0;
818 size_t readyPeakJobs = 0;
819 size_t dequeCapacity = 0;
820 size_t executedJobs = 0;
821 size_t claimBatches = 0;
822 size_t claimedJobs = 0;
823 size_t claimBatchPeak = 0;
824 size_t stealAttempts = 0;
825 size_t stolenJobs = 0;
826 size_t failedSteals = 0;
829#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
831#pragma warning(disable : 4324)
842 [[nodiscard]]
bool isActive()
const;
845 [[nodiscard]]
FiberJob* runningJob();
846 [[nodiscard]]
const FiberJob* runningJob()
const;
860 size_t localDequeCapacity = 0;
861 size_t lastDequeCapacity = 0;
862 volatile size_t localDequeTop = 0;
863 volatile size_t localDequeBottom = 0;
865 size_t localReadyPeakJobs = 0;
866 size_t executedJobs = 0;
867 size_t claimBatches = 0;
868 size_t claimedJobs = 0;
869 size_t claimBatchPeak = 0;
870 size_t stealCursor = 0;
871 size_t stealAttempts = 0;
872 size_t stolenJobs = 0;
873 size_t failedSteals = 0;
874 bool workerActive =
false;
875 bool distributedCounts =
false;
877 alignas(128)
volatile size_t ownedReadyJobs = 0;
878 alignas(128)
volatile size_t ownedActiveJobs = 0;
880#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
888#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
890#pragma warning(disable : 4324)
900 Result create(Span<FiberJob*> readyStorage);
903 Result createWorkerDeques(
FiberAllocator& allocator, Span<FiberJobWorker> workers,
size_t capacityPerWorker);
904 void releaseWorkerDeques(Span<FiberJobWorker> workers);
906 Result spawn(
FiberJob& job, FiberJob::Procedure procedure);
909 Result
spawn(Span<FiberJob> jobs, FiberJob::Procedure procedure);
911 Result runOne(
bool& outRanJob);
912 Result runOne(
FiberJobWorker& worker, Span<FiberJobWorker> workerGroup,
bool& outRanJob);
914 Result run(
FiberJobWorker& worker, Span<FiberJobWorker> workerGroup);
917 Result requestCancel(
FiberJob& job);
919 Result requestCancelAll();
921 [[nodiscard]]
bool isOpen()
const;
926 [[nodiscard]]
size_t capacity()
const;
931 [[nodiscard]]
FiberJob* currentJob();
942 Span<FiberJob*> queueStorage;
944 size_t queueHead = 0;
945 size_t queueTail = 0;
946 size_t queueCount = 0;
948 alignas(128)
volatile size_t readyJobs = 0;
949 alignas(128)
volatile size_t activeJobs = 0;
951 alignas(128)
mutable volatile int32_t queueLock = 0;
952 volatile uint32_t cancelGeneration = 0;
956 struct QueueLockGuard;
959 Result complete(
FiberJob& job, Result result);
964 bool usesDistributedAccounting(
const FiberJobWorker& worker)
const;
965 bool isJobCancellationRequested(
const FiberJob& job)
const;
966 bool isWorkerStopRequested()
const;
968#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
981 [[nodiscard]]
bool wasStarted()
const;
982 [[nodiscard]] Result result()
const;
986 friend struct FiberJobWorkerPoolThreadEntry;
988 AlignedStorage<FiberWorkerThreadStorageSize, FiberWorkerThreadStorageAlignment> threadStorage;
991 size_t workerIndex = 0;
992 uint64_t affinityMask = 0;
993 uint8_t priority = 0;
994 Result threadResult = Result(
true);
995 bool started =
false;
997 Result startThread();
999 Result runThreadEntry();
1000 Result applyThreadPolicy();
1006 size_t dequeCapacityPerWorker = 0;
1007 size_t idleSpinAttempts = 32;
1010 bool keepAliveWhenIdle =
false;
1012 Span<const uint64_t> affinityMasks;
1013 FiberWorkerThreadPriority threadPriority = FiberWorkerThreadPriority::Default;
1035 [[nodiscard]]
bool isRunning()
const;
1036 [[nodiscard]]
size_t workerCount()
const;
1037 [[nodiscard]]
size_t parkedWorkerCount()
const;
1041 static constexpr int Windows =
sizeof(
void*) * 16;
1042 static constexpr int Apple =
sizeof(
void*) * 16;
1043 static constexpr int Linux =
sizeof(
void*) * 16;
1044 static constexpr int Default = Linux;
1046 static constexpr size_t Alignment =
alignof(
void*);
1048 using Object = FiberWorkerPoolWakeEvent;
1051 using WakeEventOpaque = OpaqueObject<WakeEventDefinition>;
1058 Span<FiberJobWorker> workers;
1059 Span<FiberJobWorkerThread> threads;
1060 WakeEventOpaque wakeEvent;
1061 mutable volatile int32_t stopRequested = 0;
1062 mutable volatile int32_t running = 0;
1063 size_t idleSpinAttempts = 0;
1064 bool keepAliveWhenIdle =
false;
1065 volatile uint32_t preparingToWait = 0;
1067 void wakeOneWorker();
1068 void wakeAllWorkers();
1069 void notifyPreparingWorkers();
1070 [[nodiscard]]
bool waitForWork(uint32_t observedGeneration);
1071 [[nodiscard]]
bool waitForPreparedWork(uint32_t observedGeneration);
1072 [[nodiscard]] uint32_t prepareToWaitForWork();
1073 void cancelWaitForWork();
1074 [[nodiscard]] uint32_t wakeGeneration()
const;
1075 [[nodiscard]]
bool isStopRequested()
const;
1077 Result workerMain(
size_t workerIndex);
1082 size_t capacity = 0;
1083 size_t retainedJobs = 0;
1084 size_t availableJobs = 0;
1086 size_t peakRetainedJobs = 0;
1091 static constexpr int Windows = 48;
1092 static constexpr int Apple = 48;
1093 static constexpr int Linux = 48;
1094 static constexpr int Default = 48;
1095 static constexpr size_t Alignment =
alignof(
void*);
1097 using Object = FiberJobClassInternal;
1099using FiberJobClassOpaque = OpaqueObject<FiberJobClassDefinition>;
1108 size_t capacity = 0;
1124 [[nodiscard]] Result validateClose()
const;
1125 [[nodiscard]] Result close();
1128 [[nodiscard]]
bool isOpen()
const;
1129 [[nodiscard]]
bool owns(
const FiberJob& job)
const;
1130 [[nodiscard]]
size_t capacity()
const;
1135 FiberJobClassOpaque internal;
1147 Result create(Span<FiberJob> jobStorage);
1155 [[nodiscard]]
bool isOpen()
const;
1156 [[nodiscard]]
bool owns(
const FiberJob& job)
const;
1157 [[nodiscard]]
size_t capacity()
const;
1158 [[nodiscard]]
size_t retainedCount()
const;
1159 [[nodiscard]]
size_t availableCount()
const;
1165 Span<FiberJob> jobs;
1168 size_t retainedJobs = 0;
1169 size_t peakRetainedJobs = 0;
1171 mutable volatile int32_t poolLock = 0;
1174 void releaseAcquired(
FiberJob& job);
1175 void releaseAcquiredUnlocked(
FiberJob& job);
1176 void recordPublished();
1182 Result result = Result(
true);
1198 Result requestCancel();
1201 [[nodiscard]]
size_t pendingCount()
const;
1202 [[nodiscard]]
size_t jobCount()
const;
1203 [[nodiscard]]
size_t countErrors()
const;
1204 Result collectErrors(Span<FiberJobGroupError> errors,
size_t& outErrors)
const;
1211 size_t pendingJobs = 0;
1212 size_t totalJobs = 0;
1214 mutable volatile int32_t groupLock = 0;
1216 Result prepareSpawn()
const;
1218 void unlinkSpawnFailure(
FiberJob& job);
1227 void* userData =
nullptr;
1228 bool setUserData =
false;
1241#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
1242#pragma warning(push)
1243#pragma warning(disable : 4324)
1259 [[nodiscard]]
bool isValid()
const;
1260 [[nodiscard]]
bool isStarted()
const;
1261 [[nodiscard]]
bool isCompleted()
const;
1262 [[nodiscard]]
bool isActive()
const;
1263 [[nodiscard]]
bool isCancellationRequested()
const;
1264 [[nodiscard]] FiberTaskStatus status()
const;
1265 [[nodiscard]] Result result()
const;
1266 void setUserData(
void* data);
1267 [[nodiscard]]
void* userData()
const;
1271 friend struct FiberTaskClassInternal;
1275 AlignedStorage<FiberContextStorageSize, FiberContextStorageAlignment> contextStorage;
1277 Procedure procedure;
1293 Span<char> originStackMemory;
1296 void* runningWorker =
nullptr;
1297 void* stackOwner =
nullptr;
1298 void* taskUserData =
nullptr;
1299 Result taskResult = Result(
true);
1300 volatile int32_t taskStatus =
static_cast<int32_t
>(FiberTaskStatus::Invalid);
1301 FiberTaskSuspendAction suspendAction = FiberTaskSuspendAction::None;
1302 volatile bool cancelRequested =
false;
1303 bool suspendInterruptible =
false;
1305 [[nodiscard]] FiberContext& context();
1306 [[nodiscard]]
const FiberContext& context()
const;
1311 static constexpr int Windows = 96;
1312 static constexpr int Apple = 96;
1313 static constexpr int Linux = 96;
1314 static constexpr int Default = 96;
1315 static constexpr size_t Alignment =
alignof(
void*);
1317 using Object = FiberTaskClassInternal;
1319using FiberTaskClassOpaque = OpaqueObject<FiberTaskClassDefinition>;
1323 size_t maxTasks = 0;
1328 size_t capacity = 0;
1329 size_t activeTasks = 0;
1330 size_t availableTasks = 0;
1331 size_t peakActiveTasks = 0;
1346 [[nodiscard]] Result acquire(
FiberTask*& outTask);
1347 [[nodiscard]] Result release(
FiberTask& task);
1348 [[nodiscard]] Result waitForAvailableSlot(
FiberScheduler& scheduler);
1349 [[nodiscard]] Result validateClose()
const;
1350 [[nodiscard]] Result close();
1353 [[nodiscard]]
bool isOpen()
const;
1354 [[nodiscard]]
bool owns(
const FiberTask& task)
const;
1355 [[nodiscard]]
size_t capacity()
const;
1356 [[nodiscard]]
size_t activeCount()
const;
1357 [[nodiscard]]
size_t availableCount()
const;
1362 FiberTaskClassOpaque internal;
1374 [[nodiscard]]
size_t value()
const;
1379 volatile size_t counterValue = 0;
1389 Result result = Result(
true);
1411 Result waitAll(Result* outFirstError =
nullptr);
1412 Result waitAllCancelOnParentCancel(Result* outFirstError =
nullptr);
1413 Result waitCancelOnError(Result* outFirstError =
nullptr);
1418 [[nodiscard]]
size_t pending()
const;
1419 [[nodiscard]]
size_t countErrors()
const;
1420 Result collectErrors(Span<FiberTaskGroupError> errors,
size_t& outErrors)
const;
1429 Result prepareSpawn()
const;
1431 Result findFirstError(Result* outFirstError)
const;
1437 size_t capacity = 0;
1438 size_t activeTasks = 0;
1439 size_t availableTasks = 0;
1440 bool classBacked =
false;
1449 FiberTaskPool(Span<FiberTask> taskStorage, Span<char> stackStorage,
size_t stackSize);
1465 [[nodiscard]]
size_t capacity()
const;
1466 [[nodiscard]]
size_t activeCount()
const;
1467 [[nodiscard]]
size_t availableCount()
const;
1468 [[nodiscard]]
bool hasAvailableTask()
const;
1474 [[nodiscard]]
size_t stackSizeInBytes()
const;
1475 void fillHighWaterMarks();
1476 Result stackHighWaterUsedBytes(
size_t stackIndex,
size_t& outBytes)
const;
1477 Result stackHighWaterUnusedBytes(
size_t stackIndex,
size_t& outBytes)
const;
1486 WaitNode* next =
nullptr;
1488 size_t minimumAvailable = 1;
1489 bool notified =
false;
1492 Span<FiberTask> tasks;
1494 size_t stackSize = 0;
1495 size_t nextTask = 0;
1500 WaitNode* availabilityWaitHead =
nullptr;
1501 WaitNode* availabilityWaitTail =
nullptr;
1503 mutable volatile int32_t primitiveLock = 0;
1505 Result stackAt(
size_t stackIndex,
FiberStack& outStack)
const;
1506 void queueAvailabilityWaiter(WaitNode& node);
1508 bool removeAvailabilityWaiter(WaitNode& node);
1525 [[nodiscard]]
bool isSignaled()
const;
1531 WaitNode* next =
nullptr;
1532 bool notified =
false;
1535 WaitNode* waitHead =
nullptr;
1536 WaitNode* waitTail =
nullptr;
1537 bool signaled =
false;
1538 mutable volatile int32_t primitiveLock = 0;
1540 void queueWaiter(WaitNode& node);
1541 bool removeWaiter(WaitNode& node);
1558 [[nodiscard]]
bool isSignaled()
const;
1564 WaitNode* next =
nullptr;
1565 bool notified =
false;
1568 WaitNode* waitHead =
nullptr;
1569 WaitNode* waitTail =
nullptr;
1570 bool signaled =
false;
1571 mutable volatile int32_t primitiveLock = 0;
1573 void queueWaiter(WaitNode& node);
1574 bool popWaiter(WaitNode*& node);
1575 bool removeWaiter(WaitNode& node);
1591 [[nodiscard]]
size_t available()
const;
1597 WaitNode* next =
nullptr;
1598 bool notified =
false;
1601 WaitNode* waitHead =
nullptr;
1602 WaitNode* waitTail =
nullptr;
1603 size_t availableCount = 0;
1604 mutable volatile int32_t primitiveLock = 0;
1606 void queueWaiter(WaitNode& node);
1607 WaitNode* popWaiter();
1608 bool removeWaiter(WaitNode& node);
1623 [[nodiscard]]
bool isLocked()
const;
1624 [[nodiscard]]
bool isOwnedByCurrentTask(
FiberScheduler& scheduler)
const;
1630 WaitNode* next =
nullptr;
1632 bool notified =
false;
1635 WaitNode* waitHead =
nullptr;
1636 WaitNode* waitTail =
nullptr;
1637 bool locked =
false;
1639 mutable volatile int32_t primitiveLock = 0;
1641 void queueWaiter(WaitNode& node);
1642 WaitNode* popWaiter();
1643 bool removeWaiter(WaitNode& node);
1663 Result runOnce(
FiberWorker& worker, Span<FiberWorker> workerGroup);
1666 Result runNoWait(
FiberWorker& worker, Span<FiberWorker> workerGroup);
1667 Result runReadyFibers();
1669 Result runReadyFibers(
FiberWorker& worker, Span<FiberWorker> workerGroup);
1672 Result run(
FiberWorker& worker, Span<FiberWorker> workerGroup);
1673 Result createWorkerDeques(
FiberAllocator& allocator, Span<FiberWorker> workers,
size_t capacityPerWorker);
1674 void releaseWorkerDeques(Span<FiberWorker> workers);
1678 Result shutdown(
FiberWorker& worker, Span<FiberWorker> workerGroup);
1682 Result requestCancelAll();
1690 void clearTraceHooks();
1693 [[nodiscard]]
const FiberTask* currentTask()
const;
1694 [[nodiscard]]
bool isCurrentTaskCancellationRequested()
const;
1696 [[nodiscard]]
bool hasReadyFibers()
const;
1697 [[nodiscard]]
bool hasActiveFibers()
const;
1698 [[nodiscard]]
size_t readyFiberCount()
const;
1699 [[nodiscard]]
size_t readyFiberCount(
const FiberWorker& worker)
const;
1700 [[nodiscard]]
size_t stolenFiberCount(
const FiberWorker& worker)
const;
1701 [[nodiscard]]
size_t stolenFiberCount(Span<FiberWorker> workers)
const;
1702 [[nodiscard]]
size_t activeFiberCount()
const;
1704 void resetSchedulerDiagnostics();
1709 void resetWorkerDiagnostics(Span<FiberWorker> workers);
1714 struct InjectionSlot;
1720 InjectionSlot* injectionQueue =
nullptr;
1722 size_t injectionCapacity = 0;
1723 volatile size_t injectionHead = 0;
1724 volatile size_t injectionTail = 0;
1725 volatile size_t injectionReady = 0;
1726 volatile size_t injectionPublishing = 0;
1727 volatile size_t injectionPeak = 0;
1728 size_t injectionSpills = 0;
1729 volatile size_t injectionClaimBatchPeak = 0;
1731 mutable volatile int32_t injectionLock = 0;
1733 mutable size_t injectionLockAcquisitions = 0;
1734 mutable size_t injectionLockContentions = 0;
1735 mutable size_t injectionLockSpinRetries = 0;
1736 mutable size_t injectionLockPeakSpinRetries = 0;
1740 volatile size_t readyFibers = 0;
1741 volatile size_t globalReadyFibers = 0;
1742 volatile size_t activeFibers = 0;
1744 mutable volatile int32_t schedulerLock = 0;
1746 mutable size_t schedulerLockAcquisitions = 0;
1747 mutable size_t schedulerLockContentions = 0;
1748 mutable size_t schedulerLockSpinRetries = 0;
1749 mutable size_t schedulerLockPeakSpinRetries = 0;
1751 mutable size_t schedulerLockSpawn = 0;
1752 mutable size_t schedulerLockReady = 0;
1753 mutable size_t schedulerLockSynchronization = 0;
1754 mutable size_t schedulerLockCompletion = 0;
1755 mutable size_t schedulerLockControl = 0;
1759 enum class LockCategory : uint8_t
1769 struct InjectionLockGuard;
1771 void lock(LockCategory category)
const;
1772 void unlock()
const;
1773 void lockInjection()
const;
1774 void unlockInjection()
const;
1775 void trace(FiberTraceEventType type,
FiberTask* task,
FiberWorker* worker,
size_t value = 0)
const;
1778 Result initializeTaskForSpawn(
FiberTask& task,
FiberStack& stack, FiberTask::Procedure procedure,
1783 Result createInjectionQueue(
FiberAllocator& allocator,
size_t capacity);
1784 void releaseInjectionQueue();
1785 [[nodiscard]]
bool tryReserveInjection(
size_t& position);
1786 void publishInjection(
size_t position,
FiberTask* task);
1787 void discardInjectionTombstones();
1788 [[nodiscard]]
bool tryPushInjectionUnlocked(
FiberTask& task);
1789 [[nodiscard]]
FiberTask* popInjection();
1790 [[nodiscard]]
size_t popInjectionTasks(Span<FiberTask*> tasks);
1791 void notifyReadyWorkUnlocked();
1792 void pushReadyUnlocked(
FiberTask& task);
1797 [[nodiscard]]
FiberTask* popReadyUnlocked();
1800 [[nodiscard]]
FiberTask* popReadyUnlocked(
FiberWorker& worker, Span<FiberWorker> stealWorkers);
1803 [[nodiscard]]
FiberTask* stealReadyUnlocked(
FiberWorker& worker, Span<FiberWorker> stealWorkers);
1808 void publishSuspensionUnlocked(
FiberTask& task);
1809 void finishCurrentTask(
FiberTask& task, Result result);
1810 Result cancelTaskUnlocked(
FiberTask& task);
1811 void linkActiveUnlocked(
FiberTask& task);
1812 void unlinkActiveUnlocked(
FiberTask& task);
1814 void adoptWorkerReadyUnlocked(
FiberWorker& worker);
1815 void unlinkWorkerActive(
FiberTask& task);
1818 Result waitImpl(
FiberCounter& counter,
bool interruptible);
1820 void wakeCounterWaitersUnlocked(
FiberCounter& counter);
1822 static void taskEntry(
void* userData);
1825#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
Explicit allocator for future fiber scheduler storage.
Definition Fibers.h:338
Auto-reset event that wakes one waiting fiber per signal.
Definition Fibers.h:1546
Result wait(FiberScheduler &scheduler)
Must be called from a fiber owned by scheduler.
Caller-owned cancellation source shared by one or more fiber tasks.
Definition Fibers.h:708
Lightweight cancellation token copied into spawned fiber tasks.
Definition Fibers.h:731
Counter used to suspend fibers until a group of operations completes.
Definition Fibers.h:1367
Manual-reset event that wakes waiting fibers when signaled.
Definition Fibers.h:1513
Result wait(FiberScheduler &scheduler)
Must be called from a fiber owned by scheduler.
Allocator-backed fixed-capacity storage for stable FiberJob records.
Definition Fibers.h:1114
Restricted execution context passed to a run-to-completion FiberJob.
Definition Fibers.h:760
Retains one bounded wave of pooled FiberJob results for aggregate inspection.
Definition Fibers.h:1187
Fixed-capacity owner of reusable FiberJob records with explicit completed-result release.
Definition Fibers.h:1140
Fixed-capacity scheduler for stackless run-to-completion jobs.
Definition Fibers.h:893
void workerDiagnostics(const FiberJobWorker &worker, FiberJobWorkerDiagnostics &outDiagnostics) const
Diagnostics remain available after a worker pool joins and reset when its deque is configured again.
size_t readyJobCount() const
Returns the exact stable count. A concurrent batch ownership transfer may conservatively overcount.
Result spawn(Span< FiberJob > jobs, FiberJob::Procedure procedure)
Transactionally publishes distinct contiguous records to the current worker or bounded external queue...
size_t activeJobCount() const
Returns the exact stable count. A concurrent batch ownership transfer may conservatively overcount.
bool hasReadyJobs() const
Never reports false while ready work transfers from the external queue to a worker.
bool hasActiveJobs() const
Never reports false while active work transfers from the external queue to a worker.
No-allocation OS-thread-owning pool for bounded stackless FiberJob execution.
Definition Fibers.h:1018
Result requestStop()
Requests scheduler-wide cooperative cancellation and wakes every worker before the pool drains.
Result join()
Persistent pools reject join until requestStop() has established the terminal boundary.
Result waitIdle()
Waits for the scheduler to reach an idle observation point without stopping a persistent pool.
Caller-owned OS thread storage used by FiberJobWorkerPool.
Definition Fibers.h:974
Caller-owned parallel execution agent for the FiberJob runtime.
Definition Fibers.h:835
Caller-owned stackless CPU job. Its address must remain stable while active.
Definition Fibers.h:777
Cooperative mutex for fibers running on one FiberScheduler.
Definition Fibers.h:1613
Cooperative fiber scheduler with explicit workers and caller-owned storage.
Definition Fibers.h:1648
Counting semaphore for cooperative fibers.
Definition Fibers.h:1580
Result wait(FiberScheduler &scheduler)
Must be called from a fiber owned by scheduler.
Virtual-memory-backed fixed-size stack slots with caller-controlled capacity.
Definition Fibers.h:269
Explicit owner of process integration required by incrementally committed fiber stacks.
Definition Fibers.h:125
Thread-affine registration for one thread that may execute incrementally committed fiber stacks.
Definition Fibers.h:160
Result create(FiberStackGrowthRuntime &runtime, Span< char > signalStackStorage)
POSIX requires caller-owned alternate signal-stack storage; Windows accepts an empty span.
Common requested sizes for virtual fiber stack classes.
Definition Fibers.h:104
Caller-owned stack storage used by fiber contexts.
Definition Fibers.h:179
Allocator-backed fixed-capacity storage for reusable FiberTask objects.
Definition Fibers.h:1336
One failed task collected from a FiberTaskGroup.
Definition Fibers.h:1387
Convenience helper for spawning a group of child tasks and waiting on their completion.
Definition Fibers.h:1394
Result reset()
Releases completed task records after result/error inspection. Fails while tasks are pending.
Caller-owned pool that pairs FiberTask objects with fixed-size stack slots.
Definition Fibers.h:1436
Result waitForAvailableTasks(FiberScheduler &scheduler, size_t minimumAvailable)
Suspends until at least minimumAvailable task/stack pairs can be acquired.
Optional scheduling inputs used when the short spawn overloads are not expressive enough.
Definition Fibers.h:1224
Caller-owned task object scheduled by FiberScheduler.
Definition Fibers.h:1248
Options for reserving a virtual-memory-backed fiber stack.
Definition Fibers.h:215
Virtual-memory-backed stack storage with an optional no-access guard page below the stack.
Definition Fibers.h:392
Span< char > stackGrowthSignalStackStorage
POSIX requires at least workerCount * FiberStackGrowthSignalStackSize bytes; Windows ignores this spa...
Definition Fibers.h:560
No-allocation OS-thread-owning worker pool using caller-provided worker and thread storage.
Definition Fibers.h:648
void wakeDiagnostics(FiberWorkerPoolWakeDiagnostics &outDiagnostics) const
Returns wake diagnostics for the current or most recently joined run.
size_t parkedWorkerCount() const
Workers currently blocked in the pool wake wait.
Caller-owned OS thread storage used by FiberWorkerPool.
Definition Fibers.h:507
Caller-owned execution agent for running ready fibers on the current OS thread.
Definition Fibers.h:437