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"
20#include "FibersError.h"
29SC_DECLARE_ASSERT_PROVIDER(FibersAssert, SC_FIBERS_EXPORT);
31#define SC_FIBERS_ASSERT_RELEASE(e) SC_ASSERT_PROVIDER_RELEASE(SC::FibersAssert, e)
32#define SC_FIBERS_ASSERT_DEBUG(e) SC_ASSERT_PROVIDER_DEBUG(SC::FibersAssert, e)
33#define SC_FIBERS_TRUST_RESULT(expression) SC_FIBERS_ASSERT_RELEASE(expression)
36struct FiberAutoResetEvent;
41struct FiberJobClassDiagnostics;
42struct FiberJobClassInternal;
43struct FiberJobClassOptions;
44struct FiberJobContext;
47struct FiberJobScheduler;
49struct FiberJobWorkerPool;
50struct FiberJobWorkerPoolThreadEntry;
51struct FiberJobWorkerThread;
57struct FiberTaskClassDiagnostics;
58struct FiberTaskClassInternal;
59struct FiberTaskClassOptions;
61struct FiberTaskPoolDiagnostics;
63struct FiberVirtualStack;
64struct FiberVirtualStackInternal;
65struct FiberVirtualStackOptions;
66struct FiberStackClass;
67struct FiberStackClassDiagnostics;
68struct FiberStackClassInternal;
69struct FiberStackClassOptions;
70struct FiberStackGrowthRuntime;
71struct FiberStackGrowthRuntimeInternal;
72struct FiberStackGrowthThread;
73struct FiberStackGrowthThreadInternal;
74struct FiberSchedulerDiagnostics;
75struct FiberTraceEvent;
76struct FiberTraceHooks;
78struct FiberAllocatorInterface;
79struct FiberAllocatorVirtualOptions;
81struct FiberWorkerPoolWakeEvent;
82struct FiberWorkerPool;
83struct FiberWorkerPoolOptions;
84struct FiberWorkerPoolThreadEntry;
85struct FiberWorkerThread;
87#if SC_PLATFORM_WINDOWS
88static constexpr int FiberContextStorageSize = 2048;
89#elif SC_PLATFORM_ARM64
90static constexpr int FiberContextStorageSize = 224;
92static constexpr int FiberContextStorageSize = 128;
94static constexpr int FiberContextStorageAlignment = 16;
95static constexpr int FiberStackAlignment = 16;
96static constexpr int FiberStackMinimumSize = 4096;
98static constexpr int FiberStackGrowthSignalStackSize = 128 * 1024;
100static constexpr int FiberInjectionSlotStorageSize =
sizeof(
void*) +
sizeof(
size_t);
106 static constexpr size_t FourKiB = 4 * 1024;
107 static constexpr size_t EightKiB = 8 * 1024;
108 static constexpr size_t ThirtyTwoKiB = 32 * 1024;
109 static constexpr size_t SixtyFourKiB = 64 * 1024;
114 static constexpr int Windows = 64;
115 static constexpr int Apple = 128;
116 static constexpr int Linux = 384;
117 static constexpr int Default = Linux;
118 static constexpr size_t Alignment =
alignof(
void*);
120 using Object = FiberStackGrowthRuntimeInternal;
122using FiberStackGrowthRuntimeOpaque = OpaqueObject<FiberStackGrowthRuntimeDefinition>;
133 [[nodiscard]] Result create();
134 [[nodiscard]] Result close();
136 [[nodiscard]]
static bool isSupported();
137 [[nodiscard]]
bool isOpen()
const;
138 [[nodiscard]]
size_t registeredThreadCount()
const;
142 friend struct FiberStackGrowthThreadInternal;
144 FiberStackGrowthRuntimeOpaque internal;
149 static constexpr int Windows = 64;
150 static constexpr int Apple = 128;
151 static constexpr int Linux = 128;
152 static constexpr int Default = Linux;
153 static constexpr size_t Alignment =
alignof(
void*);
155 using Object = FiberStackGrowthThreadInternal;
157using FiberStackGrowthThreadOpaque = OpaqueObject<FiberStackGrowthThreadDefinition>;
170 [[nodiscard]] Result close();
172 [[nodiscard]]
bool isOpen()
const;
175 FiberStackGrowthThreadOpaque internal;
183 [[nodiscard]] Span<char> memory()
const;
184 [[nodiscard]]
size_t sizeInBytes()
const;
185 [[nodiscard]]
size_t usableSizeInBytes()
const;
186 [[nodiscard]]
size_t alignmentWasteInBytes()
const;
187 [[nodiscard]]
bool isUsable()
const;
188 void fillHighWaterMark();
189 [[nodiscard]]
size_t highWaterUsedBytes()
const;
190 [[nodiscard]]
size_t highWaterUnusedBytes()
const;
198 Span<char> stackMemory;
199 void* stackOwner =
nullptr;
204 static constexpr int Windows = 64;
205 static constexpr int Apple = 64;
206 static constexpr int Linux = 64;
207 static constexpr int Default = 64;
208 static constexpr size_t Alignment =
alignof(
void*);
210 using Object = FiberVirtualStackInternal;
212using FiberVirtualStackOpaque = OpaqueObject<FiberVirtualStackDefinition>;
217 size_t usableSizeInBytes = FiberStackSize::SixtyFourKiB;
218 bool guardPage =
true;
223 static constexpr int Windows = 224;
224 static constexpr int Apple = 224;
225 static constexpr int Linux = 224;
226 static constexpr int Default = 224;
227 static constexpr size_t Alignment =
alignof(
void*);
229 using Object = FiberStackClassInternal;
231using FiberStackClassOpaque = OpaqueObject<FiberStackClassDefinition>;
233enum class FiberStackCommitMode : uint8_t
241 size_t stackSizeInBytes = FiberStackSize::SixtyFourKiB;
242 size_t maxStacks = 0;
243 bool guardPage =
true;
245 FiberStackCommitMode commitMode = FiberStackCommitMode::Full;
246 size_t initialCommitSizeInBytes = 0;
248 size_t growthCommitSizeInBytes = 0;
254 size_t activeStacks = 0;
255 size_t peakActiveStacks = 0;
256 size_t stackSizeInBytes = 0;
257 size_t guardSizeInBytes = 0;
258 size_t reservedSizeBytes = 0;
259 size_t committedSizeBytes = 0;
260 size_t peakCommittedBytes = 0;
261 size_t highWaterUsedBytes = 0;
262 size_t initialCommitSizeInBytes = 0;
263 size_t growthCommitSizeInBytes = 0;
265 FiberStackCommitMode commitMode = FiberStackCommitMode::Full;
280 [[nodiscard]] Result acquire(
FiberStack& outStack);
281 [[nodiscard]] Result release(
FiberStack& stack);
282 [[nodiscard]] Result waitForAvailableSlot(
FiberScheduler& scheduler);
284 void fillHighWaterMarks();
287 [[nodiscard]]
bool isReserved()
const;
288 [[nodiscard]]
bool owns(
const FiberStack& stack)
const;
289 [[nodiscard]]
size_t capacity()
const;
290 [[nodiscard]]
size_t activeCount()
const;
296 FiberStackClassOpaque internal;
299enum class FiberAllocatorMode : uint8_t
310 size_t numAllocations = 0;
311 size_t numReleases = 0;
313 size_t requestedBytesAllocated = 0;
314 size_t requestedBytesReleased = 0;
316 size_t bytesInUse = 0;
317 size_t peakBytesInUse = 0;
319 size_t numAllocationFailures = 0;
320 size_t lastFailedAllocationSize = 0;
321 size_t largestFailedAllocationSize = 0;
326 size_t reserveBytes = 0;
327 size_t initialCommitBytes = 0;
332 virtual void* allocateImpl(
const void* owner,
size_t numBytes,
size_t alignment) = 0;
333 virtual void releaseImpl(
void* memory) = 0;
347 [[nodiscard]] Result createFixed(Span<char> storage);
349 [[nodiscard]] Result createMalloc();
351 [[nodiscard]] Result validateClose()
const;
352 [[nodiscard]] Result close();
354 [[nodiscard]]
void* allocate(
const void* owner,
size_t numBytes,
size_t alignment);
355 void release(
void* memory);
356 static void releaseFromAnyAllocator(
void* memory);
358 [[nodiscard]] FiberAllocatorMode mode()
const;
360 [[nodiscard]]
bool isOpen()
const;
362 [[nodiscard]]
size_t used()
const;
363 [[nodiscard]]
size_t capacity()
const;
364 [[nodiscard]]
size_t peakUsed()
const;
365 [[nodiscard]]
size_t failedAllocationSize()
const;
366 [[nodiscard]]
size_t reservedBytes()
const;
367 [[nodiscard]]
size_t committedBytes()
const;
372 Result initializeFixedStorage(Span<char> storage);
373 void* allocateFromBlocks(
const void* owner,
size_t numBytes,
size_t alignment);
374 void releaseBlock(BlockHeader& header);
375 bool ensureCommitted(
size_t sizeInBytes);
376 void releaseVirtualMemory();
377 void recordAllocationFailure(
size_t numBytes);
380 FiberAllocatorMode currentMode = FiberAllocatorMode::None;
382 Span<char> fixedStorage;
383 BlockHeader* firstBlock =
nullptr;
386 void* virtualMemory =
nullptr;
387 size_t virtualReservedBytes = 0;
388 size_t virtualCommittedBytes = 0;
406 [[nodiscard]] Span<char> memory()
const;
407 [[nodiscard]]
size_t usableSizeInBytes()
const;
408 [[nodiscard]]
size_t reservedSizeInBytes()
const;
409 [[nodiscard]]
size_t guardSizeInBytes()
const;
410 [[nodiscard]]
bool isReserved()
const;
413 FiberVirtualStackOpaque internal;
417enum class FiberTaskStatus
427enum class FiberTaskSuspendAction
434struct FiberCancellationToken;
445 [[nodiscard]]
bool isActive()
const;
449 [[nodiscard]]
const FiberTask* runningTask()
const;
466 size_t localReadyFibers = 0;
467 size_t localDequeCapacity = 0;
468 size_t localDequeHead = 0;
469 volatile size_t localDequeTop = 0;
470 volatile size_t localDequeBottom = 0;
471 size_t localReadyPeakFibers = 0;
472 size_t localSpilledFibers = 0;
473 size_t stealAttempts = 0;
474 size_t stealVictimProbes = 0;
475 size_t stolenFibers = 0;
476 size_t stolenBatches = 0;
477 size_t stolenBatchPeak = 0;
478 size_t failedSteals = 0;
479 size_t stealCursor = 0;
480 volatile size_t runAttempts = 0;
481 volatile size_t idlePolls = 0;
482 volatile size_t idleSpinIterations = 0;
483 volatile size_t parkAttempts = 0;
484 volatile size_t parkedWakeups = 0;
485 volatile size_t executedFibers = 0;
486 size_t completedFibers = 0;
487 volatile size_t yieldedFibers = 0;
488 size_t waitingFibers = 0;
489 volatile int32_t activeRegistryLock = 0;
490 bool workerActive =
false;
491 bool localSchedulingActive =
false;
492 bool stealCursorInitialized =
false;
494 AlignedStorage<FiberContextStorageSize, FiberContextStorageAlignment> rootContextStorage;
496 [[nodiscard]] FiberContext& rootContext();
499#if SC_PLATFORM_WINDOWS
500static constexpr int FiberWorkerThreadStorageSize =
sizeof(
void*);
502static constexpr int FiberWorkerThreadStorageSize =
sizeof(
void*) * 2;
504static constexpr int FiberWorkerThreadStorageAlignment =
alignof(
void*);
515 [[nodiscard]]
bool wasStarted()
const;
516 [[nodiscard]] Result result()
const;
520 friend struct FiberWorkerPoolThreadEntry;
522 AlignedStorage<FiberWorkerThreadStorageSize, FiberWorkerThreadStorageAlignment> threadStorage;
525 size_t workerIndex = 0;
526 uint64_t affinityMask = 0;
527 uint8_t priority = 0;
528 Result threadResult = Result(
true);
529 bool started =
false;
533 Span<char> stackGrowthSignalStackStorage;
535 Result startThread();
537 Result runThreadEntry();
538 Result applyThreadPolicy();
541enum class FiberWorkerThreadPriority : uint8_t
552 size_t dequeCapacityPerWorker = 0;
554 size_t injectionCapacity = 0;
555 size_t idleSpinAttempts = 32;
556 Span<const uint64_t> affinityMasks;
557 FiberWorkerThreadPriority threadPriority = FiberWorkerThreadPriority::Default;
566 size_t readyFibers = 0;
567 size_t readyPeakFibers = 0;
568 size_t dequeCapacity = 0;
569 size_t spilledFibers = 0;
570 size_t stealAttempts = 0;
571 size_t stealVictimProbes = 0;
572 size_t stolenFibers = 0;
573 size_t stolenBatches = 0;
574 size_t stolenBatchPeak = 0;
575 size_t failedSteals = 0;
576 size_t runAttempts = 0;
577 size_t idlePolls = 0;
578 size_t idleSpinIterations = 0;
579 size_t parkAttempts = 0;
580 size_t parkedWakeups = 0;
581 size_t executedFibers = 0;
582 size_t completedFibers = 0;
583 size_t yieldedFibers = 0;
584 size_t waitingFibers = 0;
590 size_t wakeNotifications = 0;
592 size_t wakeSignals = 0;
597 size_t readyFibers = 0;
598 size_t globalReadyFibers = 0;
599 size_t activeFibers = 0;
600 size_t injectionCapacity = 0;
601 size_t injectionReady = 0;
602 size_t injectionPublishing = 0;
603 size_t injectionPeak = 0;
604 size_t injectionSpills = 0;
605 size_t injectionClaimBatchPeak = 0;
606 size_t injectionLockAcquisitions = 0;
607 size_t injectionLockContentions = 0;
608 size_t injectionLockSpinRetries = 0;
609 size_t injectionLockPeakSpinRetries = 0;
611 size_t lockAcquisitions = 0;
612 size_t lockContentions = 0;
613 size_t lockSpinRetries = 0;
614 size_t lockPeakSpinRetries = 0;
615 size_t lockSpawn = 0;
616 size_t lockReady = 0;
617 size_t lockSynchronization = 0;
618 size_t lockCompletion = 0;
619 size_t lockControl = 0;
622enum class FiberTraceEventType : uint8_t
632 FiberTraceEventType type = FiberTraceEventType::TaskStarted;
641 using Callback = void (*)(
void* userData,
const FiberTraceEvent& event);
643 Callback callback =
nullptr;
644 void* userData =
nullptr;
656 Result start(
FiberScheduler& scheduler, Span<FiberWorker> workerStorage, Span<FiberWorkerThread> threadStorage);
657 Result start(
FiberScheduler& scheduler, Span<FiberWorker> workerStorage, Span<FiberWorkerThread> threadStorage,
659 Result requestStop();
663 [[nodiscard]]
bool isRunning()
const;
664 [[nodiscard]]
size_t workerCount()
const;
672 static constexpr int Windows =
sizeof(
void*) * 16;
673 static constexpr int Apple =
sizeof(
void*) * 16;
674 static constexpr int Linux =
sizeof(
void*) * 16;
675 static constexpr int Default = Linux;
677 static constexpr size_t Alignment =
alignof(
void*);
679 using Object = FiberWorkerPoolWakeEvent;
682 using WakeEventOpaque = OpaqueObject<WakeEventDefinition>;
689 Span<FiberWorker> workers;
690 Span<FiberWorkerThread> threads;
691 WakeEventOpaque wakeEvent;
692 mutable volatile int32_t stopRequested = 0;
693 mutable volatile int32_t running = 0;
694 size_t idleSpinAttempts = 0;
695 bool localDequesCreated =
false;
696 bool injectionCreated =
false;
698 void wakeOneWorker();
699 void wakeAllWorkers();
700 void resetWakeDiagnostics();
701 [[nodiscard]]
bool waitForWork(uint32_t observedGeneration);
702 [[nodiscard]] uint32_t wakeGeneration()
const;
704 Result workerMain(
size_t workerIndex);
715 void requestCancel();
717 Result check()
const;
719 [[nodiscard]]
bool isCancellationRequested()
const;
727 mutable volatile int32_t requested = 0;
735 Result check()
const;
737 [[nodiscard]]
bool isValid()
const;
738 [[nodiscard]]
bool isCancellationRequested()
const;
751enum class FiberJobStatus
763 [[nodiscard]]
FiberJob& job()
const;
764 [[nodiscard]]
bool isCancellationRequested()
const;
765 Result checkCancellation()
const;
789 [[nodiscard]]
bool isActive()
const;
790 [[nodiscard]]
bool isCompleted()
const;
791 [[nodiscard]]
bool isCancellationRequested()
const;
792 [[nodiscard]] FiberJobStatus status()
const;
793 [[nodiscard]] Result result()
const;
809 Result jobResult = Result(
true);
810 volatile int32_t jobStatus =
static_cast<int32_t
>(FiberJobStatus::Invalid);
811 uint32_t cancelGeneration = 0;
812 volatile bool cancelRequested =
false;
813 bool poolRetained =
false;
818 size_t readyJobs = 0;
819 size_t readyPeakJobs = 0;
820 size_t dequeCapacity = 0;
821 size_t executedJobs = 0;
822 size_t claimBatches = 0;
823 size_t claimedJobs = 0;
824 size_t claimBatchPeak = 0;
825 size_t stealAttempts = 0;
826 size_t stolenJobs = 0;
827 size_t failedSteals = 0;
830#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
832#pragma warning(disable : 4324)
843 [[nodiscard]]
bool isActive()
const;
846 [[nodiscard]]
FiberJob* runningJob();
847 [[nodiscard]]
const FiberJob* runningJob()
const;
861 size_t localDequeCapacity = 0;
862 size_t lastDequeCapacity = 0;
863 volatile size_t localDequeTop = 0;
864 volatile size_t localDequeBottom = 0;
866 size_t localReadyPeakJobs = 0;
867 size_t executedJobs = 0;
868 size_t claimBatches = 0;
869 size_t claimedJobs = 0;
870 size_t claimBatchPeak = 0;
871 size_t stealCursor = 0;
872 size_t stealAttempts = 0;
873 size_t stolenJobs = 0;
874 size_t failedSteals = 0;
875 bool workerActive =
false;
876 bool distributedCounts =
false;
878 alignas(128)
volatile size_t ownedReadyJobs = 0;
879 alignas(128)
volatile size_t ownedActiveJobs = 0;
881#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
889#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
891#pragma warning(disable : 4324)
901 Result create(Span<FiberJob*> readyStorage);
904 Result createWorkerDeques(
FiberAllocator& allocator, Span<FiberJobWorker> workers,
size_t capacityPerWorker);
905 void releaseWorkerDeques(Span<FiberJobWorker> workers);
907 Result spawn(
FiberJob& job, FiberJob::Procedure procedure);
910 Result
spawn(Span<FiberJob> jobs, FiberJob::Procedure procedure);
912 Result runOne(
bool& outRanJob);
913 Result runOne(
FiberJobWorker& worker, Span<FiberJobWorker> workerGroup,
bool& outRanJob);
915 Result run(
FiberJobWorker& worker, Span<FiberJobWorker> workerGroup);
918 Result requestCancel(
FiberJob& job);
920 Result requestCancelAll();
922 [[nodiscard]]
bool isOpen()
const;
927 [[nodiscard]]
size_t capacity()
const;
932 [[nodiscard]]
FiberJob* currentJob();
943 Span<FiberJob*> queueStorage;
945 size_t queueHead = 0;
946 size_t queueTail = 0;
947 size_t queueCount = 0;
949 alignas(128)
volatile size_t readyJobs = 0;
950 alignas(128)
volatile size_t activeJobs = 0;
952 alignas(128)
mutable volatile int32_t queueLock = 0;
953 volatile uint32_t cancelGeneration = 0;
957 struct QueueLockGuard;
960 Result complete(
FiberJob& job, Result result);
965 bool usesDistributedAccounting(
const FiberJobWorker& worker)
const;
966 bool isJobCancellationRequested(
const FiberJob& job)
const;
967 bool isWorkerStopRequested()
const;
969#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
982 [[nodiscard]]
bool wasStarted()
const;
983 [[nodiscard]] Result result()
const;
987 friend struct FiberJobWorkerPoolThreadEntry;
989 AlignedStorage<FiberWorkerThreadStorageSize, FiberWorkerThreadStorageAlignment> threadStorage;
992 size_t workerIndex = 0;
993 uint64_t affinityMask = 0;
994 uint8_t priority = 0;
995 Result threadResult = Result(
true);
996 bool started =
false;
998 Result startThread();
1000 Result runThreadEntry();
1001 Result applyThreadPolicy();
1007 size_t dequeCapacityPerWorker = 0;
1008 size_t idleSpinAttempts = 32;
1011 bool keepAliveWhenIdle =
false;
1013 Span<const uint64_t> affinityMasks;
1014 FiberWorkerThreadPriority threadPriority = FiberWorkerThreadPriority::Default;
1036 [[nodiscard]]
bool isRunning()
const;
1037 [[nodiscard]]
size_t workerCount()
const;
1038 [[nodiscard]]
size_t parkedWorkerCount()
const;
1042 static constexpr int Windows =
sizeof(
void*) * 16;
1043 static constexpr int Apple =
sizeof(
void*) * 16;
1044 static constexpr int Linux =
sizeof(
void*) * 16;
1045 static constexpr int Default = Linux;
1047 static constexpr size_t Alignment =
alignof(
void*);
1049 using Object = FiberWorkerPoolWakeEvent;
1052 using WakeEventOpaque = OpaqueObject<WakeEventDefinition>;
1059 Span<FiberJobWorker> workers;
1060 Span<FiberJobWorkerThread> threads;
1061 WakeEventOpaque wakeEvent;
1062 mutable volatile int32_t stopRequested = 0;
1063 mutable volatile int32_t running = 0;
1064 size_t idleSpinAttempts = 0;
1065 bool keepAliveWhenIdle =
false;
1066 volatile uint32_t preparingToWait = 0;
1068 void wakeOneWorker();
1069 void wakeAllWorkers();
1070 void notifyPreparingWorkers();
1071 [[nodiscard]]
bool waitForWork(uint32_t observedGeneration);
1072 [[nodiscard]]
bool waitForPreparedWork(uint32_t observedGeneration);
1073 [[nodiscard]] uint32_t prepareToWaitForWork();
1074 void cancelWaitForWork();
1075 [[nodiscard]] uint32_t wakeGeneration()
const;
1076 [[nodiscard]]
bool isStopRequested()
const;
1078 Result workerMain(
size_t workerIndex);
1083 size_t capacity = 0;
1084 size_t retainedJobs = 0;
1085 size_t availableJobs = 0;
1087 size_t peakRetainedJobs = 0;
1092 static constexpr int Windows = 48;
1093 static constexpr int Apple = 48;
1094 static constexpr int Linux = 48;
1095 static constexpr int Default = 48;
1096 static constexpr size_t Alignment =
alignof(
void*);
1098 using Object = FiberJobClassInternal;
1100using FiberJobClassOpaque = OpaqueObject<FiberJobClassDefinition>;
1109 size_t capacity = 0;
1125 [[nodiscard]] Result validateClose()
const;
1126 [[nodiscard]] Result close();
1129 [[nodiscard]]
bool isOpen()
const;
1130 [[nodiscard]]
bool owns(
const FiberJob& job)
const;
1131 [[nodiscard]]
size_t capacity()
const;
1136 FiberJobClassOpaque internal;
1148 Result create(Span<FiberJob> jobStorage);
1156 [[nodiscard]]
bool isOpen()
const;
1157 [[nodiscard]]
bool owns(
const FiberJob& job)
const;
1158 [[nodiscard]]
size_t capacity()
const;
1159 [[nodiscard]]
size_t retainedCount()
const;
1160 [[nodiscard]]
size_t availableCount()
const;
1166 Span<FiberJob> jobs;
1169 size_t retainedJobs = 0;
1170 size_t peakRetainedJobs = 0;
1172 mutable volatile int32_t poolLock = 0;
1175 void releaseAcquired(
FiberJob& job);
1176 void releaseAcquiredUnlocked(
FiberJob& job);
1177 void recordPublished();
1183 Result result = Result(
true);
1199 Result requestCancel();
1202 [[nodiscard]]
size_t pendingCount()
const;
1203 [[nodiscard]]
size_t jobCount()
const;
1204 [[nodiscard]]
size_t countErrors()
const;
1205 Result collectErrors(Span<FiberJobGroupError> errors,
size_t& outErrors)
const;
1212 size_t pendingJobs = 0;
1213 size_t totalJobs = 0;
1215 mutable volatile int32_t groupLock = 0;
1217 Result prepareSpawn()
const;
1219 void unlinkSpawnFailure(
FiberJob& job);
1228 void* userData =
nullptr;
1229 bool setUserData =
false;
1242#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
1243#pragma warning(push)
1244#pragma warning(disable : 4324)
1260 [[nodiscard]]
bool isValid()
const;
1261 [[nodiscard]]
bool isStarted()
const;
1262 [[nodiscard]]
bool isCompleted()
const;
1263 [[nodiscard]]
bool isActive()
const;
1264 [[nodiscard]]
bool isCancellationRequested()
const;
1265 [[nodiscard]] FiberTaskStatus status()
const;
1266 [[nodiscard]] Result result()
const;
1267 void setUserData(
void* data);
1268 [[nodiscard]]
void* userData()
const;
1272 friend struct FiberTaskClassInternal;
1276 AlignedStorage<FiberContextStorageSize, FiberContextStorageAlignment> contextStorage;
1278 Procedure procedure;
1294 Span<char> originStackMemory;
1297 void* runningWorker =
nullptr;
1298 void* stackOwner =
nullptr;
1299 void* taskUserData =
nullptr;
1300 Result taskResult = Result(
true);
1301 volatile int32_t taskStatus =
static_cast<int32_t
>(FiberTaskStatus::Invalid);
1302 FiberTaskSuspendAction suspendAction = FiberTaskSuspendAction::None;
1303 volatile bool cancelRequested =
false;
1304 bool suspendInterruptible =
false;
1306 [[nodiscard]] FiberContext& context();
1307 [[nodiscard]]
const FiberContext& context()
const;
1312 static constexpr int Windows = 96;
1313 static constexpr int Apple = 96;
1314 static constexpr int Linux = 96;
1315 static constexpr int Default = 96;
1316 static constexpr size_t Alignment =
alignof(
void*);
1318 using Object = FiberTaskClassInternal;
1320using FiberTaskClassOpaque = OpaqueObject<FiberTaskClassDefinition>;
1324 size_t maxTasks = 0;
1329 size_t capacity = 0;
1330 size_t activeTasks = 0;
1331 size_t availableTasks = 0;
1332 size_t peakActiveTasks = 0;
1347 [[nodiscard]] Result acquire(
FiberTask*& outTask);
1348 [[nodiscard]] Result release(
FiberTask& task);
1349 [[nodiscard]] Result waitForAvailableSlot(
FiberScheduler& scheduler);
1350 [[nodiscard]] Result validateClose()
const;
1351 [[nodiscard]] Result close();
1354 [[nodiscard]]
bool isOpen()
const;
1355 [[nodiscard]]
bool owns(
const FiberTask& task)
const;
1356 [[nodiscard]]
size_t capacity()
const;
1357 [[nodiscard]]
size_t activeCount()
const;
1358 [[nodiscard]]
size_t availableCount()
const;
1363 FiberTaskClassOpaque internal;
1375 [[nodiscard]]
size_t value()
const;
1380 volatile size_t counterValue = 0;
1390 Result result = Result(
true);
1412 Result waitAll(Result* outFirstError =
nullptr);
1413 Result waitAllCancelOnParentCancel(Result* outFirstError =
nullptr);
1414 Result waitCancelOnError(Result* outFirstError =
nullptr);
1419 [[nodiscard]]
size_t pending()
const;
1420 [[nodiscard]]
size_t countErrors()
const;
1421 Result collectErrors(Span<FiberTaskGroupError> errors,
size_t& outErrors)
const;
1430 Result prepareSpawn()
const;
1432 Result findFirstError(Result* outFirstError)
const;
1438 size_t capacity = 0;
1439 size_t activeTasks = 0;
1440 size_t availableTasks = 0;
1441 bool classBacked =
false;
1450 FiberTaskPool(Span<FiberTask> taskStorage, Span<char> stackStorage,
size_t stackSize);
1466 [[nodiscard]]
size_t capacity()
const;
1467 [[nodiscard]]
size_t activeCount()
const;
1468 [[nodiscard]]
size_t availableCount()
const;
1469 [[nodiscard]]
bool hasAvailableTask()
const;
1475 [[nodiscard]]
size_t stackSizeInBytes()
const;
1476 void fillHighWaterMarks();
1477 Result stackHighWaterUsedBytes(
size_t stackIndex,
size_t& outBytes)
const;
1478 Result stackHighWaterUnusedBytes(
size_t stackIndex,
size_t& outBytes)
const;
1487 WaitNode* next =
nullptr;
1489 size_t minimumAvailable = 1;
1490 bool notified =
false;
1493 Span<FiberTask> tasks;
1495 size_t stackSize = 0;
1496 size_t nextTask = 0;
1501 WaitNode* availabilityWaitHead =
nullptr;
1502 WaitNode* availabilityWaitTail =
nullptr;
1504 mutable volatile int32_t primitiveLock = 0;
1506 Result stackAt(
size_t stackIndex,
FiberStack& outStack)
const;
1507 void queueAvailabilityWaiter(WaitNode& node);
1509 bool removeAvailabilityWaiter(WaitNode& node);
1526 [[nodiscard]]
bool isSignaled()
const;
1532 WaitNode* next =
nullptr;
1533 bool notified =
false;
1536 WaitNode* waitHead =
nullptr;
1537 WaitNode* waitTail =
nullptr;
1538 bool signaled =
false;
1539 mutable volatile int32_t primitiveLock = 0;
1541 void queueWaiter(WaitNode& node);
1542 bool removeWaiter(WaitNode& node);
1559 [[nodiscard]]
bool isSignaled()
const;
1565 WaitNode* next =
nullptr;
1566 bool notified =
false;
1569 WaitNode* waitHead =
nullptr;
1570 WaitNode* waitTail =
nullptr;
1571 bool signaled =
false;
1572 mutable volatile int32_t primitiveLock = 0;
1574 void queueWaiter(WaitNode& node);
1575 bool popWaiter(WaitNode*& node);
1576 bool removeWaiter(WaitNode& node);
1592 [[nodiscard]]
size_t available()
const;
1598 WaitNode* next =
nullptr;
1599 bool notified =
false;
1602 WaitNode* waitHead =
nullptr;
1603 WaitNode* waitTail =
nullptr;
1604 size_t availableCount = 0;
1605 mutable volatile int32_t primitiveLock = 0;
1607 void queueWaiter(WaitNode& node);
1608 WaitNode* popWaiter();
1609 bool removeWaiter(WaitNode& node);
1624 [[nodiscard]]
bool isLocked()
const;
1625 [[nodiscard]]
bool isOwnedByCurrentTask(
FiberScheduler& scheduler)
const;
1631 WaitNode* next =
nullptr;
1633 bool notified =
false;
1636 WaitNode* waitHead =
nullptr;
1637 WaitNode* waitTail =
nullptr;
1638 bool locked =
false;
1640 mutable volatile int32_t primitiveLock = 0;
1642 void queueWaiter(WaitNode& node);
1643 WaitNode* popWaiter();
1644 bool removeWaiter(WaitNode& node);
1664 Result runOnce(
FiberWorker& worker, Span<FiberWorker> workerGroup);
1667 Result runNoWait(
FiberWorker& worker, Span<FiberWorker> workerGroup);
1668 Result runReadyFibers();
1670 Result runReadyFibers(
FiberWorker& worker, Span<FiberWorker> workerGroup);
1673 Result run(
FiberWorker& worker, Span<FiberWorker> workerGroup);
1674 Result createWorkerDeques(
FiberAllocator& allocator, Span<FiberWorker> workers,
size_t capacityPerWorker);
1675 void releaseWorkerDeques(Span<FiberWorker> workers);
1679 Result shutdown(
FiberWorker& worker, Span<FiberWorker> workerGroup);
1683 Result requestCancelAll();
1691 void clearTraceHooks();
1694 [[nodiscard]]
const FiberTask* currentTask()
const;
1695 [[nodiscard]]
bool isCurrentTaskCancellationRequested()
const;
1697 [[nodiscard]]
bool hasReadyFibers()
const;
1698 [[nodiscard]]
bool hasActiveFibers()
const;
1699 [[nodiscard]]
size_t readyFiberCount()
const;
1700 [[nodiscard]]
size_t readyFiberCount(
const FiberWorker& worker)
const;
1701 [[nodiscard]]
size_t stolenFiberCount(
const FiberWorker& worker)
const;
1702 [[nodiscard]]
size_t stolenFiberCount(Span<FiberWorker> workers)
const;
1703 [[nodiscard]]
size_t activeFiberCount()
const;
1705 void resetSchedulerDiagnostics();
1710 void resetWorkerDiagnostics(Span<FiberWorker> workers);
1715 struct InjectionSlot;
1721 InjectionSlot* injectionQueue =
nullptr;
1723 size_t injectionCapacity = 0;
1724 volatile size_t injectionHead = 0;
1725 volatile size_t injectionTail = 0;
1726 volatile size_t injectionReady = 0;
1727 volatile size_t injectionPublishing = 0;
1728 volatile size_t injectionPeak = 0;
1729 size_t injectionSpills = 0;
1730 volatile size_t injectionClaimBatchPeak = 0;
1732 mutable volatile int32_t injectionLock = 0;
1734 mutable size_t injectionLockAcquisitions = 0;
1735 mutable size_t injectionLockContentions = 0;
1736 mutable size_t injectionLockSpinRetries = 0;
1737 mutable size_t injectionLockPeakSpinRetries = 0;
1741 volatile size_t readyFibers = 0;
1742 volatile size_t globalReadyFibers = 0;
1743 volatile size_t activeFibers = 0;
1745 mutable volatile int32_t schedulerLock = 0;
1747 mutable size_t schedulerLockAcquisitions = 0;
1748 mutable size_t schedulerLockContentions = 0;
1749 mutable size_t schedulerLockSpinRetries = 0;
1750 mutable size_t schedulerLockPeakSpinRetries = 0;
1752 mutable size_t schedulerLockSpawn = 0;
1753 mutable size_t schedulerLockReady = 0;
1754 mutable size_t schedulerLockSynchronization = 0;
1755 mutable size_t schedulerLockCompletion = 0;
1756 mutable size_t schedulerLockControl = 0;
1760 enum class LockCategory : uint8_t
1770 struct InjectionLockGuard;
1772 void lock(LockCategory category)
const;
1773 void unlock()
const;
1774 void lockInjection()
const;
1775 void unlockInjection()
const;
1776 void trace(FiberTraceEventType type,
FiberTask* task,
FiberWorker* worker,
size_t value = 0)
const;
1779 Result initializeTaskForSpawn(
FiberTask& task,
FiberStack& stack, FiberTask::Procedure procedure,
1784 Result createInjectionQueue(
FiberAllocator& allocator,
size_t capacity);
1785 void releaseInjectionQueue();
1786 [[nodiscard]]
bool tryReserveInjection(
size_t& position);
1787 void publishInjection(
size_t position,
FiberTask* task);
1788 void discardInjectionTombstones();
1789 [[nodiscard]]
bool tryPushInjectionUnlocked(
FiberTask& task);
1790 [[nodiscard]]
FiberTask* popInjection();
1791 [[nodiscard]]
size_t popInjectionTasks(Span<FiberTask*> tasks);
1792 void notifyReadyWorkUnlocked();
1793 void pushReadyUnlocked(
FiberTask& task);
1798 [[nodiscard]]
FiberTask* popReadyUnlocked();
1801 [[nodiscard]]
FiberTask* popReadyUnlocked(
FiberWorker& worker, Span<FiberWorker> stealWorkers);
1804 [[nodiscard]]
FiberTask* stealReadyUnlocked(
FiberWorker& worker, Span<FiberWorker> stealWorkers);
1809 void publishSuspensionUnlocked(
FiberTask& task);
1810 void finishCurrentTask(
FiberTask& task, Result result);
1811 Result cancelTaskUnlocked(
FiberTask& task);
1812 void linkActiveUnlocked(
FiberTask& task);
1813 void unlinkActiveUnlocked(
FiberTask& task);
1815 void adoptWorkerReadyUnlocked(
FiberWorker& worker);
1816 void unlinkWorkerActive(
FiberTask& task);
1819 Result waitImpl(
FiberCounter& counter,
bool interruptible);
1821 void wakeCounterWaitersUnlocked(
FiberCounter& counter);
1823 static void taskEntry(
void* userData);
1826#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
Explicit allocator for future fiber scheduler storage.
Definition Fibers.h:339
Auto-reset event that wakes one waiting fiber per signal.
Definition Fibers.h:1547
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:709
Lightweight cancellation token copied into spawned fiber tasks.
Definition Fibers.h:732
Counter used to suspend fibers until a group of operations completes.
Definition Fibers.h:1368
Manual-reset event that wakes waiting fibers when signaled.
Definition Fibers.h:1514
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:1115
Restricted execution context passed to a run-to-completion FiberJob.
Definition Fibers.h:761
Retains one bounded wave of pooled FiberJob results for aggregate inspection.
Definition Fibers.h:1188
Fixed-capacity owner of reusable FiberJob records with explicit completed-result release.
Definition Fibers.h:1141
Fixed-capacity scheduler for stackless run-to-completion jobs.
Definition Fibers.h:894
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:1019
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:975
Caller-owned parallel execution agent for the FiberJob runtime.
Definition Fibers.h:836
Caller-owned stackless CPU job. Its address must remain stable while active.
Definition Fibers.h:778
Cooperative mutex for fibers running on one FiberScheduler.
Definition Fibers.h:1614
Cooperative fiber scheduler with explicit workers and caller-owned storage.
Definition Fibers.h:1649
Counting semaphore for cooperative fibers.
Definition Fibers.h:1581
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:270
Explicit owner of process integration required by incrementally committed fiber stacks.
Definition Fibers.h:126
Thread-affine registration for one thread that may execute incrementally committed fiber stacks.
Definition Fibers.h:161
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:105
Caller-owned stack storage used by fiber contexts.
Definition Fibers.h:180
Allocator-backed fixed-capacity storage for reusable FiberTask objects.
Definition Fibers.h:1337
One failed task collected from a FiberTaskGroup.
Definition Fibers.h:1388
Convenience helper for spawning a group of child tasks and waiting on their completion.
Definition Fibers.h:1395
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:1437
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:1225
Caller-owned task object scheduled by FiberScheduler.
Definition Fibers.h:1249
Options for reserving a virtual-memory-backed fiber stack.
Definition Fibers.h:216
Virtual-memory-backed stack storage with an optional no-access guard page below the stack.
Definition Fibers.h:393
Span< char > stackGrowthSignalStackStorage
POSIX requires at least workerCount * FiberStackGrowthSignalStackSize bytes; Windows ignores this spa...
Definition Fibers.h:561
No-allocation OS-thread-owning worker pool using caller-provided worker and thread storage.
Definition Fibers.h:649
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:508
Caller-owned execution agent for running ready fibers on the current OS thread.
Definition Fibers.h:438