Sane C++ Libraries
C++ Platform Abstraction Libraries
Loading...
Searching...
No Matches
Fibers.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4
5#include "../Common/CompilerMacrosExport.h"
6#ifndef SC_EXPORT_LIBRARY_FIBERS
7#define SC_EXPORT_LIBRARY_FIBERS 0
8#endif
9#define SC_FIBERS_EXPORT SC_COMPILER_LIBRARY_EXPORT(SC_EXPORT_LIBRARY_FIBERS)
10
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
23
26namespace SC
27{
28SC_DECLARE_ASSERT_PROVIDER(FibersAssert, SC_FIBERS_EXPORT);
29
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)
33
34struct FiberCounter;
35struct FiberAutoResetEvent;
36struct FiberContext;
37struct FiberEvent;
38struct FiberJob;
39struct FiberJobClass;
40struct FiberJobClassDiagnostics;
41struct FiberJobClassInternal;
42struct FiberJobClassOptions;
43struct FiberJobContext;
44struct FiberJobGroup;
45struct FiberJobPool;
46struct FiberJobScheduler;
47struct FiberJobWorker;
48struct FiberJobWorkerPool;
49struct FiberJobWorkerPoolThreadEntry;
50struct FiberJobWorkerThread;
51struct FiberMutex;
52struct FiberSemaphore;
53struct FiberScheduler;
54struct FiberTaskGroup;
55struct FiberTaskClass;
56struct FiberTaskClassDiagnostics;
57struct FiberTaskClassInternal;
58struct FiberTaskClassOptions;
59struct FiberTaskPool;
60struct FiberTaskPoolDiagnostics;
61struct FiberTask;
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;
76struct FiberAllocator;
77struct FiberAllocatorInterface;
78struct FiberAllocatorVirtualOptions;
79struct FiberWorker;
80struct FiberWorkerPoolWakeEvent;
81struct FiberWorkerPool;
82struct FiberWorkerPoolOptions;
83struct FiberWorkerPoolThreadEntry;
84struct FiberWorkerThread;
85
86#if SC_PLATFORM_WINDOWS
87static constexpr int FiberContextStorageSize = 2048;
88#elif SC_PLATFORM_ARM64
89static constexpr int FiberContextStorageSize = 224;
90#else
91static constexpr int FiberContextStorageSize = 128;
92#endif
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);
100
103struct SC_FIBERS_EXPORT FiberStackSize
104{
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;
109};
110
112{
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*);
118
119 using Object = FiberStackGrowthRuntimeInternal;
120};
121using FiberStackGrowthRuntimeOpaque = OpaqueObject<FiberStackGrowthRuntimeDefinition>;
122
124struct SC_FIBERS_EXPORT FiberStackGrowthRuntime
125{
128
130 FiberStackGrowthRuntime& operator=(const FiberStackGrowthRuntime&) = delete;
131
132 [[nodiscard]] Result create();
133 [[nodiscard]] Result close();
134
135 [[nodiscard]] static bool isSupported();
136 [[nodiscard]] bool isOpen() const;
137 [[nodiscard]] size_t registeredThreadCount() const;
138
139 private:
140 friend struct FiberStackGrowthThread;
141 friend struct FiberStackGrowthThreadInternal;
142
143 FiberStackGrowthRuntimeOpaque internal;
144};
145
147{
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*);
153
154 using Object = FiberStackGrowthThreadInternal;
155};
156using FiberStackGrowthThreadOpaque = OpaqueObject<FiberStackGrowthThreadDefinition>;
157
159struct SC_FIBERS_EXPORT FiberStackGrowthThread
160{
163
165 FiberStackGrowthThread& operator=(const FiberStackGrowthThread&) = delete;
166
168 [[nodiscard]] Result create(FiberStackGrowthRuntime& runtime, Span<char> signalStackStorage);
169 [[nodiscard]] Result close();
170
171 [[nodiscard]] bool isOpen() const;
172
173 private:
174 FiberStackGrowthThreadOpaque internal;
175};
176
178struct SC_FIBERS_EXPORT FiberStack
179{
180 explicit FiberStack(Span<char> memory);
181
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;
190
191 private:
192 friend struct FiberScheduler;
193 friend struct FiberVirtualStack;
194
195 FiberStack(Span<char> memory, void* owner);
196
197 Span<char> stackMemory;
198 void* stackOwner = nullptr;
199};
200
202{
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*);
208
209 using Object = FiberVirtualStackInternal;
210};
211using FiberVirtualStackOpaque = OpaqueObject<FiberVirtualStackDefinition>;
212
214struct SC_FIBERS_EXPORT FiberVirtualStackOptions
215{
216 size_t usableSizeInBytes = FiberStackSize::SixtyFourKiB;
217 bool guardPage = true;
218};
219
221{
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*);
227
228 using Object = FiberStackClassInternal;
229};
230using FiberStackClassOpaque = OpaqueObject<FiberStackClassDefinition>;
231
232enum class FiberStackCommitMode : uint8_t
233{
234 Full,
235 Incremental,
236};
237
238struct SC_FIBERS_EXPORT FiberStackClassOptions
239{
240 size_t stackSizeInBytes = FiberStackSize::SixtyFourKiB;
241 size_t maxStacks = 0;
242 bool guardPage = true;
243
244 FiberStackCommitMode commitMode = FiberStackCommitMode::Full;
245 size_t initialCommitSizeInBytes = 0;
247 size_t growthCommitSizeInBytes = 0;
248};
249
250struct SC_FIBERS_EXPORT FiberStackClassDiagnostics
251{
252 size_t capacity = 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;
263
264 FiberStackCommitMode commitMode = FiberStackCommitMode::Full;
265};
266
268struct SC_FIBERS_EXPORT FiberStackClass
269{
272
273 FiberStackClass(const FiberStackClass&) = delete;
274 FiberStackClass& operator=(const FiberStackClass&) = delete;
276 FiberStackClass& operator=(FiberStackClass&&) = delete;
277
278 [[nodiscard]] Result reserve(const FiberStackClassOptions& options);
279 [[nodiscard]] Result acquire(FiberStack& outStack);
280 [[nodiscard]] Result release(FiberStack& stack);
281 [[nodiscard]] Result waitForAvailableSlot(FiberScheduler& scheduler);
282 void release();
283 void fillHighWaterMarks();
284 void diagnostics(FiberStackClassDiagnostics& outDiagnostics) const;
285
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;
290
291 private:
292 friend struct FiberTaskPool;
293 friend struct FiberScheduler;
294
295 FiberStackClassOpaque internal;
296};
297
298enum class FiberAllocatorMode : uint8_t
299{
300 None,
301 Fixed,
302 Virtual,
303 Malloc,
304 Polymorphic,
305};
306
308{
309 size_t numAllocations = 0;
310 size_t numReleases = 0;
311
312 size_t requestedBytesAllocated = 0;
313 size_t requestedBytesReleased = 0;
314
315 size_t bytesInUse = 0;
316 size_t peakBytesInUse = 0;
317
318 size_t numAllocationFailures = 0;
319 size_t lastFailedAllocationSize = 0;
320 size_t largestFailedAllocationSize = 0;
321};
322
324{
325 size_t reserveBytes = 0;
326 size_t initialCommitBytes = 0;
327};
328
330{
331 virtual void* allocateImpl(const void* owner, size_t numBytes, size_t alignment) = 0;
332 virtual void releaseImpl(void* memory) = 0;
333 virtual ~FiberAllocatorInterface() {}
334};
335
337struct SC_FIBERS_EXPORT FiberAllocator
338{
339 FiberAllocator() = default;
340 FiberAllocator(const FiberAllocator&) = delete;
341 FiberAllocator& operator=(const FiberAllocator&) = delete;
342 FiberAllocator(FiberAllocator&&) = delete;
343 FiberAllocator& operator=(FiberAllocator&&) = delete;
345
346 [[nodiscard]] Result createFixed(Span<char> storage);
347 [[nodiscard]] Result createVirtual(FiberAllocatorVirtualOptions options);
348 [[nodiscard]] Result createMalloc();
349 [[nodiscard]] Result createPolymorphic(FiberAllocatorInterface& customAllocatorInterface);
350 [[nodiscard]] Result validateClose() const;
351 [[nodiscard]] Result close();
352
353 [[nodiscard]] void* allocate(const void* owner, size_t numBytes, size_t alignment);
354 void release(void* memory);
355 static void releaseFromAnyAllocator(void* memory);
356
357 [[nodiscard]] FiberAllocatorMode mode() const;
358 [[nodiscard]] FiberAllocatorStatistics statistics() const;
359 [[nodiscard]] bool isOpen() const;
360
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;
367
368 private:
369 struct BlockHeader;
370
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);
377 void resetState();
378
379 FiberAllocatorMode currentMode = FiberAllocatorMode::None;
380 FiberAllocatorStatistics currentStatistics;
381 Span<char> fixedStorage;
382 BlockHeader* firstBlock = nullptr;
383 FiberAllocatorInterface* allocatorInterface = nullptr;
384
385 void* virtualMemory = nullptr;
386 size_t virtualReservedBytes = 0;
387 size_t virtualCommittedBytes = 0;
388};
389
391struct SC_FIBERS_EXPORT FiberVirtualStack
392{
395
396 FiberVirtualStack(const FiberVirtualStack&) = delete;
397 FiberVirtualStack& operator=(const FiberVirtualStack&) = delete;
399 FiberVirtualStack& operator=(FiberVirtualStack&&) = delete;
400
401 Result reserve(const FiberVirtualStackOptions& options);
402 void release();
403
404 [[nodiscard]] FiberStack stack() const;
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;
410
411 private:
412 FiberVirtualStackOpaque internal;
413};
414
416enum class FiberTaskStatus
417{
418 Invalid,
419 Ready,
420 Running,
421 Waiting,
422 Completing,
423 Completed
424};
425
426enum class FiberTaskSuspendAction
427{
428 None,
429 Ready,
430 CounterWait
431};
432
433struct FiberCancellationToken;
434
436struct SC_FIBERS_EXPORT FiberWorker
437{
438 FiberWorker();
439 ~FiberWorker();
440
441 FiberWorker(const FiberWorker&) = delete;
442 FiberWorker& operator=(const FiberWorker&) = delete;
443
444 [[nodiscard]] bool isActive() const;
445 [[nodiscard]] FiberScheduler* scheduler();
446 [[nodiscard]] const FiberScheduler* scheduler() const;
447 [[nodiscard]] FiberTask* runningTask();
448 [[nodiscard]] const FiberTask* runningTask() const;
449
450 private:
451 friend struct FiberWorkerPool;
452 friend struct FiberScheduler;
453
454 Result begin(FiberScheduler& fiberScheduler);
455 void end();
456
457 FiberScheduler* workerScheduler = nullptr;
458 FiberScheduler* localQueueScheduler = nullptr;
459 FiberTask* workerTask = nullptr;
460 FiberTask* localReadyHead = nullptr;
461 FiberTask* localReadyTail = nullptr;
462 FiberTask* activeHead = nullptr;
463 FiberTask** localDeque = nullptr;
464 FiberAllocator* localDequeAllocator = nullptr;
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;
492
493 AlignedStorage<FiberContextStorageSize, FiberContextStorageAlignment> rootContextStorage;
494
495 [[nodiscard]] FiberContext& rootContext();
496};
497
498#if SC_PLATFORM_WINDOWS
499static constexpr int FiberWorkerThreadStorageSize = sizeof(void*);
500#else
501static constexpr int FiberWorkerThreadStorageSize = sizeof(void*) * 2;
502#endif
503static constexpr int FiberWorkerThreadStorageAlignment = alignof(void*);
504
506struct SC_FIBERS_EXPORT FiberWorkerThread
507{
510
511 FiberWorkerThread(const FiberWorkerThread&) = delete;
512 FiberWorkerThread& operator=(const FiberWorkerThread&) = delete;
513
514 [[nodiscard]] bool wasStarted() const;
515 [[nodiscard]] Result result() const;
516
517 private:
518 friend struct FiberWorkerPool;
519 friend struct FiberWorkerPoolThreadEntry;
520
521 AlignedStorage<FiberWorkerThreadStorageSize, FiberWorkerThreadStorageAlignment> threadStorage;
522
523 FiberWorkerPool* pool = nullptr;
524 size_t workerIndex = 0;
525 uint64_t affinityMask = 0;
526 uint8_t priority = 0;
527 Result threadResult = Result(true);
528 bool started = false;
529
530 FiberStackGrowthRuntime* stackGrowthRuntime = nullptr;
531 FiberStackGrowthThread stackGrowthThread;
532 Span<char> stackGrowthSignalStackStorage;
533
534 Result startThread();
535 Result joinThread();
536 Result runThreadEntry();
537 Result applyThreadPolicy();
538};
539
540enum class FiberWorkerThreadPriority : uint8_t
541{
542 Default,
543 Low,
544 Normal,
545 High,
546};
547
548struct SC_FIBERS_EXPORT FiberWorkerPoolOptions
549{
550 FiberAllocator* dequeAllocator = nullptr;
551 size_t dequeCapacityPerWorker = 0;
552 FiberAllocator* injectionAllocator = nullptr;
553 size_t injectionCapacity = 0;
554 size_t idleSpinAttempts = 32;
555 Span<const uint64_t> affinityMasks;
556 FiberWorkerThreadPriority threadPriority = FiberWorkerThreadPriority::Default;
558 FiberStackGrowthRuntime* stackGrowthRuntime = nullptr;
561};
562
563struct SC_FIBERS_EXPORT FiberWorkerDiagnostics
564{
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;
584};
585
586struct SC_FIBERS_EXPORT FiberWorkerPoolWakeDiagnostics
587{
589 size_t wakeNotifications = 0;
591 size_t wakeSignals = 0;
592};
593
594struct SC_FIBERS_EXPORT FiberSchedulerDiagnostics
595{
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;
609
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;
619};
620
621enum class FiberTraceEventType : uint8_t
622{
623 TaskStarted,
624 TaskYielded,
625 TaskWaiting,
626 TaskCompleted,
627};
628
629struct SC_FIBERS_EXPORT FiberTraceEvent
630{
631 FiberTraceEventType type = FiberTraceEventType::TaskStarted;
632 FiberScheduler* scheduler = nullptr;
633 FiberWorker* worker = nullptr;
634 FiberTask* task = nullptr;
635 size_t value = 0;
636};
637
638struct SC_FIBERS_EXPORT FiberTraceHooks
639{
640 using Callback = void (*)(void* userData, const FiberTraceEvent& event);
641
642 Callback callback = nullptr;
643 void* userData = nullptr;
644};
645
647struct SC_FIBERS_EXPORT FiberWorkerPool
648{
651
652 FiberWorkerPool(const FiberWorkerPool&) = delete;
653 FiberWorkerPool& operator=(const FiberWorkerPool&) = delete;
654
655 Result start(FiberScheduler& scheduler, Span<FiberWorker> workerStorage, Span<FiberWorkerThread> threadStorage);
656 Result start(FiberScheduler& scheduler, Span<FiberWorker> workerStorage, Span<FiberWorkerThread> threadStorage,
657 const FiberWorkerPoolOptions& options);
658 Result requestStop();
659 Result join();
660 Result shutdown();
661
662 [[nodiscard]] bool isRunning() const;
663 [[nodiscard]] size_t workerCount() const;
665 [[nodiscard]] size_t parkedWorkerCount() const;
668
670 {
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;
675
676 static constexpr size_t Alignment = alignof(void*);
677
678 using Object = FiberWorkerPoolWakeEvent;
679 };
680
681 using WakeEventOpaque = OpaqueObject<WakeEventDefinition>;
682
683 private:
684 friend struct FiberScheduler;
685 friend struct FiberWorkerThread;
686
687 FiberScheduler* poolScheduler = nullptr;
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;
696
697 void wakeOneWorker();
698 void wakeAllWorkers();
699 void resetWakeDiagnostics();
700 [[nodiscard]] bool waitForWork(uint32_t observedGeneration);
701 [[nodiscard]] uint32_t wakeGeneration() const;
702
703 Result workerMain(size_t workerIndex);
704};
705
707struct SC_FIBERS_EXPORT FiberCancellationTokenSource
708{
710
713
714 void requestCancel();
715 void reset();
716 Result check() const;
717
718 [[nodiscard]] bool isCancellationRequested() const;
719 [[nodiscard]] FiberCancellationToken token() const;
720
721 private:
722 friend struct FiberCancellationToken;
723 friend struct FiberJobScheduler;
724 friend struct FiberScheduler;
725
726 mutable volatile int32_t requested = 0;
727};
728
730struct SC_FIBERS_EXPORT FiberCancellationToken
731{
733
734 Result check() const;
735
736 [[nodiscard]] bool isValid() const;
737 [[nodiscard]] bool isCancellationRequested() const;
738
739 private:
740 friend struct FiberCancellationTokenSource;
741 friend struct FiberJobScheduler;
742 friend struct FiberScheduler;
743
744 explicit FiberCancellationToken(const FiberCancellationTokenSource& tokenSource);
745
746 const FiberCancellationTokenSource* source = nullptr;
747};
748
750enum class FiberJobStatus
751{
752 Invalid,
753 Ready,
754 Running,
755 Completed
756};
757
759struct SC_FIBERS_EXPORT FiberJobContext
760{
761 [[nodiscard]] FiberJobScheduler& scheduler() const;
762 [[nodiscard]] FiberJob& job() const;
763 [[nodiscard]] bool isCancellationRequested() const;
764 Result checkCancellation() const;
765
766 private:
767 friend struct FiberJobScheduler;
768
770
771 FiberJobScheduler* jobScheduler = nullptr;
772 FiberJob* currentJob = nullptr;
773};
774
776struct SC_FIBERS_EXPORT FiberJob
777{
778 using Procedure = Function<Result(FiberJobContext&)>;
779
780 FiberJob();
781 ~FiberJob();
782
783 FiberJob(const FiberJob&) = delete;
784 FiberJob& operator=(const FiberJob&) = delete;
785 FiberJob(FiberJob&&) = delete;
786 FiberJob& operator=(FiberJob&&) = delete;
787
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;
793
794 private:
795 friend struct FiberJobContext;
796 friend struct FiberJobGroup;
797 friend struct FiberJobPool;
798 friend struct FiberJobScheduler;
799
800 Procedure procedure;
801 FiberJobScheduler* ownerScheduler = nullptr;
802 FiberJobPool* ownerPool = nullptr;
803 FiberJob* nextAvailable = nullptr;
804 FiberJobGroup* originGroup = nullptr;
805 FiberJob* nextGroup = nullptr;
806 FiberCancellationToken cancellationToken;
807 FiberJobWorker* accountingWorker = nullptr;
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;
813};
814
815struct SC_FIBERS_EXPORT FiberJobWorkerDiagnostics
816{
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;
827};
828
829#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
830#pragma warning(push)
831#pragma warning(disable : 4324)
832#endif
834struct SC_FIBERS_EXPORT FiberJobWorker
835{
838
839 FiberJobWorker(const FiberJobWorker&) = delete;
840 FiberJobWorker& operator=(const FiberJobWorker&) = delete;
841
842 [[nodiscard]] bool isActive() const;
843 [[nodiscard]] FiberJobScheduler* scheduler();
844 [[nodiscard]] const FiberJobScheduler* scheduler() const;
845 [[nodiscard]] FiberJob* runningJob();
846 [[nodiscard]] const FiberJob* runningJob() const;
847
848 private:
849 friend struct FiberJobScheduler;
850 friend struct FiberJobWorkerPool;
851
852 Result begin(FiberJobScheduler& scheduler);
853 void end();
854
855 FiberJobScheduler* workerScheduler = nullptr;
856 FiberJobScheduler* localQueueScheduler = nullptr;
857 FiberJob* workerJob = nullptr;
858 FiberJob** localDeque = nullptr;
859 FiberAllocator* localDequeAllocator = nullptr;
860 size_t localDequeCapacity = 0;
861 size_t lastDequeCapacity = 0;
862 volatile size_t localDequeTop = 0;
863 volatile size_t localDequeBottom = 0;
864
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;
876
877 alignas(128) volatile size_t ownedReadyJobs = 0;
878 alignas(128) volatile size_t ownedActiveJobs = 0;
879};
880#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
881#pragma warning(pop)
882#endif
883
888#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
889#pragma warning(push)
890#pragma warning(disable : 4324)
891#endif
892struct SC_FIBERS_EXPORT FiberJobScheduler
893{
896
897 FiberJobScheduler(const FiberJobScheduler&) = delete;
898 FiberJobScheduler& operator=(const FiberJobScheduler&) = delete;
899
900 Result create(Span<FiberJob*> readyStorage);
901 Result close();
902
903 Result createWorkerDeques(FiberAllocator& allocator, Span<FiberJobWorker> workers, size_t capacityPerWorker);
904 void releaseWorkerDeques(Span<FiberJobWorker> workers);
905
906 Result spawn(FiberJob& job, FiberJob::Procedure procedure);
907 Result spawn(FiberJob& job, FiberJob::Procedure procedure, FiberCancellationToken token);
909 Result spawn(Span<FiberJob> jobs, FiberJob::Procedure procedure);
910 Result spawn(Span<FiberJob> jobs, FiberJob::Procedure procedure, FiberCancellationToken token);
911 Result runOne(bool& outRanJob);
912 Result runOne(FiberJobWorker& worker, Span<FiberJobWorker> workerGroup, bool& outRanJob);
913 Result run();
914 Result run(FiberJobWorker& worker, Span<FiberJobWorker> workerGroup);
915 Result shutdown();
916
917 Result requestCancel(FiberJob& job);
918 Result requestCancel(FiberCancellationTokenSource& tokenSource);
919 Result requestCancelAll();
920
921 [[nodiscard]] bool isOpen() const;
923 [[nodiscard]] bool hasReadyJobs() const;
925 [[nodiscard]] bool hasActiveJobs() const;
926 [[nodiscard]] size_t capacity() const;
928 [[nodiscard]] size_t readyJobCount() const;
930 [[nodiscard]] size_t activeJobCount() const;
931 [[nodiscard]] FiberJob* currentJob();
932
935 void workerDiagnostics(const FiberJobWorker& worker, FiberJobWorkerDiagnostics& outDiagnostics) const;
936
937 private:
938 friend struct FiberJobContext;
939 friend struct FiberJob;
940 friend struct FiberJobWorkerPool;
941
942 Span<FiberJob*> queueStorage;
943 FiberJob* runningJob = nullptr;
944 size_t queueHead = 0;
945 size_t queueTail = 0;
946 size_t queueCount = 0;
947
948 alignas(128) volatile size_t readyJobs = 0;
949 alignas(128) volatile size_t activeJobs = 0;
950
951 alignas(128) mutable volatile int32_t queueLock = 0;
952 volatile uint32_t cancelGeneration = 0;
953
954 FiberJobWorkerPool* workerPool = nullptr;
955
956 struct QueueLockGuard;
957
958 void initializeJobForSpawn(FiberJob& job, FiberJob::Procedure procedure, FiberCancellationToken token);
959 Result complete(FiberJob& job, Result result);
960 bool tryPushWorkerDeque(FiberJobWorker& worker, FiberJob& job);
961 FiberJob* popWorkerReady(FiberJobWorker& worker);
962 FiberJob* stealWorkerReady(FiberJobWorker& worker);
963 FiberJob* stealReady(FiberJobWorker& worker, Span<FiberJobWorker> workerGroup);
964 bool usesDistributedAccounting(const FiberJobWorker& worker) const;
965 bool isJobCancellationRequested(const FiberJob& job) const;
966 bool isWorkerStopRequested() const;
967};
968#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
969#pragma warning(pop)
970#endif
971
973struct SC_FIBERS_EXPORT FiberJobWorkerThread
974{
977
979 FiberJobWorkerThread& operator=(const FiberJobWorkerThread&) = delete;
980
981 [[nodiscard]] bool wasStarted() const;
982 [[nodiscard]] Result result() const;
983
984 private:
985 friend struct FiberJobWorkerPool;
986 friend struct FiberJobWorkerPoolThreadEntry;
987
988 AlignedStorage<FiberWorkerThreadStorageSize, FiberWorkerThreadStorageAlignment> threadStorage;
989
990 FiberJobWorkerPool* pool = nullptr;
991 size_t workerIndex = 0;
992 uint64_t affinityMask = 0;
993 uint8_t priority = 0;
994 Result threadResult = Result(true);
995 bool started = false;
996
997 Result startThread();
998 Result joinThread();
999 Result runThreadEntry();
1000 Result applyThreadPolicy();
1001};
1002
1003struct SC_FIBERS_EXPORT FiberJobWorkerPoolOptions
1004{
1005 FiberAllocator* dequeAllocator = nullptr;
1006 size_t dequeCapacityPerWorker = 0;
1007 size_t idleSpinAttempts = 32;
1008
1010 bool keepAliveWhenIdle = false;
1011
1012 Span<const uint64_t> affinityMasks;
1013 FiberWorkerThreadPriority threadPriority = FiberWorkerThreadPriority::Default;
1014};
1015
1017struct SC_FIBERS_EXPORT FiberJobWorkerPool
1018{
1021
1022 FiberJobWorkerPool(const FiberJobWorkerPool&) = delete;
1023 FiberJobWorkerPool& operator=(const FiberJobWorkerPool&) = delete;
1024
1025 Result start(FiberJobScheduler& scheduler, Span<FiberJobWorker> workerStorage,
1026 Span<FiberJobWorkerThread> threadStorage, const FiberJobWorkerPoolOptions& options);
1028 Result waitIdle();
1030 Result requestStop();
1032 Result join();
1033 Result shutdown();
1034
1035 [[nodiscard]] bool isRunning() const;
1036 [[nodiscard]] size_t workerCount() const;
1037 [[nodiscard]] size_t parkedWorkerCount() const;
1038
1040 {
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;
1045
1046 static constexpr size_t Alignment = alignof(void*);
1047
1048 using Object = FiberWorkerPoolWakeEvent;
1049 };
1050
1051 using WakeEventOpaque = OpaqueObject<WakeEventDefinition>;
1052
1053 private:
1054 friend struct FiberJobScheduler;
1055 friend struct FiberJobWorkerThread;
1056
1057 FiberJobScheduler* poolScheduler = nullptr;
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;
1066
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;
1076
1077 Result workerMain(size_t workerIndex);
1078};
1079
1080struct SC_FIBERS_EXPORT FiberJobPoolDiagnostics
1081{
1082 size_t capacity = 0;
1083 size_t retainedJobs = 0;
1084 size_t availableJobs = 0;
1086 size_t peakRetainedJobs = 0;
1087};
1088
1090{
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*);
1096
1097 using Object = FiberJobClassInternal;
1098};
1099using FiberJobClassOpaque = OpaqueObject<FiberJobClassDefinition>;
1100
1101struct SC_FIBERS_EXPORT FiberJobClassOptions
1102{
1103 size_t maxJobs = 0;
1104};
1105
1106struct SC_FIBERS_EXPORT FiberJobClassDiagnostics
1107{
1108 size_t capacity = 0;
1109 bool bound = false;
1110};
1111
1113struct SC_FIBERS_EXPORT FiberJobClass
1114{
1115 FiberJobClass();
1117
1118 FiberJobClass(const FiberJobClass&) = delete;
1119 FiberJobClass& operator=(const FiberJobClass&) = delete;
1120 FiberJobClass(FiberJobClass&&) = delete;
1121 FiberJobClass& operator=(FiberJobClass&&) = delete;
1122
1123 [[nodiscard]] Result create(FiberAllocator& allocator, const FiberJobClassOptions& options);
1124 [[nodiscard]] Result validateClose() const;
1125 [[nodiscard]] Result close();
1126 void diagnostics(FiberJobClassDiagnostics& outDiagnostics) const;
1127
1128 [[nodiscard]] bool isOpen() const;
1129 [[nodiscard]] bool owns(const FiberJob& job) const;
1130 [[nodiscard]] size_t capacity() const;
1131
1132 private:
1133 friend struct FiberJobPool;
1134
1135 FiberJobClassOpaque internal;
1136};
1137
1139struct SC_FIBERS_EXPORT FiberJobPool
1140{
1141 FiberJobPool();
1142 ~FiberJobPool();
1143
1144 FiberJobPool(const FiberJobPool&) = delete;
1145 FiberJobPool& operator=(const FiberJobPool&) = delete;
1146
1147 Result create(Span<FiberJob> jobStorage);
1148 Result create(FiberJobClass& jobClass);
1149 Result close();
1150 Result spawn(FiberJobScheduler& scheduler, FiberJob::Procedure procedure, FiberJob*& outJob);
1151 Result spawn(FiberJobScheduler& scheduler, FiberJob::Procedure procedure, FiberCancellationToken token,
1152 FiberJob*& outJob);
1153 Result release(FiberJob& job);
1154
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;
1160 void diagnostics(FiberJobPoolDiagnostics& outDiagnostics) const;
1161
1162 private:
1163 friend struct FiberJobGroup;
1164
1165 Span<FiberJob> jobs;
1166 FiberJobClass* jobClass = nullptr;
1167 FiberJob* availableHead = nullptr;
1168 size_t retainedJobs = 0;
1169 size_t peakRetainedJobs = 0;
1170
1171 mutable volatile int32_t poolLock = 0;
1172
1173 Result acquire(FiberJob*& outJob);
1174 void releaseAcquired(FiberJob& job);
1175 void releaseAcquiredUnlocked(FiberJob& job);
1176 void recordPublished();
1177};
1178
1179struct SC_FIBERS_EXPORT FiberJobGroupError
1180{
1181 FiberJob* job = nullptr;
1182 Result result = Result(true);
1183};
1184
1186struct SC_FIBERS_EXPORT FiberJobGroup
1187{
1188 explicit FiberJobGroup(FiberJobScheduler& scheduler);
1190
1191 FiberJobGroup(const FiberJobGroup&) = delete;
1192 FiberJobGroup& operator=(const FiberJobGroup&) = delete;
1193
1194 Result spawn(FiberJobPool& pool, FiberJob::Procedure procedure, FiberJob** outJob = nullptr);
1195 Result spawn(FiberJobPool& pool, FiberJob::Procedure procedure, FiberCancellationToken token,
1196 FiberJob** outJob = nullptr);
1197 Result run();
1198 Result requestCancel();
1199 Result reset();
1200
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;
1205
1206 private:
1207 friend struct FiberJobScheduler;
1208
1209 FiberJobScheduler& jobScheduler;
1210 FiberJob* jobHead = nullptr;
1211 size_t pendingJobs = 0;
1212 size_t totalJobs = 0;
1213
1214 mutable volatile int32_t groupLock = 0;
1215
1216 Result prepareSpawn() const;
1217 void linkJob(FiberJob& job);
1218 void unlinkSpawnFailure(FiberJob& job);
1219 void complete(FiberJob& job);
1220};
1221
1223struct SC_FIBERS_EXPORT FiberTaskSpawnOptions
1224{
1225 FiberCancellationToken cancellationToken;
1226 FiberCounter* counter = nullptr;
1227 void* userData = nullptr;
1228 bool setUserData = false;
1229
1230 private:
1231 friend struct FiberTaskGroup;
1232 friend struct FiberScheduler;
1233 friend struct FiberTaskPool;
1234
1235 FiberTaskGroup* originGroup = nullptr;
1236 FiberTaskPool* originPool = nullptr;
1237 FiberTaskClass* originTaskClass = nullptr;
1238 FiberStackClass* originStackClass = nullptr;
1239};
1240
1241#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
1242#pragma warning(push)
1243#pragma warning(disable : 4324)
1244#endif
1245
1247struct SC_FIBERS_EXPORT FiberTask
1248{
1249 using Procedure = Function<Result(FiberScheduler&)>;
1250
1251 FiberTask();
1252 ~FiberTask();
1253
1254 FiberTask(const FiberTask&) = delete;
1255 FiberTask& operator=(const FiberTask&) = delete;
1256 FiberTask(FiberTask&&) = delete;
1257 FiberTask& operator=(FiberTask&&) = delete;
1258
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;
1268
1269 private:
1270 friend struct FiberTaskGroup;
1271 friend struct FiberTaskClassInternal;
1272 friend struct FiberTaskPool;
1273 friend struct FiberScheduler;
1274
1275 AlignedStorage<FiberContextStorageSize, FiberContextStorageAlignment> contextStorage;
1276
1277 Procedure procedure;
1278 FiberScheduler* scheduler = nullptr;
1279 FiberCounter* completionCounter = nullptr;
1280 FiberCancellationToken cancellationToken;
1281 FiberTask* nextReady = nullptr;
1282 FiberTask* previousReady = nullptr;
1283 FiberTask* nextWaiting = nullptr;
1284 FiberTask* nextActive = nullptr;
1285 FiberTask* previousActive = nullptr;
1286 FiberTask* nextGroup = nullptr;
1287 FiberTaskGroup* originGroup = nullptr;
1288 FiberCounter* waitingCounter = nullptr;
1289 FiberCounter* suspendCounter = nullptr;
1290 FiberTaskPool* originPool = nullptr;
1291 FiberTaskClass* originTaskClass = nullptr;
1292 FiberStackClass* originStackClass = nullptr;
1293 Span<char> originStackMemory;
1294 FiberWorker* preferredWorker = nullptr;
1295 FiberWorker* activeRegistryWorker = nullptr;
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;
1304
1305 [[nodiscard]] FiberContext& context();
1306 [[nodiscard]] const FiberContext& context() const;
1307};
1308
1310{
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*);
1316
1317 using Object = FiberTaskClassInternal;
1318};
1319using FiberTaskClassOpaque = OpaqueObject<FiberTaskClassDefinition>;
1320
1321struct SC_FIBERS_EXPORT FiberTaskClassOptions
1322{
1323 size_t maxTasks = 0;
1324};
1325
1326struct SC_FIBERS_EXPORT FiberTaskClassDiagnostics
1327{
1328 size_t capacity = 0;
1329 size_t activeTasks = 0;
1330 size_t availableTasks = 0;
1331 size_t peakActiveTasks = 0;
1332};
1333
1335struct SC_FIBERS_EXPORT FiberTaskClass
1336{
1339
1340 FiberTaskClass(const FiberTaskClass&) = delete;
1341 FiberTaskClass& operator=(const FiberTaskClass&) = delete;
1342 FiberTaskClass(FiberTaskClass&&) = delete;
1343 FiberTaskClass& operator=(FiberTaskClass&&) = delete;
1344
1345 [[nodiscard]] Result create(FiberAllocator& allocator, const FiberTaskClassOptions& options);
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();
1351 void diagnostics(FiberTaskClassDiagnostics& outDiagnostics) const;
1352
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;
1358
1359 private:
1360 friend struct FiberTaskPool;
1361
1362 FiberTaskClassOpaque internal;
1363};
1364
1366struct SC_FIBERS_EXPORT FiberCounter
1367{
1368 FiberCounter();
1369 ~FiberCounter();
1370
1371 FiberCounter(const FiberCounter&) = delete;
1372 FiberCounter& operator=(const FiberCounter&) = delete;
1373
1374 [[nodiscard]] size_t value() const;
1375
1376 private:
1377 friend struct FiberScheduler;
1378
1379 volatile size_t counterValue = 0;
1380
1381 FiberTask* waitingHead = nullptr;
1382 FiberTask* waitingTail = nullptr;
1383};
1384
1386struct SC_FIBERS_EXPORT FiberTaskGroupError
1387{
1388 FiberTask* task = nullptr;
1389 Result result = Result(true);
1390};
1391
1393struct SC_FIBERS_EXPORT FiberTaskGroup
1394{
1395 explicit FiberTaskGroup(FiberScheduler& scheduler);
1397
1398 FiberTaskGroup(const FiberTaskGroup&) = delete;
1399 FiberTaskGroup& operator=(const FiberTaskGroup&) = delete;
1400
1401 Result spawn(FiberTask& task, FiberStack& stack, FiberTask::Procedure procedure);
1402 Result spawn(FiberTask& task, FiberStack& stack, FiberTask::Procedure procedure, FiberCancellationToken token);
1403 Result spawn(FiberTask& task, FiberStack& stack, FiberTask::Procedure procedure,
1404 const FiberTaskSpawnOptions& options);
1405 Result spawn(FiberTaskPool& pool, FiberTask::Procedure procedure, FiberTask** outTask = nullptr);
1406 Result spawn(FiberTaskPool& pool, FiberTask::Procedure procedure, FiberCancellationToken token,
1407 FiberTask** outTask = nullptr);
1408 Result spawn(FiberTaskPool& pool, FiberTask::Procedure procedure, const FiberTaskSpawnOptions& options,
1409 FiberTask** outTask = nullptr);
1410 Result wait();
1411 Result waitAll(Result* outFirstError = nullptr);
1412 Result waitAllCancelOnParentCancel(Result* outFirstError = nullptr);
1413 Result waitCancelOnError(Result* outFirstError = nullptr);
1414 Result cancelAll();
1416 Result reset();
1417
1418 [[nodiscard]] size_t pending() const;
1419 [[nodiscard]] size_t countErrors() const;
1420 Result collectErrors(Span<FiberTaskGroupError> errors, size_t& outErrors) const;
1421
1422 private:
1423 friend struct FiberScheduler;
1424
1425 FiberScheduler& scheduler;
1426 FiberCounter counter;
1427 FiberTask* taskHead = nullptr;
1428
1429 Result prepareSpawn() const;
1430 void linkTask(FiberTask& task);
1431 Result findFirstError(Result* outFirstError) const;
1432};
1433
1435struct SC_FIBERS_EXPORT FiberTaskPoolDiagnostics
1436{
1437 size_t capacity = 0;
1438 size_t activeTasks = 0;
1439 size_t availableTasks = 0;
1440 bool classBacked = false;
1441
1442 FiberTaskClassDiagnostics taskClass;
1443 FiberStackClassDiagnostics stackClass;
1444};
1445
1446struct SC_FIBERS_EXPORT FiberTaskPool
1447{
1448 FiberTaskPool();
1449 FiberTaskPool(Span<FiberTask> taskStorage, Span<char> stackStorage, size_t stackSize);
1451
1452 FiberTaskPool(const FiberTaskPool&) = delete;
1453 FiberTaskPool& operator=(const FiberTaskPool&) = delete;
1454
1455 Result create(FiberTaskClass& taskClass, FiberStackClass& stackClass);
1456 Result close();
1457
1458 Result spawn(FiberScheduler& scheduler, FiberTask::Procedure procedure, FiberTask** outTask = nullptr,
1459 FiberCounter* counter = nullptr);
1460 Result spawn(FiberScheduler& scheduler, FiberTask::Procedure procedure, FiberCancellationToken token,
1461 FiberTask** outTask = nullptr, FiberCounter* counter = nullptr);
1462 Result spawn(FiberScheduler& scheduler, FiberTask::Procedure procedure, const FiberTaskSpawnOptions& options,
1463 FiberTask** outTask = nullptr);
1464
1465 [[nodiscard]] size_t capacity() const;
1466 [[nodiscard]] size_t activeCount() const;
1467 [[nodiscard]] size_t availableCount() const;
1468 [[nodiscard]] bool hasAvailableTask() const;
1469 void diagnostics(FiberTaskPoolDiagnostics& outDiagnostics) const;
1470 Result waitForSpawnCapacity(FiberScheduler& scheduler);
1471 Result waitForAvailableTask(FiberScheduler& scheduler);
1473 Result waitForAvailableTasks(FiberScheduler& scheduler, size_t minimumAvailable);
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;
1478
1479 private:
1480 friend struct FiberTaskGroup;
1481 friend struct FiberScheduler;
1482
1483 struct WaitNode
1484 {
1485 FiberCounter counter;
1486 WaitNode* next = nullptr;
1487
1488 size_t minimumAvailable = 1;
1489 bool notified = false;
1490 };
1491
1492 Span<FiberTask> tasks;
1493 Span<char> stacks;
1494 size_t stackSize = 0;
1495 size_t nextTask = 0;
1496
1497 FiberTaskClass* taskClass = nullptr;
1498 FiberStackClass* stackClass = nullptr;
1499
1500 WaitNode* availabilityWaitHead = nullptr;
1501 WaitNode* availabilityWaitTail = nullptr;
1502
1503 mutable volatile int32_t primitiveLock = 0;
1504
1505 Result stackAt(size_t stackIndex, FiberStack& outStack) const;
1506 void queueAvailabilityWaiter(WaitNode& node);
1507 FiberCounter* popAvailabilityWaiterForNotification();
1508 bool removeAvailabilityWaiter(WaitNode& node);
1509};
1510
1512struct SC_FIBERS_EXPORT FiberEvent
1513{
1514 explicit FiberEvent(bool signaled = false);
1515 ~FiberEvent();
1516
1517 FiberEvent(const FiberEvent&) = delete;
1518 FiberEvent& operator=(const FiberEvent&) = delete;
1519
1521 Result wait(FiberScheduler& scheduler);
1522 Result signal(FiberScheduler& scheduler);
1523 void reset();
1524
1525 [[nodiscard]] bool isSignaled() const;
1526
1527 private:
1528 struct WaitNode
1529 {
1530 FiberCounter counter;
1531 WaitNode* next = nullptr;
1532 bool notified = false;
1533 };
1534
1535 WaitNode* waitHead = nullptr;
1536 WaitNode* waitTail = nullptr;
1537 bool signaled = false;
1538 mutable volatile int32_t primitiveLock = 0;
1539
1540 void queueWaiter(WaitNode& node);
1541 bool removeWaiter(WaitNode& node);
1542};
1543
1545struct SC_FIBERS_EXPORT FiberAutoResetEvent
1546{
1547 explicit FiberAutoResetEvent(bool signaled = false);
1549
1551 FiberAutoResetEvent& operator=(const FiberAutoResetEvent&) = delete;
1552
1554 Result wait(FiberScheduler& scheduler);
1555 Result signal(FiberScheduler& scheduler);
1556 void reset();
1557
1558 [[nodiscard]] bool isSignaled() const;
1559
1560 private:
1561 struct WaitNode
1562 {
1563 FiberCounter counter;
1564 WaitNode* next = nullptr;
1565 bool notified = false;
1566 };
1567
1568 WaitNode* waitHead = nullptr;
1569 WaitNode* waitTail = nullptr;
1570 bool signaled = false;
1571 mutable volatile int32_t primitiveLock = 0;
1572
1573 void queueWaiter(WaitNode& node);
1574 bool popWaiter(WaitNode*& node);
1575 bool removeWaiter(WaitNode& node);
1576};
1577
1579struct SC_FIBERS_EXPORT FiberSemaphore
1580{
1581 explicit FiberSemaphore(size_t initialCount = 0);
1583
1584 FiberSemaphore(const FiberSemaphore&) = delete;
1585 FiberSemaphore& operator=(const FiberSemaphore&) = delete;
1586
1588 Result wait(FiberScheduler& scheduler);
1589 Result signal(FiberScheduler& scheduler, size_t count = 1);
1590
1591 [[nodiscard]] size_t available() const;
1592
1593 private:
1594 struct WaitNode
1595 {
1596 FiberCounter counter;
1597 WaitNode* next = nullptr;
1598 bool notified = false;
1599 };
1600
1601 WaitNode* waitHead = nullptr;
1602 WaitNode* waitTail = nullptr;
1603 size_t availableCount = 0;
1604 mutable volatile int32_t primitiveLock = 0;
1605
1606 void queueWaiter(WaitNode& node);
1607 WaitNode* popWaiter();
1608 bool removeWaiter(WaitNode& node);
1609};
1610
1612struct SC_FIBERS_EXPORT FiberMutex
1613{
1614 FiberMutex();
1615 ~FiberMutex();
1616
1617 FiberMutex(const FiberMutex&) = delete;
1618 FiberMutex& operator=(const FiberMutex&) = delete;
1619
1620 Result lock(FiberScheduler& scheduler);
1621 Result unlock(FiberScheduler& scheduler);
1622
1623 [[nodiscard]] bool isLocked() const;
1624 [[nodiscard]] bool isOwnedByCurrentTask(FiberScheduler& scheduler) const;
1625
1626 private:
1627 struct WaitNode
1628 {
1629 FiberCounter counter;
1630 WaitNode* next = nullptr;
1631 FiberTask* task = nullptr;
1632 bool notified = false;
1633 };
1634
1635 WaitNode* waitHead = nullptr;
1636 WaitNode* waitTail = nullptr;
1637 bool locked = false;
1638 FiberTask* owner = nullptr;
1639 mutable volatile int32_t primitiveLock = 0;
1640
1641 void queueWaiter(WaitNode& node);
1642 WaitNode* popWaiter();
1643 bool removeWaiter(WaitNode& node);
1644};
1645
1647struct SC_FIBERS_EXPORT FiberScheduler
1648{
1651
1652 FiberScheduler(const FiberScheduler&) = delete;
1653 FiberScheduler& operator=(const FiberScheduler&) = delete;
1654
1655 Result spawn(FiberTask& task, FiberStack& stack, FiberTask::Procedure procedure, FiberCounter* counter = nullptr);
1656 Result spawn(FiberTask& task, FiberStack& stack, FiberTask::Procedure procedure, FiberCancellationToken token,
1657 FiberCounter* counter = nullptr);
1658 Result spawn(FiberTask& task, FiberStack& stack, FiberTask::Procedure procedure,
1659 const FiberTaskSpawnOptions& options);
1660
1661 Result runOnce();
1662 Result runOnce(FiberWorker& worker);
1663 Result runOnce(FiberWorker& worker, Span<FiberWorker> workerGroup);
1664 Result runNoWait();
1665 Result runNoWait(FiberWorker& worker);
1666 Result runNoWait(FiberWorker& worker, Span<FiberWorker> workerGroup);
1667 Result runReadyFibers();
1668 Result runReadyFibers(FiberWorker& worker);
1669 Result runReadyFibers(FiberWorker& worker, Span<FiberWorker> workerGroup);
1670 Result run();
1671 Result run(FiberWorker& worker);
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);
1675 Result yield();
1676 Result shutdown();
1677 Result shutdown(FiberWorker& worker);
1678 Result shutdown(FiberWorker& worker, Span<FiberWorker> workerGroup);
1679
1680 Result requestCancel(FiberTask& task);
1681 Result requestCancel(FiberCancellationTokenSource& tokenSource);
1682 Result requestCancelAll();
1683
1684 void add(FiberCounter& counter);
1685 Result done(FiberCounter& counter);
1686 Result wait(FiberCounter& counter);
1687 Result waitUninterruptible(FiberCounter& counter);
1688
1689 void setTraceHooks(const FiberTraceHooks& hooks);
1690 void clearTraceHooks();
1691
1692 [[nodiscard]] FiberTask* currentTask();
1693 [[nodiscard]] const FiberTask* currentTask() const;
1694 [[nodiscard]] bool isCurrentTaskCancellationRequested() const;
1695
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;
1703 void schedulerDiagnostics(FiberSchedulerDiagnostics& diagnostics) const;
1704 void resetSchedulerDiagnostics();
1705 void workerDiagnostics(const FiberWorker& worker, FiberWorkerDiagnostics& diagnostics) const;
1706 void workerDiagnostics(Span<FiberWorker> workers, FiberWorkerDiagnostics& diagnostics) const;
1707
1708 void resetWorkerDiagnostics(FiberWorker& worker);
1709 void resetWorkerDiagnostics(Span<FiberWorker> workers);
1710
1711 private:
1712 friend struct FiberWorkerPool;
1713
1714 struct InjectionSlot;
1715
1716 FiberTask* readyHead = nullptr;
1717 FiberTask* readyTail = nullptr;
1718 FiberTask* activeHead = nullptr;
1719
1720 InjectionSlot* injectionQueue = nullptr;
1721 FiberAllocator* injectionAllocator = 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;
1730
1731 mutable volatile int32_t injectionLock = 0;
1732
1733 mutable size_t injectionLockAcquisitions = 0;
1734 mutable size_t injectionLockContentions = 0;
1735 mutable size_t injectionLockSpinRetries = 0;
1736 mutable size_t injectionLockPeakSpinRetries = 0;
1737
1738 FiberWorkerPool* workerPool = nullptr;
1739
1740 volatile size_t readyFibers = 0;
1741 volatile size_t globalReadyFibers = 0;
1742 volatile size_t activeFibers = 0;
1743
1744 mutable volatile int32_t schedulerLock = 0;
1745
1746 mutable size_t schedulerLockAcquisitions = 0;
1747 mutable size_t schedulerLockContentions = 0;
1748 mutable size_t schedulerLockSpinRetries = 0;
1749 mutable size_t schedulerLockPeakSpinRetries = 0;
1750
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;
1756
1757 FiberTraceHooks traceHooks;
1758
1759 enum class LockCategory : uint8_t
1760 {
1761 Spawn,
1762 Ready,
1763 Synchronization,
1764 Completion,
1765 Control,
1766 };
1767
1768 struct LockGuard;
1769 struct InjectionLockGuard;
1770
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;
1776
1777 void addUnlocked(FiberCounter& counter);
1778 Result initializeTaskForSpawn(FiberTask& task, FiberStack& stack, FiberTask::Procedure procedure,
1779 const FiberTaskSpawnOptions& options);
1780 [[nodiscard]] bool canPublishOwnerSpawn(FiberWorker& worker, const FiberTaskSpawnOptions& options) const;
1781 [[nodiscard]] bool canPublishInjectionSpawn(const FiberTaskSpawnOptions& options) const;
1782 void linkWorkerActiveForSpawn(FiberTask& task, FiberWorker& worker);
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);
1793 void pushReadyUnlocked(FiberTask& task, FiberWorker* preferredWorker);
1794 [[nodiscard]] bool tryPushWorkerReadyDeque(FiberWorker& worker, FiberTask& task);
1795 void pushWorkerReadyUnlocked(FiberWorker& worker, FiberTask& task);
1796
1797 [[nodiscard]] FiberTask* popReadyUnlocked();
1798 [[nodiscard]] FiberTask* popReadyBatchUnlocked(FiberWorker& worker);
1799 [[nodiscard]] FiberTask* popInjectionBatch(FiberWorker& worker);
1800 [[nodiscard]] FiberTask* popReadyUnlocked(FiberWorker& worker, Span<FiberWorker> stealWorkers);
1801 [[nodiscard]] FiberTask* popWorkerReadyUnlocked(FiberWorker& worker);
1802 [[nodiscard]] FiberTask* stealWorkerReadyUnlocked(FiberWorker& worker);
1803 [[nodiscard]] FiberTask* stealReadyUnlocked(FiberWorker& worker, Span<FiberWorker> stealWorkers);
1804 Result runReadyTask(FiberTask& task);
1805 Result runReadyTask(FiberTask& task, FiberWorker& worker);
1806 [[nodiscard]] bool preparePreferredWorkerReadyPublish(FiberTask& task, FiberWorker& worker,
1807 FiberWorker*& outPreferredWorker);
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);
1813 void moveActiveToWorkerUnlocked(FiberTask& task, FiberWorker& worker);
1814 void adoptWorkerReadyUnlocked(FiberWorker& worker);
1815 void unlinkWorkerActive(FiberTask& task);
1816 void cancelWorkerActiveUnlocked(FiberWorker& worker, FiberCancellationTokenSource* tokenSource);
1817 bool removeCounterWaiterUnlocked(FiberCounter& counter, FiberTask& task);
1818 Result waitImpl(FiberCounter& counter, bool interruptible);
1819 Result doneUnlocked(FiberCounter& counter);
1820 void wakeCounterWaitersUnlocked(FiberCounter& counter);
1821
1822 static void taskEntry(void* userData);
1823};
1824
1825#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
1826#pragma warning(pop)
1827#endif
1828} // namespace SC
Definition Fibers.h:330
Definition Fibers.h:308
Definition Fibers.h:324
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.
Definition Fibers.h:1090
Definition Fibers.h:1107
Definition Fibers.h:1102
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
Definition Fibers.h:1180
Retains one bounded wave of pooled FiberJob results for aggregate inspection.
Definition Fibers.h:1187
Definition Fibers.h:1081
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.
Definition Fibers.h:816
Definition Fibers.h:1004
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
Definition Fibers.h:595
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.
Definition Fibers.h:221
Definition Fibers.h:251
Definition Fibers.h:239
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
Definition Fibers.h:1310
Definition Fibers.h:1327
Definition Fibers.h:1322
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
Definition Fibers.h:1447
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
Definition Fibers.h:630
Definition Fibers.h:639
Definition Fibers.h:202
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
Definition Fibers.h:564
Definition Fibers.h:549
Span< char > stackGrowthSignalStackStorage
POSIX requires at least workerCount * FiberStackGrowthSignalStackSize bytes; Windows ignores this spa...
Definition Fibers.h:560
Definition Fibers.h:587
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