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