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 FiberMutex;
39struct FiberSemaphore;
40struct FiberScheduler;
41struct FiberTaskGroup;
42struct FiberTaskClass;
43struct FiberTaskClassDiagnostics;
44struct FiberTaskClassInternal;
45struct FiberTaskClassOptions;
46struct FiberTaskPool;
47struct FiberTaskPoolDiagnostics;
48struct FiberTask;
49struct FiberVirtualStack;
50struct FiberVirtualStackInternal;
51struct FiberVirtualStackOptions;
52struct FiberStackClass;
53struct FiberStackClassDiagnostics;
54struct FiberStackClassInternal;
55struct FiberStackClassOptions;
56struct FiberSchedulerDiagnostics;
57struct FiberTraceEvent;
58struct FiberTraceHooks;
59struct FiberAllocator;
60struct FiberAllocatorInterface;
61struct FiberAllocatorVirtualOptions;
62struct FiberWorker;
63struct FiberWorkerPoolWakeEvent;
64struct FiberWorkerPool;
65struct FiberWorkerPoolOptions;
66struct FiberWorkerPoolThreadEntry;
67struct FiberWorkerThread;
68
69#if SC_PLATFORM_WINDOWS
70static constexpr int FiberContextStorageSize = 2048;
71#elif SC_PLATFORM_ARM64
72static constexpr int FiberContextStorageSize = 224;
73#else
74static constexpr int FiberContextStorageSize = 128;
75#endif
76static constexpr int FiberContextStorageAlignment = 16;
77static constexpr int FiberStackAlignment = 16;
78static constexpr int FiberStackMinimumSize = 4096;
79
81struct SC_FIBERS_EXPORT FiberStack
82{
83 explicit FiberStack(Span<char> memory);
84
85 [[nodiscard]] Span<char> memory() const;
86 [[nodiscard]] size_t sizeInBytes() const;
87 [[nodiscard]] size_t usableSizeInBytes() const;
88 [[nodiscard]] size_t alignmentWasteInBytes() const;
89 [[nodiscard]] bool isUsable() const;
90 void fillHighWaterMark();
91 [[nodiscard]] size_t highWaterUsedBytes() const;
92 [[nodiscard]] size_t highWaterUnusedBytes() const;
93
94 private:
95 friend struct FiberScheduler;
96 friend struct FiberVirtualStack;
97
98 FiberStack(Span<char> memory, void* owner);
99
100 Span<char> stackMemory;
101 void* stackOwner = nullptr;
102};
103
105{
106 static constexpr int Windows = 64;
107 static constexpr int Apple = 64;
108 static constexpr int Linux = 64;
109 static constexpr int Default = 64;
110 static constexpr size_t Alignment = alignof(void*);
111
112 using Object = FiberVirtualStackInternal;
113};
114using FiberVirtualStackOpaque = OpaqueObject<FiberVirtualStackDefinition>;
115
117struct SC_FIBERS_EXPORT FiberVirtualStackOptions
118{
119 size_t usableSizeInBytes = 64 * 1024;
120 bool guardPage = true;
121};
122
124{
125 static constexpr int Windows = 160;
126 static constexpr int Apple = 160;
127 static constexpr int Linux = 160;
128 static constexpr int Default = 160;
129 static constexpr size_t Alignment = alignof(void*);
130
131 using Object = FiberStackClassInternal;
132};
133using FiberStackClassOpaque = OpaqueObject<FiberStackClassDefinition>;
134
135struct SC_FIBERS_EXPORT FiberStackClassOptions
136{
137 size_t stackSizeInBytes = 64 * 1024;
138 size_t maxStacks = 0;
139 bool guardPage = true;
140};
141
142struct SC_FIBERS_EXPORT FiberStackClassDiagnostics
143{
144 size_t capacity = 0;
145 size_t activeStacks = 0;
146 size_t peakActiveStacks = 0;
147 size_t stackSizeInBytes = 0;
148 size_t guardSizeInBytes = 0;
149 size_t reservedSizeBytes = 0;
150 size_t committedSizeBytes = 0;
151 size_t peakCommittedBytes = 0;
152 size_t highWaterUsedBytes = 0;
153};
154
156struct SC_FIBERS_EXPORT FiberStackClass
157{
160
161 FiberStackClass(const FiberStackClass&) = delete;
162 FiberStackClass& operator=(const FiberStackClass&) = delete;
164 FiberStackClass& operator=(FiberStackClass&&) = delete;
165
166 [[nodiscard]] Result reserve(const FiberStackClassOptions& options);
167 [[nodiscard]] Result acquire(FiberStack& outStack);
168 [[nodiscard]] Result release(FiberStack& stack);
169 [[nodiscard]] Result waitForAvailableSlot(FiberScheduler& scheduler);
170 void release();
171 void fillHighWaterMarks();
172 void diagnostics(FiberStackClassDiagnostics& outDiagnostics) const;
173
174 [[nodiscard]] bool isReserved() const;
175 [[nodiscard]] bool owns(const FiberStack& stack) const;
176 [[nodiscard]] size_t capacity() const;
177 [[nodiscard]] size_t activeCount() const;
178
179 private:
180 friend struct FiberTaskPool;
181
182 FiberStackClassOpaque internal;
183};
184
185enum class FiberAllocatorMode : uint8_t
186{
187 None,
188 Fixed,
189 Virtual,
190 Malloc,
191 Polymorphic,
192};
193
195{
196 size_t numAllocations = 0;
197 size_t numReleases = 0;
198
199 size_t requestedBytesAllocated = 0;
200 size_t requestedBytesReleased = 0;
201
202 size_t bytesInUse = 0;
203 size_t peakBytesInUse = 0;
204
205 size_t numAllocationFailures = 0;
206 size_t lastFailedAllocationSize = 0;
207 size_t largestFailedAllocationSize = 0;
208};
209
211{
212 size_t reserveBytes = 0;
213 size_t initialCommitBytes = 0;
214};
215
217{
218 virtual void* allocateImpl(const void* owner, size_t numBytes, size_t alignment) = 0;
219 virtual void releaseImpl(void* memory) = 0;
220 virtual ~FiberAllocatorInterface() {}
221};
222
224struct SC_FIBERS_EXPORT FiberAllocator
225{
226 FiberAllocator() = default;
227 FiberAllocator(const FiberAllocator&) = delete;
228 FiberAllocator& operator=(const FiberAllocator&) = delete;
229 FiberAllocator(FiberAllocator&&) = delete;
230 FiberAllocator& operator=(FiberAllocator&&) = delete;
232
233 [[nodiscard]] Result createFixed(Span<char> storage);
234 [[nodiscard]] Result createVirtual(FiberAllocatorVirtualOptions options);
235 [[nodiscard]] Result createMalloc();
236 [[nodiscard]] Result createPolymorphic(FiberAllocatorInterface& customAllocatorInterface);
237 [[nodiscard]] Result validateClose() const;
238 [[nodiscard]] Result close();
239
240 [[nodiscard]] void* allocate(const void* owner, size_t numBytes, size_t alignment);
241 void release(void* memory);
242 static void releaseFromAnyAllocator(void* memory);
243
244 [[nodiscard]] FiberAllocatorMode mode() const;
245 [[nodiscard]] FiberAllocatorStatistics statistics() const;
246 [[nodiscard]] bool isOpen() const;
247
248 [[nodiscard]] size_t used() const;
249 [[nodiscard]] size_t capacity() const;
250 [[nodiscard]] size_t peakUsed() const;
251 [[nodiscard]] size_t failedAllocationSize() const;
252 [[nodiscard]] size_t reservedBytes() const;
253 [[nodiscard]] size_t committedBytes() const;
254
255 private:
256 struct BlockHeader;
257
258 Result initializeFixedStorage(Span<char> storage);
259 void* allocateFromBlocks(const void* owner, size_t numBytes, size_t alignment);
260 void releaseBlock(BlockHeader& header);
261 bool ensureCommitted(size_t sizeInBytes);
262 void releaseVirtualMemory();
263 void recordAllocationFailure(size_t numBytes);
264 void resetState();
265
266 FiberAllocatorMode currentMode = FiberAllocatorMode::None;
267 FiberAllocatorStatistics currentStatistics;
268 Span<char> fixedStorage;
269 BlockHeader* firstBlock = nullptr;
270 FiberAllocatorInterface* allocatorInterface = nullptr;
271
272 void* virtualMemory = nullptr;
273 size_t virtualReservedBytes = 0;
274 size_t virtualCommittedBytes = 0;
275};
276
278struct SC_FIBERS_EXPORT FiberVirtualStack
279{
282
283 FiberVirtualStack(const FiberVirtualStack&) = delete;
284 FiberVirtualStack& operator=(const FiberVirtualStack&) = delete;
286 FiberVirtualStack& operator=(FiberVirtualStack&&) = delete;
287
288 Result reserve(const FiberVirtualStackOptions& options);
289 void release();
290
291 [[nodiscard]] FiberStack stack() const;
292 [[nodiscard]] Span<char> memory() const;
293 [[nodiscard]] size_t usableSizeInBytes() const;
294 [[nodiscard]] size_t reservedSizeInBytes() const;
295 [[nodiscard]] size_t guardSizeInBytes() const;
296 [[nodiscard]] bool isReserved() const;
297
298 private:
299 FiberVirtualStackOpaque internal;
300};
301
303enum class FiberTaskStatus
304{
305 Invalid,
306 Ready,
307 Running,
308 Waiting,
309 Completing,
310 Completed
311};
312
313enum class FiberTaskSuspendAction
314{
315 None,
316 Ready,
317 CounterWait
318};
319
320struct FiberCancellationToken;
321
323struct SC_FIBERS_EXPORT FiberWorker
324{
325 FiberWorker();
326 ~FiberWorker();
327
328 FiberWorker(const FiberWorker&) = delete;
329 FiberWorker& operator=(const FiberWorker&) = delete;
330
331 [[nodiscard]] bool isActive() const;
332 [[nodiscard]] FiberScheduler* scheduler();
333 [[nodiscard]] const FiberScheduler* scheduler() const;
334 [[nodiscard]] FiberTask* runningTask();
335 [[nodiscard]] const FiberTask* runningTask() const;
336
337 private:
338 friend struct FiberWorkerPool;
339 friend struct FiberScheduler;
340
341 Result begin(FiberScheduler& fiberScheduler);
342 void end();
343
344 FiberScheduler* workerScheduler = nullptr;
345 FiberScheduler* localQueueScheduler = nullptr;
346 FiberTask* workerTask = nullptr;
347 FiberTask* localReadyHead = nullptr;
348 FiberTask* localReadyTail = nullptr;
349 FiberTask** localDeque = nullptr;
350 FiberAllocator* localDequeAllocator = nullptr;
351 size_t localReadyFibers = 0;
352 size_t localDequeCapacity = 0;
353 size_t localDequeHead = 0;
354 volatile size_t localDequeTop = 0;
355 volatile size_t localDequeBottom = 0;
356 size_t localReadyPeakFibers = 0;
357 size_t localSpilledFibers = 0;
358 size_t stealAttempts = 0;
359 size_t stolenFibers = 0;
360 size_t failedSteals = 0;
361 size_t runAttempts = 0;
362 size_t idlePolls = 0;
363 size_t executedFibers = 0;
364 size_t completedFibers = 0;
365 size_t yieldedFibers = 0;
366 size_t waitingFibers = 0;
367 bool workerActive = false;
368 bool localSchedulingActive = false;
369
370 AlignedStorage<FiberContextStorageSize, FiberContextStorageAlignment> rootContextStorage;
371
372 [[nodiscard]] FiberContext& rootContext();
373};
374
375#if SC_PLATFORM_WINDOWS
376static constexpr int FiberWorkerThreadStorageSize = sizeof(void*);
377#else
378static constexpr int FiberWorkerThreadStorageSize = sizeof(void*) * 2;
379#endif
380static constexpr int FiberWorkerThreadStorageAlignment = alignof(void*);
381
383struct SC_FIBERS_EXPORT FiberWorkerThread
384{
387
388 FiberWorkerThread(const FiberWorkerThread&) = delete;
389 FiberWorkerThread& operator=(const FiberWorkerThread&) = delete;
390
391 [[nodiscard]] bool wasStarted() const;
392 [[nodiscard]] Result result() const;
393
394 private:
395 friend struct FiberWorkerPool;
396 friend struct FiberWorkerPoolThreadEntry;
397
398 AlignedStorage<FiberWorkerThreadStorageSize, FiberWorkerThreadStorageAlignment> threadStorage;
399
400 FiberWorkerPool* pool = nullptr;
401 size_t workerIndex = 0;
402 uint64_t affinityMask = 0;
403 uint8_t priority = 0;
404 Result threadResult = Result(true);
405 bool started = false;
406
407 Result startThread();
408 Result joinThread();
409 Result runThreadEntry();
410 Result applyThreadPolicy();
411};
412
413enum class FiberWorkerThreadPriority : uint8_t
414{
415 Default,
416 Low,
417 Normal,
418 High,
419};
420
421struct SC_FIBERS_EXPORT FiberWorkerPoolOptions
422{
423 FiberAllocator* dequeAllocator = nullptr;
424 size_t dequeCapacityPerWorker = 0;
425 Span<const uint64_t> affinityMasks;
426 FiberWorkerThreadPriority threadPriority = FiberWorkerThreadPriority::Default;
427};
428
429struct SC_FIBERS_EXPORT FiberWorkerDiagnostics
430{
431 size_t readyFibers = 0;
432 size_t readyPeakFibers = 0;
433 size_t dequeCapacity = 0;
434 size_t spilledFibers = 0;
435 size_t stealAttempts = 0;
436 size_t stolenFibers = 0;
437 size_t failedSteals = 0;
438 size_t runAttempts = 0;
439 size_t idlePolls = 0;
440 size_t executedFibers = 0;
441 size_t completedFibers = 0;
442 size_t yieldedFibers = 0;
443 size_t waitingFibers = 0;
444};
445
446struct SC_FIBERS_EXPORT FiberSchedulerDiagnostics
447{
448 size_t readyFibers = 0;
449 size_t activeFibers = 0;
450 size_t lockAcquisitions = 0;
451 size_t lockContentions = 0;
452 size_t lockSpinRetries = 0;
453 size_t lockPeakSpinRetries = 0;
454};
455
456enum class FiberTraceEventType : uint8_t
457{
458 TaskStarted,
459 TaskYielded,
460 TaskWaiting,
461 TaskCompleted,
462};
463
464struct SC_FIBERS_EXPORT FiberTraceEvent
465{
466 FiberTraceEventType type = FiberTraceEventType::TaskStarted;
467 FiberScheduler* scheduler = nullptr;
468 FiberWorker* worker = nullptr;
469 FiberTask* task = nullptr;
470 size_t value = 0;
471};
472
473struct SC_FIBERS_EXPORT FiberTraceHooks
474{
475 using Callback = void (*)(void* userData, const FiberTraceEvent& event);
476
477 Callback callback = nullptr;
478 void* userData = nullptr;
479};
480
482struct SC_FIBERS_EXPORT FiberWorkerPool
483{
486
487 FiberWorkerPool(const FiberWorkerPool&) = delete;
488 FiberWorkerPool& operator=(const FiberWorkerPool&) = delete;
489
490 Result start(FiberScheduler& scheduler, Span<FiberWorker> workerStorage, Span<FiberWorkerThread> threadStorage);
491 Result start(FiberScheduler& scheduler, Span<FiberWorker> workerStorage, Span<FiberWorkerThread> threadStorage,
492 const FiberWorkerPoolOptions& options);
493 Result requestStop();
494 Result join();
495 Result shutdown();
496
497 [[nodiscard]] bool isRunning() const;
498 [[nodiscard]] size_t workerCount() const;
499
501 {
502 static constexpr int Windows = sizeof(void*) * 16;
503 static constexpr int Apple = sizeof(void*) * 16;
504 static constexpr int Linux = sizeof(void*) * 16;
505 static constexpr int Default = Linux;
506
507 static constexpr size_t Alignment = alignof(void*);
508
509 using Object = FiberWorkerPoolWakeEvent;
510 };
511
512 using WakeEventOpaque = OpaqueObject<WakeEventDefinition>;
513
514 private:
515 friend struct FiberScheduler;
516 friend struct FiberWorkerThread;
517
518 FiberScheduler* poolScheduler = nullptr;
519 Span<FiberWorker> workers;
520 Span<FiberWorkerThread> threads;
521 WakeEventOpaque wakeEvent;
522 mutable volatile int32_t stopRequested = 0;
523 mutable volatile int32_t running = 0;
524 bool localDequesCreated = false;
525
526 void wakeWorkers();
527 void waitForWork(uint32_t observedGeneration);
528 [[nodiscard]] uint32_t wakeGeneration() const;
529
530 Result workerMain(size_t workerIndex);
531};
532
534struct SC_FIBERS_EXPORT FiberCancellationTokenSource
535{
537
540
541 void requestCancel();
542 void reset();
543 Result check() const;
544
545 [[nodiscard]] bool isCancellationRequested() const;
546 [[nodiscard]] FiberCancellationToken token() const;
547
548 private:
549 friend struct FiberCancellationToken;
550 friend struct FiberScheduler;
551
552 mutable volatile int32_t requested = 0;
553};
554
556struct SC_FIBERS_EXPORT FiberCancellationToken
557{
559
560 Result check() const;
561
562 [[nodiscard]] bool isValid() const;
563 [[nodiscard]] bool isCancellationRequested() const;
564
565 private:
566 friend struct FiberCancellationTokenSource;
567 friend struct FiberScheduler;
568
569 explicit FiberCancellationToken(const FiberCancellationTokenSource& tokenSource);
570
571 const FiberCancellationTokenSource* source = nullptr;
572};
573
575struct SC_FIBERS_EXPORT FiberTaskSpawnOptions
576{
577 FiberCancellationToken cancellationToken;
578 FiberCounter* counter = nullptr;
579 void* userData = nullptr;
580 bool setUserData = false;
581
582 private:
583 friend struct FiberScheduler;
584 friend struct FiberTaskPool;
585
586 FiberTaskPool* originPool = nullptr;
587 FiberTaskClass* originTaskClass = nullptr;
588 FiberStackClass* originStackClass = nullptr;
589};
590
591#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
592#pragma warning(push)
593#pragma warning(disable : 4324)
594#endif
595
597struct SC_FIBERS_EXPORT FiberTask
598{
599 using Procedure = Function<Result(FiberScheduler&)>;
600
601 FiberTask();
602 ~FiberTask();
603
604 FiberTask(const FiberTask&) = delete;
605 FiberTask& operator=(const FiberTask&) = delete;
606 FiberTask(FiberTask&&) = delete;
607 FiberTask& operator=(FiberTask&&) = delete;
608
609 [[nodiscard]] bool isValid() const;
610 [[nodiscard]] bool isStarted() const;
611 [[nodiscard]] bool isCompleted() const;
612 [[nodiscard]] bool isActive() const;
613 [[nodiscard]] bool isCancellationRequested() const;
614 [[nodiscard]] FiberTaskStatus status() const;
615 [[nodiscard]] Result result() const;
616 void setUserData(void* data);
617 [[nodiscard]] void* userData() const;
618
619 private:
620 friend struct FiberTaskGroup;
621 friend struct FiberScheduler;
622
623 AlignedStorage<FiberContextStorageSize, FiberContextStorageAlignment> contextStorage;
624
625 Procedure procedure;
626 FiberScheduler* scheduler = nullptr;
627 FiberCounter* completionCounter = nullptr;
628 FiberCancellationToken cancellationToken;
629 FiberTask* nextReady = nullptr;
630 FiberTask* previousReady = nullptr;
631 FiberTask* nextWaiting = nullptr;
632 FiberTask* nextActive = nullptr;
633 FiberTask* nextGroup = nullptr;
634 FiberCounter* waitingCounter = nullptr;
635 FiberCounter* suspendCounter = nullptr;
636 FiberTaskPool* originPool = nullptr;
637 FiberTaskClass* originTaskClass = nullptr;
638 FiberStackClass* originStackClass = nullptr;
639 Span<char> originStackMemory;
640 FiberWorker* preferredWorker = nullptr;
641 void* runningWorker = nullptr;
642 void* stackOwner = nullptr;
643 void* taskUserData = nullptr;
644 Result taskResult = Result(true);
645 FiberTaskStatus taskStatus = FiberTaskStatus::Invalid;
646 FiberTaskSuspendAction suspendAction = FiberTaskSuspendAction::None;
647 bool cancelRequested = false;
648 bool suspendInterruptible = false;
649
650 [[nodiscard]] FiberContext& context();
651 [[nodiscard]] const FiberContext& context() const;
652};
653
655{
656 static constexpr int Windows = 96;
657 static constexpr int Apple = 96;
658 static constexpr int Linux = 96;
659 static constexpr int Default = 96;
660 static constexpr size_t Alignment = alignof(void*);
661
662 using Object = FiberTaskClassInternal;
663};
664using FiberTaskClassOpaque = OpaqueObject<FiberTaskClassDefinition>;
665
666struct SC_FIBERS_EXPORT FiberTaskClassOptions
667{
668 size_t maxTasks = 0;
669};
670
671struct SC_FIBERS_EXPORT FiberTaskClassDiagnostics
672{
673 size_t capacity = 0;
674 size_t activeTasks = 0;
675 size_t availableTasks = 0;
676 size_t peakActiveTasks = 0;
677};
678
680struct SC_FIBERS_EXPORT FiberTaskClass
681{
684
685 FiberTaskClass(const FiberTaskClass&) = delete;
686 FiberTaskClass& operator=(const FiberTaskClass&) = delete;
687 FiberTaskClass(FiberTaskClass&&) = delete;
688 FiberTaskClass& operator=(FiberTaskClass&&) = delete;
689
690 [[nodiscard]] Result create(FiberAllocator& allocator, const FiberTaskClassOptions& options);
691 [[nodiscard]] Result acquire(FiberTask*& outTask);
692 [[nodiscard]] Result release(FiberTask& task);
693 [[nodiscard]] Result waitForAvailableSlot(FiberScheduler& scheduler);
694 [[nodiscard]] Result validateClose() const;
695 [[nodiscard]] Result close();
696 void diagnostics(FiberTaskClassDiagnostics& outDiagnostics) const;
697
698 [[nodiscard]] bool isOpen() const;
699 [[nodiscard]] bool owns(const FiberTask& task) const;
700 [[nodiscard]] size_t capacity() const;
701 [[nodiscard]] size_t activeCount() const;
702 [[nodiscard]] size_t availableCount() const;
703
704 private:
705 friend struct FiberTaskPool;
706
707 FiberTaskClassOpaque internal;
708};
709
711struct SC_FIBERS_EXPORT FiberCounter
712{
713 FiberCounter();
715
716 FiberCounter(const FiberCounter&) = delete;
717 FiberCounter& operator=(const FiberCounter&) = delete;
718
719 [[nodiscard]] size_t value() const;
720
721 private:
722 friend struct FiberScheduler;
723
724 size_t counterValue = 0;
725 FiberTask* waitingHead = nullptr;
726 FiberTask* waitingTail = nullptr;
727};
728
730struct SC_FIBERS_EXPORT FiberTaskGroupError
731{
732 FiberTask* task = nullptr;
733 Result result = Result(true);
734};
735
737struct SC_FIBERS_EXPORT FiberTaskGroup
738{
739 explicit FiberTaskGroup(FiberScheduler& scheduler);
741
742 FiberTaskGroup(const FiberTaskGroup&) = delete;
743 FiberTaskGroup& operator=(const FiberTaskGroup&) = delete;
744
745 Result spawn(FiberTask& task, FiberStack& stack, FiberTask::Procedure procedure);
746 Result spawn(FiberTask& task, FiberStack& stack, FiberTask::Procedure procedure, FiberCancellationToken token);
747 Result spawn(FiberTask& task, FiberStack& stack, FiberTask::Procedure procedure,
748 const FiberTaskSpawnOptions& options);
749 Result spawn(FiberTaskPool& pool, FiberTask::Procedure procedure, FiberTask** outTask = nullptr);
750 Result spawn(FiberTaskPool& pool, FiberTask::Procedure procedure, FiberCancellationToken token,
751 FiberTask** outTask = nullptr);
752 Result spawn(FiberTaskPool& pool, FiberTask::Procedure procedure, const FiberTaskSpawnOptions& options,
753 FiberTask** outTask = nullptr);
754 Result wait();
755 Result waitAll(Result* outFirstError = nullptr);
756 Result waitAllCancelOnParentCancel(Result* outFirstError = nullptr);
757 Result waitCancelOnError(Result* outFirstError = nullptr);
758 Result cancelAll();
759
760 [[nodiscard]] size_t pending() const;
761 [[nodiscard]] size_t countErrors() const;
762 Result collectErrors(Span<FiberTaskGroupError> errors, size_t& outErrors) const;
763
764 private:
765 FiberScheduler& scheduler;
766 FiberCounter counter;
767 FiberTask* taskHead = nullptr;
768
769 void linkTask(FiberTask& task);
770 Result findFirstError(Result* outFirstError) const;
771};
772
774struct SC_FIBERS_EXPORT FiberTaskPoolDiagnostics
775{
776 size_t capacity = 0;
777 size_t activeTasks = 0;
778 size_t availableTasks = 0;
779 bool classBacked = false;
780
783};
784
785struct SC_FIBERS_EXPORT FiberTaskPool
786{
788 FiberTaskPool(Span<FiberTask> taskStorage, Span<char> stackStorage, size_t stackSize);
790
791 FiberTaskPool(const FiberTaskPool&) = delete;
792 FiberTaskPool& operator=(const FiberTaskPool&) = delete;
793
794 Result create(FiberTaskClass& taskClass, FiberStackClass& stackClass);
795 Result close();
796
797 Result spawn(FiberScheduler& scheduler, FiberTask::Procedure procedure, FiberTask** outTask = nullptr,
798 FiberCounter* counter = nullptr);
799 Result spawn(FiberScheduler& scheduler, FiberTask::Procedure procedure, FiberCancellationToken token,
800 FiberTask** outTask = nullptr, FiberCounter* counter = nullptr);
801 Result spawn(FiberScheduler& scheduler, FiberTask::Procedure procedure, const FiberTaskSpawnOptions& options,
802 FiberTask** outTask = nullptr);
803
804 [[nodiscard]] size_t capacity() const;
805 [[nodiscard]] size_t activeCount() const;
806 [[nodiscard]] size_t availableCount() const;
807 [[nodiscard]] bool hasAvailableTask() const;
808 void diagnostics(FiberTaskPoolDiagnostics& outDiagnostics) const;
809 Result waitForSpawnCapacity(FiberScheduler& scheduler);
810 Result waitForAvailableTask(FiberScheduler& scheduler);
811 [[nodiscard]] size_t stackSizeInBytes() const;
812 void fillHighWaterMarks();
813 Result stackHighWaterUsedBytes(size_t stackIndex, size_t& outBytes) const;
814 Result stackHighWaterUnusedBytes(size_t stackIndex, size_t& outBytes) const;
815
816 private:
817 friend struct FiberScheduler;
818
819 struct WaitNode
820 {
821 FiberCounter counter;
822 WaitNode* next = nullptr;
823 bool notified = false;
824 };
825
826 Span<FiberTask> tasks;
827 Span<char> stacks;
828 size_t stackSize = 0;
829 size_t nextTask = 0;
830
831 FiberTaskClass* taskClass = nullptr;
832 FiberStackClass* stackClass = nullptr;
833
834 WaitNode* availabilityWaitHead = nullptr;
835 WaitNode* availabilityWaitTail = nullptr;
836
837 mutable volatile int32_t primitiveLock = 0;
838
839 Result stackAt(size_t stackIndex, FiberStack& outStack) const;
840 void queueAvailabilityWaiter(WaitNode& node);
841 FiberCounter* popAvailabilityWaiterForNotification();
842 bool removeAvailabilityWaiter(WaitNode& node);
843};
844
846struct SC_FIBERS_EXPORT FiberEvent
847{
848 explicit FiberEvent(bool signaled = false);
849 ~FiberEvent();
850
851 FiberEvent(const FiberEvent&) = delete;
852 FiberEvent& operator=(const FiberEvent&) = delete;
853
854 Result wait(FiberScheduler& scheduler);
855 Result signal(FiberScheduler& scheduler);
856 void reset();
857
858 [[nodiscard]] bool isSignaled() const;
859
860 private:
861 struct WaitNode
862 {
863 FiberCounter counter;
864 WaitNode* next = nullptr;
865 bool notified = false;
866 };
867
868 WaitNode* waitHead = nullptr;
869 WaitNode* waitTail = nullptr;
870 bool signaled = false;
871 mutable volatile int32_t primitiveLock = 0;
872
873 void queueWaiter(WaitNode& node);
874 bool removeWaiter(WaitNode& node);
875};
876
878struct SC_FIBERS_EXPORT FiberAutoResetEvent
879{
880 explicit FiberAutoResetEvent(bool signaled = false);
882
884 FiberAutoResetEvent& operator=(const FiberAutoResetEvent&) = delete;
885
886 Result wait(FiberScheduler& scheduler);
887 Result signal(FiberScheduler& scheduler);
888 void reset();
889
890 [[nodiscard]] bool isSignaled() const;
891
892 private:
893 struct WaitNode
894 {
895 FiberCounter counter;
896 WaitNode* next = nullptr;
897 bool notified = false;
898 };
899
900 WaitNode* waitHead = nullptr;
901 WaitNode* waitTail = nullptr;
902 bool signaled = false;
903 mutable volatile int32_t primitiveLock = 0;
904
905 void queueWaiter(WaitNode& node);
906 bool popWaiter(WaitNode*& node);
907 bool removeWaiter(WaitNode& node);
908};
909
911struct SC_FIBERS_EXPORT FiberSemaphore
912{
913 explicit FiberSemaphore(size_t initialCount = 0);
915
916 FiberSemaphore(const FiberSemaphore&) = delete;
917 FiberSemaphore& operator=(const FiberSemaphore&) = delete;
918
919 Result wait(FiberScheduler& scheduler);
920 Result signal(FiberScheduler& scheduler, size_t count = 1);
921
922 [[nodiscard]] size_t available() const;
923
924 private:
925 struct WaitNode
926 {
927 FiberCounter counter;
928 WaitNode* next = nullptr;
929 bool notified = false;
930 };
931
932 WaitNode* waitHead = nullptr;
933 WaitNode* waitTail = nullptr;
934 size_t availableCount = 0;
935 mutable volatile int32_t primitiveLock = 0;
936
937 void queueWaiter(WaitNode& node);
938 WaitNode* popWaiter();
939 bool removeWaiter(WaitNode& node);
940};
941
943struct SC_FIBERS_EXPORT FiberMutex
944{
945 FiberMutex();
946 ~FiberMutex();
947
948 FiberMutex(const FiberMutex&) = delete;
949 FiberMutex& operator=(const FiberMutex&) = delete;
950
951 Result lock(FiberScheduler& scheduler);
952 Result unlock(FiberScheduler& scheduler);
953
954 [[nodiscard]] bool isLocked() const;
955 [[nodiscard]] bool isOwnedByCurrentTask(FiberScheduler& scheduler) const;
956
957 private:
958 struct WaitNode
959 {
960 FiberCounter counter;
961 WaitNode* next = nullptr;
962 bool notified = false;
963 };
964
965 WaitNode* waitHead = nullptr;
966 WaitNode* waitTail = nullptr;
967 bool locked = false;
968 FiberTask* owner = nullptr;
969 mutable volatile int32_t primitiveLock = 0;
970
971 void queueWaiter(WaitNode& node);
972 WaitNode* popWaiter();
973 bool removeWaiter(WaitNode& node);
974};
975
977struct SC_FIBERS_EXPORT FiberScheduler
978{
981
982 FiberScheduler(const FiberScheduler&) = delete;
983 FiberScheduler& operator=(const FiberScheduler&) = delete;
984
985 Result spawn(FiberTask& task, FiberStack& stack, FiberTask::Procedure procedure, FiberCounter* counter = nullptr);
986 Result spawn(FiberTask& task, FiberStack& stack, FiberTask::Procedure procedure, FiberCancellationToken token,
987 FiberCounter* counter = nullptr);
988 Result spawn(FiberTask& task, FiberStack& stack, FiberTask::Procedure procedure,
989 const FiberTaskSpawnOptions& options);
990
991 Result runOnce();
992 Result runOnce(FiberWorker& worker);
993 Result runOnce(FiberWorker& worker, Span<FiberWorker> workerGroup);
994 Result runNoWait();
995 Result runNoWait(FiberWorker& worker);
996 Result runNoWait(FiberWorker& worker, Span<FiberWorker> workerGroup);
997 Result runReadyFibers();
998 Result runReadyFibers(FiberWorker& worker);
999 Result runReadyFibers(FiberWorker& worker, Span<FiberWorker> workerGroup);
1000 Result run();
1001 Result run(FiberWorker& worker);
1002 Result run(FiberWorker& worker, Span<FiberWorker> workerGroup);
1003 Result createWorkerDeques(FiberAllocator& allocator, Span<FiberWorker> workers, size_t capacityPerWorker);
1004 void releaseWorkerDeques(Span<FiberWorker> workers);
1005 Result yield();
1006 Result shutdown();
1007 Result shutdown(FiberWorker& worker);
1008 Result shutdown(FiberWorker& worker, Span<FiberWorker> workerGroup);
1009
1010 Result requestCancel(FiberTask& task);
1011 Result requestCancel(FiberCancellationTokenSource& tokenSource);
1012 Result requestCancelAll();
1013
1014 void add(FiberCounter& counter);
1015 Result done(FiberCounter& counter);
1016 Result wait(FiberCounter& counter);
1017 Result waitUninterruptible(FiberCounter& counter);
1018
1019 void setTraceHooks(const FiberTraceHooks& hooks);
1020 void clearTraceHooks();
1021
1022 [[nodiscard]] FiberTask* currentTask();
1023 [[nodiscard]] const FiberTask* currentTask() const;
1024 [[nodiscard]] bool isCurrentTaskCancellationRequested() const;
1025
1026 [[nodiscard]] bool hasReadyFibers() const;
1027 [[nodiscard]] bool hasActiveFibers() const;
1028 [[nodiscard]] size_t readyFiberCount() const;
1029 [[nodiscard]] size_t readyFiberCount(const FiberWorker& worker) const;
1030 [[nodiscard]] size_t stolenFiberCount(const FiberWorker& worker) const;
1031 [[nodiscard]] size_t stolenFiberCount(Span<FiberWorker> workers) const;
1032 [[nodiscard]] size_t activeFiberCount() const;
1033 void schedulerDiagnostics(FiberSchedulerDiagnostics& diagnostics) const;
1034 void resetSchedulerDiagnostics();
1035 void workerDiagnostics(const FiberWorker& worker, FiberWorkerDiagnostics& diagnostics) const;
1036 void workerDiagnostics(Span<FiberWorker> workers, FiberWorkerDiagnostics& diagnostics) const;
1037
1038 void resetWorkerDiagnostics(FiberWorker& worker);
1039 void resetWorkerDiagnostics(Span<FiberWorker> workers);
1040
1041 private:
1042 friend struct FiberWorkerPool;
1043
1044 FiberTask* readyHead = nullptr;
1045 FiberTask* readyTail = nullptr;
1046 FiberTask* activeHead = nullptr;
1047
1048 FiberWorkerPool* workerPool = nullptr;
1049
1050 volatile size_t readyFibers = 0;
1051 size_t activeFibers = 0;
1052
1053 mutable volatile int32_t schedulerLock = 0;
1054
1055 mutable size_t schedulerLockAcquisitions = 0;
1056 mutable size_t schedulerLockContentions = 0;
1057 mutable size_t schedulerLockSpinRetries = 0;
1058 mutable size_t schedulerLockPeakSpinRetries = 0;
1059
1060 FiberTraceHooks traceHooks;
1061
1062 struct LockGuard;
1063
1064 void lock() const;
1065 void unlock() const;
1066 void trace(FiberTraceEventType type, FiberTask* task, FiberWorker* worker, size_t value = 0) const;
1067
1068 void addUnlocked(FiberCounter& counter);
1069 void notifyReadyWorkUnlocked();
1070 void pushReadyUnlocked(FiberTask& task);
1071 void pushReadyUnlocked(FiberTask& task, FiberWorker* preferredWorker);
1072 [[nodiscard]] bool tryPushWorkerReadyDeque(FiberWorker& worker, FiberTask& task);
1073 void pushWorkerReadyUnlocked(FiberWorker& worker, FiberTask& task);
1074
1075 [[nodiscard]] FiberTask* popReadyUnlocked();
1076 [[nodiscard]] FiberTask* popReadyUnlocked(FiberWorker& worker, Span<FiberWorker> stealWorkers);
1077 [[nodiscard]] FiberTask* popWorkerReadyUnlocked(FiberWorker& worker);
1078 [[nodiscard]] FiberTask* stealWorkerReadyUnlocked(FiberWorker& worker);
1079 [[nodiscard]] FiberTask* stealReadyUnlocked(FiberWorker& worker, Span<FiberWorker> stealWorkers);
1080 Result runReadyTask(FiberTask& task);
1081 Result runReadyTask(FiberTask& task, FiberWorker& worker);
1082 [[nodiscard]] bool preparePreferredWorkerReadyPublishUnlocked(FiberTask& task, FiberWorker& worker,
1083 FiberWorker*& outPreferredWorker);
1084 void publishSuspensionUnlocked(FiberTask& task);
1085 void finishCurrentTask(FiberTask& task, Result result);
1086 Result cancelTaskUnlocked(FiberTask& task);
1087 void linkActiveUnlocked(FiberTask& task);
1088 void unlinkActiveUnlocked(FiberTask& task);
1089 bool removeCounterWaiterUnlocked(FiberCounter& counter, FiberTask& task);
1090 Result waitImpl(FiberCounter& counter, bool interruptible);
1091 Result doneUnlocked(FiberCounter& counter);
1092 void wakeCounterWaitersUnlocked(FiberCounter& counter);
1093
1094 static void taskEntry(void* userData);
1095};
1096
1097#if SC_PLATFORM_WINDOWS && (SC_COMPILER_MSVC || SC_COMPILER_CLANG_CL)
1098#pragma warning(pop)
1099#endif
1100} // namespace SC
Definition Fibers.h:217
Definition Fibers.h:195
Definition Fibers.h:211
Explicit allocator for future fiber scheduler storage.
Definition Fibers.h:225
Auto-reset event that wakes one waiting fiber per signal.
Definition Fibers.h:879
Caller-owned cancellation source shared by one or more fiber tasks.
Definition Fibers.h:535
Lightweight cancellation token copied into spawned fiber tasks.
Definition Fibers.h:557
Counter used to suspend fibers until a group of operations completes.
Definition Fibers.h:712
Manual-reset event that wakes waiting fibers when signaled.
Definition Fibers.h:847
Cooperative mutex for fibers running on one FiberScheduler.
Definition Fibers.h:944
Definition Fibers.h:447
Cooperative fiber scheduler with explicit workers and caller-owned storage.
Definition Fibers.h:978
Counting semaphore for cooperative fibers.
Definition Fibers.h:912
Definition Fibers.h:124
Definition Fibers.h:143
Definition Fibers.h:136
Virtual-memory-backed fixed-size stack slots with caller-controlled capacity.
Definition Fibers.h:157
Caller-owned stack storage used by fiber contexts.
Definition Fibers.h:82
Definition Fibers.h:655
Definition Fibers.h:672
Definition Fibers.h:667
Allocator-backed fixed-capacity storage for reusable FiberTask objects.
Definition Fibers.h:681
One failed task collected from a FiberTaskGroup.
Definition Fibers.h:731
Convenience helper for spawning a group of child tasks and waiting on their completion.
Definition Fibers.h:738
Caller-owned pool that pairs FiberTask objects with fixed-size stack slots.
Definition Fibers.h:775
Definition Fibers.h:786
Optional scheduling inputs used when the short spawn overloads are not expressive enough.
Definition Fibers.h:576
Caller-owned task object scheduled by FiberScheduler.
Definition Fibers.h:598
Definition Fibers.h:465
Definition Fibers.h:474
Definition Fibers.h:105
Options for reserving a virtual-memory-backed fiber stack.
Definition Fibers.h:118
Virtual-memory-backed stack storage with an optional no-access guard page below the stack.
Definition Fibers.h:279
Definition Fibers.h:430
Definition Fibers.h:422
No-allocation OS-thread-owning worker pool using caller-provided worker and thread storage.
Definition Fibers.h:483
Caller-owned OS thread storage used by FiberWorkerPool.
Definition Fibers.h:384
Caller-owned execution agent for running ready fibers on the current OS thread.
Definition Fibers.h:324