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 FiberSchedulerDiagnostics;
70struct FiberTraceEvent;
71struct FiberTraceHooks;
72struct FiberAllocator;
73struct FiberAllocatorInterface;
74struct FiberAllocatorVirtualOptions;
75struct FiberWorker;
76struct FiberWorkerPoolWakeEvent;
77struct FiberWorkerPool;
78struct FiberWorkerPoolOptions;
79struct FiberWorkerPoolThreadEntry;
80struct FiberWorkerThread;
81
82#if SC_PLATFORM_WINDOWS
83static constexpr int FiberContextStorageSize = 2048;
84#elif SC_PLATFORM_ARM64
85static constexpr int FiberContextStorageSize = 224;
86#else
87static constexpr int FiberContextStorageSize = 128;
88#endif
89static constexpr int FiberContextStorageAlignment = 16;
90static constexpr int FiberStackAlignment = 16;
91static constexpr int FiberStackMinimumSize = 4096;
93static constexpr int FiberInjectionSlotStorageSize = sizeof(void*) + sizeof(size_t);
94
97struct SC_FIBERS_EXPORT FiberStackSize
98{
99 static constexpr size_t FourKiB = 4 * 1024;
100 static constexpr size_t EightKiB = 8 * 1024;
101 static constexpr size_t ThirtyTwoKiB = 32 * 1024;
102 static constexpr size_t SixtyFourKiB = 64 * 1024;
103};
104
106struct SC_FIBERS_EXPORT FiberStack
107{
108 explicit FiberStack(Span<char> memory);
109
110 [[nodiscard]] Span<char> memory() const;
111 [[nodiscard]] size_t sizeInBytes() const;
112 [[nodiscard]] size_t usableSizeInBytes() const;
113 [[nodiscard]] size_t alignmentWasteInBytes() const;
114 [[nodiscard]] bool isUsable() const;
115 void fillHighWaterMark();
116 [[nodiscard]] size_t highWaterUsedBytes() const;
117 [[nodiscard]] size_t highWaterUnusedBytes() const;
118
119 private:
120 friend struct FiberScheduler;
121 friend struct FiberVirtualStack;
122
123 FiberStack(Span<char> memory, void* owner);
124
125 Span<char> stackMemory;
126 void* stackOwner = nullptr;
127};
128
130{
131 static constexpr int Windows = 64;
132 static constexpr int Apple = 64;
133 static constexpr int Linux = 64;
134 static constexpr int Default = 64;
135 static constexpr size_t Alignment = alignof(void*);
136
137 using Object = FiberVirtualStackInternal;
138};
139using FiberVirtualStackOpaque = OpaqueObject<FiberVirtualStackDefinition>;
140
142struct SC_FIBERS_EXPORT FiberVirtualStackOptions
143{
144 size_t usableSizeInBytes = FiberStackSize::SixtyFourKiB;
145 bool guardPage = true;
146};
147
149{
150 static constexpr int Windows = 176;
151 static constexpr int Apple = 176;
152 static constexpr int Linux = 176;
153 static constexpr int Default = 176;
154 static constexpr size_t Alignment = alignof(void*);
155
156 using Object = FiberStackClassInternal;
157};
158using FiberStackClassOpaque = OpaqueObject<FiberStackClassDefinition>;
159
160struct SC_FIBERS_EXPORT FiberStackClassOptions
161{
162 size_t stackSizeInBytes = FiberStackSize::SixtyFourKiB;
163 size_t maxStacks = 0;
164 bool guardPage = true;
165};
166
167struct SC_FIBERS_EXPORT FiberStackClassDiagnostics
168{
169 size_t capacity = 0;
170 size_t activeStacks = 0;
171 size_t peakActiveStacks = 0;
172 size_t stackSizeInBytes = 0;
173 size_t guardSizeInBytes = 0;
174 size_t reservedSizeBytes = 0;
175 size_t committedSizeBytes = 0;
176 size_t peakCommittedBytes = 0;
177 size_t highWaterUsedBytes = 0;
178};
179
181struct SC_FIBERS_EXPORT FiberStackClass
182{
185
186 FiberStackClass(const FiberStackClass&) = delete;
187 FiberStackClass& operator=(const FiberStackClass&) = delete;
189 FiberStackClass& operator=(FiberStackClass&&) = delete;
190
191 [[nodiscard]] Result reserve(const FiberStackClassOptions& options);
192 [[nodiscard]] Result acquire(FiberStack& outStack);
193 [[nodiscard]] Result release(FiberStack& stack);
194 [[nodiscard]] Result waitForAvailableSlot(FiberScheduler& scheduler);
195 void release();
196 void fillHighWaterMarks();
197 void diagnostics(FiberStackClassDiagnostics& outDiagnostics) const;
198
199 [[nodiscard]] bool isReserved() const;
200 [[nodiscard]] bool owns(const FiberStack& stack) const;
201 [[nodiscard]] size_t capacity() const;
202 [[nodiscard]] size_t activeCount() const;
203
204 private:
205 friend struct FiberTaskPool;
206
207 FiberStackClassOpaque internal;
208};
209
210enum class FiberAllocatorMode : uint8_t
211{
212 None,
213 Fixed,
214 Virtual,
215 Malloc,
216 Polymorphic,
217};
218
220{
221 size_t numAllocations = 0;
222 size_t numReleases = 0;
223
224 size_t requestedBytesAllocated = 0;
225 size_t requestedBytesReleased = 0;
226
227 size_t bytesInUse = 0;
228 size_t peakBytesInUse = 0;
229
230 size_t numAllocationFailures = 0;
231 size_t lastFailedAllocationSize = 0;
232 size_t largestFailedAllocationSize = 0;
233};
234
236{
237 size_t reserveBytes = 0;
238 size_t initialCommitBytes = 0;
239};
240
242{
243 virtual void* allocateImpl(const void* owner, size_t numBytes, size_t alignment) = 0;
244 virtual void releaseImpl(void* memory) = 0;
245 virtual ~FiberAllocatorInterface() {}
246};
247
249struct SC_FIBERS_EXPORT FiberAllocator
250{
251 FiberAllocator() = default;
252 FiberAllocator(const FiberAllocator&) = delete;
253 FiberAllocator& operator=(const FiberAllocator&) = delete;
254 FiberAllocator(FiberAllocator&&) = delete;
255 FiberAllocator& operator=(FiberAllocator&&) = delete;
257
258 [[nodiscard]] Result createFixed(Span<char> storage);
259 [[nodiscard]] Result createVirtual(FiberAllocatorVirtualOptions options);
260 [[nodiscard]] Result createMalloc();
261 [[nodiscard]] Result createPolymorphic(FiberAllocatorInterface& customAllocatorInterface);
262 [[nodiscard]] Result validateClose() const;
263 [[nodiscard]] Result close();
264
265 [[nodiscard]] void* allocate(const void* owner, size_t numBytes, size_t alignment);
266 void release(void* memory);
267 static void releaseFromAnyAllocator(void* memory);
268
269 [[nodiscard]] FiberAllocatorMode mode() const;
270 [[nodiscard]] FiberAllocatorStatistics statistics() const;
271 [[nodiscard]] bool isOpen() const;
272
273 [[nodiscard]] size_t used() const;
274 [[nodiscard]] size_t capacity() const;
275 [[nodiscard]] size_t peakUsed() const;
276 [[nodiscard]] size_t failedAllocationSize() const;
277 [[nodiscard]] size_t reservedBytes() const;
278 [[nodiscard]] size_t committedBytes() const;
279
280 private:
281 struct BlockHeader;
282
283 Result initializeFixedStorage(Span<char> storage);
284 void* allocateFromBlocks(const void* owner, size_t numBytes, size_t alignment);
285 void releaseBlock(BlockHeader& header);
286 bool ensureCommitted(size_t sizeInBytes);
287 void releaseVirtualMemory();
288 void recordAllocationFailure(size_t numBytes);
289 void resetState();
290
291 FiberAllocatorMode currentMode = FiberAllocatorMode::None;
292 FiberAllocatorStatistics currentStatistics;
293 Span<char> fixedStorage;
294 BlockHeader* firstBlock = nullptr;
295 FiberAllocatorInterface* allocatorInterface = nullptr;
296
297 void* virtualMemory = nullptr;
298 size_t virtualReservedBytes = 0;
299 size_t virtualCommittedBytes = 0;
300};
301
303struct SC_FIBERS_EXPORT FiberVirtualStack
304{
307
308 FiberVirtualStack(const FiberVirtualStack&) = delete;
309 FiberVirtualStack& operator=(const FiberVirtualStack&) = delete;
311 FiberVirtualStack& operator=(FiberVirtualStack&&) = delete;
312
313 Result reserve(const FiberVirtualStackOptions& options);
314 void release();
315
316 [[nodiscard]] FiberStack stack() const;
317 [[nodiscard]] Span<char> memory() const;
318 [[nodiscard]] size_t usableSizeInBytes() const;
319 [[nodiscard]] size_t reservedSizeInBytes() const;
320 [[nodiscard]] size_t guardSizeInBytes() const;
321 [[nodiscard]] bool isReserved() const;
322
323 private:
324 FiberVirtualStackOpaque internal;
325};
326
328enum class FiberTaskStatus
329{
330 Invalid,
331 Ready,
332 Running,
333 Waiting,
334 Completing,
335 Completed
336};
337
338enum class FiberTaskSuspendAction
339{
340 None,
341 Ready,
342 CounterWait
343};
344
345struct FiberCancellationToken;
346
348struct SC_FIBERS_EXPORT FiberWorker
349{
350 FiberWorker();
351 ~FiberWorker();
352
353 FiberWorker(const FiberWorker&) = delete;
354 FiberWorker& operator=(const FiberWorker&) = delete;
355
356 [[nodiscard]] bool isActive() const;
357 [[nodiscard]] FiberScheduler* scheduler();
358 [[nodiscard]] const FiberScheduler* scheduler() const;
359 [[nodiscard]] FiberTask* runningTask();
360 [[nodiscard]] const FiberTask* runningTask() const;
361
362 private:
363 friend struct FiberWorkerPool;
364 friend struct FiberScheduler;
365
366 Result begin(FiberScheduler& fiberScheduler);
367 void end();
368
369 FiberScheduler* workerScheduler = nullptr;
370 FiberScheduler* localQueueScheduler = nullptr;
371 FiberTask* workerTask = nullptr;
372 FiberTask* localReadyHead = nullptr;
373 FiberTask* localReadyTail = nullptr;
374 FiberTask* activeHead = nullptr;
375 FiberTask** localDeque = nullptr;
376 FiberAllocator* localDequeAllocator = nullptr;
377 size_t localReadyFibers = 0;
378 size_t localDequeCapacity = 0;
379 size_t localDequeHead = 0;
380 volatile size_t localDequeTop = 0;
381 volatile size_t localDequeBottom = 0;
382 size_t localReadyPeakFibers = 0;
383 size_t localSpilledFibers = 0;
384 size_t stealAttempts = 0;
385 size_t stealVictimProbes = 0;
386 size_t stolenFibers = 0;
387 size_t stolenBatches = 0;
388 size_t stolenBatchPeak = 0;
389 size_t failedSteals = 0;
390 size_t stealCursor = 0;
391 volatile size_t runAttempts = 0;
392 volatile size_t idlePolls = 0;
393 volatile size_t idleSpinIterations = 0;
394 volatile size_t parkAttempts = 0;
395 volatile size_t parkedWakeups = 0;
396 volatile size_t executedFibers = 0;
397 size_t completedFibers = 0;
398 volatile size_t yieldedFibers = 0;
399 size_t waitingFibers = 0;
400 volatile int32_t activeRegistryLock = 0;
401 bool workerActive = false;
402 bool localSchedulingActive = false;
403 bool stealCursorInitialized = false;
404
405 AlignedStorage<FiberContextStorageSize, FiberContextStorageAlignment> rootContextStorage;
406
407 [[nodiscard]] FiberContext& rootContext();
408};
409
410#if SC_PLATFORM_WINDOWS
411static constexpr int FiberWorkerThreadStorageSize = sizeof(void*);
412#else
413static constexpr int FiberWorkerThreadStorageSize = sizeof(void*) * 2;
414#endif
415static constexpr int FiberWorkerThreadStorageAlignment = alignof(void*);
416
418struct SC_FIBERS_EXPORT FiberWorkerThread
419{
422
423 FiberWorkerThread(const FiberWorkerThread&) = delete;
424 FiberWorkerThread& operator=(const FiberWorkerThread&) = delete;
425
426 [[nodiscard]] bool wasStarted() const;
427 [[nodiscard]] Result result() const;
428
429 private:
430 friend struct FiberWorkerPool;
431 friend struct FiberWorkerPoolThreadEntry;
432
433 AlignedStorage<FiberWorkerThreadStorageSize, FiberWorkerThreadStorageAlignment> threadStorage;
434
435 FiberWorkerPool* pool = nullptr;
436 size_t workerIndex = 0;
437 uint64_t affinityMask = 0;
438 uint8_t priority = 0;
439 Result threadResult = Result(true);
440 bool started = false;
441
442 Result startThread();
443 Result joinThread();
444 Result runThreadEntry();
445 Result applyThreadPolicy();
446};
447
448enum class FiberWorkerThreadPriority : uint8_t
449{
450 Default,
451 Low,
452 Normal,
453 High,
454};
455
456struct SC_FIBERS_EXPORT FiberWorkerPoolOptions
457{
458 FiberAllocator* dequeAllocator = nullptr;
459 size_t dequeCapacityPerWorker = 0;
460 FiberAllocator* injectionAllocator = nullptr;
461 size_t injectionCapacity = 0;
462 size_t idleSpinAttempts = 32;
463 Span<const uint64_t> affinityMasks;
464 FiberWorkerThreadPriority threadPriority = FiberWorkerThreadPriority::Default;
465};
466
467struct SC_FIBERS_EXPORT FiberWorkerDiagnostics
468{
469 size_t readyFibers = 0;
470 size_t readyPeakFibers = 0;
471 size_t dequeCapacity = 0;
472 size_t spilledFibers = 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 runAttempts = 0;
480 size_t idlePolls = 0;
481 size_t idleSpinIterations = 0;
482 size_t parkAttempts = 0;
483 size_t parkedWakeups = 0;
484 size_t executedFibers = 0;
485 size_t completedFibers = 0;
486 size_t yieldedFibers = 0;
487 size_t waitingFibers = 0;
488};
489
490struct SC_FIBERS_EXPORT FiberWorkerPoolWakeDiagnostics
491{
493 size_t wakeNotifications = 0;
495 size_t wakeSignals = 0;
496};
497
498struct SC_FIBERS_EXPORT FiberSchedulerDiagnostics
499{
500 size_t readyFibers = 0;
501 size_t globalReadyFibers = 0;
502 size_t activeFibers = 0;
503 size_t injectionCapacity = 0;
504 size_t injectionReady = 0;
505 size_t injectionPublishing = 0;
506 size_t injectionPeak = 0;
507 size_t injectionSpills = 0;
508 size_t injectionClaimBatchPeak = 0;
509 size_t injectionLockAcquisitions = 0;
510 size_t injectionLockContentions = 0;
511 size_t injectionLockSpinRetries = 0;
512 size_t injectionLockPeakSpinRetries = 0;
513
514 size_t lockAcquisitions = 0;
515 size_t lockContentions = 0;
516 size_t lockSpinRetries = 0;
517 size_t lockPeakSpinRetries = 0;
518 size_t lockSpawn = 0;
519 size_t lockReady = 0;
520 size_t lockSynchronization = 0;
521 size_t lockCompletion = 0;
522 size_t lockControl = 0;
523};
524
525enum class FiberTraceEventType : uint8_t
526{
527 TaskStarted,
528 TaskYielded,
529 TaskWaiting,
530 TaskCompleted,
531};
532
533struct SC_FIBERS_EXPORT FiberTraceEvent
534{
535 FiberTraceEventType type = FiberTraceEventType::TaskStarted;
536 FiberScheduler* scheduler = nullptr;
537 FiberWorker* worker = nullptr;
538 FiberTask* task = nullptr;
539 size_t value = 0;
540};
541
542struct SC_FIBERS_EXPORT FiberTraceHooks
543{
544 using Callback = void (*)(void* userData, const FiberTraceEvent& event);
545
546 Callback callback = nullptr;
547 void* userData = nullptr;
548};
549
551struct SC_FIBERS_EXPORT FiberWorkerPool
552{
555
556 FiberWorkerPool(const FiberWorkerPool&) = delete;
557 FiberWorkerPool& operator=(const FiberWorkerPool&) = delete;
558
559 Result start(FiberScheduler& scheduler, Span<FiberWorker> workerStorage, Span<FiberWorkerThread> threadStorage);
560 Result start(FiberScheduler& scheduler, Span<FiberWorker> workerStorage, Span<FiberWorkerThread> threadStorage,
561 const FiberWorkerPoolOptions& options);
562 Result requestStop();
563 Result join();
564 Result shutdown();
565
566 [[nodiscard]] bool isRunning() const;
567 [[nodiscard]] size_t workerCount() const;
569 [[nodiscard]] size_t parkedWorkerCount() const;
572
574 {
575 static constexpr int Windows = sizeof(void*) * 16;
576 static constexpr int Apple = sizeof(void*) * 16;
577 static constexpr int Linux = sizeof(void*) * 16;
578 static constexpr int Default = Linux;
579
580 static constexpr size_t Alignment = alignof(void*);
581
582 using Object = FiberWorkerPoolWakeEvent;
583 };
584
585 using WakeEventOpaque = OpaqueObject<WakeEventDefinition>;
586
587 private:
588 friend struct FiberScheduler;
589 friend struct FiberWorkerThread;
590
591 FiberScheduler* poolScheduler = nullptr;
592 Span<FiberWorker> workers;
593 Span<FiberWorkerThread> threads;
594 WakeEventOpaque wakeEvent;
595 mutable volatile int32_t stopRequested = 0;
596 mutable volatile int32_t running = 0;
597 size_t idleSpinAttempts = 0;
598 bool localDequesCreated = false;
599 bool injectionCreated = false;
600
601 void wakeOneWorker();
602 void wakeAllWorkers();
603 void resetWakeDiagnostics();
604 [[nodiscard]] bool waitForWork(uint32_t observedGeneration);
605 [[nodiscard]] uint32_t wakeGeneration() const;
606
607 Result workerMain(size_t workerIndex);
608};
609
611struct SC_FIBERS_EXPORT FiberCancellationTokenSource
612{
614
617
618 void requestCancel();
619 void reset();
620 Result check() const;
621
622 [[nodiscard]] bool isCancellationRequested() const;
623 [[nodiscard]] FiberCancellationToken token() const;
624
625 private:
626 friend struct FiberCancellationToken;
627 friend struct FiberJobScheduler;
628 friend struct FiberScheduler;
629
630 mutable volatile int32_t requested = 0;
631};
632
634struct SC_FIBERS_EXPORT FiberCancellationToken
635{
637
638 Result check() const;
639
640 [[nodiscard]] bool isValid() const;
641 [[nodiscard]] bool isCancellationRequested() const;
642
643 private:
644 friend struct FiberCancellationTokenSource;
645 friend struct FiberJobScheduler;
646 friend struct FiberScheduler;
647
648 explicit FiberCancellationToken(const FiberCancellationTokenSource& tokenSource);
649
650 const FiberCancellationTokenSource* source = nullptr;
651};
652
654enum class FiberJobStatus
655{
656 Invalid,
657 Ready,
658 Running,
659 Completed
660};
661
663struct SC_FIBERS_EXPORT FiberJobContext
664{
665 [[nodiscard]] FiberJobScheduler& scheduler() const;
666 [[nodiscard]] FiberJob& job() const;
667 [[nodiscard]] bool isCancellationRequested() const;
668 Result checkCancellation() const;
669
670 private:
671 friend struct FiberJobScheduler;
672
674
675 FiberJobScheduler* jobScheduler = nullptr;
676 FiberJob* currentJob = nullptr;
677};
678
680struct SC_FIBERS_EXPORT FiberJob
681{
682 using Procedure = Function<Result(FiberJobContext&)>;
683
684 FiberJob();
685 ~FiberJob();
686
687 FiberJob(const FiberJob&) = delete;
688 FiberJob& operator=(const FiberJob&) = delete;
689 FiberJob(FiberJob&&) = delete;
690 FiberJob& operator=(FiberJob&&) = delete;
691
692 [[nodiscard]] bool isActive() const;
693 [[nodiscard]] bool isCompleted() const;
694 [[nodiscard]] bool isCancellationRequested() const;
695 [[nodiscard]] FiberJobStatus status() const;
696 [[nodiscard]] Result result() const;
697
698 private:
699 friend struct FiberJobContext;
700 friend struct FiberJobGroup;
701 friend struct FiberJobPool;
702 friend struct FiberJobScheduler;
703
704 Procedure procedure;
705 FiberJobScheduler* ownerScheduler = nullptr;
706 FiberJobPool* ownerPool = nullptr;
707 FiberJob* nextAvailable = nullptr;
708 FiberJobGroup* originGroup = nullptr;
709 FiberJob* nextGroup = nullptr;
710 FiberCancellationToken cancellationToken;
711 FiberJobWorker* accountingWorker = nullptr;
712 Result jobResult = Result(true);
713 volatile int32_t jobStatus = static_cast<int32_t>(FiberJobStatus::Invalid);
714 uint32_t cancelGeneration = 0;
715 volatile bool cancelRequested = false;
716 bool poolRetained = false;
717};
718
719struct SC_FIBERS_EXPORT FiberJobWorkerDiagnostics
720{
721 size_t readyJobs = 0;
722 size_t readyPeakJobs = 0;
723 size_t dequeCapacity = 0;
724 size_t executedJobs = 0;
725 size_t claimBatches = 0;
726 size_t claimedJobs = 0;
727 size_t claimBatchPeak = 0;
728 size_t stealAttempts = 0;
729 size_t stolenJobs = 0;
730 size_t failedSteals = 0;
731};
732
733#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
734#pragma warning(push)
735#pragma warning(disable : 4324)
736#endif
738struct SC_FIBERS_EXPORT FiberJobWorker
739{
742
743 FiberJobWorker(const FiberJobWorker&) = delete;
744 FiberJobWorker& operator=(const FiberJobWorker&) = delete;
745
746 [[nodiscard]] bool isActive() const;
747 [[nodiscard]] FiberJobScheduler* scheduler();
748 [[nodiscard]] const FiberJobScheduler* scheduler() const;
749 [[nodiscard]] FiberJob* runningJob();
750 [[nodiscard]] const FiberJob* runningJob() const;
751
752 private:
753 friend struct FiberJobScheduler;
754 friend struct FiberJobWorkerPool;
755
756 Result begin(FiberJobScheduler& scheduler);
757 void end();
758
759 FiberJobScheduler* workerScheduler = nullptr;
760 FiberJobScheduler* localQueueScheduler = nullptr;
761 FiberJob* workerJob = nullptr;
762 FiberJob** localDeque = nullptr;
763 FiberAllocator* localDequeAllocator = nullptr;
764 size_t localDequeCapacity = 0;
765 size_t lastDequeCapacity = 0;
766 volatile size_t localDequeTop = 0;
767 volatile size_t localDequeBottom = 0;
768
769 size_t localReadyPeakJobs = 0;
770 size_t executedJobs = 0;
771 size_t claimBatches = 0;
772 size_t claimedJobs = 0;
773 size_t claimBatchPeak = 0;
774 size_t stealCursor = 0;
775 size_t stealAttempts = 0;
776 size_t stolenJobs = 0;
777 size_t failedSteals = 0;
778 bool workerActive = false;
779 bool distributedCounts = false;
780
781 alignas(128) volatile size_t ownedReadyJobs = 0;
782 alignas(128) volatile size_t ownedActiveJobs = 0;
783};
784#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
785#pragma warning(pop)
786#endif
787
792#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
793#pragma warning(push)
794#pragma warning(disable : 4324)
795#endif
796struct SC_FIBERS_EXPORT FiberJobScheduler
797{
800
801 FiberJobScheduler(const FiberJobScheduler&) = delete;
802 FiberJobScheduler& operator=(const FiberJobScheduler&) = delete;
803
804 Result create(Span<FiberJob*> readyStorage);
805 Result close();
806
807 Result createWorkerDeques(FiberAllocator& allocator, Span<FiberJobWorker> workers, size_t capacityPerWorker);
808 void releaseWorkerDeques(Span<FiberJobWorker> workers);
809
810 Result spawn(FiberJob& job, FiberJob::Procedure procedure);
811 Result spawn(FiberJob& job, FiberJob::Procedure procedure, FiberCancellationToken token);
813 Result spawn(Span<FiberJob> jobs, FiberJob::Procedure procedure);
814 Result spawn(Span<FiberJob> jobs, FiberJob::Procedure procedure, FiberCancellationToken token);
815 Result runOne(bool& outRanJob);
816 Result runOne(FiberJobWorker& worker, Span<FiberJobWorker> workerGroup, bool& outRanJob);
817 Result run();
818 Result run(FiberJobWorker& worker, Span<FiberJobWorker> workerGroup);
819 Result shutdown();
820
821 Result requestCancel(FiberJob& job);
822 Result requestCancel(FiberCancellationTokenSource& tokenSource);
823 Result requestCancelAll();
824
825 [[nodiscard]] bool isOpen() const;
827 [[nodiscard]] bool hasReadyJobs() const;
829 [[nodiscard]] bool hasActiveJobs() const;
830 [[nodiscard]] size_t capacity() const;
832 [[nodiscard]] size_t readyJobCount() const;
834 [[nodiscard]] size_t activeJobCount() const;
835 [[nodiscard]] FiberJob* currentJob();
836
839 void workerDiagnostics(const FiberJobWorker& worker, FiberJobWorkerDiagnostics& outDiagnostics) const;
840
841 private:
842 friend struct FiberJobContext;
843 friend struct FiberJob;
844 friend struct FiberJobWorkerPool;
845
846 Span<FiberJob*> queueStorage;
847 FiberJob* runningJob = nullptr;
848 size_t queueHead = 0;
849 size_t queueTail = 0;
850 size_t queueCount = 0;
851
852 alignas(128) volatile size_t readyJobs = 0;
853 alignas(128) volatile size_t activeJobs = 0;
854
855 alignas(128) mutable volatile int32_t queueLock = 0;
856 volatile uint32_t cancelGeneration = 0;
857
858 FiberJobWorkerPool* workerPool = nullptr;
859
860 struct QueueLockGuard;
861
862 void initializeJobForSpawn(FiberJob& job, FiberJob::Procedure procedure, FiberCancellationToken token);
863 Result complete(FiberJob& job, Result result);
864 bool tryPushWorkerDeque(FiberJobWorker& worker, FiberJob& job);
865 FiberJob* popWorkerReady(FiberJobWorker& worker);
866 FiberJob* stealWorkerReady(FiberJobWorker& worker);
867 FiberJob* stealReady(FiberJobWorker& worker, Span<FiberJobWorker> workerGroup);
868 bool usesDistributedAccounting(const FiberJobWorker& worker) const;
869 bool isWorkerStopRequested() const;
870};
871#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
872#pragma warning(pop)
873#endif
874
876struct SC_FIBERS_EXPORT FiberJobWorkerThread
877{
880
882 FiberJobWorkerThread& operator=(const FiberJobWorkerThread&) = delete;
883
884 [[nodiscard]] bool wasStarted() const;
885 [[nodiscard]] Result result() const;
886
887 private:
888 friend struct FiberJobWorkerPool;
889 friend struct FiberJobWorkerPoolThreadEntry;
890
891 AlignedStorage<FiberWorkerThreadStorageSize, FiberWorkerThreadStorageAlignment> threadStorage;
892
893 FiberJobWorkerPool* pool = nullptr;
894 size_t workerIndex = 0;
895 uint64_t affinityMask = 0;
896 uint8_t priority = 0;
897 Result threadResult = Result(true);
898 bool started = false;
899
900 Result startThread();
901 Result joinThread();
902 Result runThreadEntry();
903 Result applyThreadPolicy();
904};
905
906struct SC_FIBERS_EXPORT FiberJobWorkerPoolOptions
907{
908 FiberAllocator* dequeAllocator = nullptr;
909 size_t dequeCapacityPerWorker = 0;
910 size_t idleSpinAttempts = 32;
911
913 bool keepAliveWhenIdle = false;
914
915 Span<const uint64_t> affinityMasks;
916 FiberWorkerThreadPriority threadPriority = FiberWorkerThreadPriority::Default;
917};
918
920struct SC_FIBERS_EXPORT FiberJobWorkerPool
921{
924
925 FiberJobWorkerPool(const FiberJobWorkerPool&) = delete;
926 FiberJobWorkerPool& operator=(const FiberJobWorkerPool&) = delete;
927
928 Result start(FiberJobScheduler& scheduler, Span<FiberJobWorker> workerStorage,
929 Span<FiberJobWorkerThread> threadStorage, const FiberJobWorkerPoolOptions& options);
931 Result waitIdle();
932 Result requestStop();
934 Result join();
935 Result shutdown();
936
937 [[nodiscard]] bool isRunning() const;
938 [[nodiscard]] size_t workerCount() const;
939 [[nodiscard]] size_t parkedWorkerCount() const;
940
942 {
943 static constexpr int Windows = sizeof(void*) * 16;
944 static constexpr int Apple = sizeof(void*) * 16;
945 static constexpr int Linux = sizeof(void*) * 16;
946 static constexpr int Default = Linux;
947
948 static constexpr size_t Alignment = alignof(void*);
949
950 using Object = FiberWorkerPoolWakeEvent;
951 };
952
953 using WakeEventOpaque = OpaqueObject<WakeEventDefinition>;
954
955 private:
956 friend struct FiberJobScheduler;
957 friend struct FiberJobWorkerThread;
958
959 FiberJobScheduler* poolScheduler = nullptr;
960 Span<FiberJobWorker> workers;
961 Span<FiberJobWorkerThread> threads;
962 WakeEventOpaque wakeEvent;
963 mutable volatile int32_t stopRequested = 0;
964 mutable volatile int32_t running = 0;
965 size_t idleSpinAttempts = 0;
966 bool keepAliveWhenIdle = false;
967
968 void wakeOneWorker();
969 void wakeAllWorkers();
970 [[nodiscard]] bool waitForWork(uint32_t observedGeneration);
971 [[nodiscard]] uint32_t wakeGeneration() const;
972 [[nodiscard]] bool isStopRequested() const;
973
974 Result workerMain(size_t workerIndex);
975};
976
977struct SC_FIBERS_EXPORT FiberJobPoolDiagnostics
978{
979 size_t capacity = 0;
980 size_t retainedJobs = 0;
981 size_t availableJobs = 0;
983 size_t peakRetainedJobs = 0;
984};
985
987{
988 static constexpr int Windows = 48;
989 static constexpr int Apple = 48;
990 static constexpr int Linux = 48;
991 static constexpr int Default = 48;
992 static constexpr size_t Alignment = alignof(void*);
993
994 using Object = FiberJobClassInternal;
995};
996using FiberJobClassOpaque = OpaqueObject<FiberJobClassDefinition>;
997
998struct SC_FIBERS_EXPORT FiberJobClassOptions
999{
1000 size_t maxJobs = 0;
1001};
1002
1003struct SC_FIBERS_EXPORT FiberJobClassDiagnostics
1004{
1005 size_t capacity = 0;
1006 bool bound = false;
1007};
1008
1010struct SC_FIBERS_EXPORT FiberJobClass
1011{
1012 FiberJobClass();
1014
1015 FiberJobClass(const FiberJobClass&) = delete;
1016 FiberJobClass& operator=(const FiberJobClass&) = delete;
1017 FiberJobClass(FiberJobClass&&) = delete;
1018 FiberJobClass& operator=(FiberJobClass&&) = delete;
1019
1020 [[nodiscard]] Result create(FiberAllocator& allocator, const FiberJobClassOptions& options);
1021 [[nodiscard]] Result validateClose() const;
1022 [[nodiscard]] Result close();
1023 void diagnostics(FiberJobClassDiagnostics& outDiagnostics) const;
1024
1025 [[nodiscard]] bool isOpen() const;
1026 [[nodiscard]] bool owns(const FiberJob& job) const;
1027 [[nodiscard]] size_t capacity() const;
1028
1029 private:
1030 friend struct FiberJobPool;
1031
1032 FiberJobClassOpaque internal;
1033};
1034
1036struct SC_FIBERS_EXPORT FiberJobPool
1037{
1038 FiberJobPool();
1039 ~FiberJobPool();
1040
1041 FiberJobPool(const FiberJobPool&) = delete;
1042 FiberJobPool& operator=(const FiberJobPool&) = delete;
1043
1044 Result create(Span<FiberJob> jobStorage);
1045 Result create(FiberJobClass& jobClass);
1046 Result close();
1047 Result spawn(FiberJobScheduler& scheduler, FiberJob::Procedure procedure, FiberJob*& outJob);
1048 Result spawn(FiberJobScheduler& scheduler, FiberJob::Procedure procedure, FiberCancellationToken token,
1049 FiberJob*& outJob);
1050 Result release(FiberJob& job);
1051
1052 [[nodiscard]] bool isOpen() const;
1053 [[nodiscard]] bool owns(const FiberJob& job) const;
1054 [[nodiscard]] size_t capacity() const;
1055 [[nodiscard]] size_t retainedCount() const;
1056 [[nodiscard]] size_t availableCount() const;
1057 void diagnostics(FiberJobPoolDiagnostics& outDiagnostics) const;
1058
1059 private:
1060 friend struct FiberJobGroup;
1061
1062 Span<FiberJob> jobs;
1063 FiberJobClass* jobClass = nullptr;
1064 FiberJob* availableHead = nullptr;
1065 size_t retainedJobs = 0;
1066 size_t peakRetainedJobs = 0;
1067
1068 mutable volatile int32_t poolLock = 0;
1069
1070 Result acquire(FiberJob*& outJob);
1071 void releaseAcquired(FiberJob& job);
1072 void releaseAcquiredUnlocked(FiberJob& job);
1073 void recordPublished();
1074};
1075
1076struct SC_FIBERS_EXPORT FiberJobGroupError
1077{
1078 FiberJob* job = nullptr;
1079 Result result = Result(true);
1080};
1081
1083struct SC_FIBERS_EXPORT FiberJobGroup
1084{
1085 explicit FiberJobGroup(FiberJobScheduler& scheduler);
1087
1088 FiberJobGroup(const FiberJobGroup&) = delete;
1089 FiberJobGroup& operator=(const FiberJobGroup&) = delete;
1090
1091 Result spawn(FiberJobPool& pool, FiberJob::Procedure procedure, FiberJob** outJob = nullptr);
1092 Result spawn(FiberJobPool& pool, FiberJob::Procedure procedure, FiberCancellationToken token,
1093 FiberJob** outJob = nullptr);
1094 Result run();
1095 Result requestCancel();
1096 Result reset();
1097
1098 [[nodiscard]] size_t pendingCount() const;
1099 [[nodiscard]] size_t jobCount() const;
1100 [[nodiscard]] size_t countErrors() const;
1101 Result collectErrors(Span<FiberJobGroupError> errors, size_t& outErrors) const;
1102
1103 private:
1104 friend struct FiberJobScheduler;
1105
1106 FiberJobScheduler& jobScheduler;
1107 FiberJob* jobHead = nullptr;
1108 size_t pendingJobs = 0;
1109 size_t totalJobs = 0;
1110
1111 mutable volatile int32_t groupLock = 0;
1112
1113 Result prepareSpawn() const;
1114 void linkJob(FiberJob& job);
1115 void unlinkSpawnFailure(FiberJob& job);
1116 void complete(FiberJob& job);
1117};
1118
1120struct SC_FIBERS_EXPORT FiberTaskSpawnOptions
1121{
1122 FiberCancellationToken cancellationToken;
1123 FiberCounter* counter = nullptr;
1124 void* userData = nullptr;
1125 bool setUserData = false;
1126
1127 private:
1128 friend struct FiberTaskGroup;
1129 friend struct FiberScheduler;
1130 friend struct FiberTaskPool;
1131
1132 FiberTaskGroup* originGroup = nullptr;
1133 FiberTaskPool* originPool = nullptr;
1134 FiberTaskClass* originTaskClass = nullptr;
1135 FiberStackClass* originStackClass = nullptr;
1136};
1137
1138#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
1139#pragma warning(push)
1140#pragma warning(disable : 4324)
1141#endif
1142
1144struct SC_FIBERS_EXPORT FiberTask
1145{
1146 using Procedure = Function<Result(FiberScheduler&)>;
1147
1148 FiberTask();
1149 ~FiberTask();
1150
1151 FiberTask(const FiberTask&) = delete;
1152 FiberTask& operator=(const FiberTask&) = delete;
1153 FiberTask(FiberTask&&) = delete;
1154 FiberTask& operator=(FiberTask&&) = delete;
1155
1156 [[nodiscard]] bool isValid() const;
1157 [[nodiscard]] bool isStarted() const;
1158 [[nodiscard]] bool isCompleted() const;
1159 [[nodiscard]] bool isActive() const;
1160 [[nodiscard]] bool isCancellationRequested() const;
1161 [[nodiscard]] FiberTaskStatus status() const;
1162 [[nodiscard]] Result result() const;
1163 void setUserData(void* data);
1164 [[nodiscard]] void* userData() const;
1165
1166 private:
1167 friend struct FiberTaskGroup;
1168 friend struct FiberTaskClassInternal;
1169 friend struct FiberTaskPool;
1170 friend struct FiberScheduler;
1171
1172 AlignedStorage<FiberContextStorageSize, FiberContextStorageAlignment> contextStorage;
1173
1174 Procedure procedure;
1175 FiberScheduler* scheduler = nullptr;
1176 FiberCounter* completionCounter = nullptr;
1177 FiberCancellationToken cancellationToken;
1178 FiberTask* nextReady = nullptr;
1179 FiberTask* previousReady = nullptr;
1180 FiberTask* nextWaiting = nullptr;
1181 FiberTask* nextActive = nullptr;
1182 FiberTask* previousActive = nullptr;
1183 FiberTask* nextGroup = nullptr;
1184 FiberTaskGroup* originGroup = nullptr;
1185 FiberCounter* waitingCounter = nullptr;
1186 FiberCounter* suspendCounter = nullptr;
1187 FiberTaskPool* originPool = nullptr;
1188 FiberTaskClass* originTaskClass = nullptr;
1189 FiberStackClass* originStackClass = nullptr;
1190 Span<char> originStackMemory;
1191 FiberWorker* preferredWorker = nullptr;
1192 FiberWorker* activeRegistryWorker = nullptr;
1193 void* runningWorker = nullptr;
1194 void* stackOwner = nullptr;
1195 void* taskUserData = nullptr;
1196 Result taskResult = Result(true);
1197 volatile int32_t taskStatus = static_cast<int32_t>(FiberTaskStatus::Invalid);
1198 FiberTaskSuspendAction suspendAction = FiberTaskSuspendAction::None;
1199 volatile bool cancelRequested = false;
1200 bool suspendInterruptible = false;
1201
1202 [[nodiscard]] FiberContext& context();
1203 [[nodiscard]] const FiberContext& context() const;
1204};
1205
1207{
1208 static constexpr int Windows = 96;
1209 static constexpr int Apple = 96;
1210 static constexpr int Linux = 96;
1211 static constexpr int Default = 96;
1212 static constexpr size_t Alignment = alignof(void*);
1213
1214 using Object = FiberTaskClassInternal;
1215};
1216using FiberTaskClassOpaque = OpaqueObject<FiberTaskClassDefinition>;
1217
1218struct SC_FIBERS_EXPORT FiberTaskClassOptions
1219{
1220 size_t maxTasks = 0;
1221};
1222
1223struct SC_FIBERS_EXPORT FiberTaskClassDiagnostics
1224{
1225 size_t capacity = 0;
1226 size_t activeTasks = 0;
1227 size_t availableTasks = 0;
1228 size_t peakActiveTasks = 0;
1229};
1230
1232struct SC_FIBERS_EXPORT FiberTaskClass
1233{
1236
1237 FiberTaskClass(const FiberTaskClass&) = delete;
1238 FiberTaskClass& operator=(const FiberTaskClass&) = delete;
1239 FiberTaskClass(FiberTaskClass&&) = delete;
1240 FiberTaskClass& operator=(FiberTaskClass&&) = delete;
1241
1242 [[nodiscard]] Result create(FiberAllocator& allocator, const FiberTaskClassOptions& options);
1243 [[nodiscard]] Result acquire(FiberTask*& outTask);
1244 [[nodiscard]] Result release(FiberTask& task);
1245 [[nodiscard]] Result waitForAvailableSlot(FiberScheduler& scheduler);
1246 [[nodiscard]] Result validateClose() const;
1247 [[nodiscard]] Result close();
1248 void diagnostics(FiberTaskClassDiagnostics& outDiagnostics) const;
1249
1250 [[nodiscard]] bool isOpen() const;
1251 [[nodiscard]] bool owns(const FiberTask& task) const;
1252 [[nodiscard]] size_t capacity() const;
1253 [[nodiscard]] size_t activeCount() const;
1254 [[nodiscard]] size_t availableCount() const;
1255
1256 private:
1257 friend struct FiberTaskPool;
1258
1259 FiberTaskClassOpaque internal;
1260};
1261
1263struct SC_FIBERS_EXPORT FiberCounter
1264{
1265 FiberCounter();
1266 ~FiberCounter();
1267
1268 FiberCounter(const FiberCounter&) = delete;
1269 FiberCounter& operator=(const FiberCounter&) = delete;
1270
1271 [[nodiscard]] size_t value() const;
1272
1273 private:
1274 friend struct FiberScheduler;
1275
1276 volatile size_t counterValue = 0;
1277
1278 FiberTask* waitingHead = nullptr;
1279 FiberTask* waitingTail = nullptr;
1280};
1281
1283struct SC_FIBERS_EXPORT FiberTaskGroupError
1284{
1285 FiberTask* task = nullptr;
1286 Result result = Result(true);
1287};
1288
1290struct SC_FIBERS_EXPORT FiberTaskGroup
1291{
1292 explicit FiberTaskGroup(FiberScheduler& scheduler);
1294
1295 FiberTaskGroup(const FiberTaskGroup&) = delete;
1296 FiberTaskGroup& operator=(const FiberTaskGroup&) = delete;
1297
1298 Result spawn(FiberTask& task, FiberStack& stack, FiberTask::Procedure procedure);
1299 Result spawn(FiberTask& task, FiberStack& stack, FiberTask::Procedure procedure, FiberCancellationToken token);
1300 Result spawn(FiberTask& task, FiberStack& stack, FiberTask::Procedure procedure,
1301 const FiberTaskSpawnOptions& options);
1302 Result spawn(FiberTaskPool& pool, FiberTask::Procedure procedure, FiberTask** outTask = nullptr);
1303 Result spawn(FiberTaskPool& pool, FiberTask::Procedure procedure, FiberCancellationToken token,
1304 FiberTask** outTask = nullptr);
1305 Result spawn(FiberTaskPool& pool, FiberTask::Procedure procedure, const FiberTaskSpawnOptions& options,
1306 FiberTask** outTask = nullptr);
1307 Result wait();
1308 Result waitAll(Result* outFirstError = nullptr);
1309 Result waitAllCancelOnParentCancel(Result* outFirstError = nullptr);
1310 Result waitCancelOnError(Result* outFirstError = nullptr);
1311 Result cancelAll();
1313 Result reset();
1314
1315 [[nodiscard]] size_t pending() const;
1316 [[nodiscard]] size_t countErrors() const;
1317 Result collectErrors(Span<FiberTaskGroupError> errors, size_t& outErrors) const;
1318
1319 private:
1320 friend struct FiberScheduler;
1321
1322 FiberScheduler& scheduler;
1323 FiberCounter counter;
1324 FiberTask* taskHead = nullptr;
1325
1326 Result prepareSpawn() const;
1327 void linkTask(FiberTask& task);
1328 Result findFirstError(Result* outFirstError) const;
1329};
1330
1332struct SC_FIBERS_EXPORT FiberTaskPoolDiagnostics
1333{
1334 size_t capacity = 0;
1335 size_t activeTasks = 0;
1336 size_t availableTasks = 0;
1337 bool classBacked = false;
1338
1339 FiberTaskClassDiagnostics taskClass;
1340 FiberStackClassDiagnostics stackClass;
1341};
1342
1343struct SC_FIBERS_EXPORT FiberTaskPool
1344{
1345 FiberTaskPool();
1346 FiberTaskPool(Span<FiberTask> taskStorage, Span<char> stackStorage, size_t stackSize);
1348
1349 FiberTaskPool(const FiberTaskPool&) = delete;
1350 FiberTaskPool& operator=(const FiberTaskPool&) = delete;
1351
1352 Result create(FiberTaskClass& taskClass, FiberStackClass& stackClass);
1353 Result close();
1354
1355 Result spawn(FiberScheduler& scheduler, FiberTask::Procedure procedure, FiberTask** outTask = nullptr,
1356 FiberCounter* counter = nullptr);
1357 Result spawn(FiberScheduler& scheduler, FiberTask::Procedure procedure, FiberCancellationToken token,
1358 FiberTask** outTask = nullptr, FiberCounter* counter = nullptr);
1359 Result spawn(FiberScheduler& scheduler, FiberTask::Procedure procedure, const FiberTaskSpawnOptions& options,
1360 FiberTask** outTask = nullptr);
1361
1362 [[nodiscard]] size_t capacity() const;
1363 [[nodiscard]] size_t activeCount() const;
1364 [[nodiscard]] size_t availableCount() const;
1365 [[nodiscard]] bool hasAvailableTask() const;
1366 void diagnostics(FiberTaskPoolDiagnostics& outDiagnostics) const;
1367 Result waitForSpawnCapacity(FiberScheduler& scheduler);
1368 Result waitForAvailableTask(FiberScheduler& scheduler);
1370 Result waitForAvailableTasks(FiberScheduler& scheduler, size_t minimumAvailable);
1371 [[nodiscard]] size_t stackSizeInBytes() const;
1372 void fillHighWaterMarks();
1373 Result stackHighWaterUsedBytes(size_t stackIndex, size_t& outBytes) const;
1374 Result stackHighWaterUnusedBytes(size_t stackIndex, size_t& outBytes) const;
1375
1376 private:
1377 friend struct FiberTaskGroup;
1378 friend struct FiberScheduler;
1379
1380 struct WaitNode
1381 {
1382 FiberCounter counter;
1383 WaitNode* next = nullptr;
1384
1385 size_t minimumAvailable = 1;
1386 bool notified = false;
1387 };
1388
1389 Span<FiberTask> tasks;
1390 Span<char> stacks;
1391 size_t stackSize = 0;
1392 size_t nextTask = 0;
1393
1394 FiberTaskClass* taskClass = nullptr;
1395 FiberStackClass* stackClass = nullptr;
1396
1397 WaitNode* availabilityWaitHead = nullptr;
1398 WaitNode* availabilityWaitTail = nullptr;
1399
1400 mutable volatile int32_t primitiveLock = 0;
1401
1402 Result stackAt(size_t stackIndex, FiberStack& outStack) const;
1403 void queueAvailabilityWaiter(WaitNode& node);
1404 FiberCounter* popAvailabilityWaiterForNotification();
1405 bool removeAvailabilityWaiter(WaitNode& node);
1406};
1407
1409struct SC_FIBERS_EXPORT FiberEvent
1410{
1411 explicit FiberEvent(bool signaled = false);
1412 ~FiberEvent();
1413
1414 FiberEvent(const FiberEvent&) = delete;
1415 FiberEvent& operator=(const FiberEvent&) = delete;
1416
1418 Result wait(FiberScheduler& scheduler);
1419 Result signal(FiberScheduler& scheduler);
1420 void reset();
1421
1422 [[nodiscard]] bool isSignaled() const;
1423
1424 private:
1425 struct WaitNode
1426 {
1427 FiberCounter counter;
1428 WaitNode* next = nullptr;
1429 bool notified = false;
1430 };
1431
1432 WaitNode* waitHead = nullptr;
1433 WaitNode* waitTail = nullptr;
1434 bool signaled = false;
1435 mutable volatile int32_t primitiveLock = 0;
1436
1437 void queueWaiter(WaitNode& node);
1438 bool removeWaiter(WaitNode& node);
1439};
1440
1442struct SC_FIBERS_EXPORT FiberAutoResetEvent
1443{
1444 explicit FiberAutoResetEvent(bool signaled = false);
1446
1448 FiberAutoResetEvent& operator=(const FiberAutoResetEvent&) = delete;
1449
1451 Result wait(FiberScheduler& scheduler);
1452 Result signal(FiberScheduler& scheduler);
1453 void reset();
1454
1455 [[nodiscard]] bool isSignaled() const;
1456
1457 private:
1458 struct WaitNode
1459 {
1460 FiberCounter counter;
1461 WaitNode* next = nullptr;
1462 bool notified = false;
1463 };
1464
1465 WaitNode* waitHead = nullptr;
1466 WaitNode* waitTail = nullptr;
1467 bool signaled = false;
1468 mutable volatile int32_t primitiveLock = 0;
1469
1470 void queueWaiter(WaitNode& node);
1471 bool popWaiter(WaitNode*& node);
1472 bool removeWaiter(WaitNode& node);
1473};
1474
1476struct SC_FIBERS_EXPORT FiberSemaphore
1477{
1478 explicit FiberSemaphore(size_t initialCount = 0);
1480
1481 FiberSemaphore(const FiberSemaphore&) = delete;
1482 FiberSemaphore& operator=(const FiberSemaphore&) = delete;
1483
1485 Result wait(FiberScheduler& scheduler);
1486 Result signal(FiberScheduler& scheduler, size_t count = 1);
1487
1488 [[nodiscard]] size_t available() const;
1489
1490 private:
1491 struct WaitNode
1492 {
1493 FiberCounter counter;
1494 WaitNode* next = nullptr;
1495 bool notified = false;
1496 };
1497
1498 WaitNode* waitHead = nullptr;
1499 WaitNode* waitTail = nullptr;
1500 size_t availableCount = 0;
1501 mutable volatile int32_t primitiveLock = 0;
1502
1503 void queueWaiter(WaitNode& node);
1504 WaitNode* popWaiter();
1505 bool removeWaiter(WaitNode& node);
1506};
1507
1509struct SC_FIBERS_EXPORT FiberMutex
1510{
1511 FiberMutex();
1512 ~FiberMutex();
1513
1514 FiberMutex(const FiberMutex&) = delete;
1515 FiberMutex& operator=(const FiberMutex&) = delete;
1516
1517 Result lock(FiberScheduler& scheduler);
1518 Result unlock(FiberScheduler& scheduler);
1519
1520 [[nodiscard]] bool isLocked() const;
1521 [[nodiscard]] bool isOwnedByCurrentTask(FiberScheduler& scheduler) const;
1522
1523 private:
1524 struct WaitNode
1525 {
1526 FiberCounter counter;
1527 WaitNode* next = nullptr;
1528 FiberTask* task = nullptr;
1529 bool notified = false;
1530 };
1531
1532 WaitNode* waitHead = nullptr;
1533 WaitNode* waitTail = nullptr;
1534 bool locked = false;
1535 FiberTask* owner = nullptr;
1536 mutable volatile int32_t primitiveLock = 0;
1537
1538 void queueWaiter(WaitNode& node);
1539 WaitNode* popWaiter();
1540 bool removeWaiter(WaitNode& node);
1541};
1542
1544struct SC_FIBERS_EXPORT FiberScheduler
1545{
1548
1549 FiberScheduler(const FiberScheduler&) = delete;
1550 FiberScheduler& operator=(const FiberScheduler&) = delete;
1551
1552 Result spawn(FiberTask& task, FiberStack& stack, FiberTask::Procedure procedure, FiberCounter* counter = nullptr);
1553 Result spawn(FiberTask& task, FiberStack& stack, FiberTask::Procedure procedure, FiberCancellationToken token,
1554 FiberCounter* counter = nullptr);
1555 Result spawn(FiberTask& task, FiberStack& stack, FiberTask::Procedure procedure,
1556 const FiberTaskSpawnOptions& options);
1557
1558 Result runOnce();
1559 Result runOnce(FiberWorker& worker);
1560 Result runOnce(FiberWorker& worker, Span<FiberWorker> workerGroup);
1561 Result runNoWait();
1562 Result runNoWait(FiberWorker& worker);
1563 Result runNoWait(FiberWorker& worker, Span<FiberWorker> workerGroup);
1564 Result runReadyFibers();
1565 Result runReadyFibers(FiberWorker& worker);
1566 Result runReadyFibers(FiberWorker& worker, Span<FiberWorker> workerGroup);
1567 Result run();
1568 Result run(FiberWorker& worker);
1569 Result run(FiberWorker& worker, Span<FiberWorker> workerGroup);
1570 Result createWorkerDeques(FiberAllocator& allocator, Span<FiberWorker> workers, size_t capacityPerWorker);
1571 void releaseWorkerDeques(Span<FiberWorker> workers);
1572 Result yield();
1573 Result shutdown();
1574 Result shutdown(FiberWorker& worker);
1575 Result shutdown(FiberWorker& worker, Span<FiberWorker> workerGroup);
1576
1577 Result requestCancel(FiberTask& task);
1578 Result requestCancel(FiberCancellationTokenSource& tokenSource);
1579 Result requestCancelAll();
1580
1581 void add(FiberCounter& counter);
1582 Result done(FiberCounter& counter);
1583 Result wait(FiberCounter& counter);
1584 Result waitUninterruptible(FiberCounter& counter);
1585
1586 void setTraceHooks(const FiberTraceHooks& hooks);
1587 void clearTraceHooks();
1588
1589 [[nodiscard]] FiberTask* currentTask();
1590 [[nodiscard]] const FiberTask* currentTask() const;
1591 [[nodiscard]] bool isCurrentTaskCancellationRequested() const;
1592
1593 [[nodiscard]] bool hasReadyFibers() const;
1594 [[nodiscard]] bool hasActiveFibers() const;
1595 [[nodiscard]] size_t readyFiberCount() const;
1596 [[nodiscard]] size_t readyFiberCount(const FiberWorker& worker) const;
1597 [[nodiscard]] size_t stolenFiberCount(const FiberWorker& worker) const;
1598 [[nodiscard]] size_t stolenFiberCount(Span<FiberWorker> workers) const;
1599 [[nodiscard]] size_t activeFiberCount() const;
1600 void schedulerDiagnostics(FiberSchedulerDiagnostics& diagnostics) const;
1601 void resetSchedulerDiagnostics();
1602 void workerDiagnostics(const FiberWorker& worker, FiberWorkerDiagnostics& diagnostics) const;
1603 void workerDiagnostics(Span<FiberWorker> workers, FiberWorkerDiagnostics& diagnostics) const;
1604
1605 void resetWorkerDiagnostics(FiberWorker& worker);
1606 void resetWorkerDiagnostics(Span<FiberWorker> workers);
1607
1608 private:
1609 friend struct FiberWorkerPool;
1610
1611 struct InjectionSlot;
1612
1613 FiberTask* readyHead = nullptr;
1614 FiberTask* readyTail = nullptr;
1615 FiberTask* activeHead = nullptr;
1616
1617 InjectionSlot* injectionQueue = nullptr;
1618 FiberAllocator* injectionAllocator = nullptr;
1619 size_t injectionCapacity = 0;
1620 volatile size_t injectionHead = 0;
1621 volatile size_t injectionTail = 0;
1622 volatile size_t injectionReady = 0;
1623 volatile size_t injectionPublishing = 0;
1624 volatile size_t injectionPeak = 0;
1625 size_t injectionSpills = 0;
1626 volatile size_t injectionClaimBatchPeak = 0;
1627
1628 mutable volatile int32_t injectionLock = 0;
1629
1630 mutable size_t injectionLockAcquisitions = 0;
1631 mutable size_t injectionLockContentions = 0;
1632 mutable size_t injectionLockSpinRetries = 0;
1633 mutable size_t injectionLockPeakSpinRetries = 0;
1634
1635 FiberWorkerPool* workerPool = nullptr;
1636
1637 volatile size_t readyFibers = 0;
1638 volatile size_t globalReadyFibers = 0;
1639 volatile size_t activeFibers = 0;
1640
1641 mutable volatile int32_t schedulerLock = 0;
1642
1643 mutable size_t schedulerLockAcquisitions = 0;
1644 mutable size_t schedulerLockContentions = 0;
1645 mutable size_t schedulerLockSpinRetries = 0;
1646 mutable size_t schedulerLockPeakSpinRetries = 0;
1647
1648 mutable size_t schedulerLockSpawn = 0;
1649 mutable size_t schedulerLockReady = 0;
1650 mutable size_t schedulerLockSynchronization = 0;
1651 mutable size_t schedulerLockCompletion = 0;
1652 mutable size_t schedulerLockControl = 0;
1653
1654 FiberTraceHooks traceHooks;
1655
1656 enum class LockCategory : uint8_t
1657 {
1658 Spawn,
1659 Ready,
1660 Synchronization,
1661 Completion,
1662 Control,
1663 };
1664
1665 struct LockGuard;
1666 struct InjectionLockGuard;
1667
1668 void lock(LockCategory category) const;
1669 void unlock() const;
1670 void lockInjection() const;
1671 void unlockInjection() const;
1672 void trace(FiberTraceEventType type, FiberTask* task, FiberWorker* worker, size_t value = 0) const;
1673
1674 void addUnlocked(FiberCounter& counter);
1675 Result initializeTaskForSpawn(FiberTask& task, FiberStack& stack, FiberTask::Procedure procedure,
1676 const FiberTaskSpawnOptions& options);
1677 [[nodiscard]] bool canPublishOwnerSpawn(FiberWorker& worker, const FiberTaskSpawnOptions& options) const;
1678 [[nodiscard]] bool canPublishInjectionSpawn(const FiberTaskSpawnOptions& options) const;
1679 void linkWorkerActiveForSpawn(FiberTask& task, FiberWorker& worker);
1680 Result createInjectionQueue(FiberAllocator& allocator, size_t capacity);
1681 void releaseInjectionQueue();
1682 [[nodiscard]] bool tryReserveInjection(size_t& position);
1683 void publishInjection(size_t position, FiberTask* task);
1684 void discardInjectionTombstones();
1685 [[nodiscard]] bool tryPushInjectionUnlocked(FiberTask& task);
1686 [[nodiscard]] FiberTask* popInjection();
1687 void notifyReadyWorkUnlocked();
1688 void pushReadyUnlocked(FiberTask& task);
1689 void pushReadyUnlocked(FiberTask& task, FiberWorker* preferredWorker);
1690 [[nodiscard]] bool tryPushWorkerReadyDeque(FiberWorker& worker, FiberTask& task);
1691 void pushWorkerReadyUnlocked(FiberWorker& worker, FiberTask& task);
1692
1693 [[nodiscard]] FiberTask* popReadyUnlocked();
1694 [[nodiscard]] FiberTask* popReadyBatchUnlocked(FiberWorker& worker);
1695 [[nodiscard]] FiberTask* popInjectionBatch(FiberWorker& worker);
1696 [[nodiscard]] FiberTask* popReadyUnlocked(FiberWorker& worker, Span<FiberWorker> stealWorkers);
1697 [[nodiscard]] FiberTask* popWorkerReadyUnlocked(FiberWorker& worker);
1698 [[nodiscard]] FiberTask* stealWorkerReadyUnlocked(FiberWorker& worker);
1699 [[nodiscard]] FiberTask* stealReadyUnlocked(FiberWorker& worker, Span<FiberWorker> stealWorkers);
1700 Result runReadyTask(FiberTask& task);
1701 Result runReadyTask(FiberTask& task, FiberWorker& worker);
1702 [[nodiscard]] bool preparePreferredWorkerReadyPublish(FiberTask& task, FiberWorker& worker,
1703 FiberWorker*& outPreferredWorker);
1704 void publishSuspensionUnlocked(FiberTask& task);
1705 void finishCurrentTask(FiberTask& task, Result result);
1706 Result cancelTaskUnlocked(FiberTask& task);
1707 void linkActiveUnlocked(FiberTask& task);
1708 void unlinkActiveUnlocked(FiberTask& task);
1709 void moveActiveToWorkerUnlocked(FiberTask& task, FiberWorker& worker);
1710 void adoptWorkerReadyUnlocked(FiberWorker& worker);
1711 void unlinkWorkerActive(FiberTask& task);
1712 void cancelWorkerActiveUnlocked(FiberWorker& worker, FiberCancellationTokenSource* tokenSource);
1713 bool removeCounterWaiterUnlocked(FiberCounter& counter, FiberTask& task);
1714 Result waitImpl(FiberCounter& counter, bool interruptible);
1715 Result doneUnlocked(FiberCounter& counter);
1716 void wakeCounterWaitersUnlocked(FiberCounter& counter);
1717
1718 static void taskEntry(void* userData);
1719};
1720
1721#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
1722#pragma warning(pop)
1723#endif
1724} // namespace SC
Definition Fibers.h:242
Definition Fibers.h:220
Definition Fibers.h:236
Explicit allocator for future fiber scheduler storage.
Definition Fibers.h:250
Auto-reset event that wakes one waiting fiber per signal.
Definition Fibers.h:1443
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:612
Lightweight cancellation token copied into spawned fiber tasks.
Definition Fibers.h:635
Counter used to suspend fibers until a group of operations completes.
Definition Fibers.h:1264
Manual-reset event that wakes waiting fibers when signaled.
Definition Fibers.h:1410
Result wait(FiberScheduler &scheduler)
Must be called from a fiber owned by scheduler.
Definition Fibers.h:987
Definition Fibers.h:1004
Definition Fibers.h:999
Allocator-backed fixed-capacity storage for stable FiberJob records.
Definition Fibers.h:1011
Restricted execution context passed to a run-to-completion FiberJob.
Definition Fibers.h:664
Definition Fibers.h:1077
Retains one bounded wave of pooled FiberJob results for aggregate inspection.
Definition Fibers.h:1084
Definition Fibers.h:978
Fixed-capacity owner of reusable FiberJob records with explicit completed-result release.
Definition Fibers.h:1037
Fixed-capacity scheduler for stackless run-to-completion jobs.
Definition Fibers.h:797
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:720
Definition Fibers.h:907
No-allocation OS-thread-owning pool for bounded stackless FiberJob execution.
Definition Fibers.h:921
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:877
Caller-owned parallel execution agent for the FiberJob runtime.
Definition Fibers.h:739
Caller-owned stackless CPU job. Its address must remain stable while active.
Definition Fibers.h:681
Cooperative mutex for fibers running on one FiberScheduler.
Definition Fibers.h:1510
Definition Fibers.h:499
Cooperative fiber scheduler with explicit workers and caller-owned storage.
Definition Fibers.h:1545
Counting semaphore for cooperative fibers.
Definition Fibers.h:1477
Result wait(FiberScheduler &scheduler)
Must be called from a fiber owned by scheduler.
Definition Fibers.h:149
Definition Fibers.h:168
Definition Fibers.h:161
Virtual-memory-backed fixed-size stack slots with caller-controlled capacity.
Definition Fibers.h:182
Common requested sizes for virtual fiber stack classes.
Definition Fibers.h:98
Caller-owned stack storage used by fiber contexts.
Definition Fibers.h:107
Definition Fibers.h:1207
Definition Fibers.h:1224
Definition Fibers.h:1219
Allocator-backed fixed-capacity storage for reusable FiberTask objects.
Definition Fibers.h:1233
One failed task collected from a FiberTaskGroup.
Definition Fibers.h:1284
Convenience helper for spawning a group of child tasks and waiting on their completion.
Definition Fibers.h:1291
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:1333
Definition Fibers.h:1344
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:1121
Caller-owned task object scheduled by FiberScheduler.
Definition Fibers.h:1145
Definition Fibers.h:534
Definition Fibers.h:543
Definition Fibers.h:130
Options for reserving a virtual-memory-backed fiber stack.
Definition Fibers.h:143
Virtual-memory-backed stack storage with an optional no-access guard page below the stack.
Definition Fibers.h:304
Definition Fibers.h:468
Definition Fibers.h:457
Definition Fibers.h:491
No-allocation OS-thread-owning worker pool using caller-provided worker and thread storage.
Definition Fibers.h:552
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:419
Caller-owned execution agent for running ready fibers on the current OS thread.
Definition Fibers.h:349