├── Documentation ├── header.md ├── read_copy_update.md ├── sync_wait.md ├── async_promise.md ├── async_scope.md ├── schedulers.md ├── promise_allocator.md ├── async_mutex.md ├── conventions.md ├── shared_task.md ├── task.md └── awaiter_wrapper.md ├── Tla ├── ThreadPool.cfg ├── LockFreeFifoQueue │ ├── AbstractFifoQueue.cfg │ └── AbstractFifoQueue.tla ├── ReaderWriterLock │ ├── AbstractReaderWriterLock.cfg │ ├── FairReaderWriterLock.cfg │ ├── FairReaderWriterLock_AtomicState_Queue.cfg │ ├── FairReaderWriterLock_LockBased.cfg │ └── AbstractReaderWriterLock.tla ├── AutoResetEvent │ ├── AbstractAutoResetEvent.cfg │ ├── AutoResetEvent_v2.cfg │ └── AbstractEventListener.tla ├── .gitignore ├── ReadCopyUpdate │ ├── MCReadCopyUpdate_MemorySafety.tla │ ├── MCReadCopyUpdate_MemorySafety.cfg │ └── ReadCopyUpdate_MemorySafety.cfg ├── SequenceLock │ └── SequenceLock.cfg ├── ThreadPool_Liveness_3_Items_2_Threads.tla ├── ThreadPool_Correctness_3_Items_2_Threads.tla ├── ThreadPool_Liveness_3_Items_3_Threads.tla ├── ThreadPool_Correctness_3_Items_3_Threads.tla ├── ThreadPool_Wakeup_Liveness_3_Items_3_Threads.tla ├── ThreadPool_Wakeup_Liveness_4_Items_3_Threads.tla ├── ThreadPool_Wakeup_Liveness_5_Items_3_Threads.tla ├── ThreadPool_Correctness_5_Items_3_Threads.tla ├── ThreadPool_Correctness_3_Items_2_Threads.cfg ├── ThreadPool_Correctness_3_Items_3_Threads.cfg ├── ThreadPool_Liveness_3_Items_2_Threads.cfg ├── ThreadPool_Correctness_5_Items_3_Threads.cfg ├── ThreadPool_Wakeup_Liveness_3_Items_3_Threads.cfg ├── ThreadPool_Wakeup_Liveness_4_Items_3_Threads.cfg ├── ThreadPool_Wakeup_Liveness_5_Items_3_Threads.cfg ├── ThreadPool_Liveness_3_Items_3_Threads.cfg ├── ThreadPool.toolbox │ ├── .project │ ├── ThreadPool___Correctness_3_Items_3_Threads.launch │ ├── ThreadPool___Correctness_5_Items_3_Threads.launch │ ├── ThreadPool___Liveness_3_Items_3_Threads.launch │ └── ThreadPool___Liveness_5_Items_3_Threads.launch └── ReusableConsecutiveId.toolbox │ ├── .project │ ├── ReusableConsecutiveId___Thread_3.launch │ └── ReusableConsecutiveId___Thread_3_Live.launch ├── Phantom.Coroutines.cppcoro.Test ├── cppcoro_generator_test.cpp ├── cppcoro_inline_scheduler_test.cpp ├── cppcoro_async_latch_test.cpp ├── cppcoro_static_thread_pool_test.cpp ├── cppcoro_single_consumer_event_test.cpp ├── cppcoro_is_awaitable_test.cpp ├── cppcoro_async_auto_reset_event_test.cpp ├── cppcoro_async_manual_reset_event_test.cpp ├── cppcoro_async_scope_test.cpp ├── cppcoro_task_test.cpp ├── cppcoro_shared_task_test.cpp ├── async_test.cpp ├── cppcoro_cancellation_source_test.cpp ├── CMakeLists.txt ├── async_test.h ├── cppcoro_sequence_barrier_test.cpp ├── cppcoro_async_generator_test.cpp └── cppcoro_async_mutex_test.cpp ├── .gitignore ├── Phantom.Coroutines.Modules ├── awaiter_wrapper.ixx ├── value_awaiter.ixx ├── non_copyable.ixx ├── config_globals.ixx ├── immovable_object.ixx ├── scheduler.ixx ├── construct_from_base.ixx ├── coroutine.ixx ├── await_none_await_transform.ixx ├── consecutive_global_id.ixx ├── thread_local_context.ixx ├── aligned_array.ixx ├── final_suspend_transfer.ixx ├── inline_scheduler.ixx ├── policies.ixx ├── assert_same_thread.ixx ├── sequence_lock.ixx ├── await_all_await_transform.ixx ├── function_traits.ixx ├── sharding.ixx ├── consecutive_thread_id.ixx ├── nonatomic_shared_ptr.ixx ├── reusable_consecutive_global_id.ixx ├── scope_guard.ixx ├── task.ixx ├── promise_allocator.ixx ├── make_task.ixx ├── tagged_pointer.ixx ├── direct_initialized_optional.ixx ├── fibonacci_heap.ixx ├── storage_for.ixx ├── variant_result_storage.ixx ├── double_wide_atomic.ixx ├── error_condition_early_termination.ixx ├── atomic_shared_ptr.ixx ├── awaiter_list.ixx ├── reusable_task.ixx ├── static_thread_pool.ixx ├── suspend_result.ixx ├── async_latch.ixx ├── contextual_promise.ixx ├── generator.ixx ├── thread_local_contextual_promise.ixx ├── async_atomic.ixx ├── type_traits.ixx ├── expected_early_termination.ixx ├── async_auto_reset_event.ixx ├── async_manual_reset_event.ixx ├── atomic_state.ixx ├── polymorphic_promise.ixx ├── sync_wait.ixx ├── extensible_promise.ixx ├── tracing.ixx ├── async_promise.ixx ├── async_scope.ixx ├── async_reader_writer_lock.ixx ├── async_sequence_barrier.ixx ├── thread_local_storage.ixx ├── async_mutex.ixx ├── async_generator.ixx ├── async_sharded_reader_writer_lock.ixx ├── read_copy_update.ixx ├── shared_task.ixx ├── thread_pool_scheduler.ixx ├── core_task.ixx ├── early_termination_task.ixx ├── CMakeLists.txt └── Phantom.Coroutines.ixx ├── vcpkg ├── toolchains │ └── clang-libc++-21.cmake └── triplets │ └── x64-linux-clang-libcxx-21.cmake ├── .runsettings ├── Phantom.Coroutines.Test ├── lifetime_tracker.ixx ├── pmr_task.ixx ├── manual_scheduler.h ├── detail │ ├── scope_guard_test.cpp │ ├── non_copyable_test.cpp │ └── awaiter_wrapper_test.cpp ├── sharding_test.cpp ├── inline_scheduler_test.cpp ├── value_awaiter_test.cpp ├── async_test.cpp ├── async_test.h ├── double_wide_atomic_test.cpp ├── async_latch_test.cpp ├── make_Task_test.cpp ├── async_promise_test.cpp ├── pmr_task.h ├── thread_local_contextual_promise_test.cpp ├── tagged_pointer_test.cpp ├── awaiter_list_test.cpp ├── async_atomic_test.cpp ├── thread_local_storage_test.cpp ├── async_sharded_reader_writer_lock_test.cpp └── reusable_consecutive_global_id_test.cpp ├── Phantom.Coroutines ├── CMakeLists.txt └── include │ └── Phantom.Coroutines │ ├── scheduler.h │ ├── detail │ ├── non_copyable.h │ ├── immovable_object.h │ ├── config_globals.h │ ├── scope_guard.h │ ├── assert_same_thread.h │ ├── consecutive_thread_id.h │ ├── config_macros_clang.h │ ├── config_macros.h │ ├── config_macros_clang_msvc.h │ ├── construct_from_base.h │ ├── storage_for.h │ ├── config_macros_msvc.h │ ├── coroutine.h │ └── atomic_shared_ptr.h │ ├── value_awaiter.h │ ├── inline_scheduler.h │ ├── await_none_await_transform.h │ ├── await_all_await_transform.h │ ├── make_task.h │ ├── async_latch.h │ ├── final_suspend_transfer.h │ ├── consecutive_global_id.h │ ├── task.h │ ├── thread_local_contextual_promise.h │ ├── static_thread_pool.h │ ├── thread_local_context.h │ ├── tagged_pointer.h │ ├── polymorphic_promise.h │ ├── async_promise.h │ └── reusable_task.h ├── Phantom.Coroutines.cppcoro ├── include │ └── cppcoro │ │ ├── sync_wait.hpp │ │ ├── async_scope.hpp │ │ ├── async_latch.hpp │ │ ├── inline_scheduler.hpp │ │ ├── generator.hpp │ │ ├── static_thread_pool.hpp │ │ ├── async_auto_reset_event.hpp │ │ ├── async_manual_reset_event.hpp │ │ ├── single_consumer_event.hpp │ │ ├── is_awaitable.hpp │ │ ├── operation_cancelled.hpp │ │ ├── async_mutex.hpp │ │ ├── task.hpp │ │ ├── shared_task.hpp │ │ ├── awaitable_traits.hpp │ │ ├── async_generator.hpp │ │ ├── sequence_barrier.hpp │ │ ├── cancellation_source.hpp │ │ └── cancellation_token.hpp └── CMakeLists.txt ├── vcpkg.json ├── CONTRIBUTING.md ├── LICENSE └── CMakeLists.txt /Documentation/header.md: -------------------------------------------------------------------------------- 1 | # [Phantom.Coroutines](../README.md) 2 | 3 | -------------------------------------------------------------------------------- /Tla/ThreadPool.cfg: -------------------------------------------------------------------------------- 1 | SPECIFICATION Spec 2 | \* Add statements after this line. 3 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro.Test/cppcoro_generator_test.cpp: -------------------------------------------------------------------------------- 1 | #include "cppcoro/generator.hpp" 2 | -------------------------------------------------------------------------------- /Tla/LockFreeFifoQueue/AbstractFifoQueue.cfg: -------------------------------------------------------------------------------- 1 | SPECIFICATION Spec 2 | CONSTANT Items = { i1, i2, i3 } 3 | CONSTRAINT Constraint 4 | -------------------------------------------------------------------------------- /Tla/ReaderWriterLock/AbstractReaderWriterLock.cfg: -------------------------------------------------------------------------------- 1 | SPECIFICATION Spec 2 | PROPERTY Property 3 | CONSTANT Threads = { t1, t2, t3, t4 } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.vs 2 | /out 3 | *.coverage 4 | /.vscode 5 | Tla/ThreadPool_Wakeup.cfg 6 | Tla/*.old 7 | vc140.pdb 8 | /CMakeUserPresets.json 9 | -------------------------------------------------------------------------------- /Tla/AutoResetEvent/AbstractAutoResetEvent.cfg: -------------------------------------------------------------------------------- 1 | SPECIFICATION Spec 2 | PROPERTY Property 3 | CONSTANT ListeningThreads = { t1, t2, t3, t4 } 4 | -------------------------------------------------------------------------------- /Tla/ReaderWriterLock/FairReaderWriterLock.cfg: -------------------------------------------------------------------------------- 1 | SPECIFICATION Spec 2 | PROPERTY Property 3 | CONSTANT Threads = { t1, t2, t3 } 4 | ALIAS Alias -------------------------------------------------------------------------------- /Tla/.gitignore: -------------------------------------------------------------------------------- 1 | *.toolbox/*/* 2 | *.toolbox/*SnapShot_*.launch 3 | states/ 4 | *.toolbox/_Snapshot_* 5 | *.toolbox/_Snapshot_*/ 6 | *.out 7 | *.old 8 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/awaiter_wrapper.ixx: -------------------------------------------------------------------------------- 1 | export module Phantom.Coroutines.awaiter_wrapper; 2 | export import Phantom.Coroutines.extensible_promise; 3 | -------------------------------------------------------------------------------- /Tla/ReadCopyUpdate/MCReadCopyUpdate_MemorySafety.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE MCReadCopyUpdate_MemorySafety ---- 2 | 3 | EXTENDS ReadCopyUpdate_MemorySafety 4 | 5 | ==== 6 | -------------------------------------------------------------------------------- /Tla/ReadCopyUpdate/MCReadCopyUpdate_MemorySafety.cfg: -------------------------------------------------------------------------------- 1 | SPECIFICATION Spec 2 | INVARIANT TypeOk 3 | CONSTANT SectionCount = 2 4 | CONSTANT OperationCount = 2 5 | CONSTANT ThreadCount = 2 6 | -------------------------------------------------------------------------------- /vcpkg/toolchains/clang-libc++-21.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_C_COMPILER "clang-21") 2 | set(CMAKE_CXX_COMPILER "clang++-21") 3 | add_compile_options(-stdlib=libc++) 4 | add_link_options(-stdlib=libc++) 5 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro.Test/cppcoro_inline_scheduler_test.cpp: -------------------------------------------------------------------------------- 1 | #include "cppcoro/inline_scheduler.hpp" 2 | 3 | static_assert(std::same_as); 4 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/value_awaiter.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include "Phantom.Coroutines/detail/config_macros.h" 3 | export module Phantom.Coroutines.value_awaiter; 4 | #include "Phantom.Coroutines/value_awaiter.h" 5 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/non_copyable.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include "Phantom.Coroutines/detail/config_macros.h" 3 | export module Phantom.Coroutines.non_copyable; 4 | #include "Phantom.Coroutines/detail/non_copyable.h" 5 | -------------------------------------------------------------------------------- /.runsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/config_globals.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include "Phantom.Coroutines/detail/config_macros.h" 3 | export module Phantom.Coroutines.config_globals; 4 | #include "Phantom.Coroutines/detail/config_globals.h" 5 | -------------------------------------------------------------------------------- /Tla/ReadCopyUpdate/ReadCopyUpdate_MemorySafety.cfg: -------------------------------------------------------------------------------- 1 | \* Add statements after this line. 2 | SPECIFICATION Spec 3 | INVARIANT TypeOk 4 | CONSTANT SectionCount = 1 5 | CONSTANT OperationCount = 1 6 | CONSTANT ThreadCount = 1 7 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/immovable_object.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include "Phantom.Coroutines/detail/config_macros.h" 3 | export module Phantom.Coroutines.immovable_object; 4 | #include "Phantom.Coroutines/detail/immovable_object.h" 5 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro.Test/cppcoro_async_latch_test.cpp: -------------------------------------------------------------------------------- 1 | #include "cppcoro/async_latch.hpp" 2 | 3 | static_assert( 4 | std::same_as< 5 | ::Phantom::Coroutines::async_latch<>, 6 | ::cppcoro::async_latch 7 | > 8 | ); 9 | -------------------------------------------------------------------------------- /Tla/SequenceLock/SequenceLock.cfg: -------------------------------------------------------------------------------- 1 | SPECIFICATION Spec 2 | \* Add statements after this line. 3 | CONSTANT 4 | Readers = { r1, r2 } 5 | Writers = { w1, w2, w3, w4 } 6 | ValueSize = 3 7 | INVARIANT Invariant 8 | SYMMETRY Symmetry 9 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/scheduler.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include "Phantom.Coroutines/detail/config_macros.h" 3 | export module Phantom.Coroutines.scheduler; 4 | import Phantom.Coroutines.type_traits; 5 | #include "Phantom.Coroutines/scheduler.h" 6 | -------------------------------------------------------------------------------- /Tla/ThreadPool_Liveness_3_Items_2_Threads.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE ThreadPool_Liveness_3_Items_2_Threads ---- 2 | EXTENDS ThreadPool, TLC 3 | 4 | CONSTANT t1, t2 5 | CONSTANT i1, i2, i3 6 | 7 | MC_Threads == {t1, t2} 8 | MC_Items == {i1, i2, i3} 9 | ==== -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/lifetime_tracker.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include "Phantom.Coroutines/detail/config_macros.h" 5 | export module Phantom.Coroutines.Test.lifetime_tracker; 6 | #include "lifetime_tracker.h" 7 | -------------------------------------------------------------------------------- /Tla/ThreadPool_Correctness_3_Items_2_Threads.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE ThreadPool_Correctness_3_Items_2_Threads ---- 2 | EXTENDS ThreadPool, TLC 3 | 4 | CONSTANT t1, t2 5 | CONSTANT i1, i2, i3 6 | 7 | MC_Threads == {t1, t2} 8 | MC_Items == {i1, i2, i3} 9 | ==== -------------------------------------------------------------------------------- /Tla/ThreadPool_Liveness_3_Items_3_Threads.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE ThreadPool_Liveness_3_Items_3_Threads ---- 2 | EXTENDS ThreadPool, TLC 3 | 4 | CONSTANT t1, t2, t3 5 | CONSTANT i1, i2, i3 6 | 7 | MC_Threads == {t1, t2, t3} 8 | MC_Items == {i1, i2, i3} 9 | ==== -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro.Test/cppcoro_static_thread_pool_test.cpp: -------------------------------------------------------------------------------- 1 | #include "cppcoro/static_thread_pool.hpp" 2 | 3 | static_assert( 4 | std::same_as< 5 | ::Phantom::Coroutines::static_thread_pool<>, 6 | ::cppcoro::static_thread_pool 7 | >); 8 | -------------------------------------------------------------------------------- /Tla/ThreadPool_Correctness_3_Items_3_Threads.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE ThreadPool_Correctness_3_Items_3_Threads ---- 2 | EXTENDS ThreadPool, TLC 3 | 4 | CONSTANT t1, t2, t3 5 | CONSTANT i1, i2, i3 6 | 7 | MC_Threads == {t1, t2, t3} 8 | MC_Items == {i1, i2, i3} 9 | ==== -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/construct_from_base.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include "Phantom.Coroutines/detail/config_macros.h" 4 | export module Phantom.Coroutines.construct_from_base; 5 | #include "Phantom.Coroutines/detail/construct_from_base.h" 6 | -------------------------------------------------------------------------------- /Phantom.Coroutines/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.30) 2 | 3 | add_library ( 4 | Phantom.Coroutines 5 | INTERFACE 6 | ) 7 | 8 | target_include_directories( 9 | Phantom.Coroutines 10 | INTERFACE 11 | include) 12 | -------------------------------------------------------------------------------- /Tla/ThreadPool_Wakeup_Liveness_3_Items_3_Threads.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE ThreadPool_Wakeup_Liveness_3_Items_3_Threads ---- 2 | EXTENDS ThreadPool_Wakeup, TLC 3 | 4 | CONSTANT w1, w2, r3 5 | 6 | MC_Items == 3 7 | MC_WorkerThreads == {w1, w2} 8 | MC_RemoteThreads == {r3} 9 | ==== -------------------------------------------------------------------------------- /Tla/ThreadPool_Wakeup_Liveness_4_Items_3_Threads.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE ThreadPool_Wakeup_Liveness_4_Items_3_Threads ---- 2 | EXTENDS ThreadPool_Wakeup, TLC 3 | 4 | CONSTANT w1, w2, r3 5 | 6 | MC_Items == 4 7 | MC_WorkerThreads == {w1, w2} 8 | MC_RemoteThreads == {r3} 9 | ==== -------------------------------------------------------------------------------- /Tla/ThreadPool_Wakeup_Liveness_5_Items_3_Threads.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE ThreadPool_Wakeup_Liveness_5_Items_3_Threads ---- 2 | EXTENDS ThreadPool_Wakeup, TLC 3 | 4 | CONSTANT w1, w2, r3 5 | 6 | MC_Items == 5 7 | MC_WorkerThreads == {w1, w2} 8 | MC_RemoteThreads == {r3} 9 | ==== -------------------------------------------------------------------------------- /Tla/ThreadPool_Correctness_5_Items_3_Threads.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE ThreadPool_Correctness_5_Items_3_Threads ---- 2 | EXTENDS ThreadPool, TLC 3 | 4 | CONSTANT t1, t2, t3 5 | CONSTANT i1, i2, i3, i4, i5 6 | 7 | MC_Threads == {t1, t2, t3} 8 | MC_Items == {i1, i2, i3, i4, i5} 9 | ==== -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/coroutine.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include "Phantom.Coroutines/detail/config_macros.h" 6 | export module Phantom.Coroutines.coroutine; 7 | #include "Phantom.Coroutines/detail/coroutine.h" 8 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/await_none_await_transform.ixx: -------------------------------------------------------------------------------- 1 | #include "Phantom.Coroutines/detail/config_macros.h" 2 | export module Phantom.Coroutines.await_none_await_transform; 3 | import Phantom.Coroutines.type_traits; 4 | #include "Phantom.Coroutines/await_none_await_transform.h" 5 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/consecutive_global_id.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include "Phantom.Coroutines/detail/config_macros.h" 5 | export module Phantom.Coroutines.consecutive_global_id; 6 | #include "Phantom.Coroutines/consecutive_global_id.h" 7 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/thread_local_context.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include "Phantom.Coroutines/detail/config_macros.h" 5 | export module Phantom.Coroutines.thread_local_context; 6 | #include "Phantom.Coroutines/thread_local_context.h" 7 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/aligned_array.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Phantom.Coroutines/detail/config_macros.h" 7 | export module Phantom.Coroutines.aligned_array; 8 | #include "Phantom.Coroutines/aligned_array.h" 9 | -------------------------------------------------------------------------------- /Tla/AutoResetEvent/AutoResetEvent_v2.cfg: -------------------------------------------------------------------------------- 1 | \* Add statements after this line. 2 | SPECIFICATION Spec 3 | CONSTANT ListeningThreads = { l1, l2, l3 } 4 | CONSTANT SignallingThreads = { s1, s2, s3 } 5 | 6 | PROPERTY AbstractEventSpec 7 | INVARIANT TypeOk 8 | ALIAS Alias 9 | PROPERTY Property 10 | -------------------------------------------------------------------------------- /vcpkg/triplets/x64-linux-clang-libcxx-21.cmake: -------------------------------------------------------------------------------- 1 | set(VCPKG_TARGET_ARCHITECTURE x64) 2 | set(VCPKG_CRT_LINKAGE dynamic) 3 | set(VCPKG_LIBRARY_LINKAGE static) 4 | set(VCPKG_CMAKE_SYSTEM_NAME Linux) 5 | set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/../toolchains/clang-libc++-21.cmake) 6 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/final_suspend_transfer.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include "Phantom.Coroutines/detail/config_macros.h" 4 | export module Phantom.Coroutines.final_suspend_transfer; 5 | import Phantom.Coroutines.coroutine; 6 | #include "Phantom.Coroutines/final_suspend_transfer.h" 7 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/inline_scheduler.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include "Phantom.Coroutines/detail/config_macros.h" 3 | export module Phantom.Coroutines.inline_scheduler; 4 | import Phantom.Coroutines.coroutine; 5 | import Phantom.Coroutines.scheduler; 6 | #include "Phantom.Coroutines/inline_scheduler.h" 7 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/policies.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include "Phantom.Coroutines/detail/config_macros.h" 4 | export module Phantom.Coroutines.policies; 5 | import Phantom.Coroutines.coroutine; 6 | import Phantom.Coroutines.type_traits; 7 | #include "Phantom.Coroutines/policies.h" 8 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/assert_same_thread.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #if !NDEBUG 3 | #include 4 | #include 5 | #endif 6 | #include "Phantom.Coroutines/detail/config_macros.h" 7 | export module Phantom.Coroutines.assert_same_thread; 8 | #include "Phantom.Coroutines/detail/assert_same_thread.h" 9 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/sequence_lock.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Phantom.Coroutines/detail/config_macros.h" 7 | export module Phantom.Coroutines.sequence_lock; 8 | #include "Phantom.Coroutines/sequence_lock.h" 9 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/await_all_await_transform.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include "Phantom.Coroutines/detail/config_macros.h" 4 | export module Phantom.Coroutines.await_all_await_transform; 5 | import Phantom.Coroutines.type_traits; 6 | #include "Phantom.Coroutines/await_all_await_transform.h" 7 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/function_traits.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Phantom.Coroutines/detail/config_macros.h" 7 | export module Phantom.Coroutines.function_traits; 8 | #include "Phantom.Coroutines/function_traits.h" 9 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/sharding.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include "Phantom.Coroutines/detail/config_macros.h" 6 | export module Phantom.Coroutines.sharding; 7 | import Phantom.Coroutines.aligned_array; 8 | #include "Phantom.Coroutines/sharding.h" 9 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro.Test/cppcoro_single_consumer_event_test.cpp: -------------------------------------------------------------------------------- 1 | #include "cppcoro/single_consumer_event.hpp" 2 | 3 | static_assert(std::same_as< 4 | ::cppcoro::single_consumer_event, 5 | ::Phantom::Coroutines::async_manual_reset_event< 6 | ::Phantom::Coroutines::single_awaiter 7 | >>); 8 | -------------------------------------------------------------------------------- /Tla/ReaderWriterLock/FairReaderWriterLock_AtomicState_Queue.cfg: -------------------------------------------------------------------------------- 1 | SPECIFICATION Spec 2 | \* Add statements after this line. 3 | CONSTANT Threads = { t1, t2 } 4 | \* CONSTANT PreferWriters = FALSE 5 | INVARIANT TypeOk 6 | CHECK_DEADLOCK TRUE 7 | \*SYMMETRY Symmetry 8 | PROPERTY Property 9 | ALIAS Alias 10 | -------------------------------------------------------------------------------- /Tla/ReaderWriterLock/FairReaderWriterLock_LockBased.cfg: -------------------------------------------------------------------------------- 1 | SPECIFICATION Spec 2 | \* Add statements after this line. 3 | CONSTANT Threads = { t1, t2, t3, t4 } 4 | \* CONSTANT PreferWriters = FALSE 5 | INVARIANT TypeOk 6 | CHECK_DEADLOCK TRUE 7 | \*SYMMETRY Symmetry 8 | PROPERTY Property 9 | ALIAS Alias 10 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/consecutive_thread_id.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include "Phantom.Coroutines/detail/config_macros.h" 4 | export module Phantom.Coroutines.consecutive_thread_id; 5 | import Phantom.Coroutines.reusable_consecutive_global_id; 6 | #include "Phantom.Coroutines/detail/consecutive_thread_id.h" 7 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/nonatomic_shared_ptr.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Phantom.Coroutines/detail/config_macros.h" 7 | export module Phantom.Coroutines.nonatomic_shared_ptr; 8 | #include "Phantom.Coroutines/nonatomic_shared_ptr.h" 9 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/reusable_consecutive_global_id.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include "Phantom.Coroutines/detail/config_macros.h" 6 | export module Phantom.Coroutines.reusable_consecutive_global_id; 7 | #include "Phantom.Coroutines/reusable_consecutive_global_id.h" 8 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/scope_guard.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include "Phantom.Coroutines/detail/config_macros.h" 6 | export module Phantom.Coroutines.scope_guard; 7 | import Phantom.Coroutines.immovable_object; 8 | #include "Phantom.Coroutines/detail/scope_guard.h" 9 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/task.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include "Phantom.Coroutines/detail/config_macros.h" 4 | export module Phantom.Coroutines.task; 5 | import Phantom.Coroutines.core_task; 6 | import Phantom.Coroutines.coroutine; 7 | import Phantom.Coroutines.policies; 8 | #include "Phantom.Coroutines/task.h" 9 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/promise_allocator.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include "Phantom.Coroutines/detail/config_macros.h" 4 | export module Phantom.Coroutines.promise_allocator; 5 | import Phantom.Coroutines.extensible_promise; 6 | import Phantom.Coroutines.type_traits; 7 | #include "Phantom.Coroutines/promise_allocator.h" 8 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro/include/cppcoro/sync_wait.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CORO_SYNC_WAIT_HPP 2 | #define PHANTOM_COROUTINES_INCLUDE_CORO_SYNC_WAIT_HPP 3 | 4 | #include "Phantom.Coroutines/sync_wait.h" 5 | 6 | namespace cppcoro 7 | { 8 | 9 | using ::Phantom::Coroutines::sync_wait; 10 | 11 | } 12 | #endif 13 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/make_task.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include "Phantom.Coroutines/detail/config_macros.h" 5 | export module Phantom.Coroutines.make_task; 6 | import Phantom.Coroutines.task; 7 | import Phantom.Coroutines.type_traits; 8 | #include "Phantom.Coroutines/make_task.h" 9 | 10 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/tagged_pointer.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "Phantom.Coroutines/detail/config_macros.h" 8 | export module Phantom.Coroutines.tagged_pointer; 9 | #include "Phantom.Coroutines/tagged_pointer.h" 10 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/direct_initialized_optional.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Phantom.Coroutines/detail/config_macros.h" 7 | export module Phantom.Coroutines.direct_initialized_optional; 8 | #include "Phantom.Coroutines/direct_initialized_optional.h" 9 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro/include/cppcoro/async_scope.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CORO_ASYNC_SCOPE_H 2 | #define PHANTOM_COROUTINES_INCLUDE_CORO_ASYNC_SCOPE_H 3 | 4 | #include "Phantom.Coroutines/async_scope.h" 5 | 6 | namespace cppcoro 7 | { 8 | using async_scope = ::Phantom::Coroutines::async_scope<>; 9 | } 10 | #endif 11 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/fibonacci_heap.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "Phantom.Coroutines/detail/config_macros.h" 8 | export module Phantom.Coroutines.fibonacci_heap; 9 | #include "Phantom.Coroutines/detail/fibonacci_heap.h" 10 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro/include/cppcoro/async_latch.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CORO_ASYNC_LATCH_HPP 2 | #define PHANTOM_COROUTINES_INCLUDE_CORO_ASYNC_LATCH_HPP 3 | 4 | #include "Phantom.Coroutines/async_latch.h" 5 | 6 | namespace cppcoro 7 | { 8 | using async_latch = ::Phantom::Coroutines::async_latch<>; 9 | } 10 | #endif 11 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/storage_for.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include "Phantom.Coroutines/detail/config_macros.h" 5 | export module Phantom.Coroutines.storage_for; 6 | import Phantom.Coroutines.immovable_object; 7 | import Phantom.Coroutines.type_traits; 8 | #include "Phantom.Coroutines/detail/storage_for.h" 9 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/variant_result_storage.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include "Phantom.Coroutines/detail/config_macros.h" 6 | export module Phantom.Coroutines.variant_result_storage; 7 | import Phantom.Coroutines.type_traits; 8 | #include "Phantom.Coroutines/detail/variant_result_storage.h" 9 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro/include/cppcoro/inline_scheduler.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CORO_INLINE_SCHEDULER_HPP 2 | #define PHANTOM_COROUTINES_INCLUDE_CORO_INLINE_SCHEDULER_HPP 3 | 4 | #include "Phantom.Coroutines/inline_scheduler.h" 5 | 6 | namespace cppcoro 7 | { 8 | 9 | using Phantom::Coroutines::inline_scheduler; 10 | 11 | } 12 | #endif 13 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/double_wide_atomic.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Phantom.Coroutines/detail/config_macros.h" 7 | export module Phantom.Coroutines.double_wide_atomic; 8 | import Phantom.Coroutines.type_traits; 9 | #include "Phantom.Coroutines/double_wide_atomic.h" 10 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/error_condition_early_termination.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include "Phantom.Coroutines/detail/config_macros.h" 5 | export module Phantom.Coroutines.error_condition_early_termination; 6 | import Phantom.Coroutines.early_termination_task; 7 | #include "Phantom.Coroutines/error_condition_early_termination.h" 8 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro/include/cppcoro/generator.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CORO_GENERATOR_HPP 2 | #define PHANTOM_COROUTINES_INCLUDE_CORO_GENERATOR_HPP 3 | 4 | #include "Phantom.Coroutines/generator.h" 5 | 6 | namespace cppcoro 7 | { 8 | template< 9 | typename T 10 | > 11 | using generator = ::Phantom::Coroutines::generator; 12 | } 13 | #endif 14 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/atomic_shared_ptr.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #if __cpp_lib_atomic_shared_ptr 3 | #include 4 | #else 5 | #include 6 | #include 7 | #include 8 | #endif 9 | #include "Phantom.Coroutines/detail/config_macros.h" 10 | export module Phantom.Coroutines.atomic_shared_ptr; 11 | #include "Phantom.Coroutines/detail/atomic_shared_ptr.h" 12 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/awaiter_list.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Phantom.Coroutines/detail/config_macros.h" 7 | export module Phantom.Coroutines.awaiter_list; 8 | import Phantom.Coroutines.atomic_state; 9 | import Phantom.Coroutines.policies; 10 | #include "Phantom.Coroutines/awaiter_list.h" 11 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/reusable_task.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include "Phantom.Coroutines/detail/config_macros.h" 5 | export module Phantom.Coroutines.reusable_task; 6 | import Phantom.Coroutines.core_task; 7 | import Phantom.Coroutines.coroutine; 8 | import Phantom.Coroutines.policies; 9 | #include "Phantom.Coroutines/reusable_task.h" 10 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.30) 2 | 3 | add_library ( 4 | Phantom.Coroutines.cppcoro 5 | INTERFACE 6 | ) 7 | 8 | target_include_directories( 9 | Phantom.Coroutines.cppcoro 10 | INTERFACE 11 | include) 12 | 13 | target_link_libraries( 14 | Phantom.Coroutines.cppcoro 15 | INTERFACE 16 | Phantom.Coroutines) 17 | -------------------------------------------------------------------------------- /Tla/ThreadPool_Correctness_3_Items_2_Threads.cfg: -------------------------------------------------------------------------------- 1 | CONSTANT 2 | i1 = i1 3 | i2 = i2 4 | i3 = i3 5 | 6 | CONSTANT 7 | t1 = t1 8 | t2 = t2 9 | 10 | CONSTANT 11 | Items <- MC_Items 12 | Threads <- MC_Threads 13 | 14 | SPECIFICATION 15 | Spec 16 | 17 | INVARIANT 18 | TypeOk 19 | NoItemIsProcessedInDuplicate 20 | 21 | SYMMETRY 22 | Symmetry 23 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/static_thread_pool.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include "Phantom.Coroutines/detail/config_macros.h" 6 | export module Phantom.Coroutines.static_thread_pool; 7 | import Phantom.Coroutines.scheduler; 8 | import Phantom.Coroutines.thread_pool_scheduler; 9 | #include "Phantom.Coroutines/static_thread_pool.h" 10 | 11 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro/include/cppcoro/static_thread_pool.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CORO_STATIC_THREAD_POOL_HPP 2 | #define PHANTOM_COROUTINES_INCLUDE_CORO_STATIC_THREAD_POOL_HPP 3 | 4 | #include "Phantom.Coroutines/static_thread_pool.h" 5 | 6 | namespace cppcoro 7 | { 8 | 9 | using static_thread_pool = ::Phantom::Coroutines::static_thread_pool<>; 10 | 11 | } 12 | #endif 13 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/suspend_result.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include "Phantom.Coroutines/detail/config_macros.h" 5 | export module Phantom.Coroutines.suspend_result; 6 | import Phantom.Coroutines.awaiter_wrapper; 7 | import Phantom.Coroutines.coroutine; 8 | import Phantom.Coroutines.type_traits; 9 | #include "Phantom.Coroutines/suspend_result.h" 10 | -------------------------------------------------------------------------------- /Tla/ThreadPool_Correctness_3_Items_3_Threads.cfg: -------------------------------------------------------------------------------- 1 | CONSTANT 2 | i1 = i1 3 | i2 = i2 4 | i3 = i3 5 | 6 | CONSTANT 7 | t1 = t1 8 | t2 = t2 9 | t3 = t3 10 | 11 | CONSTANT 12 | Items <- MC_Items 13 | Threads <- MC_Threads 14 | 15 | SPECIFICATION 16 | Spec 17 | 18 | INVARIANT 19 | TypeOk 20 | NoItemIsProcessedInDuplicate 21 | 22 | SYMMETRY 23 | Symmetry -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/async_latch.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include "Phantom.Coroutines/detail/config_macros.h" 5 | export module Phantom.Coroutines.async_latch; 6 | export import Phantom.Coroutines.async_manual_reset_event; 7 | export import Phantom.Coroutines.policies; 8 | import Phantom.Coroutines.type_traits; 9 | #include "Phantom.Coroutines/async_latch.h" 10 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/pmr_task.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Phantom.Coroutines/detail/config_macros.h" 7 | export module Phantom.Coroutines.Test.pmr_task; 8 | import Phantom.Coroutines.promise_allocator; 9 | import Phantom.Coroutines.reusable_task; 10 | import Phantom.Coroutines.task; 11 | #include "pmr_task.h" 12 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro/include/cppcoro/async_auto_reset_event.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CORO_ASYNC_AUTO_RESET_EVENT_HPP 2 | #define PHANTOM_COROUTINES_INCLUDE_CORO_ASYNC_AUTO_RESET_EVENT_HPP 3 | 4 | #include "Phantom.Coroutines/async_auto_reset_event.h" 5 | 6 | namespace cppcoro 7 | { 8 | using async_auto_reset_event = ::Phantom::Coroutines::async_auto_reset_event<>; 9 | } 10 | #endif 11 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/contextual_promise.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include "Phantom.Coroutines/detail/config_macros.h" 5 | export module Phantom.Coroutines.contextual_promise; 6 | import Phantom.Coroutines.awaiter_wrapper; 7 | import Phantom.Coroutines.extensible_promise; 8 | import Phantom.Coroutines.type_traits; 9 | #include "Phantom.Coroutines/contextual_promise.h" 10 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/generator.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "Phantom.Coroutines/detail/config_macros.h" 8 | export module Phantom.Coroutines.generator; 9 | import Phantom.Coroutines.coroutine; 10 | import Phantom.Coroutines.immovable_object; 11 | #include "Phantom.Coroutines/generator.h" 12 | -------------------------------------------------------------------------------- /Tla/ThreadPool_Liveness_3_Items_2_Threads.cfg: -------------------------------------------------------------------------------- 1 | CONSTANT 2 | i1 = i1 3 | i2 = i2 4 | i3 = i3 5 | 6 | CONSTANT 7 | t1 = t1 8 | t2 = t2 9 | 10 | CONSTANT 11 | Items <- MC_Items 12 | Threads <- MC_Threads 13 | 14 | SPECIFICATION 15 | SpecWithFairness 16 | 17 | INVARIANT 18 | TypeOk 19 | NoItemIsProcessedInDuplicate 20 | 21 | PROPERTY 22 | AllItemsGetProcessed 23 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro/include/cppcoro/async_manual_reset_event.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CORO_ASYNC_MANUAL_RESET_EVENT_HPP 2 | #define PHANTOM_COROUTINES_INCLUDE_CORO_ASYNC_MANUAL_RESET_EVENT_HPP 3 | 4 | #include "Phantom.Coroutines/async_manual_reset_event.h" 5 | 6 | namespace cppcoro 7 | { 8 | using async_manual_reset_event = ::Phantom::Coroutines::async_manual_reset_event<>; 9 | } 10 | #endif 11 | -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json", 3 | "name": "phantom-coroutines", 4 | "version": "0.0.0", 5 | "dependencies": [ 6 | "gtest" 7 | ], 8 | "builtin-baseline": "dee924de74e81388140a53c32a919ecec57d20ab", 9 | "overrides": [ 10 | { 11 | "name": "gtest", 12 | "version": "1.14.0" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro.Test/cppcoro_is_awaitable_test.cpp: -------------------------------------------------------------------------------- 1 | #include "cppcoro/is_awaitable.hpp" 2 | #include "cppcoro/task.hpp" 3 | 4 | static_assert(cppcoro::is_awaitable_v>); 5 | static_assert(!cppcoro::is_awaitable_v); 6 | 7 | static_assert(std::same_as>>); 8 | static_assert(std::same_as>); 9 | 10 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/thread_local_contextual_promise.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include "Phantom.Coroutines/detail/config_macros.h" 4 | export module Phantom.Coroutines.thread_local_contextual_promise; 5 | import Phantom.Coroutines.contextual_promise; 6 | import Phantom.Coroutines.extensible_promise; 7 | import Phantom.Coroutines.thread_local_context; 8 | #include "Phantom.Coroutines/thread_local_contextual_promise.h" 9 | -------------------------------------------------------------------------------- /Tla/ThreadPool_Correctness_5_Items_3_Threads.cfg: -------------------------------------------------------------------------------- 1 | CONSTANT 2 | i1 = i1 3 | i2 = i2 4 | i3 = i3 5 | i4 = i4 6 | i5 = i5 7 | 8 | CONSTANT 9 | t1 = t1 10 | t2 = t2 11 | t3 = t3 12 | 13 | CONSTANT 14 | Items <- MC_Items 15 | Threads <- MC_Threads 16 | 17 | SPECIFICATION 18 | Spec 19 | 20 | INVARIANT 21 | TypeOk 22 | NoItemIsProcessedInDuplicate 23 | 24 | SYMMETRY 25 | Symmetry -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/async_atomic.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include "Phantom.Coroutines/detail/config_macros.h" 5 | export module Phantom.Coroutines.async_atomic; 6 | import Phantom.Coroutines.coroutine; 7 | import Phantom.Coroutines.double_wide_atomic; 8 | import Phantom.Coroutines.immovable_object; 9 | import Phantom.Coroutines.type_traits; 10 | #include "Phantom.Coroutines/async_atomic.h" 11 | -------------------------------------------------------------------------------- /Tla/ThreadPool_Wakeup_Liveness_3_Items_3_Threads.cfg: -------------------------------------------------------------------------------- 1 | CONSTANT 2 | w1 = w1 3 | w2 = w2 4 | r3 = r3 5 | 6 | CONSTANT 7 | Items <- MC_Items 8 | WorkerThreads <- MC_WorkerThreads 9 | RemoteThreads <- MC_RemoteThreads 10 | 11 | SPECIFICATION 12 | Spec 13 | 14 | PROPERTY 15 | AllItemsGetProcessed 16 | EnqueueCanAlwaysMakeProgress 17 | EventuallyEverythingSleeps 18 | 19 | CHECK_DEADLOCK FALSE 20 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/type_traits.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "Phantom.Coroutines/detail/config_macros.h" 9 | export module Phantom.Coroutines.type_traits; 10 | import Phantom.Coroutines.coroutine; 11 | import Phantom.Coroutines.function_traits; 12 | #include "Phantom.Coroutines/type_traits.h" 13 | -------------------------------------------------------------------------------- /Tla/ThreadPool_Wakeup_Liveness_4_Items_3_Threads.cfg: -------------------------------------------------------------------------------- 1 | CONSTANT 2 | w1 = w1 3 | w2 = w2 4 | r3 = r3 5 | 6 | CONSTANT 7 | Items <- MC_Items 8 | WorkerThreads <- MC_WorkerThreads 9 | RemoteThreads <- MC_RemoteThreads 10 | 11 | SPECIFICATION 12 | Spec 13 | 14 | PROPERTY 15 | AllItemsGetProcessed 16 | EnqueueCanAlwaysMakeProgress 17 | EventuallyEverythingSleeps 18 | 19 | CHECK_DEADLOCK FALSE 20 | -------------------------------------------------------------------------------- /Tla/ThreadPool_Wakeup_Liveness_5_Items_3_Threads.cfg: -------------------------------------------------------------------------------- 1 | CONSTANT 2 | w1 = w1 3 | w2 = w2 4 | r3 = r3 5 | 6 | CONSTANT 7 | Items <- MC_Items 8 | WorkerThreads <- MC_WorkerThreads 9 | RemoteThreads <- MC_RemoteThreads 10 | 11 | SPECIFICATION 12 | Spec 13 | 14 | PROPERTY 15 | AllItemsGetProcessed 16 | EnqueueCanAlwaysMakeProgress 17 | EventuallyEverythingSleeps 18 | 19 | CHECK_DEADLOCK FALSE 20 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/expected_early_termination.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Phantom.Coroutines/detail/config_macros.h" 7 | export module Phantom.Coroutines.expected_early_termination; 8 | import Phantom.Coroutines.early_termination_task; 9 | import Phantom.Coroutines.type_traits; 10 | #include "Phantom.Coroutines/expected_early_termination.h" 11 | -------------------------------------------------------------------------------- /Tla/ThreadPool_Liveness_3_Items_3_Threads.cfg: -------------------------------------------------------------------------------- 1 | CONSTANT 2 | i1 = i1 3 | i2 = i2 4 | i3 = i3 5 | 6 | CONSTANT 7 | t1 = t1 8 | t2 = t2 9 | t3 = t3 10 | 11 | CONSTANT 12 | Items <- MC_Items 13 | Threads <- MC_Threads 14 | 15 | SPECIFICATION 16 | SpecWithFairness 17 | 18 | INVARIANT 19 | TypeOk 20 | NoItemIsProcessedInDuplicate 21 | 22 | PROPERTY 23 | AllItemsGetProcessed 24 | 25 | ALIAS 26 | Alias -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro/include/cppcoro/single_consumer_event.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CORO_SINGLE_CONSUMER_EVENT_HPP 2 | #define PHANTOM_COROUTINES_INCLUDE_CORO_SINGLE_CONSUMER_EVENT_HPP 3 | 4 | #include "Phantom.Coroutines/async_manual_reset_event.h" 5 | 6 | namespace cppcoro 7 | { 8 | using single_consumer_event = ::Phantom::Coroutines::async_manual_reset_event< 9 | ::Phantom::Coroutines::single_awaiter 10 | >; 11 | } 12 | #endif 13 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/async_auto_reset_event.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include "Phantom.Coroutines/detail/config_macros.h" 5 | export module Phantom.Coroutines.async_auto_reset_event; 6 | import Phantom.Coroutines.coroutine; 7 | import Phantom.Coroutines.double_wide_atomic; 8 | import Phantom.Coroutines.policies; 9 | import Phantom.Coroutines.type_traits; 10 | #include "Phantom.Coroutines/async_auto_reset_event.h" 11 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/async_manual_reset_event.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include "Phantom.Coroutines/detail/config_macros.h" 5 | export module Phantom.Coroutines.async_manual_reset_event; 6 | import Phantom.Coroutines.atomic_state; 7 | import Phantom.Coroutines.coroutine; 8 | import Phantom.Coroutines.policies; 9 | import Phantom.Coroutines.type_traits; 10 | #include "Phantom.Coroutines/async_manual_reset_event.h" 11 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/atomic_state.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "Phantom.Coroutines/detail/config_macros.h" 10 | export module Phantom.Coroutines.atomic_state; 11 | import Phantom.Coroutines.coroutine; 12 | import Phantom.Coroutines.type_traits; 13 | #include "Phantom.Coroutines/detail/atomic_state.h" 14 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/polymorphic_promise.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Phantom.Coroutines/detail/config_macros.h" 7 | export module Phantom.Coroutines.polymorphic_promise; 8 | import Phantom.Coroutines.coroutine; 9 | export import Phantom.Coroutines.extensible_promise; 10 | import Phantom.Coroutines.type_traits; 11 | #include "Phantom.Coroutines/polymorphic_promise.h" 12 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Pull requests are welcome. For now, we are building using the latest version of Visual Studio. 4 | The project uses vcpkg to reference Google Tests, after which all the tests can be built and run inside Visual Studio. 5 | Running the tests takes about 5 seconds. 6 | 7 | Please include appropriate unit tests with your PRs. Follow the naming style in existing tests, where 8 | tests use very discriptive names and have a single-purpose. 9 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro.Test/cppcoro_async_auto_reset_event_test.cpp: -------------------------------------------------------------------------------- 1 | #include "cppcoro/async_auto_reset_event.hpp" 2 | 3 | static_assert(std::same_as< 4 | cppcoro::async_auto_reset_event, 5 | Phantom::Coroutines::async_auto_reset_event< 6 | Phantom::Coroutines::multiple_awaiters, 7 | Phantom::Coroutines::noop_on_destroy, 8 | Phantom::Coroutines::default_continuation_type, 9 | Phantom::Coroutines::await_is_not_cancellable 10 | >>); 11 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro.Test/cppcoro_async_manual_reset_event_test.cpp: -------------------------------------------------------------------------------- 1 | #include "cppcoro/async_manual_reset_event.hpp" 2 | 3 | static_assert(std::same_as< 4 | cppcoro::async_manual_reset_event, 5 | Phantom::Coroutines::async_manual_reset_event< 6 | Phantom::Coroutines::multiple_awaiters, 7 | Phantom::Coroutines::noop_on_destroy, 8 | Phantom::Coroutines::default_continuation_type, 9 | Phantom::Coroutines::await_is_not_cancellable 10 | >>); 11 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/sync_wait.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include "Phantom.Coroutines/detail/config_macros.h" 6 | #ifdef PHANTOM_COROUTINES_FUTURE_DOESNT_ACCEPT_NOT_DEFAULT_CONSTRUCTIBLE 7 | #include 8 | #endif 9 | export module Phantom.Coroutines.sync_wait; 10 | import Phantom.Coroutines.coroutine; 11 | import Phantom.Coroutines.task; 12 | import Phantom.Coroutines.type_traits; 13 | #include "Phantom.Coroutines/sync_wait.h" 14 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro.Test/cppcoro_async_scope_test.cpp: -------------------------------------------------------------------------------- 1 | #include "cppcoro/async_scope.hpp" 2 | 3 | static_assert(std::same_as< 4 | cppcoro::async_scope, 5 | Phantom::Coroutines::async_scope< 6 | Phantom::Coroutines::single_awaiter, 7 | Phantom::Coroutines::fail_on_destroy_with_awaiters, 8 | Phantom::Coroutines::default_continuation_type, 9 | Phantom::Coroutines::await_is_not_cancellable, 10 | Phantom::Coroutines::fail_on_use_after_join 11 | >>); 12 | -------------------------------------------------------------------------------- /Documentation/read_copy_update.md: -------------------------------------------------------------------------------- 1 | # [Phantom.Coroutines](../README.md) 2 | 3 | ## ```read_copy_update.h``` 4 | 5 | ```read_copy_update.h``` provides a read-copy-update section in the class ```read_copy_update```. 6 | A read-copy-update section has very high read performance. 7 | 8 | See [https://en.wikipedia.org/wiki/Read-copy-update]. 9 | 10 | ## ```read_copy_update``` 11 | 12 | ```read_copy_update``` implements a read-copy-update section 13 | holding at least one instance of T. -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro/include/cppcoro/is_awaitable.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CORO_IS_AWAITABLE_HPP 2 | #define PHANTOM_COROUTINES_INCLUDE_CORO_IS_AWAITABLE_HPP 3 | 4 | #include "Phantom.Coroutines/type_traits.h" 5 | 6 | namespace cppcoro 7 | { 8 | 9 | template< 10 | typename T 11 | > 12 | using is_awaitable = std::bool_constant< 13 | ::Phantom::Coroutines::is_awaitable 14 | >; 15 | 16 | template 17 | constexpr bool is_awaitable_v = is_awaitable::value; 18 | } 19 | #endif 20 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/extensible_promise.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "Phantom.Coroutines/detail/config_macros.h" 8 | export module Phantom.Coroutines.extensible_promise; 9 | import Phantom.Coroutines.coroutine; 10 | import Phantom.Coroutines.scope_guard; 11 | import Phantom.Coroutines.type_traits; 12 | #include "Phantom.Coroutines/extensible_promise.h" 13 | #include "Phantom.Coroutines/awaiter_wrapper.h" 14 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/tracing.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "Phantom.Coroutines/detail/config_macros.h" 9 | export module Phantom.Coroutines.tracing; 10 | import Phantom.Coroutines.awaiter_wrapper; 11 | import Phantom.Coroutines.coroutine; 12 | import Phantom.Coroutines.extensible_promise; 13 | import Phantom.Coroutines.type_traits; 14 | #include "Phantom.Coroutines/tracing.h" 15 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/async_promise.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include "Phantom.Coroutines/detail/config_macros.h" 5 | export module Phantom.Coroutines.async_promise; 6 | import Phantom.Coroutines.async_manual_reset_event; 7 | import Phantom.Coroutines.awaiter_wrapper; 8 | import Phantom.Coroutines.immovable_object; 9 | import Phantom.Coroutines.policies; 10 | import Phantom.Coroutines.storage_for; 11 | import Phantom.Coroutines.type_traits; 12 | #include "Phantom.Coroutines/async_promise.h" 13 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/async_scope.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "Phantom.Coroutines/detail/config_macros.h" 10 | export module Phantom.Coroutines.async_scope; 11 | import Phantom.Coroutines.coroutine; 12 | import Phantom.Coroutines.final_suspend_transfer; 13 | import Phantom.Coroutines.policies; 14 | import Phantom.Coroutines.type_traits; 15 | #include "Phantom.Coroutines/async_scope.h" 16 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro/include/cppcoro/operation_cancelled.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CORO_OPERATION_CANCELLED_HPP 2 | #define PHANTOM_COROUTINES_INCLUDE_CORO_OPERATION_CANCELLED_HPP 3 | 4 | #include 5 | 6 | namespace cppcoro 7 | { 8 | 9 | class operation_cancelled : public std::exception 10 | { 11 | public: 12 | 13 | operation_cancelled() noexcept 14 | : std::exception() 15 | {} 16 | 17 | const char* what() const noexcept override { return "operation cancelled"; } 18 | }; 19 | 20 | } 21 | #endif 22 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/async_reader_writer_lock.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include "Phantom.Coroutines/detail/config_macros.h" 5 | export module Phantom.Coroutines.async_reader_writer_lock; 6 | import Phantom.Coroutines.awaiter_list; 7 | import Phantom.Coroutines.double_wide_atomic; 8 | import Phantom.Coroutines.immovable_object; 9 | import Phantom.Coroutines.policies; 10 | import Phantom.Coroutines.tagged_pointer; 11 | import Phantom.Coroutines.type_traits; 12 | #include "Phantom.Coroutines/async_reader_writer_lock.h" 13 | -------------------------------------------------------------------------------- /Tla/AutoResetEvent/AbstractEventListener.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE AbstractEventListener ---- 2 | 3 | VARIABLE State 4 | vars == << State >> 5 | 6 | TypeOk == State \in { "Idle", "Waiting", "Complete" } 7 | 8 | Init == State = "Idle" 9 | 10 | Wait == 11 | /\ State = "Idle" 12 | /\ State' = "Waiting" 13 | 14 | Complete == 15 | /\ State' = "Complete" 16 | 17 | Next == 18 | \/ Wait 19 | \/ Complete 20 | 21 | Spec == 22 | /\ Init 23 | /\ [][Next]_vars 24 | 25 | Property == 26 | /\ Spec 27 | /\ [](TypeOk) 28 | 29 | ==== 30 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro/include/cppcoro/async_mutex.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CORO_ASYNC_MUTEX_HPP 2 | #define PHANTOM_COROUTINES_INCLUDE_CORO_ASYNC_MUTEX_HPP 3 | 4 | #include "Phantom.Coroutines/async_mutex.h" 5 | 6 | namespace cppcoro 7 | { 8 | using async_mutex = ::Phantom::Coroutines::async_mutex<>; 9 | using async_mutex_lock = typename async_mutex::lock_type; 10 | using async_mutex_lock_operation = typename async_mutex::lock_operation; 11 | using async_mutex_scoped_lock_operation = typename async_mutex::scoped_lock_operation; 12 | } 13 | #endif 14 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/async_sequence_barrier.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "Phantom.Coroutines/detail/config_macros.h" 8 | export module Phantom.Coroutines.async_sequence_barrier; 9 | import Phantom.Coroutines.atomic_state; 10 | import Phantom.Coroutines.coroutine; 11 | import Phantom.Coroutines.fibonacci_heap; 12 | import Phantom.Coroutines.immovable_object; 13 | import Phantom.Coroutines.policies; 14 | #include "Phantom.Coroutines/async_sequence_barrier.h" 15 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/thread_local_storage.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "Phantom.Coroutines/detail/config_macros.h" 11 | export module Phantom.Coroutines.thread_local_storage; 12 | import Phantom.Coroutines.atomic_shared_ptr; 13 | import Phantom.Coroutines.consecutive_thread_id; 14 | import Phantom.Coroutines.reusable_consecutive_global_id; 15 | #include "Phantom.Coroutines/thread_local_storage.h" 16 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/async_mutex.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Phantom.Coroutines/detail/config_macros.h" 7 | export module Phantom.Coroutines.async_mutex; 8 | import Phantom.Coroutines.atomic_state; 9 | import Phantom.Coroutines.awaiter_list; 10 | import Phantom.Coroutines.coroutine; 11 | import Phantom.Coroutines.immovable_object; 12 | import Phantom.Coroutines.non_copyable; 13 | import Phantom.Coroutines.policies; 14 | import Phantom.Coroutines.type_traits; 15 | #include "Phantom.Coroutines/async_mutex.h" 16 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/async_generator.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "Phantom.Coroutines/detail/config_macros.h" 8 | export module Phantom.Coroutines.async_generator; 9 | export import Phantom.Coroutines.coroutine; 10 | import Phantom.Coroutines.awaiter_wrapper; 11 | import Phantom.Coroutines.extensible_promise; 12 | import Phantom.Coroutines.policies; 13 | import Phantom.Coroutines.task; 14 | import Phantom.Coroutines.type_traits; 15 | #include "Phantom.Coroutines/async_generator.h" 16 | -------------------------------------------------------------------------------- /Tla/LockFreeFifoQueue/AbstractFifoQueue.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE AbstractFifoQueue ---- 2 | EXTENDS Sequences, Integers 3 | 4 | CONSTANT 5 | Items 6 | 7 | VARIABLE 8 | Queue 9 | 10 | Init == Queue = << >> 11 | 12 | Enqueue(item) == 13 | /\ Queue' = Queue \o << item >> 14 | 15 | Dequeue(item) == 16 | /\ Queue # << >> 17 | /\ Queue[1] = item 18 | /\ Queue' = Tail(Queue) 19 | 20 | Next == 21 | \E item \in Items : 22 | \/ Enqueue(item) 23 | \/ Dequeue(item) 24 | 25 | Spec == 26 | /\ Init 27 | /\ [][Next]_Queue 28 | 29 | Constraint == Len(Queue) < 10 30 | 31 | ==== 32 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/async_sharded_reader_writer_lock.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include "Phantom.Coroutines/detail/config_macros.h" 5 | export module Phantom.Coroutines.async_sharded_reader_writer_lock; 6 | import Phantom.Coroutines.async_reader_writer_lock; 7 | import Phantom.Coroutines.direct_initialized_optional; 8 | import Phantom.Coroutines.immovable_object; 9 | import Phantom.Coroutines.policies; 10 | import Phantom.Coroutines.scope_guard; 11 | import Phantom.Coroutines.sharding; 12 | import Phantom.Coroutines.type_traits; 13 | #include "Phantom.Coroutines/async_sharded_reader_writer_lock.h" 14 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/manual_scheduler.h: -------------------------------------------------------------------------------- 1 | #include "Phantom.Coroutines/async_auto_reset_event.h" 2 | 3 | namespace Phantom::Coroutines::detail 4 | { 5 | 6 | class manual_scheduler 7 | { 8 | async_auto_reset_event<> m_event; 9 | std::atomic m_events; 10 | 11 | public: 12 | auto& schedule() noexcept 13 | { 14 | m_events.fetch_add(1, std::memory_order_relaxed); 15 | return m_event; 16 | } 17 | 18 | void release() noexcept 19 | { 20 | m_event.set(); 21 | } 22 | 23 | auto events() const noexcept 24 | { 25 | return m_events.load(std::memory_order_acquire); 26 | } 27 | }; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Tla/ThreadPool.toolbox/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | ThreadPool 4 | 5 | 6 | 7 | 8 | 9 | toolbox.builder.TLAParserBuilder 10 | 11 | 12 | 13 | 14 | 15 | toolbox.natures.TLANature 16 | 17 | 18 | 19 | ThreadPool.tla 20 | 1 21 | PARENT-1-PROJECT_LOC/ThreadPool.tla 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/read_copy_update.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "Phantom.Coroutines/detail/config_macros.h" 10 | export module Phantom.Coroutines.read_copy_update; 11 | import Phantom.Coroutines.assert_same_thread; 12 | import Phantom.Coroutines.atomic_shared_ptr; 13 | import Phantom.Coroutines.immovable_object; 14 | import Phantom.Coroutines.nonatomic_shared_ptr; 15 | import Phantom.Coroutines.scope_guard; 16 | import Phantom.Coroutines.thread_local_storage; 17 | #include "Phantom.Coroutines/read_copy_update.h" 18 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro/include/cppcoro/task.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CORO_TASK_HPP 2 | #define PHANTOM_COROUTINES_INCLUDE_CORO_TASK_HPP 3 | 4 | #include "Phantom.Coroutines/awaiter_wrapper.h" 5 | #include "Phantom.Coroutines/make_task.h" 6 | #include "Phantom.Coroutines/reusable_task.h" 7 | 8 | namespace cppcoro 9 | { 10 | 11 | template< 12 | typename Result = void 13 | > using task = ::Phantom::Coroutines::reusable_task; 14 | 15 | constexpr auto make_task = []( 16 | auto&& awaitable 17 | ) 18 | { 19 | return ::Phantom::Coroutines::make_task( 20 | std::forward(awaitable)); 21 | }; 22 | 23 | } 24 | #endif 25 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro.Test/cppcoro_task_test.cpp: -------------------------------------------------------------------------------- 1 | #include "async_test.h" 2 | #include 3 | #include "cppcoro/task.hpp" 4 | 5 | 6 | namespace Phantom::cppcoro_test 7 | { 8 | using namespace ::cppcoro; 9 | static_assert(std::same_as, decltype(make_task(std::suspend_always{})) > ); 10 | 11 | ASYNC_TEST(task_test, make_task_returns_task) 12 | { 13 | auto lambda = []() -> ::cppcoro::task 14 | { 15 | co_return "hello world"; 16 | }; 17 | 18 | auto task = cppcoro::make_task(lambda()); 19 | static_assert(std::same_as<::cppcoro::task, decltype(task)>); 20 | EXPECT_EQ("hello world", co_await task); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro/include/cppcoro/shared_task.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CORO_SHARED_TASK_HPP 2 | #define PHANTOM_COROUTINES_INCLUDE_CORO_SHARED_TASK_HPP 3 | 4 | #include "Phantom.Coroutines/make_task.h" 5 | #include "Phantom.Coroutines/shared_task.h" 6 | 7 | namespace cppcoro 8 | { 9 | 10 | template< 11 | typename T = void 12 | > 13 | using shared_task = Phantom::Coroutines::shared_task; 14 | 15 | template< 16 | typename Awaitable 17 | > decltype(auto) make_shared_task( 18 | Awaitable&& awaitable 19 | ) 20 | { 21 | return ::Phantom::Coroutines::make_task( 22 | std::forward(awaitable)); 23 | } 24 | 25 | } 26 | #endif 27 | -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/scheduler.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_SCHEDULER_H 2 | #define PHANTOM_COROUTINES_INCLUDE_SCHEDULER_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #include "type_traits.h" 5 | #endif 6 | 7 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 8 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 9 | 10 | namespace Phantom::Coroutines 11 | { 12 | namespace detail 13 | { 14 | 15 | PHANTOM_COROUTINES_MODULE_EXPORT 16 | template< 17 | typename T 18 | > concept is_scheduler = requires (T t) 19 | { 20 | { t.schedule() } -> is_awaitable; 21 | }; 22 | 23 | } 24 | 25 | PHANTOM_COROUTINES_MODULE_EXPORT 26 | using detail::is_scheduler; 27 | 28 | } 29 | #endif 30 | -------------------------------------------------------------------------------- /Tla/ReusableConsecutiveId.toolbox/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | ReusableConsecutiveId 4 | 5 | 6 | 7 | 8 | 9 | toolbox.builder.TLAParserBuilder 10 | 11 | 12 | 13 | 14 | 15 | toolbox.natures.TLANature 16 | 17 | 18 | 19 | ReusableConsecutiveId.tla 20 | 1 21 | PARENT-1-PROJECT_LOC/ReusableConsecutiveId.tla 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/detail/scope_guard_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 4 | import Phantom.Coroutines; 5 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 6 | import Phantom.Coroutines.scope_guard; 7 | #elif defined(PHANTOM_COROUTINES_TESTING_HEADERS) 8 | #include "Phantom.Coroutines/detail/scope_guard.h" 9 | #endif 10 | 11 | using namespace Phantom::Coroutines::detail; 12 | 13 | TEST(scope_guard_test, invokes_lambda_on_destruction) 14 | { 15 | bool invoked = false; 16 | { 17 | scope_guard guard{ [&]() { invoked = true; } }; 18 | ASSERT_EQ(false, invoked); 19 | } 20 | ASSERT_EQ(true, invoked); 21 | } -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/shared_task.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "Phantom.Coroutines/detail/config_macros.h" 10 | export module Phantom.Coroutines.shared_task; 11 | import Phantom.Coroutines.atomic_state; 12 | import Phantom.Coroutines.coroutine; 13 | import Phantom.Coroutines.extensible_promise; 14 | import Phantom.Coroutines.final_suspend_transfer; 15 | import Phantom.Coroutines.immovable_object; 16 | import Phantom.Coroutines.policies; 17 | import Phantom.Coroutines.type_traits; 18 | import Phantom.Coroutines.variant_result_storage; 19 | #include "Phantom.Coroutines/shared_task.h" 20 | 21 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro/include/cppcoro/awaitable_traits.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CORO_AWAITABLE_TRAITS_HPP 2 | #define PHANTOM_COROUTINES_INCLUDE_CORO_AWAITABLE_TRAITS_HPP 3 | 4 | #include "Phantom.Coroutines/type_traits.h" 5 | 6 | namespace cppcoro 7 | { 8 | 9 | template< 10 | typename Awaitable 11 | > struct awaitable_traits 12 | {}; 13 | 14 | template< 15 | typename Awaitable 16 | > 17 | requires ::Phantom::Coroutines::is_awaitable 18 | struct awaitable_traits< 19 | Awaitable 20 | > 21 | { 22 | using awaiter_t = decltype(::Phantom::Coroutines::get_awaiter(std::declval())); 23 | using await_result_t = decltype(std::declval().await_resume()); 24 | }; 25 | 26 | } 27 | #endif 28 | -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/detail/non_copyable.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_NON_COPYABLE_H 2 | #define PHANTOM_COROUTINES_INCLUDE_NON_COPYABLE_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #include "Phantom.Coroutines/detail/config_macros.h" 5 | #endif 6 | 7 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 8 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 9 | 10 | namespace Phantom::Coroutines::detail 11 | { 12 | PHANTOM_COROUTINES_MODULE_EXPORT 13 | class noncopyable 14 | { 15 | protected: 16 | noncopyable() {} 17 | 18 | private: 19 | noncopyable( 20 | const noncopyable& 21 | ); 22 | 23 | noncopyable& operator=( 24 | const noncopyable& 25 | ) = delete; 26 | }; 27 | } 28 | #endif 29 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/thread_pool_scheduler.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "Phantom.Coroutines/detail/config_macros.h" 16 | export module Phantom.Coroutines.thread_pool_scheduler; 17 | import Phantom.Coroutines.coroutine; 18 | import Phantom.Coroutines.policies; 19 | import Phantom.Coroutines.read_copy_update; 20 | import Phantom.Coroutines.scheduler; 21 | import Phantom.Coroutines.task; 22 | import Phantom.Coroutines.type_traits; 23 | #include "Phantom.Coroutines/thread_pool_scheduler.h" 24 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/detail/non_copyable_test.cpp: -------------------------------------------------------------------------------- 1 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 2 | import Phantom.Coroutines; 3 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 4 | import Phantom.Coroutines.non_copyable; 5 | #elif defined(PHANTOM_COROUTINES_TESTING_HEADERS) 6 | #include "Phantom.Coroutines/detail/non_copyable.h" 7 | #endif 8 | #include 9 | #include 10 | 11 | namespace 12 | { 13 | static_assert(!std::is_copy_constructible_v); 14 | static_assert(!std::is_move_constructible_v); 15 | static_assert(!std::is_copy_assignable_v); 16 | static_assert(!std::is_move_assignable_v); 17 | } 18 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro.Test/cppcoro_shared_task_test.cpp: -------------------------------------------------------------------------------- 1 | #include "async_test.h" 2 | #include "cppcoro/shared_task.hpp" 3 | 4 | static_assert(std::same_as, Phantom::Coroutines::shared_task<>>); 5 | static_assert(std::same_as<::Phantom::Coroutines::shared_task<>, decltype(::cppcoro::make_shared_task(std::suspend_always{}))>); 6 | 7 | namespace cppcoro 8 | { 9 | ASYNC_TEST(shared_task_test, make_shared_task_returns_shared_task) 10 | { 11 | auto lambda = []() -> ::Phantom::Coroutines::task 12 | { 13 | co_return "hello world"; 14 | }; 15 | 16 | auto task = make_shared_task(lambda()); 17 | static_assert(std::same_as<::cppcoro::shared_task, decltype(task)>); 18 | EXPECT_EQ("hello world", co_await task); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/core_task.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "Phantom.Coroutines/detail/config_macros.h" 10 | export module Phantom.Coroutines.core_task; 11 | import Phantom.Coroutines.construct_from_base; 12 | import Phantom.Coroutines.coroutine; 13 | import Phantom.Coroutines.extensible_promise; 14 | import Phantom.Coroutines.final_suspend_transfer; 15 | import Phantom.Coroutines.immovable_object; 16 | import Phantom.Coroutines.non_copyable; 17 | import Phantom.Coroutines.policies; 18 | import Phantom.Coroutines.type_traits; 19 | import Phantom.Coroutines.variant_result_storage; 20 | #include "Phantom.Coroutines/detail/core_task.h" 21 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/sharding_test.cpp: -------------------------------------------------------------------------------- 1 | #include "async_test.h" 2 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 3 | import Phantom.Coroutines; 4 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 5 | import Phantom.Coroutines.aligned_array; 6 | import Phantom.Coroutines.sharding; 7 | #elif defined(PHANTOM_COROUTINES_TESTING_HEADERS) 8 | #include "Phantom.Coroutines/sharding.h" 9 | #endif 10 | #include 11 | #include 12 | 13 | namespace Phantom::Coroutines 14 | { 15 | 16 | static_assert( 17 | std::same_as< 18 | cache_aligned_array&, 19 | decltype(std::declval>().get_shards()) 20 | >); 21 | 22 | static_assert( 23 | is_sharded> 24 | ); 25 | 26 | } -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro.Test/async_test.cpp: -------------------------------------------------------------------------------- 1 | #include "async_test.h" 2 | #include "Phantom.Coroutines/static_thread_pool.h" 3 | #include "Phantom.Coroutines/sync_wait.h" 4 | 5 | namespace Phantom::Coroutines::cppcoro::Test 6 | { 7 | void ExecuteTest( 8 | ::Phantom::Coroutines::reusable_task<> testTask) 9 | { 10 | // Create a thread pool to ensure that if the test itself does any threading, we 11 | // return control back to this thread pool. 12 | ::Phantom::Coroutines::static_thread_pool threadPool(1); 13 | 14 | auto runTestBody = [&]() -> Phantom::Coroutines::reusable_task<> 15 | { 16 | co_await testTask.when_ready(); 17 | co_await threadPool.schedule(); 18 | co_await testTask; 19 | }; 20 | 21 | ::Phantom::Coroutines::sync_wait(runTestBody()); 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/value_awaiter.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_VALUE_AWAITER_H 2 | #define PHANTOM_COROUTINES_INCLUDE_VALUE_AWAITER_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #endif 5 | 6 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 7 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 8 | 9 | namespace Phantom::Coroutines 10 | { 11 | 12 | PHANTOM_COROUTINES_MODULE_EXPORT 13 | template< 14 | typename Value 15 | > 16 | struct value_awaiter 17 | { 18 | Value m_value; 19 | 20 | bool await_ready() const noexcept 21 | { 22 | return true; 23 | } 24 | 25 | void await_suspend(auto&&...) const noexcept 26 | { 27 | } 28 | 29 | Value await_resume() const noexcept 30 | { 31 | return m_value; 32 | } 33 | }; 34 | 35 | } 36 | #endif 37 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro/include/cppcoro/async_generator.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CORO_ASYNC_GENERATOR_HPP 2 | #define PHANTOM_COROUTINES_INCLUDE_CORO_ASYNC_GENERATOR_HPP 3 | 4 | #include "Phantom.Coroutines/async_generator.h" 5 | 6 | namespace cppcoro 7 | { 8 | template< 9 | typename T 10 | > 11 | class async_generator 12 | : 13 | public ::Phantom::Coroutines::async_generator 14 | { 15 | using base_generator_type = ::Phantom::Coroutines::async_generator; 16 | 17 | public: 18 | async_generator() 19 | {} 20 | 21 | async_generator( 22 | auto&& generator 23 | ) : 24 | base_generator_type{ std::forward(generator) } 25 | {} 26 | 27 | using iterator = typename base_generator_type::iterator_type; 28 | }; 29 | 30 | } 31 | #endif 32 | -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/inline_scheduler.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_INLINE_SCHEDULER_H 2 | #define PHANTOM_COROUTINES_INCLUDE_INLINE_SCHEDULER_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #include "detail/coroutine.h" 5 | #include "scheduler.h" 6 | #endif 7 | 8 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 9 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 10 | 11 | namespace Phantom::Coroutines 12 | { 13 | namespace detail 14 | { 15 | 16 | PHANTOM_COROUTINES_MODULE_EXPORT 17 | class inline_scheduler 18 | { 19 | public: 20 | suspend_never schedule() noexcept 21 | { 22 | return suspend_never{}; 23 | } 24 | }; 25 | 26 | static_assert(is_scheduler); 27 | 28 | } 29 | 30 | PHANTOM_COROUTINES_MODULE_EXPORT 31 | using detail::inline_scheduler; 32 | 33 | } 34 | #endif 35 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/early_termination_task.ixx: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "Phantom.Coroutines/detail/config_macros.h" 11 | export module Phantom.Coroutines.early_termination_task; 12 | import Phantom.Coroutines.await_all_await_transform; 13 | import Phantom.Coroutines.awaiter_wrapper; 14 | import Phantom.Coroutines.coroutine; 15 | import Phantom.Coroutines.extensible_promise; 16 | import Phantom.Coroutines.final_suspend_transfer; 17 | import Phantom.Coroutines.policies; 18 | import Phantom.Coroutines.task; 19 | import Phantom.Coroutines.type_traits; 20 | import Phantom.Coroutines.variant_result_storage; 21 | #include "Phantom.Coroutines/early_termination_task.h" 22 | -------------------------------------------------------------------------------- /Tla/ReaderWriterLock/AbstractReaderWriterLock.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE AbstractReaderWriterLock ---- 2 | EXTENDS FiniteSets, Integers 3 | 4 | CONSTANT Threads 5 | 6 | VARIABLE 7 | Locks 8 | 9 | vars == << 10 | Locks 11 | >> 12 | 13 | LockType == [ 14 | Type : { "Read", "Write" }, 15 | Thread : Threads 16 | ] 17 | 18 | TypeOk == 19 | /\ Locks \in SUBSET LockType 20 | 21 | LocksAreCompatible == 22 | \/ Cardinality(Locks) <= 1 23 | \/ \A lock \in Locks : 24 | lock.Type = "Read" 25 | 26 | Invariant == 27 | /\ TypeOk 28 | /\ LocksAreCompatible 29 | 30 | Init == 31 | /\ Locks \in SUBSET LockType 32 | /\ Invariant 33 | 34 | Next == 35 | /\ Locks' \in SUBSET LockType 36 | /\ Invariant' 37 | 38 | Spec == 39 | /\ Init 40 | /\ [][Next]_vars 41 | 42 | Property == 43 | /\ Spec 44 | /\ []Invariant 45 | 46 | ==== -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro/include/cppcoro/sequence_barrier.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CORO_SEQUENCE_BARRIER_HPP 2 | #define PHANTOM_COROUTINES_INCLUDE_CORO_SEQUENCE_BARRIER_HPP 3 | 4 | #include "Phantom.Coroutines/async_sequence_barrier.h" 5 | #include "task.hpp" 6 | 7 | namespace cppcoro 8 | { 9 | 10 | template< 11 | typename Value 12 | > class sequence_barrier 13 | : 14 | public ::Phantom::Coroutines::async_sequence_barrier 15 | { 16 | using ::Phantom::Coroutines::async_sequence_barrier::async_sequence_barrier; 17 | 18 | public: 19 | task wait_until_published( 20 | Value value, 21 | auto& scheduler 22 | ) 23 | { 24 | auto result = co_await this->::Phantom::Coroutines::async_sequence_barrier::wait_until_published( 25 | value); 26 | co_await scheduler.schedule(); 27 | co_return result; 28 | } 29 | }; 30 | 31 | } 32 | #endif 33 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro/include/cppcoro/cancellation_source.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CORO_CANCELLATION_SOURCE_HPP 2 | #define PHANTOM_COROUTINES_INCLUDE_CORO_CANCELLATION_SOURCE_HPP 3 | 4 | #include 5 | #include "cancellation_token.hpp" 6 | 7 | namespace cppcoro 8 | { 9 | 10 | class cancellation_token; 11 | 12 | class cancellation_source 13 | { 14 | std::stop_source m_stopSource; 15 | 16 | public: 17 | bool can_be_cancelled() const noexcept 18 | { 19 | return m_stopSource.stop_possible(); 20 | } 21 | 22 | cancellation_token token() const noexcept 23 | { 24 | return cancellation_token(m_stopSource.get_token()); 25 | } 26 | 27 | void request_cancellation() 28 | { 29 | m_stopSource.request_stop(); 30 | } 31 | 32 | bool is_cancellation_requested() const noexcept 33 | { 34 | return m_stopSource.stop_requested(); 35 | } 36 | }; 37 | } 38 | #endif 39 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/inline_scheduler_test.cpp: -------------------------------------------------------------------------------- 1 | #include "async_test.h" 2 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 3 | import Phantom.Coroutines; 4 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 5 | import Phantom.Coroutines.inline_scheduler; 6 | import Phantom.Coroutines.scheduler; 7 | #elif defined(PHANTOM_COROUTINES_TESTING_HEADERS) 8 | #include "Phantom.Coroutines/inline_scheduler.h" 9 | #include "Phantom.Coroutines/scheduler.h" 10 | #endif 11 | #include 12 | 13 | using namespace Phantom::Coroutines; 14 | using namespace Phantom::Coroutines::detail; 15 | 16 | static_assert(is_scheduler); 17 | 18 | ASYNC_TEST(inline_scheduler_test, schedules_on_current_thread) 19 | { 20 | inline_scheduler scheduler; 21 | auto currentThreadId = std::this_thread::get_id(); 22 | co_await scheduler.schedule(); 23 | auto invokedThreadId = std::this_thread::get_id(); 24 | EXPECT_EQ(currentThreadId, invokedThreadId); 25 | } -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/detail/immovable_object.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_IMMOVABLE_OBJECT_H 2 | #define PHANTOM_COROUTINES_INCLUDE_IMMOVABLE_OBJECT_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #include "config_macros.h" 5 | #endif 6 | 7 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 8 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 9 | 10 | namespace Phantom::Coroutines::detail 11 | { 12 | PHANTOM_COROUTINES_MODULE_EXPORT 13 | class immovable_object 14 | { 15 | protected: 16 | immovable_object(){} 17 | 18 | private: 19 | immovable_object( 20 | const immovable_object& 21 | ) = delete; 22 | 23 | immovable_object( 24 | immovable_object&& 25 | ) = delete; 26 | 27 | immovable_object& operator=( 28 | const immovable_object& 29 | ) = delete; 30 | 31 | immovable_object& operator=( 32 | immovable_object&& 33 | ) = delete; 34 | }; 35 | } 36 | #endif 37 | -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/await_none_await_transform.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_AWAIT_NONE_AWAIT_TRANSFORM_H 2 | #define PHANTOM_COROUTINES_INCLUDE_AWAIT_NONE_AWAIT_TRANSFORM_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #include "type_traits.h" 5 | #else 6 | import Phantom.Coroutines.type_traits; 7 | #endif 8 | 9 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 10 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 11 | 12 | namespace Phantom::Coroutines 13 | { 14 | namespace detail 15 | { 16 | // This utility class can be used by a promise type 17 | // to disable co_await on all inherently awaitable types. 18 | PHANTOM_COROUTINES_MODULE_EXPORT 19 | class await_none_await_transform 20 | { 21 | public: 22 | template< 23 | is_awaitable T 24 | > T&& await_transform( 25 | T&& t 26 | ) = delete; 27 | }; 28 | } 29 | PHANTOM_COROUTINES_MODULE_EXPORT 30 | using detail::await_none_await_transform; 31 | 32 | } 33 | #endif 34 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/value_awaiter_test.cpp: -------------------------------------------------------------------------------- 1 | #include "async_test.h" 2 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 3 | import Phantom.Coroutines; 4 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 5 | import Phantom.Coroutines.value_awaiter; 6 | #elif defined(PHANTOM_COROUTINES_TESTING_HEADERS) 7 | #include "Phantom.Coroutines/value_awaiter.h" 8 | #endif 9 | 10 | namespace Phantom::Coroutines 11 | { 12 | 13 | ASYNC_TEST(value_awaiter_test, can_await_by_value) 14 | { 15 | value_awaiter awaiter{ "hello" }; 16 | decltype(auto) result = co_await awaiter; 17 | static_assert(std::same_as); 18 | EXPECT_EQ("hello", result); 19 | } 20 | 21 | ASYNC_TEST(value_awaiter_test, can_await_by_reference) 22 | { 23 | std::string expected = "hello"; 24 | value_awaiter awaiter{ expected }; 25 | decltype(auto) result = co_await awaiter; 26 | static_assert(std::same_as); 27 | EXPECT_EQ(&expected, &result); 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/await_all_await_transform.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_AWAIT_ALL_AWAIT_TRANSFORM_H 2 | #define PHANTOM_COROUTINES_INCLUDE_AWAIT_ALL_AWAIT_TRANSFORM_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #include 5 | #include "type_traits.h" 6 | #endif 7 | 8 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 9 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 10 | 11 | namespace Phantom::Coroutines 12 | { 13 | namespace detail 14 | { 15 | // This utility class can be used by a promise type 16 | // that declares an await_transform method to enable 17 | // awaiting on all other types that are inherently awaitable. 18 | PHANTOM_COROUTINES_MODULE_EXPORT 19 | class await_all_await_transform 20 | { 21 | public: 22 | template< 23 | is_awaitable T 24 | > T&& await_transform( 25 | T&& t 26 | ) 27 | { 28 | return std::forward(t); 29 | } 30 | }; 31 | } 32 | PHANTOM_COROUTINES_MODULE_EXPORT 33 | using detail::await_all_await_transform; 34 | 35 | } 36 | #endif 37 | -------------------------------------------------------------------------------- /Documentation/sync_wait.md: -------------------------------------------------------------------------------- 1 | # [Phantom.Coroutines](../README.md) 2 | 3 | ## ```sync_wait.h``` 4 | 5 | There are times when it is necessary to wait for the result of coroutine synchronously. This is 6 | especially true for the ```main()``` function of a program. ```sync_wait.h``` provides two primitives 7 | for this: 8 | 9 | ``` 10 | template< 11 | is_awaitable TAwaitable 12 | > decltype(auto) as_future( 13 | TAwaitable&& awaitable 14 | ) -> std::future>; 15 | 16 | template< 17 | is_awaitable TAwaitable 18 | > decltype(auto) sync_wait( 19 | TAwaitable&& awaitable 20 | ) -> awaitable_result_type_t; 21 | ``` 22 | 23 | Note that the restrictions on std::future apply to sync_wait: a future cannot return an rvalue-reference. 24 | 25 | ```as_future()``` performs a co_await on the awaitable object before returning the ```std::future``` object. Thus, 26 | it can be used to proactively start a coroutine. Discarding the future object will result in the coroutine 27 | running to completion. 28 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro/include/cppcoro/cancellation_token.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CORO_CANCELLATION_TOKEN_HPP 2 | #define PHANTOM_COROUTINES_INCLUDE_CORO_CANCELLATION_TOKEN_HPP 3 | 4 | #include 5 | #include "operation_cancelled.hpp" 6 | 7 | namespace cppcoro 8 | { 9 | 10 | class cancellation_source; 11 | 12 | class cancellation_token 13 | { 14 | friend class cancellation_source; 15 | 16 | std::stop_token m_token; 17 | 18 | public: 19 | cancellation_token( 20 | std::stop_token token 21 | ) : m_token{ std::move(token) } 22 | {} 23 | 24 | bool can_be_cancelled() const noexcept 25 | { 26 | return m_token.stop_possible(); 27 | } 28 | 29 | bool is_cancellation_requested() const noexcept 30 | { 31 | return m_token.stop_requested(); 32 | } 33 | 34 | void throw_if_cancellation_requested() const 35 | { 36 | if (is_cancellation_requested()) 37 | { 38 | throw operation_cancelled(); 39 | } 40 | } 41 | }; 42 | 43 | } 44 | #endif 45 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/async_test.cpp: -------------------------------------------------------------------------------- 1 | #include "async_test.h" 2 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 3 | import Phantom.Coroutines; 4 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 5 | import Phantom.Coroutines.static_thread_pool; 6 | import Phantom.Coroutines.sync_wait; 7 | #elif defined(PHANTOM_COROUTINES_TESTING_HEADERS) 8 | #include "Phantom.Coroutines/static_thread_pool.h" 9 | #include "Phantom.Coroutines/sync_wait.h" 10 | #endif 11 | 12 | namespace Phantom::Coroutines::Test 13 | { 14 | void ExecuteTest( 15 | ::Phantom::Coroutines::reusable_task<> testTask) 16 | { 17 | // Create a thread pool to ensure that if the test itself does any threading, we 18 | // return control back to this thread pool. 19 | ::Phantom::Coroutines::static_thread_pool threadPool(1); 20 | 21 | auto runTestBody = [&]() -> Phantom::Coroutines::reusable_task<> 22 | { 23 | co_await testTask.when_ready(); 24 | co_await threadPool.schedule(); 25 | co_await testTask; 26 | }; 27 | 28 | ::Phantom::Coroutines::sync_wait(runTestBody()); 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Joshua Rowe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/detail/config_globals.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CONFIG_GLOBALS_H 2 | #define PHANTOM_COROUTINES_INCLUDE_CONFIG_GLOBALS_H 3 | 4 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 5 | #include "config_macros.h" 6 | #endif 7 | 8 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 9 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 10 | 11 | namespace Phantom::Coroutines 12 | { 13 | 14 | PHANTOM_COROUTINES_MODULE_EXPORT 15 | struct Config 16 | { 17 | static constexpr bool Symmetric_Transfer_Incorrectly_Lifted_to_Coroutine_Frame 18 | = PHANTOM_COROUTINES_SYMMETRIC_TRANSFER_INCORRECTLY_LIFTED_TO_COROUTINE_FRAME; 19 | 20 | static constexpr bool No_Reject_Lambda_with_Invalid_Member 21 | = PHANTOM_COROUTINES_NO_REJECT_LAMBDA_WITH_INVALID_MEMBER; 22 | 23 | // PHANTOM_COROUTINES_LAMBDA_REFERENCE_IS_FIRST_ARGUMENT_OF_PROMISE_CONSTRUCTOR 24 | static constexpr bool Lambda_Reference_Is_First_Argument_Of_Promise_Constructor 25 | = PHANTOM_COROUTINES_LAMBDA_REFERENCE_IS_FIRST_ARGUMENT_OF_PROMISE_CONSTRUCTOR; 26 | }; 27 | 28 | } 29 | 30 | #endif // PHANTOM_COROUTINES_INCLUDE_CONFIG_GLOBALS_H 31 | -------------------------------------------------------------------------------- /Documentation/async_promise.md: -------------------------------------------------------------------------------- 1 | # [Phantom.Coroutines](../README.md) 2 | 3 | ## ```async_promise.h``` 4 | 5 | ```async_promise``` provides a standard way to expose the 6 | future ability to acquire a value, wrapping the ordinary 7 | construct of a ```Value``` that is available after some ```Event``` 8 | has been signalled. 9 | 10 | The default ```Event``` type is [```async_manual_reset_event<>```](async_manual_reset_event.md). The ```Value``` type is returned by reference when ```co_await```'ing the ```async_promise<>```. The value can be provided either at construction time 11 | or by calling the ```emplace``` method. 12 | 13 | ```c++ 14 | async_promise myDelayedString; 15 | 16 | task<> UseDelayedString() 17 | { 18 | std::cout << co_await myDelayedString; 19 | } 20 | 21 | task<> AcquireTheString() 22 | { 23 | // ... do some things here 24 | myDelayedString.emplace(L"Hello world"); 25 | // ... do some more things here 26 | co_return; 27 | } 28 | 29 | task<> HelloWorld() 30 | { 31 | async_scope<> scope; 32 | scope.spawn(UseDelayedString()); 33 | scope.spawn(AcquireTheString()); 34 | co_await scope.join(); 35 | } 36 | ``` 37 | -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/detail/scope_guard.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_SCOPE_GUARD_H 2 | #define PHANTOM_COROUTINES_INCLUDE_SCOPE_GUARD_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #include 5 | #include 6 | #include 7 | #include "config_macros.h" 8 | #include "immovable_object.h" 9 | #endif 10 | 11 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 12 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 13 | 14 | namespace Phantom::Coroutines 15 | { 16 | namespace detail 17 | { 18 | 19 | PHANTOM_COROUTINES_MODULE_EXPORT 20 | template< 21 | std::invocable<> Lambda 22 | > 23 | class scope_guard 24 | : 25 | private immovable_object 26 | { 27 | Lambda m_lambda; 28 | public: 29 | template< 30 | std::invocable<> TLambda 31 | > 32 | scope_guard( 33 | TLambda lambda 34 | ) : m_lambda { std::forward(lambda) } 35 | {} 36 | 37 | ~scope_guard() 38 | { 39 | m_lambda(); 40 | } 41 | }; 42 | 43 | PHANTOM_COROUTINES_MODULE_EXPORT 44 | template< 45 | std::invocable<> Lambda 46 | > scope_guard(Lambda)->scope_guard>; 47 | 48 | } 49 | } 50 | #endif 51 | -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/make_task.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_MAKE_TASK_H 2 | #define PHANTOM_COROUTINES_INCLUDE_MAKE_TASK_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #include 5 | #include 6 | #include "task.h" 7 | #include "type_traits.h" 8 | #endif 9 | 10 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 11 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 12 | 13 | namespace Phantom::Coroutines 14 | { 15 | 16 | PHANTOM_COROUTINES_MODULE_EXPORT 17 | template< 18 | template typename Task = task, 19 | typename Awaitable 20 | > auto make_task( 21 | Awaitable&& awaitable 22 | ) 23 | { 24 | using result_task_type = Task>>; 25 | 26 | if constexpr (std::is_lvalue_reference_v) 27 | { 28 | return [](Awaitable awaitable) -> result_task_type 29 | { 30 | co_return co_await awaitable; 31 | }(awaitable); 32 | } 33 | else 34 | { 35 | return [](Awaitable awaitable) -> result_task_type 36 | { 37 | co_return co_await std::move(awaitable); 38 | }(std::move(awaitable)); 39 | } 40 | } 41 | 42 | } 43 | #endif 44 | -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/detail/assert_same_thread.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_ASSERT_SAME_THREAD_H 2 | #define PHANTOM_COROUTINES_INCLUDE_ASSERT_SAME_THREAD_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #if !NDEBUG 5 | #include 6 | #include 7 | #endif 8 | #endif 9 | 10 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 11 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 12 | 13 | namespace Phantom::Coroutines::detail 14 | { 15 | 16 | #if NDEBUG 17 | PHANTOM_COROUTINES_MODULE_EXPORT 18 | class assert_same_thread 19 | { 20 | public: 21 | assert_same_thread() 22 | { 23 | } 24 | 25 | void check_thread() const 26 | { 27 | } 28 | 29 | ~assert_same_thread() 30 | { 31 | } 32 | }; 33 | #else 34 | PHANTOM_COROUTINES_MODULE_EXPORT 35 | class assert_same_thread 36 | { 37 | std::thread::id m_threadId; 38 | public: 39 | assert_same_thread() 40 | : 41 | m_threadId{ std::this_thread::get_id() } 42 | { 43 | } 44 | 45 | void check_thread() const 46 | { 47 | assert(m_threadId == std::this_thread::get_id()); 48 | } 49 | 50 | ~assert_same_thread() 51 | { 52 | check_thread(); 53 | } 54 | }; 55 | #endif 56 | 57 | } 58 | #endif 59 | -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/async_latch.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_ASYNC_LATCH_H 2 | #define PHANTOM_COROUTINES_INCLUDE_ASYNC_LATCH_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #include 5 | #include 6 | #include "async_manual_reset_event.h" 7 | #include "type_traits.h" 8 | #endif 9 | 10 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 11 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 12 | 13 | namespace Phantom::Coroutines 14 | { 15 | 16 | PHANTOM_COROUTINES_MODULE_EXPORT 17 | template< 18 | typename Event = async_manual_reset_event<> 19 | > 20 | class async_latch 21 | { 22 | std::atomic m_count; 23 | Event m_event; 24 | 25 | public: 26 | async_latch( 27 | ptrdiff_t initialCount 28 | ) : m_count { initialCount } 29 | { 30 | if (m_count <= 0) 31 | { 32 | m_event.set(); 33 | } 34 | } 35 | 36 | void count_down(ptrdiff_t count = 1) 37 | { 38 | if (m_count.fetch_sub(count, std::memory_order_relaxed) <= count) 39 | { 40 | m_event.set(); 41 | } 42 | } 43 | 44 | auto operator co_await() const noexcept 45 | { 46 | return get_awaiter(m_event); 47 | } 48 | }; 49 | 50 | } 51 | #endif 52 | -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/final_suspend_transfer.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_FINAL_SUSPEND_TRANSFER_H 2 | #define PHANTOM_COROUTINES_INCLUDE_FINAL_SUSPEND_TRANSFER_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #include 5 | #include "Phantom.Coroutines/detail/coroutine.h" 6 | #include "Phantom.Coroutines/detail/config_macros.h" 7 | #endif 8 | 9 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 10 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 11 | 12 | namespace Phantom::Coroutines 13 | { 14 | 15 | PHANTOM_COROUTINES_MODULE_EXPORT 16 | class final_suspend_transfer 17 | { 18 | coroutine_handle<> m_continuation; 19 | public: 20 | final_suspend_transfer( 21 | coroutine_handle<> continuation 22 | ) noexcept : m_continuation( 23 | continuation 24 | ) 25 | {} 26 | 27 | constexpr bool await_ready() const noexcept { return false; } 28 | 29 | constexpr coroutine_handle<> await_suspend( 30 | coroutine_handle<> 31 | ) const noexcept 32 | { 33 | return m_continuation; 34 | } 35 | 36 | constexpr void await_resume() const noexcept 37 | { 38 | // This line of code should be impossible to hit. 39 | assert(false); 40 | } 41 | }; 42 | } 43 | #endif 44 | -------------------------------------------------------------------------------- /Documentation/async_scope.md: -------------------------------------------------------------------------------- 1 | # [Phantom.Coroutines](../README.md) 2 | 3 | ## ```async_scope.h``` 4 | 5 | The ```async_scope``` class allows starting a series of background tasks 6 | and waiting for all of them to complete. Any awaitable / awaiter object 7 | can be passed to ```async_scope::spawn```. To wait for all the pending objects, 8 | call ```async_scope::join```. It is an error to ```spawn``` more tasks after 9 | calling ``join```; the result of calling ``spawn``` after calling ```join`` is 10 | controlled by the [```use_after_join_policy```](policies.md#use_after_join_policy). 11 | 12 | Most awaitable objects in ```Phantom.Coroutines``` start upon a ```co_await``` operation, 13 | and run lazily. ```async_scope``` provides a mechanism to start operations 14 | eagerly, as the operation is ```co_await```'ed before ```spawn``` returns. 15 | 16 | Any return value from these tasks is ignored. Any unhandled exception will be uncaught 17 | and dealt with by the default C++ semantics. 18 | 19 | ``` 20 | task<> Task1(); 21 | task<> Task2(); 22 | 23 | task<> ControllingTask() 24 | { 25 | async_scope scope; 26 | scope.spawn(Task1()); 27 | scope.spawn(Task2()); 28 | co_await scope.Join(); 29 | // This is illegal: 30 | // scope.spawn(Task3()); 31 | } 32 | ``` 33 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro.Test/cppcoro_cancellation_source_test.cpp: -------------------------------------------------------------------------------- 1 | #include "../Phantom.Coroutines.Test/async_test.h" 2 | #include "cppcoro/cancellation_source.hpp" 3 | 4 | namespace cppcoro 5 | { 6 | 7 | TEST(cppcoro_cancellation_source_test, can_be_cancelled_starts_true) 8 | { 9 | cancellation_source source; 10 | ASSERT_TRUE(source.can_be_cancelled()); 11 | } 12 | 13 | TEST(cppcoro_cancellation_source_test, cancellation_token_is_cancelled_if_source_is_requested) 14 | { 15 | cancellation_source source; 16 | cancellation_token token = source.token(); 17 | ASSERT_FALSE(source.is_cancellation_requested()); 18 | ASSERT_FALSE(token.is_cancellation_requested()); 19 | source.request_cancellation(); 20 | ASSERT_TRUE(source.is_cancellation_requested()); 21 | ASSERT_TRUE(token.is_cancellation_requested()); 22 | } 23 | 24 | TEST(cppcoro_cancellation_source_test, cancellation_token_throw_if_cancellation_requested_throws_operation_cancelled_exception) 25 | { 26 | cancellation_source source; 27 | cancellation_token token = source.token(); 28 | token.throw_if_cancellation_requested(), 29 | source.request_cancellation(); 30 | ASSERT_THROW( 31 | token.throw_if_cancellation_requested(), 32 | operation_cancelled 33 | ); 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/detail/consecutive_thread_id.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CONSECUTIVE_THREAD_ID_H 2 | #define PHANTOM_COROUTINES_INCLUDE_CONSECUTIVE_THREAD_ID_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #include 5 | #include "../reusable_consecutive_global_id.h" 6 | #endif 7 | 8 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 9 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 10 | 11 | namespace Phantom::Coroutines::detail 12 | { 13 | 14 | PHANTOM_COROUTINES_MODULE_EXPORT 15 | class consecutive_thread_id 16 | { 17 | static inline thread_local size_t consecutive_thread_id_current_value = 0; 18 | using id_generator_type = reusable_consecutive_global_id< 19 | struct thread_id_label, 20 | size_t, 21 | 1 22 | >; 23 | 24 | [[msvc::noinline]] 25 | static size_t construct_current() 26 | { 27 | static thread_local id_generator_type generator; 28 | 29 | return consecutive_thread_id_current_value = generator; 30 | } 31 | 32 | public: 33 | static size_t current() 34 | { 35 | if (consecutive_thread_id_current_value) 36 | { 37 | return consecutive_thread_id_current_value; 38 | } 39 | 40 | return construct_current(); 41 | } 42 | }; 43 | } 44 | #endif 45 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro.Test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.30) 2 | 3 | add_executable( 4 | Phantom.Coroutines.cppcoro.Test 5 | "async_test.cpp" 6 | "async_test.h" 7 | "cppcoro_async_auto_reset_event_test.cpp" 8 | "cppcoro_async_generator_test.cpp" 9 | "cppcoro_async_latch_test.cpp" 10 | "cppcoro_async_manual_reset_event_test.cpp" 11 | "cppcoro_async_mutex_test.cpp" 12 | "cppcoro_async_scope_test.cpp" 13 | "cppcoro_cancellation_source_test.cpp" 14 | "cppcoro_generator_test.cpp" 15 | "cppcoro_inline_scheduler_test.cpp" 16 | "cppcoro_is_awaitable_test.cpp" 17 | "cppcoro_sequence_barrier_test.cpp" 18 | "cppcoro_shared_task_test.cpp" 19 | "cppcoro_single_consumer_event_test.cpp" 20 | "cppcoro_static_thread_pool_test.cpp" 21 | "cppcoro_task_test.cpp" 22 | ) 23 | 24 | target_compile_definitions( 25 | Phantom.Coroutines.cppcoro.Test 26 | PRIVATE 27 | PHANTOM_COROUTINES_TESTING_HEADERS 28 | ) 29 | 30 | target_link_libraries( 31 | Phantom.Coroutines.cppcoro.Test 32 | PUBLIC 33 | Phantom.Coroutines 34 | Phantom.Coroutines.cppcoro 35 | GTest::gtest 36 | GTest::gtest_main 37 | ) 38 | 39 | enable_testing() 40 | 41 | gtest_discover_tests( 42 | Phantom.Coroutines.cppcoro.Test 43 | EXTRA_ARGS "--gtest_break_on_failure") 44 | -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/consecutive_global_id.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CONSECUTIVE_GLOBAL_ID_H 2 | #define PHANTOM_COROUTINES_INCLUDE_CONSECUTIVE_GLOBAL_ID_H 3 | 4 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 5 | #include 6 | #include 7 | #endif 8 | 9 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 10 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 11 | 12 | namespace Phantom::Coroutines 13 | { 14 | namespace detail 15 | { 16 | 17 | PHANTOM_COROUTINES_MODULE_EXPORT 18 | template< 19 | typename Label, 20 | typename Value = std::size_t 21 | > 22 | class consecutive_global_id 23 | { 24 | static std::atomic& global_value() 25 | { 26 | static std::atomic globalValue; 27 | return globalValue; 28 | } 29 | Value m_value; 30 | 31 | public: 32 | consecutive_global_id() 33 | : 34 | m_value( 35 | global_value().fetch_add(1) 36 | ) 37 | { 38 | } 39 | 40 | Value get() const 41 | { 42 | return m_value; 43 | } 44 | 45 | operator Value() const 46 | { 47 | return get(); 48 | } 49 | 50 | Value operator*() const 51 | { 52 | return get(); 53 | } 54 | 55 | const Value* operator->() const 56 | { 57 | return &m_value; 58 | } 59 | }; 60 | 61 | } 62 | } 63 | #endif 64 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro.Test/async_test.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_ASYNC_TEST_H 2 | #define PHANTOM_COROUTINES_INCLUDE_ASYNC_TEST_H 3 | 4 | #include 5 | #include "Phantom.Coroutines/reusable_task.h" 6 | 7 | 8 | namespace Phantom::Coroutines::cppcoro::Test 9 | { 10 | void ExecuteTest( 11 | ::Phantom::Coroutines::reusable_task<> testTask 12 | ); 13 | } 14 | 15 | #define ASYNC_TEST_CLASS_NAME(test_suite_name, test_name) test_suite_name ## _ ## test_name ## _AsyncTest 16 | 17 | #define ASYNC_TEST_(test_suite_name, test_name, parent_class, parent_id) \ 18 | class ASYNC_TEST_CLASS_NAME(test_suite_name, test_name) \ 19 | : public parent_class \ 20 | { \ 21 | public: \ 22 | ::Phantom::Coroutines::reusable_task<> AsyncTestBody(); \ 23 | }; \ 24 | \ 25 | GTEST_TEST_(test_suite_name, test_name, ASYNC_TEST_CLASS_NAME(test_suite_name, test_name), ::testing::internal::GetTestTypeId()) \ 26 | { \ 27 | ::Phantom::Coroutines::cppcoro::Test::ExecuteTest(AsyncTestBody()); \ 28 | } \ 29 | \ 30 | ::Phantom::Coroutines::reusable_task<> ASYNC_TEST_CLASS_NAME(test_suite_name, test_name)::AsyncTestBody() 31 | 32 | #define ASYNC_TEST(test_suite_name, test_name) ASYNC_TEST_(test_suite_name, test_name, ::testing::Test, ::testing::internal::GetTestTypeId()) 33 | #define ASYNC_TEST_F(test_suite_name, test_name) ASYNC_TEST_(test_suite_name, test_name, test_suite_name, ::testing::internal::GetTypeId()) 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/detail/config_macros_clang.h: -------------------------------------------------------------------------------- 1 | #define PHANTOM_COROUTINES_COMPILER_CLANG 1 2 | 3 | #define PHANTOM_COROUTINES_MSVC_INSTRINSIC 4 | #define PHANTOM_COROUTINES_MSVC_FORCEINLINE 5 | #define PHANTOM_COROUTINES_MSVC_SUPPRESS_PACKING_ALIGNMENT_WARNING 6 | #define PHANTOM_COROUTINES_MSVC_PUSH_DISABLE_WARNING(warnings) 7 | #define PHANTOM_COROUTINES_MSVC_POP_WARNINGS() 8 | 9 | // CLang captures the result of get_return_object using auto, 10 | // which decays reference types. Therefore, get_return_object 11 | // cannot return a reference type. 12 | #define PHANTOM_COROUTINES_USE_REFERENCE_WRAPPER_RETURN_TYPE_FOR_GET_RETURN_OBJECT 1 13 | 14 | #define PHANTOM_COROUTINES_LAMBDA_REFERENCE_IS_FIRST_ARGUMENT_OF_PROMISE_CONSTRUCTOR 0 15 | #define PHANTOM_COROUTINES_FUTURE_DOESNT_ACCEPT_NOT_DEFAULT_CONSTRUCTIBLE 0 16 | #define PHANTOM_COROUTINES_SYMMETRIC_TRANSFER_INCORRECTLY_LIFTED_TO_COROUTINE_FRAME 0 17 | #define PHANTOM_COROUTINES_NO_REJECT_LAMBDA_WITH_INVALID_MEMBER 0 18 | 19 | #define PHANTOM_COROUTINES_CLANG_PUSH_DISABLE_WARNING(warnings) _Pragma("clang diagnostic push") _Pragma(warnings) 20 | #define PHANTOM_COROUTINES_CLANG_POP_WARNINGS() _Pragma("clang diagnostic pop") 21 | 22 | #define PHANTOM_COROUTINES_PUSH_DISABLE_INTERNAL_LINKAGE_WARNING() PHANTOM_COROUTINES_CLANG_PUSH_DISABLE_WARNING("clang diagnostic ignored \"-Wundefined-internal\"") 23 | #define PHANTOM_COROUTINES_POP_WARNINGS() PHANTOM_COROUTINES_CLANG_POP_WARNINGS() 24 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.30) 2 | project(Phantom.Coroutines) 3 | 4 | find_package(GTest REQUIRED) 5 | 6 | option(DISABLE_COMPILE_MODULES "Disable compiling modules and module tests" OFF) 7 | option(DISABLE_LINK_MODULES "Disable linking module tests" OFF) 8 | 9 | set(CMAKE_COMPILE_WARNING_AS_ERROR ON) 10 | set(CMAKE_CXX_STANDARD 23) 11 | add_compile_options($<$:/W4>) 12 | 13 | if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 14 | if(CMAKE_SIZEOF_VOID_P EQUAL 8) 15 | # This is to make Intellisense work. 16 | add_compile_definitions(_M_AMD64=100) 17 | endif() 18 | 19 | string(REPLACE "/Ob1" "/Ob3" CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}) 20 | 21 | add_compile_options($<$:/fsanitize=address>) 22 | 23 | add_compile_options($<$:/GL>) 24 | add_link_options($<$:/LTCG>) 25 | string(REPLACE "/INCREMENTAL" "" CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO ${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}) 26 | endif() 27 | 28 | if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") 29 | if(LINUX) 30 | add_compile_options(-stdlib=libc++) 31 | add_link_options(-stdlib=libc++ -latomic) 32 | endif() 33 | set(ENABLE_LINK_MODULES OFF) 34 | endif() 35 | 36 | enable_testing() 37 | 38 | add_subdirectory(Phantom.Coroutines) 39 | add_subdirectory(Phantom.Coroutines.Modules) 40 | add_subdirectory(Phantom.Coroutines.Test) 41 | add_subdirectory(Phantom.Coroutines.cppcoro) 42 | add_subdirectory(Phantom.Coroutines.cppcoro.Test) 43 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro.Test/cppcoro_sequence_barrier_test.cpp: -------------------------------------------------------------------------------- 1 | #include "async_test.h" 2 | #include "../Phantom.Coroutines.Test/manual_scheduler.h" 3 | #include "cppcoro/async_scope.hpp" 4 | #include "cppcoro/sequence_barrier.hpp" 5 | #include "cppcoro/task.hpp" 6 | 7 | namespace cppcoro 8 | { 9 | static_assert(!std::is_copy_constructible_v>); 10 | 11 | ASYNC_TEST(cppcoro_sequence_barrier_test, wait_until_published_resumes_on_scheduler) 12 | { 13 | ::Phantom::Coroutines::detail::manual_scheduler scheduler; 14 | sequence_barrier barrier; 15 | 16 | bool reached5 = false; 17 | bool reached7 = false; 18 | 19 | auto lambda = [&]() -> task<> 20 | { 21 | co_await barrier.wait_until_published(5, scheduler); 22 | reached5 = true; 23 | co_await barrier.wait_until_published(7, scheduler); 24 | reached7 = true; 25 | }; 26 | async_scope scope; 27 | scope.spawn(lambda()); 28 | 29 | barrier.publish(4); 30 | EXPECT_EQ(0, scheduler.events()); 31 | EXPECT_EQ(false, reached5); 32 | barrier.publish(5); 33 | EXPECT_EQ(1, scheduler.events()); 34 | EXPECT_EQ(false, reached5); 35 | scheduler.release(); 36 | EXPECT_EQ(true, reached5); 37 | barrier.publish(6); 38 | EXPECT_EQ(1, scheduler.events()); 39 | EXPECT_EQ(false, reached7); 40 | barrier.publish(7); 41 | EXPECT_EQ(2, scheduler.events()); 42 | EXPECT_EQ(false, reached7); 43 | scheduler.release(); 44 | EXPECT_EQ(true, reached7); 45 | 46 | co_await scope.join(); 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /Documentation/schedulers.md: -------------------------------------------------------------------------------- 1 | # [Phantom.Coroutines](../README.md) 2 | 3 | ## ```scheduler.h``` 4 | 5 | Schedulers implement the following concept: 6 | 7 | ``` 8 | template< 9 | typename T 10 | > concept is_scheduler = requires (T t) 11 | { 12 | { t.schedule() } -> is_awaitable; 13 | }; 14 | ``` 15 | 16 | Phantom.Coroutines comes with two scheduler implementation and one wrapper: 17 | * ```inline_scheduler``` 18 | * ```thread_pool_scheduler``` 19 | * ```static_thread_pool``` 20 | 21 | ## [```inline_scheduler.h```](#inline_scheduler) 22 | 23 | ```inline_scheduler``` implements the ```is_scheduler``` concept by never suspending, 24 | i.e. returning ```std::never_suspend{}```. ```inline_scheduler``` is useful mostly for testing. 25 | 26 | ## [```thread_pool_scheduler.h```](#thread_pool_scheduler) 27 | 28 | ```thread_pool_scheduler``` implements a high-performance work-stealing mostly lock-free implementation 29 | of a scheduler. The ```thread_pool_scheduler``` exposes a ```scheduler``` method that returns 30 | an awaitable, and a ```process_items``` method that a caller can use to process items on that thread 31 | until the ```std::stop_signal``` parameter is signalled. 32 | 33 | The typical use case is to not use ```thread_pool_scheduler``` directly, but instead to use 34 | [```static_thread_pool```](schedulers.md#static_thread_pool). 35 | 36 | ## [```static_thread_pool.h```](#static_thread_pool) 37 | 38 | ```static_thread_pool``` wraps ```thread_pool_scheduler``` and provides a fixed-size pool 39 | of threads to perform processing of work items. It can be stopped by calling the ```shutdown``` method 40 | or by destroying it. 41 | -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/detail/config_macros.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CONFIG_MACROS_H 2 | #define PHANTOM_COROUTINES_INCLUDE_CONFIG_MACROS_H 3 | 4 | #if defined(_MSVC_LANG) && defined(__clang__) 5 | // Using CLang in MSVC compatibility mode 6 | #include "config_macros_clang_msvc.h" 7 | 8 | #elif defined(_MSVC_LANG) && !defined(__clang__) 9 | #include "config_macros_msvc.h" 10 | 11 | #elif defined(__clang__) && !defined(_MSVC_LANG) 12 | #include "config_macros_clang.h" 13 | 14 | #else 15 | #error "Unsupported compiler" 16 | #endif 17 | 18 | #if !NDEBUG 19 | #ifndef PHANTOM_COROUTINES_THREAD_POOL_SCHEDULER_DETECT_ALL_THREADS_SLEEPING 20 | #define PHANTOM_COROUTINES_THREAD_POOL_SCHEDULER_DETECT_ALL_THREADS_SLEEPING 1 21 | #endif 22 | #endif 23 | 24 | #ifdef PHANTOM_COROUTINES_COMPILING_MODULES 25 | #define PHANTOM_COROUTINES_MODULE_EXPORT export 26 | #else 27 | #define PHANTOM_COROUTINES_MODULE_EXPORT 28 | #endif 29 | 30 | #ifndef PHANTOM_COROUTINES_IS_COMPILED_MODULE 31 | #define PHANTOM_COROUTINES_IS_COMPILED_MODULE 0 32 | #endif 33 | 34 | #ifndef PHANTOM_COROUTINES_ASSERT_IS_MODULE 35 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 36 | #define PHANTOM_COROUTINES_ASSERT_IS_MODULE static_assert(PHANTOM_COROUTINES_IS_COMPILED_MODULE) 37 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 38 | #define PHANTOM_COROUTINES_ASSERT_IS_MODULE static_assert(PHANTOM_COROUTINES_IS_COMPILED_MODULE) 39 | #else 40 | #define PHANTOM_COROUTINES_ASSERT_IS_MODULE static_assert(true) 41 | #endif 42 | #endif 43 | 44 | #define PHANTOM_COROUTINES_IS_CONFIGURED 1 45 | 46 | #endif // PHANTOM_COROUTINES_INCLUDE_CONFIG_MACROS_H 47 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/async_test.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_ASYNC_TEST_H_H 2 | #define PHANTOM_COROUTINES_INCLUDE_ASYNC_TEST_H_H 3 | 4 | #include 5 | #include 6 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 7 | import Phantom.Coroutines; 8 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 9 | import Phantom.Coroutines.reusable_task; 10 | #elif defined(PHANTOM_COROUTINES_TESTING_HEADERS) 11 | #include "Phantom.Coroutines/reusable_task.h" 12 | #endif 13 | 14 | namespace Phantom::Coroutines::Test 15 | { 16 | void ExecuteTest( 17 | ::Phantom::Coroutines::reusable_task<> testTask 18 | ); 19 | } 20 | 21 | #define ASYNC_TEST_CLASS_NAME(test_suite_name, test_name) test_suite_name ## _ ## test_name ## _AsyncTest 22 | 23 | #define ASYNC_TEST_(test_suite_name, test_name, parent_class, parent_id) \ 24 | class ASYNC_TEST_CLASS_NAME(test_suite_name, test_name) \ 25 | : public parent_class \ 26 | { \ 27 | public: \ 28 | ::Phantom::Coroutines::reusable_task<> AsyncTestBody(); \ 29 | }; \ 30 | \ 31 | GTEST_TEST_(test_suite_name, test_name, ASYNC_TEST_CLASS_NAME(test_suite_name, test_name), ::testing::internal::GetTestTypeId()) \ 32 | { \ 33 | ::Phantom::Coroutines::Test::ExecuteTest(AsyncTestBody()); \ 34 | } \ 35 | \ 36 | ::Phantom::Coroutines::reusable_task<> ASYNC_TEST_CLASS_NAME(test_suite_name, test_name)::AsyncTestBody() 37 | 38 | #define ASYNC_TEST(test_suite_name, test_name) ASYNC_TEST_(test_suite_name, test_name, ::testing::Test, ::testing::internal::GetTestTypeId()) 39 | #define ASYNC_TEST_F(test_suite_name, test_name) ASYNC_TEST_(test_suite_name, test_name, test_suite_name, ::testing::internal::GetTypeId()) 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/detail/config_macros_clang_msvc.h: -------------------------------------------------------------------------------- 1 | #define PHANTOM_COROUTINES_COMPILER_CLANG_MSVC 1 2 | 3 | #define PHANTOM_COROUTINES_MSVC_INSTRINSIC 4 | #define PHANTOM_COROUTINES_MSVC_FORCEINLINE 5 | #define PHANTOM_COROUTINES_MSVC_SUPPRESS_PACKING_ALIGNMENT_WARNING 6 | #define PHANTOM_COROUTINES_MSVC_PUSH_DISABLE_WARNING(warnings) 7 | #define PHANTOM_COROUTINES_MSVC_POP_WARNINGS() 8 | 9 | // CLang captures the result of get_return_object using auto, 10 | // which decays reference types. Therefore, get_return_object 11 | // cannot return a reference type. 12 | #define PHANTOM_COROUTINES_USE_REFERENCE_WRAPPER_RETURN_TYPE_FOR_GET_RETURN_OBJECT 1 13 | 14 | #define PHANTOM_COROUTINES_LAMBDA_REFERENCE_IS_FIRST_ARGUMENT_OF_PROMISE_CONSTRUCTOR 0 15 | #define PHANTOM_COROUTINES_FUTURE_DOESNT_ACCEPT_NOT_DEFAULT_CONSTRUCTIBLE 0 16 | #define PHANTOM_COROUTINES_SYMMETRIC_TRANSFER_INCORRECTLY_LIFTED_TO_COROUTINE_FRAME 0 17 | #define PHANTOM_COROUTINES_NO_REJECT_LAMBDA_WITH_INVALID_MEMBER 0 18 | 19 | // CLang targetting Windows has a bug where exception data is not correctly when compiling with modules. 20 | // https://github.com/llvm/llvm-project/issues/53486 21 | #define PHANTOM_COROUTINES_INCORRECT_EXCEPTION_DATA_IN_MODULES 1 22 | 23 | #define PHANTOM_COROUTINES_CLANG_PUSH_DISABLE_WARNING(warnings) _Pragma("clang diagnostic push") _Pragma(warnings) 24 | #define PHANTOM_COROUTINES_CLANG_POP_WARNINGS() _Pragma("clang diagnostic pop") 25 | 26 | #define PHANTOM_COROUTINES_PUSH_DISABLE_INTERNAL_LINKAGE_WARNING() PHANTOM_COROUTINES_CLANG_PUSH_DISABLE_WARNING("clang diagnostic ignored \"-Wundefined-internal\"") 27 | #define PHANTOM_COROUTINES_POP_WARNINGS() PHANTOM_COROUTINES_CLANG_POP_WARNINGS() 28 | -------------------------------------------------------------------------------- /Documentation/promise_allocator.md: -------------------------------------------------------------------------------- 1 | # [Phantom.Coroutines](../README.md) 2 | 3 | ## ```promise_allocator.h``` 4 | 5 | ```promise_allocator.h``` provides support for using custom allocators 6 | to allocate the memory for promise objects. 7 | 8 | To use a custom allocator for an extensible promise type, use promise_allocator 9 | to derive from the promise type. Any promise function invocation that 10 | has an allocator argument will use the allocator provided, otherwise 11 | the default allocator will be used if the allocator can be default-constructed. 12 | 13 | For example, to use the ```std::pmr::polymorphic_allocator``` as the allocator, 14 | one might use: 15 | 16 | ```c++ 17 | template< 18 | typename Promise 19 | > using polymorphic_allocator_promise = 20 | promise_allocator< 21 | Promise, 22 | std::pmr::polymorphic_allocator<> 23 | >; 24 | 25 | template< 26 | typename Result = void 27 | > using pmr_task = 28 | basic_task< 29 | polymorphic_allocator_promise< 30 | task_promise>>; 31 | 32 | pmr_task<> promise_that_uses_allocator( 33 | polymorphic_allocator<> allocator, 34 | int otherArgument 35 | ) 36 | { 37 | co_return otherArgument * otherArgument; 38 | } 39 | 40 | task<> call_promise_that_uses_allocator() 41 | { 42 | polymorphic_allocator<> allocator; 43 | co_await promise_that_uses_allocator(allocator, otherArgument); 44 | } 45 | ``` 46 | 47 | If the base promise class has a ```get_return_object_on_allocation_failure``` 48 | method, then the promise_allocator will catch std::bad_alloc exceptions 49 | and return a nullptr value from the allocation attempt, triggering 50 | the compiler's ability to call ```get_return_object_on_allocation_failure```. 51 | -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro.Test/cppcoro_async_generator_test.cpp: -------------------------------------------------------------------------------- 1 | #include "async_test.h" 2 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 3 | import Phantom.Coroutines; 4 | import Phantom.Coroutines.Test.lifetime_tracker; 5 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 6 | import Phantom.Coroutines.Test.lifetime_tracker; 7 | #else 8 | #include "../Phantom.Coroutines.Test/lifetime_tracker.h" 9 | #endif 10 | #include "cppcoro/async_generator.hpp" 11 | 12 | namespace cppcoro 13 | { 14 | ASYNC_TEST(cppcoro_async_generator_test, can_enumerate_items) 15 | { 16 | auto lambda = []() -> async_generator 17 | { 18 | co_yield "hello"; 19 | co_yield "world"; 20 | }; 21 | 22 | auto generator = lambda(); 23 | std::vector items; 24 | for (auto iterator = co_await generator.begin(); 25 | iterator != generator.end(); 26 | co_await ++iterator) 27 | { 28 | items.push_back(*iterator); 29 | } 30 | 31 | EXPECT_EQ(items, (std::vector{ "hello", "world" })); 32 | } 33 | 34 | ASYNC_TEST(cppcoro_async_generator_test, destroys_promise) 35 | { 36 | Phantom::Coroutines::lifetime_statistics statistics; 37 | 38 | auto lambda = [&]() -> async_generator 39 | { 40 | auto tracker = statistics.tracker(); 41 | co_yield "hello"; 42 | co_yield "world"; 43 | }; 44 | 45 | { 46 | auto generator = lambda(); 47 | EXPECT_EQ(0, statistics.instance_count); 48 | auto iterator = co_await generator.begin(); 49 | EXPECT_EQ(1, statistics.instance_count); 50 | EXPECT_EQ("hello", *iterator); 51 | } 52 | 53 | EXPECT_EQ(0, statistics.instance_count); 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /Documentation/async_mutex.md: -------------------------------------------------------------------------------- 1 | # [Phantom.Coroutines](../README.md) 2 | 3 | ## ```async_mutex.h``` 4 | 5 | ```async_mutex.h``` provides a mutual exclusion primitive optimized for 6 | coroutines. Unlike thread-based mutual exclusion, the thread that 7 | releases a mutex need not be the same thread that acquired the mutex. 8 | 9 | The ```async_mutex``` template supports these members: 10 | 11 | ```c++ 12 | template< 13 | is_async_mutex_policy ... Policy 14 | > class async_mutex 15 | { 16 | public: 17 | using lock_type = std::unique_lock; 18 | 19 | bool try_lock() noexcept; 20 | [[nodiscard]] lock_type try_scoped_lock() noexcept; 21 | 22 | async_mutex_lock_operation lock() noexcept; 23 | async_mutex_scoped_lock_operation scoped_lock() noexcept; 24 | void unlock() noexcept; 25 | }; 26 | ``` 27 | 28 | The typical usage is: 29 | 30 | ``` 31 | class MyClass 32 | { 33 | async_mutex<> m_mutex; 34 | std::string m_message; 35 | 36 | task<> set_message_of_the_day( 37 | std::string message 38 | ) 39 | { 40 | auto lock = co_await m_mutex.scoped_lock(); 41 | m_message = std::move(message); 42 | } 43 | }; 44 | ``` 45 | 46 | The ```lock_type``` member is a template instantiation of ```std::unique_lock```, 47 | and thus supports the ```unlock()``` and ```try_lock``` methods. 48 | 49 | ### Policies 50 | 51 | ```async_mutex``` can have [policies](policies.md) applied to it from this set: 52 | 53 | ```c++ 54 | template< 55 | typename T 56 | > concept is_async_mutex_policy = 57 | is_concrete_policy 58 | || is_await_result_on_destruction_policy 59 | || is_awaiter_cardinality_policy 60 | || is_continuation_type_policy; 61 | ``` 62 | -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/task.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_TASK_H 2 | #define PHANTOM_COROUTINES_INCLUDE_TASK_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #include 5 | #include "detail/config_macros.h" 6 | #include "detail/core_task.h" 7 | #include "detail/coroutine.h" 8 | #include "policies.h" 9 | #endif 10 | 11 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 12 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 13 | 14 | namespace Phantom::Coroutines::detail 15 | { 16 | 17 | PHANTOM_COROUTINES_MODULE_EXPORT 18 | template< 19 | typename Policy 20 | > concept is_task_policy = 21 | is_continuation_type_policy 22 | || is_concrete_policy 23 | || is_concrete_policy 24 | || is_concrete_policy; 25 | 26 | PHANTOM_COROUTINES_MODULE_EXPORT 27 | template< 28 | typename Result, 29 | is_task_policy ... Policies 30 | > 31 | using task_promise = core_task_promise< 32 | Result, 33 | select_continuation_type< 34 | Policies..., 35 | continuation_type>>, 36 | core_non_reusable_task_promise_base 37 | >; 38 | 39 | PHANTOM_COROUTINES_MODULE_EXPORT 40 | template< 41 | typename Promise 42 | > using basic_task = core_task; 43 | 44 | PHANTOM_COROUTINES_MODULE_EXPORT 45 | template< 46 | typename Result = void, 47 | is_task_policy... Policies 48 | > using task = basic_task>; 49 | 50 | } 51 | 52 | namespace Phantom::Coroutines 53 | { 54 | PHANTOM_COROUTINES_MODULE_EXPORT 55 | using detail::basic_task; 56 | PHANTOM_COROUTINES_MODULE_EXPORT 57 | using detail::task; 58 | PHANTOM_COROUTINES_MODULE_EXPORT 59 | using detail::task_promise; 60 | 61 | } 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/double_wide_atomic_test.cpp: -------------------------------------------------------------------------------- 1 | #include "async_test.h" 2 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 3 | import Phantom.Coroutines; 4 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 5 | import Phantom.Coroutines.double_wide_atomic; 6 | #elif defined(PHANTOM_COROUTINES_TESTING_HEADERS) 7 | #include "Phantom.Coroutines/double_wide_atomic.h" 8 | #endif 9 | 10 | namespace Phantom::Coroutines 11 | { 12 | namespace 13 | { 14 | struct TestDoubleWide 15 | { 16 | std::int64_t value1 = 5; 17 | std::int64_t value2 = 6; 18 | 19 | bool friend operator==( 20 | const TestDoubleWide&, 21 | const TestDoubleWide& 22 | ) = default; 23 | }; 24 | } 25 | 26 | TEST(double_wide_atomic_test, Can_load_and_store) 27 | { 28 | std::atomic> atomic; 29 | EXPECT_EQ(TestDoubleWide{}, atomic.load()); 30 | atomic.store({ 1, 2 }); 31 | EXPECT_EQ(atomic.load(), (TestDoubleWide { 1, 2 } )); 32 | } 33 | 34 | TEST(double_wide_atomic_test, Can_compare_exchange) 35 | { 36 | std::atomic> atomic = { { 5, 6 } }; 37 | double_wide_value expected = { { 1, 2 } }; 38 | 39 | EXPECT_FALSE(atomic.compare_exchange_weak( 40 | expected, 41 | { { 3, 4 } } 42 | )); 43 | EXPECT_NE(expected, (TestDoubleWide { 3, 4 })); 44 | EXPECT_EQ(expected, (TestDoubleWide { 5, 6 })); 45 | EXPECT_EQ(atomic.load(), (TestDoubleWide{ 5, 6 })); 46 | 47 | EXPECT_TRUE(atomic.compare_exchange_weak( 48 | expected, 49 | { { 3, 4 } } 50 | )); 51 | 52 | EXPECT_NE(expected, (TestDoubleWide{ 3, 4 })); 53 | EXPECT_EQ(expected, (TestDoubleWide{ 5, 6 })); 54 | EXPECT_EQ(atomic.load(), (TestDoubleWide{ 3, 4 })); 55 | } 56 | } -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/async_latch_test.cpp: -------------------------------------------------------------------------------- 1 | #include "async_test.h" 2 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 3 | import Phantom.Coroutines; 4 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 5 | import Phantom.Coroutines.async_latch; 6 | import Phantom.Coroutines.async_scope; 7 | import Phantom.Coroutines.task; 8 | #elif defined(PHANTOM_COROUTINES_TESTING_HEADERS) 9 | #include "Phantom.Coroutines/async_latch.h" 10 | #include "Phantom.Coroutines/async_scope.h" 11 | #include "Phantom.Coroutines/task.h" 12 | #endif 13 | 14 | namespace Phantom::Coroutines 15 | { 16 | 17 | ASYNC_TEST(async_latch_test, latch_start_signalled_at_count_0_or_less) 18 | { 19 | async_latch<> latch1(0); 20 | async_latch<> latch2(-1); 21 | co_await latch1; 22 | co_await latch2; 23 | } 24 | 25 | ASYNC_TEST(async_latch_test, latch_releases_at_count_0) 26 | { 27 | bool completed = false; 28 | async_scope<> scope; 29 | 30 | async_latch<> latch(2); 31 | 32 | auto lambda = [&]() -> task<> 33 | { 34 | co_await latch; 35 | completed = true; 36 | }; 37 | scope.spawn(lambda); 38 | 39 | EXPECT_FALSE(completed); 40 | latch.count_down(); 41 | EXPECT_FALSE(completed); 42 | latch.count_down(); 43 | EXPECT_TRUE(completed); 44 | co_await scope.join(); 45 | } 46 | 47 | ASYNC_TEST(async_latch_test, latch_releases_at_count_less_than_0) 48 | { 49 | bool completed = false; 50 | async_scope<> scope; 51 | 52 | async_latch<> latch(3); 53 | 54 | auto lambda = [&]() -> task<> 55 | { 56 | co_await latch; 57 | completed = true; 58 | }; 59 | scope.spawn(lambda); 60 | 61 | EXPECT_FALSE(completed); 62 | latch.count_down(2); 63 | EXPECT_FALSE(completed); 64 | latch.count_down(2); 65 | EXPECT_TRUE(completed); 66 | co_await scope.join(); 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /Phantom.Coroutines.cppcoro.Test/cppcoro_async_mutex_test.cpp: -------------------------------------------------------------------------------- 1 | #include "async_test.h" 2 | #include "cppcoro/async_auto_reset_event.hpp" 3 | #include "cppcoro/async_scope.hpp" 4 | #include "cppcoro/async_mutex.hpp" 5 | #include "cppcoro/task.hpp" 6 | 7 | static_assert(std::same_as< 8 | cppcoro::async_mutex, 9 | Phantom::Coroutines::async_mutex< 10 | Phantom::Coroutines::multiple_awaiters, 11 | Phantom::Coroutines::noop_on_destroy, 12 | Phantom::Coroutines::default_continuation_type, 13 | Phantom::Coroutines::await_is_not_cancellable 14 | >>); 15 | 16 | static_assert( 17 | std::same_as< 18 | std::unique_lock< 19 | cppcoro::async_mutex 20 | >, 21 | cppcoro::async_mutex_lock 22 | > 23 | ); 24 | 25 | static_assert( 26 | std::same_as< 27 | ::Phantom::Coroutines::async_mutex<>::lock_operation, 28 | ::cppcoro::async_mutex_lock_operation 29 | >); 30 | 31 | static_assert( 32 | std::same_as< 33 | ::Phantom::Coroutines::async_mutex<>::scoped_lock_operation, 34 | ::cppcoro::async_mutex_scoped_lock_operation 35 | >); 36 | 37 | namespace cppcoro 38 | { 39 | ASYNC_TEST(cppcoro_async_mutex_test, mutex_is_mutually_exclusive) 40 | { 41 | async_mutex mutex; 42 | async_auto_reset_event event; 43 | 44 | bool complete1 = false; 45 | bool complete2 = false; 46 | 47 | auto lambda = [&]( 48 | bool& complete 49 | ) -> task<> 50 | { 51 | auto lock = co_await mutex.scoped_lock_async(); 52 | complete = true; 53 | co_await event; 54 | }; 55 | 56 | async_scope scope; 57 | scope.spawn(lambda(complete1)); 58 | scope.spawn(lambda(complete2)); 59 | 60 | EXPECT_TRUE(complete1); 61 | EXPECT_FALSE(complete2); 62 | event.set(); 63 | EXPECT_TRUE(complete2); 64 | event.set(); 65 | 66 | co_await scope.join(); 67 | } 68 | } -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/thread_local_contextual_promise.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 2 | #include 3 | #include "detail/config_macros.h" 4 | #include "contextual_promise.h" 5 | #include "extensible_promise.h" 6 | #include "thread_local_context.h" 7 | #endif 8 | 9 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 10 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 11 | 12 | namespace Phantom::Coroutines 13 | { 14 | namespace detail 15 | { 16 | 17 | PHANTOM_COROUTINES_MODULE_EXPORT 18 | template< 19 | is_thread_local_context ThreadLocalContext, 20 | typename BasePromise 21 | > class thread_local_contextual_promise 22 | : 23 | public derived_promise 24 | < 25 | contextual_promise 26 | < 27 | BasePromise 28 | > 29 | > 30 | { 31 | using base_promise = thread_local_contextual_promise::derived_promise; 32 | using thread_local_context_scope = thread_local_context_scope; 33 | 34 | public: 35 | using value_type = typename ThreadLocalContext::value_type; 36 | 37 | private: 38 | std::optional m_scope; 39 | std::optional m_value; 40 | 41 | public: 42 | template< 43 | typename... Args 44 | > thread_local_contextual_promise( 45 | Args&&... args 46 | ) : 47 | thread_local_contextual_promise::derived_promise{ std::forward(args)... }, 48 | m_value 49 | { 50 | ThreadLocalContext::current() 51 | } 52 | {} 53 | 54 | void enter() 55 | { 56 | m_scope.emplace(std::move(*m_value)); 57 | m_value.reset(); 58 | } 59 | 60 | void leave() 61 | { 62 | m_value.emplace(std::move(ThreadLocalContext::current())); 63 | m_scope.reset(); 64 | } 65 | }; 66 | 67 | } 68 | PHANTOM_COROUTINES_MODULE_EXPORT 69 | using detail::thread_local_contextual_promise; 70 | } -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/static_thread_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_STATIC_THREAD_POOL_H 2 | #define PHANTOM_COROUTINES_INCLUDE_STATIC_THREAD_POOL_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #include 5 | #include 6 | #include 7 | #include "detail/config_macros.h" 8 | #include "scheduler.h" 9 | #include "thread_pool_scheduler.h" 10 | #endif 11 | 12 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 13 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 14 | 15 | namespace Phantom::Coroutines 16 | { 17 | namespace detail 18 | { 19 | 20 | PHANTOM_COROUTINES_MODULE_EXPORT 21 | template< 22 | typename ThreadPoolScheduler = thread_pool_scheduler<> 23 | > 24 | class static_thread_pool 25 | { 26 | ThreadPoolScheduler m_scheduler; 27 | std::stop_source m_stopSource; 28 | std::latch m_stopLatch; 29 | size_t m_threadCount; 30 | 31 | public: 32 | explicit static_thread_pool( 33 | size_t threadCount = std::thread::hardware_concurrency() 34 | ) : 35 | m_threadCount(threadCount), 36 | m_stopLatch(threadCount) 37 | { 38 | for (size_t threadCounter = 0; threadCounter < threadCount; threadCounter++) 39 | { 40 | std::thread([&] 41 | { 42 | m_scheduler.process_items(m_stopSource.get_token()); 43 | m_stopLatch.count_down(); 44 | }).detach(); 45 | } 46 | } 47 | 48 | auto thread_count() const noexcept 49 | { 50 | return m_threadCount; 51 | } 52 | 53 | ~static_thread_pool() 54 | { 55 | m_stopSource.request_stop(); 56 | m_stopLatch.wait(); 57 | } 58 | 59 | auto schedule() noexcept 60 | { 61 | return m_scheduler.schedule(); 62 | } 63 | }; 64 | 65 | } 66 | 67 | PHANTOM_COROUTINES_MODULE_EXPORT 68 | using Phantom::Coroutines::detail::static_thread_pool; 69 | } 70 | #endif 71 | -------------------------------------------------------------------------------- /Tla/ReusableConsecutiveId.toolbox/ReusableConsecutiveId___Thread_3.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/detail/construct_from_base.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_CONSTRUCTIBLE_FROM_BASE_H 2 | #define PHANTOM_COROUTINES_INCLUDE_CONSTRUCTIBLE_FROM_BASE_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #include 5 | #endif 6 | 7 | namespace Phantom::Coroutines::detail 8 | { 9 | // This class allows constructing a derived class from one of its base classes, 10 | // essentially supporting "using base::base" for copy and move constructors. 11 | // All the base class's constructors are inherited. 12 | // 13 | // For example: 14 | // 15 | //struct base 16 | //{ 17 | // base() {} 18 | // base(const base&) {} 19 | //}; 20 | // 21 | //struct derived1 : construct_from_base 22 | //{ 23 | // using derived1::construct_from_base::construct_from_base; 24 | //}; 25 | // 26 | //struct derived2 : construct_from_base 27 | //{ 28 | // using derived2::construct_from_base::construct_from_base; 29 | //}; 30 | // 31 | //int main() 32 | //{ 33 | // derived1 d1; 34 | // derived2 d2{ d1 }; 35 | //} 36 | PHANTOM_COROUTINES_MODULE_EXPORT 37 | template< 38 | typename T 39 | > 40 | struct construct_from_base 41 | : 42 | public T 43 | { 44 | using T::T; 45 | 46 | construct_from_base( 47 | const T& other 48 | ) 49 | requires std::constructible_from 50 | : T(other) 51 | { 52 | } 53 | 54 | construct_from_base( 55 | T& other 56 | ) 57 | requires std::constructible_from 58 | : T(other) 59 | { 60 | } 61 | 62 | construct_from_base( 63 | T&& other 64 | ) 65 | requires std::constructible_from 66 | : T(std::move(other)) 67 | { 68 | } 69 | 70 | construct_from_base( 71 | const T&& other 72 | ) 73 | requires std::constructible_from 74 | : T(std::move(other)) 75 | { 76 | } 77 | }; 78 | } 79 | #endif 80 | -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/detail/storage_for.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 2 | #include 3 | #include 4 | #include "immovable_object.h" 5 | #include "../type_traits.h" 6 | #endif 7 | 8 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 9 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 10 | 11 | namespace Phantom::Coroutines::detail 12 | { 13 | 14 | template< 15 | size_t Size, 16 | size_t Alignment 17 | > 18 | struct storage_for_impl 19 | { 20 | alignas(Alignment) char m_storage[Size]; 21 | }; 22 | 23 | template< 24 | > 25 | struct storage_for_impl< 26 | 0, 27 | 0 28 | > 29 | { 30 | inline static char m_storage; 31 | }; 32 | 33 | PHANTOM_COROUTINES_MODULE_EXPORT 34 | template< 35 | typename ... TValues 36 | > struct storage_for 37 | : 38 | public storage_for_impl< 39 | std::max({ static_cast(0), sizeof(TValues)... }), 40 | std::max({ static_cast(0), alignof(TValues)... }) 41 | >, 42 | private immovable_object 43 | { 44 | using storage_for::storage_for_impl::m_storage; 45 | 46 | template< 47 | is_in_types T, 48 | typename... Args 49 | > T& emplace( 50 | Args&&... args 51 | ) noexcept(noexcept(new (&m_storage)T(std::forward(args)...))) 52 | { 53 | if constexpr (sizeof(T) == 0) 54 | { 55 | return; 56 | } 57 | else 58 | { 59 | new (&m_storage)T(std::forward(args)...); 60 | } 61 | return as(); 62 | } 63 | 64 | template< 65 | is_in_types T 66 | > T& as() noexcept 67 | { 68 | return *reinterpret_cast(&m_storage); 69 | } 70 | 71 | template< 72 | is_in_types T 73 | > void destroy() 74 | { 75 | if constexpr (sizeof(T) == 0) 76 | { 77 | return; 78 | } 79 | else 80 | { 81 | as().~T(); 82 | } 83 | } 84 | }; 85 | 86 | } -------------------------------------------------------------------------------- /Tla/ThreadPool.toolbox/ThreadPool___Correctness_3_Items_3_Threads.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Documentation/conventions.md: -------------------------------------------------------------------------------- 1 | # [Phantom.Coroutines](../README.md) 2 | 3 | ## Conventions 4 | 5 | The Phantom.Coroutines implementation follows these conventions. 6 | 7 | ### Use of explicit object argument deduction 8 | 9 | Most methods in the library use the C++23 construct ```this auto& self``` as the explicit argument. 10 | This eliminates the need to use the [curiously recurring template pattern](https://www.bing.com/search?q=curiously+recurring+template+pattern) 11 | and virtual functions to implement extensibility. Instead, objects generally are expected to accept knowledge 12 | of the exact type of the object via this parameter. 13 | 14 | ### return_, get_, resume_ names 15 | 16 | ```return_``` accepts a value _into_ a promise object for the purpose of later resuming 17 | the awaiter which will obtain that value. Notably, ```return_value``` and ```return_void``` 18 | in the C++ standard follow this pattern. 19 | 20 | ```value``` in the library refers to a value provided by the C++ runtime or a user. ```result`` refers 21 | to a value being _returned_ to the caller or user. 22 | 23 | ```get_``` means to obtain, with as little interpretation as possible, the specified value name. 24 | 25 | ```resume_``` means to obtain for the use of a caller the specified value name in a context where 26 | the value will be used to resume a caller. 27 | 28 | ### basic_ names 29 | 30 | A ```basic_``` name represents the raw, low-level type implementing a concept. These names 31 | have more implementation details associated with them about how they work, and users using 32 | these names should expect significant changes from one version of Phantom.Coroutines to the next. 33 | 34 | For example, [```task```](task.md) is an alias to ```basic_task```. ```task``` accepts user-friendly policy-driven 35 | type arguments, whereas ```basic_task``` accepts the ```promise_type``` to use. Similarly, 36 | ```task_promise``` also accepts user-friendly policy-driven type arguments, whereas ```basic_task_promise``` 37 | accepts specific type arguments for each supported policy. 38 | 39 | -------------------------------------------------------------------------------- /Tla/ThreadPool.toolbox/ThreadPool___Correctness_5_Items_3_Threads.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/make_Task_test.cpp: -------------------------------------------------------------------------------- 1 | #include "async_test.h" 2 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 3 | import Phantom.Coroutines; 4 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 5 | import Phantom.Coroutines.async_manual_reset_event; 6 | import Phantom.Coroutines.make_task; 7 | import Phantom.Coroutines.shared_task; 8 | import Phantom.Coroutines.task; 9 | #elif defined(PHANTOM_COROUTINES_TESTING_HEADERS) 10 | #include "Phantom.Coroutines/async_manual_reset_event.h" 11 | #include "Phantom.Coroutines/make_task.h" 12 | #include "Phantom.Coroutines/shared_task.h" 13 | #include "Phantom.Coroutines/task.h" 14 | #endif 15 | 16 | namespace Phantom::Coroutines 17 | { 18 | 19 | ASYNC_TEST(make_task, Can_make_task_returning_void) 20 | { 21 | async_manual_reset_event<> event; 22 | auto result = make_task(event); 23 | 24 | static_assert(std::same_as, decltype(result)>); 25 | 26 | event.set(); 27 | co_await std::move(result); 28 | } 29 | 30 | ASYNC_TEST(make_task, Can_make_shared_task_returning_void) 31 | { 32 | async_manual_reset_event<> event; 33 | auto result = make_task(event); 34 | 35 | static_assert(std::same_as, decltype(result)>); 36 | 37 | event.set(); 38 | co_await result; 39 | } 40 | 41 | ASYNC_TEST(make_task, Can_make_shared_task_from_task) 42 | { 43 | async_manual_reset_event<> event; 44 | auto lambda = [&]() -> task<> 45 | { 46 | co_return; 47 | }; 48 | auto result = make_task(lambda()); 49 | 50 | static_assert(std::same_as, decltype(result)>); 51 | 52 | event.set(); 53 | co_await result; 54 | } 55 | 56 | ASYNC_TEST(make_task, Can_make_shared_task_from_task_return_value) 57 | { 58 | auto lambda = [&]() -> task 59 | { 60 | co_return "hello world"; 61 | }; 62 | auto result = make_task(lambda()); 63 | 64 | static_assert(std::same_as, decltype(result)>); 65 | 66 | EXPECT_EQ("hello world", co_await result); 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /Tla/ThreadPool.toolbox/ThreadPool___Liveness_3_Items_3_Threads.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Tla/ThreadPool.toolbox/ThreadPool___Liveness_5_Items_3_Threads.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/async_promise_test.cpp: -------------------------------------------------------------------------------- 1 | #include "async_test.h" 2 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 3 | import Phantom.Coroutines; 4 | import Phantom.Coroutines.Test.lifetime_tracker; 5 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 6 | import Phantom.Coroutines.async_promise; 7 | import Phantom.Coroutines.suspend_result; 8 | import Phantom.Coroutines.sync_wait; 9 | import Phantom.Coroutines.task; 10 | import Phantom.Coroutines.Test.lifetime_tracker; 11 | #elif defined(PHANTOM_COROUTINES_TESTING_HEADERS) 12 | #include "Phantom.Coroutines/async_promise.h" 13 | #include "Phantom.Coroutines/suspend_result.h" 14 | #include "Phantom.Coroutines/sync_wait.h" 15 | #include "Phantom.Coroutines/task.h" 16 | #include "lifetime_tracker.h" 17 | #endif 18 | #include 19 | 20 | namespace Phantom::Coroutines 21 | { 22 | 23 | using namespace std::string_literals; 24 | 25 | TEST(async_promise_test, Set_after_await_causes_await_to_continue) 26 | { 27 | async_promise promise; 28 | suspend_result suspendResult; 29 | 30 | auto future = as_future([&]() -> task 31 | { 32 | co_return co_await(suspendResult << promise); 33 | }); 34 | 35 | promise.emplace(L"hello world"s); 36 | ASSERT_EQ(future.get(), L"hello world"s); 37 | ASSERT_EQ(true, suspendResult.did_suspend()); 38 | } 39 | 40 | TEST(async_promise_test, Set_before_await_causes_await_to_not_suspend) 41 | { 42 | async_promise promise; 43 | std::optional didSuspend; 44 | suspend_result suspendResult; 45 | 46 | promise.emplace(L"hello world"s); 47 | 48 | auto future = as_future([&]() -> task 49 | { 50 | co_return co_await(suspendResult << promise); 51 | }); 52 | 53 | ASSERT_EQ(future.get(), L"hello world"s); 54 | ASSERT_EQ(false, suspendResult.did_suspend()); 55 | } 56 | 57 | 58 | TEST(async_promise_test, Destroy_calls_destructor_of_embedded_value) 59 | { 60 | lifetime_statistics statistics; 61 | { 62 | async_promise promise; 63 | 64 | promise.emplace(statistics.tracker()); 65 | ASSERT_EQ(1, statistics.instance_count); 66 | } 67 | ASSERT_EQ(0, statistics.instance_count); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/pmr_task.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_PMR_TASK_H 2 | #define PHANTOM_COROUTINES_INCLUDE_PMR_TASK_H 3 | #if defined(PHANTOM_COROUTINES_TESTING_HEADERS) 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "Phantom.Coroutines/detail/config_macros.h" 9 | #include "Phantom.Coroutines/promise_allocator.h" 10 | #include "Phantom.Coroutines/reusable_task.h" 11 | #include "Phantom.Coroutines/task.h" 12 | #endif 13 | 14 | namespace Phantom::Coroutines::Test 15 | { 16 | 17 | PHANTOM_COROUTINES_MODULE_EXPORT 18 | struct memory_tracker_data 19 | { 20 | std::atomic m_allocatedMemory = 0; 21 | }; 22 | 23 | PHANTOM_COROUTINES_MODULE_EXPORT 24 | class memory_tracker : 25 | public std::pmr::memory_resource 26 | { 27 | std::shared_ptr m_trackerData 28 | = std::make_shared(); 29 | 30 | // Inherited via memory_resource 31 | virtual void* do_allocate(size_t _Bytes, size_t _Align) override 32 | { 33 | m_trackerData->m_allocatedMemory += _Bytes; 34 | return std::pmr::get_default_resource()->allocate(_Bytes, _Align); 35 | } 36 | 37 | virtual void do_deallocate(void* _Ptr, size_t _Bytes, size_t _Align) override 38 | { 39 | m_trackerData->m_allocatedMemory -= _Bytes; 40 | return std::pmr::get_default_resource()->deallocate(_Ptr, _Bytes, _Align); 41 | } 42 | 43 | virtual bool do_is_equal(const memory_resource&) const noexcept override 44 | { 45 | return false; 46 | } 47 | 48 | public: 49 | size_t allocated_memory() const 50 | { 51 | return m_trackerData->m_allocatedMemory; 52 | } 53 | }; 54 | 55 | PHANTOM_COROUTINES_MODULE_EXPORT 56 | template< 57 | typename Result = void 58 | > using pmr_task = basic_task< 59 | promise_allocator< 60 | task_promise, 61 | std::pmr::polymorphic_allocator<> 62 | > 63 | >; 64 | 65 | PHANTOM_COROUTINES_MODULE_EXPORT 66 | template< 67 | typename Result = void 68 | > using pmr_reusable_task = basic_reusable_task< 69 | promise_allocator< 70 | reusable_task_promise, 71 | std::pmr::polymorphic_allocator<> 72 | > 73 | >; 74 | 75 | } 76 | #endif 77 | -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/thread_local_context.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_THREAD_LOCAL_CONTEXT_H 2 | #define PHANTOM_COROUTINES_INCLUDE_THREAD_LOCAL_CONTEXT_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #include 5 | #include 6 | #include "detail/config_macros.h" 7 | #endif 8 | 9 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 10 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 11 | 12 | namespace Phantom::Coroutines 13 | { 14 | 15 | PHANTOM_COROUTINES_MODULE_EXPORT 16 | template< 17 | typename ThreadLocalContext 18 | > concept is_thread_local_context = requires (typename ThreadLocalContext::value_type value) 19 | { 20 | typename ThreadLocalContext::value_type; 21 | { ThreadLocalContext::current() = value }; 22 | { ThreadLocalContext::current() } -> std::convertible_to; 23 | }; 24 | 25 | PHANTOM_COROUTINES_MODULE_EXPORT 26 | template< 27 | typename Value, 28 | // The unique identifying label for the thread local context. 29 | typename Label 30 | > class thread_local_context 31 | { 32 | public: 33 | typedef Value value_type; 34 | 35 | private: 36 | static inline thread_local Value m_value; 37 | 38 | public: 39 | static value_type& current() 40 | { 41 | return m_value; 42 | } 43 | }; 44 | 45 | PHANTOM_COROUTINES_MODULE_EXPORT 46 | template< 47 | is_thread_local_context ThreadLocalContext 48 | > class thread_local_context_scope 49 | { 50 | public: 51 | typedef ThreadLocalContext context_type; 52 | typedef typename context_type::value_type value_type; 53 | 54 | private: 55 | value_type m_previousValue; 56 | 57 | public: 58 | // Prevent all copying and moving. 59 | auto operator=( 60 | thread_local_context_scope&& 61 | ) = delete; 62 | 63 | template< 64 | typename Value 65 | > 66 | explicit thread_local_context_scope( 67 | Value&& value 68 | ) : 69 | m_previousValue 70 | { 71 | std::move(ThreadLocalContext::current()) 72 | } 73 | { 74 | ThreadLocalContext::current() = std::forward(value); 75 | } 76 | 77 | ~thread_local_context_scope() 78 | { 79 | ThreadLocalContext::current() = std::forward(m_previousValue); 80 | } 81 | }; 82 | 83 | } 84 | #endif 85 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/thread_local_contextual_promise_test.cpp: -------------------------------------------------------------------------------- 1 | #include "async_test.h" 2 | #include 3 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 4 | import Phantom.Coroutines; 5 | import Phantom.Coroutines.Test.lifetime_tracker; 6 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 7 | import Phantom.Coroutines.async_manual_reset_event; 8 | import Phantom.Coroutines.async_scope; 9 | import Phantom.Coroutines.Test.lifetime_tracker; 10 | import Phantom.Coroutines.task; 11 | import Phantom.Coroutines.thread_local_contextual_promise; 12 | import Phantom.Coroutines.thread_local_context; 13 | #elif defined(PHANTOM_COROUTINES_TESTING_HEADERS) 14 | #include "Phantom.Coroutines/async_manual_reset_event.h" 15 | #include "Phantom.Coroutines/async_scope.h" 16 | #include "Phantom.Coroutines/task.h" 17 | #include "Phantom.Coroutines/thread_local_contextual_promise.h" 18 | #include "Phantom.Coroutines/thread_local_context.h" 19 | #include "lifetime_tracker.h" 20 | #endif 21 | 22 | namespace Phantom::Coroutines 23 | { 24 | 25 | namespace 26 | { 27 | using TestContext = thread_local_context; 28 | 29 | template< 30 | typename Result = void 31 | > using TestThreadLocalContextTask = basic_task< 32 | thread_local_contextual_promise< 33 | TestContext, 34 | task_promise 35 | > 36 | >; 37 | } 38 | 39 | ASYNC_TEST(thread_local_contextual_promise, sets_context_during_promise_execution) 40 | { 41 | lifetime_statistics statistics; 42 | async_manual_reset_event<> signal; 43 | 44 | EXPECT_EQ(nullptr, TestContext::current()); 45 | auto tracker = statistics.tracker(); 46 | 47 | std::thread::id expectedThreadId; 48 | 49 | TestContext::current() = &tracker; 50 | async_scope<> scope; 51 | scope.spawn([&]() -> TestThreadLocalContextTask<> 52 | { 53 | EXPECT_EQ(&tracker, TestContext::current()); 54 | co_await signal; 55 | EXPECT_EQ(expectedThreadId, std::this_thread::get_id()); 56 | EXPECT_EQ(&tracker, TestContext::current()); 57 | }); 58 | 59 | std::thread backgroundThread([&]() 60 | { 61 | expectedThreadId = std::this_thread::get_id(); 62 | signal.set(); 63 | EXPECT_EQ(nullptr, TestContext::current()); 64 | }); 65 | 66 | co_await scope.join(); 67 | backgroundThread.detach(); 68 | } 69 | } -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/tagged_pointer.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_TAGGED_POINTER_H 2 | #define PHANTOM_COROUTINES_INCLUDE_TAGGED_POINTER_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "detail/config_macros.h" 10 | #endif 11 | 12 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 13 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 14 | 15 | namespace Phantom::Coroutines 16 | { 17 | 18 | template< 19 | typename Value, 20 | unsigned short BitCount, 21 | typename Tag = uintptr_t 22 | > constexpr bool is_valid_tagged_pointer = 23 | ( 24 | BitCount < std::bit_width(alignof(Value)) 25 | // true 26 | || 27 | std::same_as 28 | ) 29 | && 30 | ( 31 | std::is_integral_v 32 | || 33 | std::is_enum_v 34 | ); 35 | 36 | PHANTOM_COROUTINES_MODULE_EXPORT 37 | template< 38 | typename Value, 39 | unsigned short BitCount, 40 | typename Tag = uintptr_t 41 | > 42 | class tagged_pointer 43 | { 44 | static_assert(is_valid_tagged_pointer< 45 | Value, 46 | BitCount, 47 | Tag 48 | >); 49 | 50 | static constexpr uintptr_t mask = (1 << BitCount) - 1; 51 | 52 | uintptr_t m_value; 53 | 54 | void assign( 55 | Value* value, 56 | Tag tag 57 | ) 58 | { 59 | assert((reinterpret_cast(value) & ~mask) == reinterpret_cast(value)); 60 | assert((static_cast(tag) & mask) == static_cast(tag)); 61 | 62 | m_value = reinterpret_cast(value) | static_cast(tag); 63 | } 64 | 65 | public: 66 | tagged_pointer() = default; 67 | 68 | tagged_pointer( 69 | Value* value, 70 | Tag tag) 71 | { 72 | assign(value, tag); 73 | } 74 | 75 | Value* operator ->() const 76 | { 77 | return value(); 78 | } 79 | 80 | Value* value() const 81 | { 82 | return reinterpret_cast(m_value & ~mask); 83 | } 84 | 85 | Tag tag() const 86 | { 87 | return static_cast(m_value & mask); 88 | } 89 | 90 | friend bool operator==(const tagged_pointer&, const tagged_pointer&) = default; 91 | }; 92 | 93 | } 94 | #endif 95 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/tagged_pointer_test.cpp: -------------------------------------------------------------------------------- 1 | #include "async_test.h" 2 | #include "Phantom.Coroutines/detail/config_macros.h" 3 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 4 | import Phantom.Coroutines; 5 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 6 | import Phantom.Coroutines.tagged_pointer; 7 | #elif defined(PHANTOM_COROUTINES_TESTING_HEADERS) 8 | #include "Phantom.Coroutines/tagged_pointer.h" 9 | #endif 10 | 11 | namespace Phantom::Coroutines 12 | { 13 | namespace 14 | { 15 | 16 | enum TaggedPointerTag 17 | { 18 | Zero = 0, 19 | Seven = 7, 20 | }; 21 | 22 | // Disable warning that structure padded due to alignment specifier 23 | PHANTOM_COROUTINES_MSVC_PUSH_DISABLE_WARNING(4324) 24 | struct alignas(8) TaggedPointerStruct 25 | { 26 | }; 27 | PHANTOM_COROUTINES_MSVC_POP_WARNINGS() 28 | 29 | } 30 | 31 | TEST(tagged_pointer_test, can_initialize_and_retrieve_value_and_tag) 32 | { 33 | TaggedPointerStruct value; 34 | tagged_pointer pointer{ &value, Seven }; 35 | ASSERT_EQ(&value, pointer.value()); 36 | ASSERT_EQ(&value, pointer.operator ->()); 37 | ASSERT_EQ(Seven, pointer.tag()); 38 | } 39 | 40 | 41 | TEST(tagged_pointer_test, can_compare) 42 | { 43 | TaggedPointerStruct value1; 44 | TaggedPointerStruct value2; 45 | tagged_pointer pointer1{ &value1, Zero }; 46 | tagged_pointer pointer2{ &value1, Seven }; 47 | tagged_pointer pointer3{ &value2, Seven }; 48 | 49 | ASSERT_TRUE(pointer1 == pointer1); 50 | ASSERT_FALSE(pointer1 == pointer2); 51 | ASSERT_FALSE(pointer1 == pointer3); 52 | 53 | ASSERT_FALSE(pointer2 == pointer1); 54 | ASSERT_TRUE(pointer2 == pointer2); 55 | ASSERT_FALSE(pointer2 == pointer3); 56 | 57 | ASSERT_FALSE(pointer3 == pointer1); 58 | ASSERT_FALSE(pointer3 == pointer2); 59 | ASSERT_TRUE(pointer3 == pointer3); 60 | 61 | ASSERT_FALSE(pointer1 != pointer1); 62 | ASSERT_TRUE(pointer1 != pointer2); 63 | ASSERT_TRUE(pointer1 != pointer3); 64 | 65 | ASSERT_TRUE(pointer2 != pointer1); 66 | ASSERT_FALSE(pointer2 != pointer2); 67 | ASSERT_TRUE(pointer2 != pointer3); 68 | 69 | ASSERT_TRUE(pointer3 != pointer1); 70 | ASSERT_TRUE(pointer3 != pointer2); 71 | ASSERT_FALSE(pointer3 != pointer3); 72 | } 73 | } -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/detail/config_macros_msvc.h: -------------------------------------------------------------------------------- 1 | #define PHANTOM_COROUTINES_COMPILER_MSVC 1 2 | 3 | // Using MSVC 4 | // Bug https ://developercommunity.visualstudio.com/t/msvc-2022-c-stdfuture-still-requires-default-const/1582239 5 | #ifndef PHANTOM_COROUTINES_FUTURE_DOESNT_ACCEPT_NOT_DEFAULT_CONSTRUCTIBLE 6 | #define PHANTOM_COROUTINES_FUTURE_DOESNT_ACCEPT_NOT_DEFAULT_CONSTRUCTIBLE 1 7 | #endif 8 | 9 | // Bug https://developercommunity.visualstudio.com/t/Incorrect-code-generation-for-symmetric/1659260?q=%22symmetric+transfer%22 10 | #ifndef PHANTOM_COROUTINES_SYMMETRIC_TRANSFER_INCORRECTLY_LIFTED_TO_COROUTINE_FRAME 11 | #define PHANTOM_COROUTINES_SYMMETRIC_TRANSFER_INCORRECTLY_LIFTED_TO_COROUTINE_FRAME 1 12 | #endif 13 | 14 | // Bug https://developercommunity.visualstudio.com/t/Incorrect-code-generation-for-symmetric/1659260?q=%22symmetric+transfer%22 15 | #ifndef PHANTOM_COROUTINES_SYMMETRIC_TRANSFER_INCORRECTLY_LIFTED_TO_COROUTINE_FRAME 16 | #define PHANTOM_COROUTINES_SYMMETRIC_TRANSFER_INCORRECTLY_LIFTED_TO_COROUTINE_FRAME 1 17 | #endif 18 | 19 | // Bug https://developercommunity.visualstudio.com/t/MSVC-accepts-lambda-expression-referring/10306965 20 | #ifndef PHANTOM_COROUTINES_NO_REJECT_LAMBDA_WITH_INVALID_MEMBER 21 | #define PHANTOM_COROUTINES_NO_REJECT_LAMBDA_WITH_INVALID_MEMBER 1 22 | #endif 23 | 24 | // MSVC captures the result of get_return_object as-is. 25 | #define PHANTOM_COROUTINES_USE_REFERENCE_WRAPPER_RETURN_TYPE_FOR_GET_RETURN_OBJECT 0 26 | 27 | // MSVC does present the lambda object as the first reference 28 | // argument to the promise constructor. 29 | #define PHANTOM_COROUTINES_LAMBDA_REFERENCE_IS_FIRST_ARGUMENT_OF_PROMISE_CONSTRUCTOR 1 30 | 31 | #define PHANTOM_COROUTINES_MSVC_INSTRINSIC [[msvc::intrinsic]] 32 | #define PHANTOM_COROUTINES_MSVC_FORCEINLINE [[msvc::forceinline]] 33 | #define PHANTOM_COROUTINES_MSVC_SUPPRESS_PACKING_ALIGNMENT_WARNING __pragma(warning(suppress:4324)) 34 | 35 | #define PHANTOM_COROUTINES_MSVC_PUSH_DISABLE_WARNING(warnings) __pragma(warning(push)) __pragma(warning(disable: warnings)) 36 | #define PHANTOM_COROUTINES_MSVC_POP_WARNINGS() __pragma(warning(pop)) 37 | 38 | #define PHANTOM_COROUTINES_CLANG_PUSH_DISABLE_WARNING(warnings) 39 | #define PHANTOM_COROUTINES_CLANG_POP_WARNINGS() 40 | 41 | #define PHANTOM_COROUTINES_PUSH_DISABLE_INTERNAL_LINKAGE_WARNING() PHANTOM_COROUTINES_MSVC_PUSH_DISABLE_WARNING(5046) 42 | #define PHANTOM_COROUTINES_POP_WARNINGS() PHANTOM_COROUTINES_MSVC_POP_WARNINGS() 43 | -------------------------------------------------------------------------------- /Tla/ReusableConsecutiveId.toolbox/ReusableConsecutiveId___Thread_3_Live.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Documentation/shared_task.md: -------------------------------------------------------------------------------- 1 | # [Phantom.Coroutines](../README.md) 2 | 3 | ## ```shared_task.h``` 4 | 5 | 6 | ```shared_task.h``` provides an extensible ```shared_task``` implementation for implementing lazily-started coroutines 7 | that have can have multiple awaiters and can be copied and moved. 8 | 9 | 10 | The result type of the task is a value reference to the type parameter to ```task``` 11 | unless the task returns void. 12 | 13 | The default return type of the task is void, so a typical usage is: 14 | 15 | ``` 16 | shared_task<> DoSomethingThatDoesntReturnAnything() { co_return; } 17 | ``` 18 | 19 | 20 | ## ```shared_task``` 21 | 22 | ```shared_task``` is an alias for ```basic_shared_task>```. 23 | 24 | ## [```shared_task_promise```](#shared_task_promise) 25 | 26 | This alias template provides a ```basic_shared_task_promise``` with the requested 27 | policies applied. 28 | 29 | ## ```basic_shared_task``` 30 | 31 | ```shared_task``` is an alias for ```basic_shared_task>```. 32 | 33 | ## ```basic_shared_task_promise``` 34 | 35 | ```basic_shared_task_promise``` implements the behavior of a shared task promise. 36 | It accepts explicitly selected policies. Typically, users extending ```shared_task``` 37 | should extend ```shared_task_promise<...>```, as its type parameters are documented. 38 | 39 | ## Extending shared tasks 40 | 41 | ### Policy selection 42 | 43 | It is possible to use non-default policies to the ```shared_task``` classes without 44 | performing explicit extension. The typical way to do so is to declare an alias 45 | template: 46 | 47 | ```c++ 48 | class other_promise{}; 49 | 50 | template 51 | using waited_on_by_other_promise_task_type 52 | = 53 | basic_shared_task< 54 | shared_task_promise< 55 | continuation_type> 56 | > 57 | >; 58 | ``` 59 | 60 | Now, ```waited_on_by_other_promise_task_type``` can be used like a ```shared_task<>```: 61 | 62 | ```c++ 63 | task<> OtherFunction(); 64 | 65 | waited_on_by_other_promise_task_type MyFunction() 66 | { 67 | co_await OtherFunction(); 68 | co_return 6; 69 | } 70 | ``` 71 | 72 | ### Extension 73 | 74 | ```shared_task<>``` is backed by the extension mechanisms in []```extensible_promise.h```](extensible_promise.md). 75 | 76 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/awaiter_list_test.cpp: -------------------------------------------------------------------------------- 1 | #include "async_test.h" 2 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 3 | import Phantom.Coroutines; 4 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 5 | import Phantom.Coroutines.awaiter_list; 6 | import Phantom.Coroutines.policies; 7 | #elif defined(PHANTOM_COROUTINES_TESTING_HEADERS) 8 | #include "Phantom.Coroutines/awaiter_list.h" 9 | #include "Phantom.Coroutines/policies.h" 10 | #endif 11 | 12 | namespace Phantom::Coroutines 13 | { 14 | 15 | namespace 16 | { 17 | struct test_awaiter : awaiter_list_entry 18 | { 19 | }; 20 | } 21 | 22 | TEST(awaiter_list_test, reverse_and_prepend_awaiter_list_handles_null_lists) 23 | { 24 | test_awaiter* destination = nullptr; 25 | test_awaiter* source = nullptr; 26 | 27 | reverse_and_prepend_awaiter_list( 28 | source, 29 | &destination); 30 | 31 | ASSERT_EQ(nullptr, destination); 32 | ASSERT_EQ(nullptr, source); 33 | } 34 | 35 | TEST(awaiter_list_test, reverse_and_prepend_awaiter_list_handles_null_source_list) 36 | { 37 | test_awaiter awaiter1; 38 | 39 | test_awaiter* destination = &awaiter1; 40 | test_awaiter* source = nullptr; 41 | 42 | reverse_and_prepend_awaiter_list( 43 | source, 44 | &destination); 45 | 46 | ASSERT_EQ(&awaiter1, destination); 47 | ASSERT_EQ(nullptr, destination->next()); 48 | ASSERT_EQ(nullptr, source); 49 | } 50 | 51 | TEST(awaiter_list_test, reverse_and_prepend_awaiter_list_handles_null_destination_list) 52 | { 53 | test_awaiter awaiter1; 54 | 55 | test_awaiter* destination = nullptr; 56 | test_awaiter* source = &awaiter1; 57 | 58 | reverse_and_prepend_awaiter_list( 59 | source, 60 | &destination); 61 | 62 | ASSERT_EQ(&awaiter1, destination); 63 | ASSERT_EQ(&awaiter1, source); 64 | ASSERT_EQ(nullptr, source->next()); 65 | } 66 | 67 | TEST(awaiter_list_test, reverse_and_prepend_awaiter_list_does_indeed_reverse_source_and_prepend_to_destination) 68 | { 69 | test_awaiter awaiter1; 70 | test_awaiter awaiter2; 71 | test_awaiter awaiter3; 72 | test_awaiter awaiter4; 73 | 74 | test_awaiter* destination = &awaiter1; 75 | awaiter1.set_next(&awaiter2); 76 | test_awaiter* source = &awaiter3; 77 | awaiter3.set_next(&awaiter4); 78 | 79 | reverse_and_prepend_awaiter_list( 80 | source, 81 | &destination); 82 | 83 | ASSERT_EQ(&awaiter4, destination); 84 | ASSERT_EQ(&awaiter3, awaiter4.next()); 85 | ASSERT_EQ(&awaiter1, awaiter3.next()); 86 | ASSERT_EQ(&awaiter2, awaiter1.next()); 87 | ASSERT_EQ(nullptr, awaiter2.next()); 88 | ASSERT_EQ(&awaiter3, source); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/detail/coroutine.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_COROUTINE_H 2 | #define PHANTOM_COROUTINES_INCLUDE_COROUTINE_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #include "Phantom.Coroutines/detail/config_macros.h" 5 | #include 6 | #include 7 | #include 8 | #endif 9 | 10 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 11 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 12 | 13 | namespace Phantom::Coroutines 14 | { 15 | namespace detail 16 | { 17 | using std::coroutine_traits; 18 | 19 | PHANTOM_COROUTINES_MODULE_EXPORT 20 | template< 21 | typename TPromise = void 22 | > 23 | using coroutine_handle = std::coroutine_handle; 24 | 25 | PHANTOM_COROUTINES_MODULE_EXPORT 26 | using suspend_always = std::suspend_always; 27 | 28 | PHANTOM_COROUTINES_MODULE_EXPORT 29 | using suspend_never = std::suspend_never; 30 | 31 | PHANTOM_COROUTINES_MODULE_EXPORT 32 | inline auto noop_coroutine() 33 | { 34 | return std::noop_coroutine(); 35 | } 36 | 37 | // Create a coroutine handle that is not null but isn't valid either, 38 | // for debugging purposes. 39 | PHANTOM_COROUTINES_MODULE_EXPORT 40 | inline auto invalid_coroutine_handle() 41 | { 42 | return coroutine_handle<>::from_address( 43 | reinterpret_cast(0x0cfcfcfcfcfcfcfcULL) 44 | ); 45 | } 46 | 47 | PHANTOM_COROUTINES_MODULE_EXPORT 48 | template< 49 | typename Promise 50 | > 51 | auto copy_and_invalidate( 52 | coroutine_handle& handle 53 | ) 54 | { 55 | auto result = handle; 56 | #ifndef NDEBUG 57 | handle = invalid_coroutine_handle(); 58 | #endif 59 | return result; 60 | } 61 | 62 | PHANTOM_COROUTINES_MODULE_EXPORT 63 | inline bool is_valid( 64 | coroutine_handle<> coroutine 65 | ) 66 | { 67 | return coroutine && coroutine != invalid_coroutine_handle(); 68 | } 69 | 70 | PHANTOM_COROUTINES_MODULE_EXPORT 71 | inline void assert_is_valid( 72 | coroutine_handle<> coroutine 73 | ) 74 | { 75 | std::ignore = coroutine; 76 | assert(is_valid(coroutine)); 77 | } 78 | } 79 | 80 | PHANTOM_COROUTINES_MODULE_EXPORT 81 | using detail::coroutine_handle; 82 | PHANTOM_COROUTINES_MODULE_EXPORT 83 | using detail::suspend_always; 84 | PHANTOM_COROUTINES_MODULE_EXPORT 85 | using detail::suspend_never; 86 | PHANTOM_COROUTINES_MODULE_EXPORT 87 | using detail::noop_coroutine; 88 | 89 | } 90 | 91 | namespace std 92 | { 93 | PHANTOM_COROUTINES_MODULE_EXPORT 94 | using std::coroutine_traits; 95 | 96 | PHANTOM_COROUTINES_MODULE_EXPORT 97 | using std::operator==; 98 | PHANTOM_COROUTINES_MODULE_EXPORT 99 | using std::operator<=>; 100 | } 101 | #endif 102 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/async_atomic_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "async_test.h" 3 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 4 | import Phantom.Coroutines; 5 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 6 | import Phantom.Coroutines.async_atomic; 7 | import Phantom.Coroutines.async_scope; 8 | import Phantom.Coroutines.suspend_result; 9 | import Phantom.Coroutines.task; 10 | #elif defined(PHANTOM_COROUTINES_TESTING_HEADERS) 11 | #include "Phantom.Coroutines/async_atomic.h" 12 | #include "Phantom.Coroutines/async_scope.h" 13 | #include "Phantom.Coroutines/suspend_result.h" 14 | #include "Phantom.Coroutines/task.h" 15 | #endif 16 | 17 | namespace Phantom::Coroutines 18 | { 19 | 20 | ASYNC_TEST(async_atomic_test, load_returns_initialized_value) 21 | { 22 | async_atomic atomic(42); 23 | EXPECT_EQ(42, atomic.load()); 24 | co_return; 25 | } 26 | 27 | ASYNC_TEST(async_atomic_test, wait_returns_immediately_if_value_is_not_matched) 28 | { 29 | async_atomic atomic(42); 30 | suspend_result suspendResult; 31 | auto result = co_await(suspendResult << atomic.wait(43)); 32 | EXPECT_EQ(false, suspendResult.did_suspend()); 33 | EXPECT_EQ(42, result); 34 | } 35 | 36 | ASYNC_TEST(async_atomic_test, wait_returns_only_when_exchange_changes_value) 37 | { 38 | async_scope<> scope; 39 | async_atomic atomic(42); 40 | bool complete = false; 41 | 42 | scope.spawn([&]() -> task<> 43 | { 44 | auto result = co_await atomic.wait(42); 45 | EXPECT_EQ(result, 50); 46 | complete = true; 47 | }); 48 | 49 | EXPECT_EQ(42, atomic.exchange(42)); 50 | EXPECT_EQ(false, complete); 51 | 52 | EXPECT_EQ(42, atomic.exchange(50)); 53 | EXPECT_EQ(true, complete); 54 | 55 | co_await scope.join(); 56 | } 57 | 58 | ASYNC_TEST(async_atomic_test, wait_returns_only_when_compare_exchange_changes_value) 59 | { 60 | async_scope<> scope; 61 | async_atomic atomic(42); 62 | bool complete = false; 63 | 64 | scope.spawn([&]() -> task<> 65 | { 66 | auto result = co_await atomic.wait(42); 67 | EXPECT_EQ(result, 50); 68 | complete = true; 69 | }); 70 | 71 | auto expected = 42; 72 | EXPECT_EQ(true, atomic.compare_exchange_strong(expected, 42)); 73 | EXPECT_EQ(42, expected); 74 | EXPECT_EQ(false, complete); 75 | 76 | expected = 50; 77 | EXPECT_EQ(false, atomic.compare_exchange_strong(expected, 42)); 78 | EXPECT_EQ(42, expected); 79 | EXPECT_EQ(false, complete); 80 | 81 | expected = 42; 82 | EXPECT_EQ(true, atomic.compare_exchange_strong(expected, 50)); 83 | EXPECT_EQ(42, expected); 84 | EXPECT_EQ(true, complete); 85 | 86 | EXPECT_EQ(50, atomic.load()); 87 | 88 | co_await scope.join(); 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/polymorphic_promise.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_POLYMORPHIC_PROMISE_H 2 | #define PHANTOM_COROUTINES_INCLUDE_POLYMORPHIC_PROMISE_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "Phantom.Coroutines/detail/config_macros.h" 10 | #include "Phantom.Coroutines/detail/coroutine.h" 11 | #include "Phantom.Coroutines/extensible_promise.h" 12 | #include "type_traits.h" 13 | #endif 14 | 15 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 16 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 17 | 18 | namespace Phantom::Coroutines 19 | { 20 | 21 | PHANTOM_COROUTINES_MODULE_EXPORT 22 | template< 23 | typename ... Bases 24 | > 25 | class polymorphic_promise 26 | : 27 | public Bases ... 28 | { 29 | public: 30 | using Bases::Bases...; 31 | 32 | virtual coroutine_handle<> handle() noexcept 33 | { 34 | return coroutine_handle::from_promise(*this); 35 | } 36 | }; 37 | 38 | PHANTOM_COROUTINES_MODULE_EXPORT 39 | template< 40 | detail::is_derived_instantiation Promise 41 | > 42 | class extensible_promise_handle< 43 | Promise 44 | > 45 | { 46 | public: 47 | using promise_type = Promise; 48 | using coroutine_handle_type = coroutine_handle<>; 49 | 50 | private: 51 | promise_type* m_promise = nullptr; 52 | 53 | public: 54 | extensible_promise_handle() = default; 55 | 56 | explicit extensible_promise_handle( 57 | promise_type& promise 58 | ) : 59 | m_promise{ std::addressof(promise) } 60 | { 61 | } 62 | 63 | protected: 64 | // Access the coroutine handle. 65 | coroutine_handle_type handle() const noexcept 66 | { 67 | return m_promise ? m_promise->handle() : coroutine_handle_type{}; 68 | } 69 | 70 | // Access the promise by reference 71 | promise_type& promise() const 72 | { 73 | return *m_promise; 74 | } 75 | 76 | void destroy() 77 | { 78 | if (m_promise) 79 | { 80 | handle().destroy(); 81 | m_promise = nullptr; 82 | } 83 | } 84 | 85 | public: 86 | explicit operator bool() const noexcept 87 | { 88 | return m_promise; 89 | } 90 | 91 | friend auto operator <=> ( 92 | const extensible_promise_handle& left, 93 | const extensible_promise_handle& right 94 | ) noexcept 95 | { 96 | return left.m_promise <=> right.m_promise; 97 | } 98 | 99 | friend bool operator == ( 100 | const extensible_promise_handle& left, 101 | const extensible_promise_handle& right 102 | ) noexcept 103 | { 104 | return left.m_promise == right.m_promise; 105 | } 106 | }; 107 | 108 | } // namespace Phantom::Coroutines 109 | 110 | #endif -------------------------------------------------------------------------------- /Documentation/task.md: -------------------------------------------------------------------------------- 1 | # [Phantom.Coroutines](../README.md) 2 | 3 | ## ```task.h``` 4 | 5 | Task.h provides an extensible ```task``` implementation for implementing lazily-started coroutines 6 | that have a single awaiter. 7 | 8 | This implementation is particularly inexpensive, and doesn't even use atomic operations. 9 | The only supported operation on a task is to move it and to co_await it once. 10 | Upon co_await'ing the task, the coroutine is started. When the coroutine completes, 11 | the awaiting coroutine is resumed via symmetric transfer, and the return value 12 | or exception is propagated to the caller. 13 | 14 | The result type of the task is an rvalue reference to the type parameter to ```task``` 15 | unless the task returns void. This is done to permit using the result of a task with no additional 16 | copying. 17 | 18 | The default return type of the task is void, so a typical usage is: 19 | 20 | ``` 21 | task<> DoSomethingThatDoesntReturnAnything() { co_return; } 22 | ``` 23 | 24 | ## ```task``` 25 | 26 | ```task``` is an alias for ```basic_task>```. 27 | 28 | ## [```task_promise```](#task_promise) 29 | 30 | This alias template provides a ```basic_task_promise``` with the requested 31 | policies applied. 32 | 33 | ## ```basic_task``` 34 | 35 | ```task``` is an alias for ```basic_task>```. 36 | 37 | ## ```basic_task_promise``` 38 | 39 | ```basic_task_promise``` implements the behavior of a lightweight task promise. 40 | It accepts explicitly selected policies. Typically, users extending ```task``` 41 | should extend ```task_promise<...>```, as its type parameters are documented. 42 | 43 | ## Extending tasks 44 | 45 | ### Policy selection 46 | 47 | It is possible to use non-default policies to the ```task``` classes without 48 | performing explicit extension. The typical way to do so is to declare an alias 49 | template: 50 | 51 | ```c++ 52 | class other_promise{}; 53 | 54 | template 55 | using waited_on_by_other_promise_task_type 56 | = 57 | basic_task< 58 | task_promise< 59 | continuation_type> 60 | > 61 | >; 62 | ``` 63 | 64 | Now, ```waited_on_by_other_promise_task_type``` can be used like a ```task<>```: 65 | 66 | ```c++ 67 | task<> OtherFunction(); 68 | 69 | waited_on_by_other_promise_task_type MyFunction() 70 | { 71 | co_await OtherFunction(); 72 | co_return 6; 73 | } 74 | ``` 75 | 76 | ### Extension 77 | 78 | ```task<>``` is backed by the extension mechanisms in []```extensible_promise.h```](extensible_promise.md). 79 | 80 | 81 | ### Implementation Notes 82 | 83 | ```task``` stores its return value in the awaiter for the task and releases 84 | the promise object as soon as the task's awaiter is resumed. Thus, promises 85 | that embody a large amount of state will be released quickly, and the lifetime 86 | of the result value does not negatively impact the amount of allocated memory. 87 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/thread_local_storage_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 5 | import Phantom.Coroutines; 6 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 7 | import Phantom.Coroutines.Test.lifetime_tracker; 8 | import Phantom.Coroutines.thread_local_storage; 9 | #elif defined(PHANTOM_COROUTINES_TESTING_HEADERS) 10 | #include "Phantom.Coroutines/thread_local_storage.h" 11 | #include "lifetime_tracker.h" 12 | #endif 13 | 14 | using namespace Phantom::Coroutines; 15 | using namespace Phantom::Coroutines::detail; 16 | 17 | TEST(thread_local_storage_test, starts_with_default_value_on_multiple_threads) 18 | { 19 | thread_local_storage threadLocalStorage; 20 | 21 | std::thread thread 22 | { 23 | [&]() 24 | { 25 | ASSERT_EQ(threadLocalStorage.get(), ""); 26 | } 27 | }; 28 | thread.join(); 29 | 30 | ASSERT_EQ(threadLocalStorage.get(), ""); 31 | } 32 | 33 | TEST(thread_local_storage_test, starts_with_constructed_value_on_multiple_threads) 34 | { 35 | thread_local_storage threadLocalStorage{ size_t(2), 'c'}; 36 | 37 | std::thread thread 38 | { 39 | [&]() 40 | { 41 | ASSERT_EQ(threadLocalStorage.get(), "cc"); 42 | } 43 | }; 44 | thread.join(); 45 | 46 | ASSERT_EQ(threadLocalStorage.get(), "cc"); 47 | } 48 | 49 | TEST(thread_local_storage_test, starts_with_initializer_value_on_multiple_threads) 50 | { 51 | size_t invocationNumber = 0; 52 | thread_local_storage threadLocalStorage{ [&]() { return std::string(++invocationNumber, 'c'); } }; 53 | 54 | std::thread thread 55 | { 56 | [&]() 57 | { 58 | ASSERT_EQ(threadLocalStorage.get(), "c"); 59 | } 60 | }; 61 | thread.join(); 62 | 63 | ASSERT_EQ(threadLocalStorage.get(), "cc"); 64 | } 65 | 66 | namespace 67 | { 68 | auto& get_from_thread_local_storage( 69 | thread_local_storage& storage) 70 | { 71 | return storage.get(); 72 | } 73 | } 74 | 75 | TEST(thread_local_storage_test, new_instances_get_new_values) 76 | { 77 | { 78 | thread_local_storage threadLocalStorage{ "hello" }; 79 | ASSERT_EQ(get_from_thread_local_storage(threadLocalStorage), "hello"); 80 | ASSERT_EQ(get_from_thread_local_storage(threadLocalStorage), "hello"); 81 | threadLocalStorage.emplace("hello2"); 82 | ASSERT_EQ(get_from_thread_local_storage(threadLocalStorage), "hello2"); 83 | ASSERT_EQ(get_from_thread_local_storage(threadLocalStorage), "hello2"); 84 | } 85 | { 86 | thread_local_storage threadLocalStorage{ "world" }; 87 | ASSERT_EQ(get_from_thread_local_storage(threadLocalStorage), "world"); 88 | ASSERT_EQ(get_from_thread_local_storage(threadLocalStorage), "world"); 89 | threadLocalStorage.emplace("world2"); 90 | ASSERT_EQ(get_from_thread_local_storage(threadLocalStorage), "world2"); 91 | ASSERT_EQ(get_from_thread_local_storage(threadLocalStorage), "world2"); 92 | } 93 | } 94 | 95 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.30) 2 | 3 | add_library( 4 | Phantom.Coroutines.Modules 5 | ) 6 | 7 | target_compile_definitions( 8 | Phantom.Coroutines.Modules 9 | PRIVATE 10 | PHANTOM_COROUTINES_COMPILING_MODULES=1 11 | PHANTOM_COROUTINES_IS_COMPILED_MODULE=1 12 | ) 13 | 14 | target_sources( 15 | Phantom.Coroutines.Modules 16 | PUBLIC 17 | FILE_SET CXX_MODULES 18 | TYPE CXX_MODULES 19 | FILES 20 | "aligned_array.ixx" 21 | "assert_same_thread.ixx" 22 | "async_atomic.ixx" 23 | "async_auto_reset_event.ixx" 24 | "async_generator.ixx" 25 | "async_latch.ixx" 26 | "async_manual_reset_event.ixx" 27 | "async_mutex.ixx" 28 | "async_promise.ixx" 29 | "async_reader_writer_lock.ixx" 30 | "async_scope.ixx" 31 | "async_sequence_barrier.ixx" 32 | "async_sharded_reader_writer_lock.ixx" 33 | "atomic_shared_ptr.ixx" 34 | "atomic_state.ixx" 35 | "await_all_await_transform.ixx" 36 | "await_none_await_transform.ixx" 37 | "awaiter_list.ixx" 38 | "awaiter_wrapper.ixx" 39 | "config_globals.ixx" 40 | "consecutive_global_id.ixx" 41 | "consecutive_thread_id.ixx" 42 | "construct_from_base.ixx" 43 | "contextual_promise.ixx" 44 | "core_task.ixx" 45 | "coroutine.ixx" 46 | "direct_initialized_optional.ixx" 47 | "double_wide_atomic.ixx" 48 | "early_termination_task.ixx" 49 | "error_condition_early_termination.ixx" 50 | "expected_early_termination.ixx" 51 | "extensible_promise.ixx" 52 | "fibonacci_heap.ixx" 53 | "final_suspend_transfer.ixx" 54 | "function_traits.ixx" 55 | "generator.ixx" 56 | "immovable_object.ixx" 57 | "inline_scheduler.ixx" 58 | "make_task.ixx" 59 | "non_copyable.ixx" 60 | "nonatomic_shared_ptr.ixx" 61 | "Phantom.Coroutines.ixx" 62 | "policies.ixx" 63 | "polymorphic_promise.ixx" 64 | "promise_allocator.ixx" 65 | "read_copy_update.ixx" 66 | "reusable_consecutive_global_id.ixx" 67 | "reusable_task.ixx" 68 | "scheduler.ixx" 69 | "scope_guard.ixx" 70 | "sequence_lock.ixx" 71 | "sharding.ixx" 72 | "shared_task.ixx" 73 | "static_thread_pool.ixx" 74 | "storage_for.ixx" 75 | "suspend_result.ixx" 76 | "sync_wait.ixx" 77 | "tagged_pointer.ixx" 78 | "task.ixx" 79 | "thread_local_context.ixx" 80 | "thread_local_contextual_promise.ixx" 81 | "thread_local_storage.ixx" 82 | "thread_pool_scheduler.ixx" 83 | "tracing.ixx" 84 | "type_traits.ixx" 85 | "value_awaiter.ixx" 86 | "variant_result_storage.ixx" 87 | ) 88 | 89 | target_link_libraries( 90 | Phantom.Coroutines.Modules 91 | PRIVATE 92 | Phantom.Coroutines 93 | ) 94 | 95 | set_target_properties( 96 | Phantom.Coroutines.Modules 97 | PROPERTIES 98 | CXX_SCAN_FOR_MODULES true) 99 | 100 | if(DISABLE_COMPILE_MODULES) 101 | set_target_properties( 102 | Phantom.Coroutines.Modules 103 | PROPERTIES 104 | EXCLUDE_FROM_ALL TRUE) 105 | endif() 106 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/detail/awaiter_wrapper_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "awaiters.h" 3 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 4 | import Phantom.Coroutines; 5 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 6 | import Phantom.Coroutines.awaiter_wrapper; 7 | #elif defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 8 | import Phantom.Coroutines; 9 | #elif defined(PHANTOM_COROUTINES_TESTING_HEADERS) 10 | #include "Phantom.Coroutines/awaiter_wrapper.h" 11 | #endif 12 | 13 | namespace Phantom::Coroutines::detail 14 | { 15 | template< 16 | typename Awaiter 17 | > struct awaiter_wrapper_test_awaiter 18 | : 19 | public awaiter_wrapper 20 | { 21 | public: 22 | using awaiter_wrapper_test_awaiter::awaiter_wrapper::awaiter_wrapper; 23 | using awaiter_wrapper_test_awaiter::awaiter_wrapper::awaiter; 24 | }; 25 | 26 | TEST(awaiter_wrapper_test, can_wrap_awaiter_value) 27 | { 28 | generic_awaiter awaiter; 29 | awaiter_wrapper_test_awaiter> wrapper{ [&]() { return awaiter; } }; 30 | ASSERT_NE(&wrapper.awaiter(), &awaiter); 31 | } 32 | 33 | TEST(awaiter_wrapper_test, can_wrap_awaiter_lvalue) 34 | { 35 | generic_awaiter awaiter; 36 | awaiter_wrapper_test_awaiter&> wrapper{ [&]() -> auto& { return awaiter; } }; 37 | ASSERT_EQ(&wrapper.awaiter(), &awaiter); 38 | } 39 | 40 | TEST(awaiter_wrapper_test, can_wrap_awaiter_rvalue) 41 | { 42 | generic_awaiter awaiter; 43 | awaiter_wrapper_test_awaiter&&> wrapper{ [&]() -> auto&& { return std::move(awaiter); } }; 44 | ASSERT_EQ(&wrapper.awaiter(), &awaiter); 45 | } 46 | 47 | TEST(awaiter_wrapper_test, can_wrap_awaiter_lvalue_from_lambda) 48 | { 49 | generic_awaiter awaiter; 50 | awaiter_wrapper_test_awaiter&> wrapper{ [&]() -> auto& { return awaiter; } }; 51 | ASSERT_EQ(&wrapper.awaiter(), &awaiter); 52 | } 53 | 54 | TEST(awaiter_wrapper_test, can_wrap_awaiter_rvalue_from_lambda) 55 | { 56 | generic_awaiter awaiter; 57 | awaiter_wrapper_test_awaiter&&> wrapper{ [&]() -> auto&& { return std::move(awaiter); } }; 58 | ASSERT_EQ(&wrapper.awaiter(), &awaiter); 59 | } 60 | 61 | TEST(awaiter_wrapper_test, can_wrap_awaitable_value) 62 | { 63 | awaiter_wrapper_test_awaiter> wrapper{ [&]() { return generic_awaitable_value{}; } }; 64 | } 65 | 66 | TEST(awaiter_wrapper_test, can_wrap_awaitable_lvalue) 67 | { 68 | generic_awaitable_lvalue awaitable; 69 | awaiter_wrapper_test_awaiter&> wrapper{ [&]() -> auto& { return awaitable; } }; 70 | ASSERT_EQ(&wrapper.awaiter(), &awaitable.m_awaiter); 71 | } 72 | 73 | TEST(awaiter_wrapper_test, can_wrap_awaitable_rvalue) 74 | { 75 | generic_awaitable_rvalue awaitable; 76 | awaiter_wrapper_test_awaiter&&> wrapper{ [&]() -> auto&& { return std::move(awaitable); } }; 77 | ASSERT_EQ(&wrapper.awaiter(), &awaitable.m_awaiter); 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/async_sharded_reader_writer_lock_test.cpp: -------------------------------------------------------------------------------- 1 | #include "async_test.h" 2 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 3 | import Phantom.Coroutines; 4 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 5 | import Phantom.Coroutines.async_sharded_reader_writer_lock; 6 | #elif defined(PHANTOM_COROUTINES_TESTING_HEADERS) 7 | #include "Phantom.Coroutines/async_sharded_reader_writer_lock.h" 8 | #endif 9 | 10 | namespace Phantom::Coroutines 11 | { 12 | 13 | ASYNC_TEST(async_sharded_reader_writer_lock_test, can_construct) 14 | { 15 | async_sharded_reader_writer_lock<> lock; 16 | co_return; 17 | } 18 | 19 | ASYNC_TEST(async_sharded_reader_writer_lock_test, read_lock_blocks_write_lock_but_not_read) 20 | { 21 | async_sharded_reader_writer_lock<> lock; 22 | auto read_lock_1 = co_await lock.reader().scoped_lock_async(); 23 | EXPECT_EQ(true, read_lock_1.owns_lock()); 24 | EXPECT_EQ(false, lock.writer().try_lock()); 25 | auto read_lock2 = lock.reader().try_scoped_lock(); 26 | EXPECT_EQ(true, read_lock2.owns_lock()); 27 | co_return; 28 | } 29 | 30 | ASYNC_TEST(async_sharded_reader_writer_lock_test, write_lock_blocks_read_lock) 31 | { 32 | async_sharded_reader_writer_lock<> lock; 33 | auto write_lock_1 = co_await lock.writer().scoped_lock_async(); 34 | auto read_lock_1 = lock.reader().try_scoped_lock(); 35 | EXPECT_EQ(false, read_lock_1.owns_lock()); 36 | co_return; 37 | } 38 | 39 | ASYNC_TEST(async_sharded_reader_writer_lock_test, write_lock_blocks_write_lock) 40 | { 41 | async_sharded_reader_writer_lock<> lock; 42 | auto write_lock_1 = co_await lock.writer().scoped_lock_async(); 43 | auto write_lock_2 = lock.writer().try_scoped_lock(); 44 | EXPECT_EQ(false, write_lock_2.owns_lock()); 45 | co_return; 46 | } 47 | 48 | ASYNC_TEST(async_sharded_reader_writer_lock_test, unlock_read_unblocks_write) 49 | { 50 | async_sharded_reader_writer_lock<> lock; 51 | auto read_lock_1 = co_await lock.reader().scoped_lock_async(); 52 | auto read_lock_2 = co_await lock.reader().scoped_lock_async(); 53 | EXPECT_EQ(false, lock.writer().try_lock()); 54 | read_lock_1.unlock(); 55 | EXPECT_EQ(false, lock.writer().try_lock()); 56 | read_lock_2.unlock(); 57 | EXPECT_EQ(true, lock.writer().try_lock()); 58 | co_return; 59 | } 60 | 61 | ASYNC_TEST(async_sharded_reader_writer_lock_test, unlock_write_unblocks_write) 62 | { 63 | async_sharded_reader_writer_lock<> lock; 64 | auto write_lock_1 = co_await lock.writer().scoped_lock_async(); 65 | EXPECT_EQ(false, lock.writer().try_lock()); 66 | write_lock_1.unlock(); 67 | EXPECT_EQ(true, lock.writer().try_lock()); 68 | co_return; 69 | } 70 | 71 | ASYNC_TEST(async_sharded_reader_writer_lock_test, unlock_write_unblocks_read) 72 | { 73 | async_sharded_reader_writer_lock<> lock; 74 | auto write_lock_1 = co_await lock.writer().scoped_lock_async(); 75 | auto read_lock_1 = lock.reader().try_scoped_lock(); 76 | EXPECT_EQ(false, read_lock_1.owns_lock()); 77 | write_lock_1.unlock(); 78 | auto read_lock_2 = lock.reader().try_scoped_lock(); 79 | EXPECT_EQ(true, read_lock_2.owns_lock()); 80 | co_return; 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/async_promise.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 2 | #include 3 | #include "detail/config_macros.h" 4 | #include "detail/immovable_object.h" 5 | #include "detail/storage_for.h" 6 | #include "awaiter_wrapper.h" 7 | #include "async_manual_reset_event.h" 8 | #include "policies.h" 9 | #include "type_traits.h" 10 | #endif 11 | 12 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 13 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 14 | 15 | namespace Phantom::Coroutines 16 | { 17 | namespace detail 18 | { 19 | 20 | PHANTOM_COROUTINES_MODULE_EXPORT 21 | template< 22 | typename Value, 23 | is_template_instantiation Event 24 | > class basic_async_promise; 25 | 26 | PHANTOM_COROUTINES_MODULE_EXPORT 27 | template< 28 | typename Value, 29 | is_async_manual_reset_event_policy... Policy 30 | > using async_promise = basic_async_promise< 31 | Value, 32 | async_manual_reset_event 33 | >; 34 | 35 | template< 36 | typename Value, 37 | is_template_instantiation Event 38 | > class basic_async_promise 39 | : 40 | storage_for 41 | { 42 | Event m_event; 43 | 44 | typedef awaiter_type> manual_reset_event_awaiter_type; 45 | 46 | struct awaiter_key {}; 47 | 48 | class awaiter 49 | : 50 | public awaiter_wrapper 51 | { 52 | basic_async_promise& m_promise; 53 | 54 | public: 55 | awaiter( 56 | basic_async_promise& promise, 57 | awaiter_key = {} 58 | ) : 59 | m_promise(promise), 60 | awaiter_wrapper{ [&]() -> decltype(auto) { return (promise.basic_async_promise::m_event); } } 61 | {} 62 | 63 | Value& await_resume() const noexcept 64 | { 65 | return m_promise.basic_async_promise::template as(); 66 | } 67 | }; 68 | 69 | public: 70 | basic_async_promise() 71 | {} 72 | 73 | template< 74 | typename... Args 75 | > basic_async_promise( 76 | Args&&... args 77 | ) 78 | { 79 | emplace( 80 | std::forward(args)... 81 | ); 82 | } 83 | 84 | ~basic_async_promise() 85 | { 86 | if (m_event.is_set()) 87 | { 88 | this->template destroy(); 89 | } 90 | } 91 | 92 | awaiter operator co_await( 93 | this auto& self 94 | ) noexcept 95 | { 96 | return awaiter{ self }; 97 | } 98 | 99 | template< 100 | typename ... Args 101 | > Value& emplace( 102 | this auto& self, 103 | Args&&... args 104 | ) 105 | { 106 | assert(!self.basic_async_promise::m_event.is_set()); 107 | 108 | auto& result = self.basic_async_promise::storage_for::template emplace( 109 | std::forward(args)... 110 | ); 111 | 112 | self.basic_async_promise::m_event.set(); 113 | 114 | return result; 115 | } 116 | }; 117 | } 118 | 119 | PHANTOM_COROUTINES_MODULE_EXPORT 120 | using detail::async_promise; 121 | 122 | } -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/detail/atomic_shared_ptr.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_ATOMIC_SHARED_PTR_H 2 | #define PHANTOM_COROUTINES_INCLUDE_ATOMIC_SHARED_PTR_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #if __cpp_lib_atomic_shared_ptr 5 | #include 6 | #else 7 | #include 8 | #include 9 | #include 10 | #endif 11 | #include "config_macros.h" 12 | #endif 13 | 14 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 15 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 16 | 17 | namespace Phantom::Coroutines::detail 18 | { 19 | 20 | #if __cpp_lib_atomic_shared_ptr 21 | PHANTOM_COROUTINES_MODULE_EXPORT 22 | template< 23 | typename T 24 | > using atomic_shared_ptr = std::atomic>; 25 | #else 26 | template< 27 | typename T 28 | > 29 | class atomic_mutex_impl 30 | { 31 | atomic_mutex_impl(const atomic_mutex_impl&) = delete; 32 | atomic_mutex_impl(atomic_mutex_impl&&) = delete; 33 | atomic_mutex_impl& operator=(const atomic_mutex_impl&) = delete; 34 | atomic_mutex_impl& operator=(atomic_mutex_impl&&) = delete; 35 | 36 | mutable std::mutex m_mutex; 37 | T m_value; 38 | 39 | public: 40 | template< 41 | typename... Args 42 | > 43 | requires std::constructible_from 44 | atomic_mutex_impl( 45 | Args&& ... args 46 | ) : 47 | m_value{ std::forward(args)... } 48 | { } 49 | 50 | T load( 51 | std::memory_order = std::memory_order_seq_cst 52 | ) const 53 | { 54 | std::unique_lock lock{ m_mutex }; 55 | return m_value; 56 | } 57 | 58 | void store( 59 | T value, 60 | std::memory_order = std::memory_order_seq_cst 61 | ) 62 | { 63 | std::unique_lock lock{ m_mutex }; 64 | m_value = value; 65 | } 66 | 67 | T exchange( 68 | T value, 69 | std::memory_order = std::memory_order_seq_cst 70 | ) 71 | { 72 | std::unique_lock lock{ m_mutex }; 73 | using std::swap; 74 | swap(m_value, value); 75 | return std::move(value); 76 | } 77 | 78 | bool compare_exchange_strong( 79 | T& expected, 80 | T desired, 81 | std::memory_order = std::memory_order_seq_cst, 82 | std::memory_order = std::memory_order_seq_cst 83 | ) 84 | { 85 | using std::swap; 86 | std::unique_lock lock{ m_mutex }; 87 | if (m_value == expected) 88 | { 89 | swap(expected, m_value); 90 | swap(m_value, desired); 91 | return true; 92 | } 93 | return false; 94 | } 95 | 96 | bool compare_exchange_weak( 97 | T& expected, 98 | T desired, 99 | std::memory_order = std::memory_order_seq_cst, 100 | std::memory_order = std::memory_order_seq_cst 101 | ) 102 | { 103 | return compare_exchange_strong(expected, desired); 104 | } 105 | }; 106 | 107 | PHANTOM_COROUTINES_MODULE_EXPORT 108 | template< 109 | typename T 110 | > 111 | class atomic_shared_ptr : 112 | public atomic_mutex_impl> 113 | { 114 | using atomic_shared_ptr::atomic_mutex_impl::atomic_mutex_impl; 115 | }; 116 | #endif 117 | 118 | } 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /Phantom.Coroutines/include/Phantom.Coroutines/reusable_task.h: -------------------------------------------------------------------------------- 1 | #ifndef PHANTOM_COROUTINES_INCLUDE_RESUSABLE_TASK_H 2 | #define PHANTOM_COROUTINES_INCLUDE_RESUSABLE_TASK_H 3 | #ifndef PHANTOM_COROUTINES_COMPILING_MODULES 4 | #include 5 | #include 6 | #include "detail/core_task.h" 7 | #include "detail/coroutine.h" 8 | #include "policies.h" 9 | #else 10 | #endif 11 | 12 | static_assert(PHANTOM_COROUTINES_IS_CONFIGURED); 13 | PHANTOM_COROUTINES_ASSERT_IS_MODULE; 14 | 15 | namespace Phantom::Coroutines::detail 16 | { 17 | 18 | PHANTOM_COROUTINES_MODULE_EXPORT 19 | template< 20 | typename Policy 21 | > concept is_reusable_task_policy = 22 | is_continuation_type_policy 23 | || is_concrete_policy 24 | || is_concrete_policy 25 | || is_concrete_policy; 26 | 27 | PHANTOM_COROUTINES_MODULE_EXPORT 28 | template< 29 | typename Result, 30 | is_reusable_task_policy ... Policies 31 | > 32 | using reusable_task_promise = core_task_promise< 33 | Result, 34 | select_continuation_type 35 | < 36 | Policies..., 37 | default_continuation_type 38 | >, 39 | core_reusable_task_promise_base 40 | >; 41 | 42 | PHANTOM_COROUTINES_MODULE_EXPORT 43 | template< 44 | typename Promise 45 | > using basic_reusable_task = core_task; 46 | 47 | PHANTOM_COROUTINES_MODULE_EXPORT 48 | template< 49 | typename Result = void, 50 | is_reusable_task_policy... Policies 51 | > using reusable_task = basic_reusable_task>; 52 | 53 | // Make a reusable_task from a value. The resulting 54 | // task will already be completed, and can be used and reused 55 | // as many times as desired, including by multiple threads. 56 | PHANTOM_COROUTINES_MODULE_EXPORT 57 | template< 58 | typename Result, 59 | typename Task = reusable_task 60 | > Task make_reusable_task_from_value( 61 | Result&& result 62 | ) 63 | { 64 | auto lambda = [&]() -> Task 65 | { 66 | co_return std::forward(result); 67 | }; 68 | auto task = lambda(); 69 | auto awaiter = task.when_ready(); 70 | awaiter.await_ready(); 71 | awaiter.await_suspend(noop_coroutine()).resume(); 72 | 73 | return std::move(task); 74 | } 75 | 76 | // Make a completed reusable_task that returns void. The resulting 77 | // task will already be completed, and can be used and reused 78 | // as many times as desired, including by multiple threads. 79 | PHANTOM_COROUTINES_MODULE_EXPORT 80 | template< 81 | typename Task = reusable_task<> 82 | > Task make_reusable_task_from_void() 83 | { 84 | auto lambda = [&]() -> Task 85 | { 86 | co_return; 87 | }; 88 | auto task = lambda(); 89 | auto awaiter = task.when_ready(); 90 | awaiter.await_ready(); 91 | awaiter.await_suspend(noop_coroutine()).resume(); 92 | 93 | return std::move(task); 94 | } 95 | 96 | } 97 | 98 | namespace Phantom::Coroutines 99 | { 100 | PHANTOM_COROUTINES_MODULE_EXPORT 101 | using detail::basic_reusable_task; 102 | PHANTOM_COROUTINES_MODULE_EXPORT 103 | using detail::reusable_task_promise; 104 | PHANTOM_COROUTINES_MODULE_EXPORT 105 | using detail::reusable_task; 106 | PHANTOM_COROUTINES_MODULE_EXPORT 107 | using detail::make_reusable_task_from_value; 108 | PHANTOM_COROUTINES_MODULE_EXPORT 109 | using detail::make_reusable_task_from_void; 110 | 111 | } 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /Documentation/awaiter_wrapper.md: -------------------------------------------------------------------------------- 1 | # [Phantom.Coroutines](../README.md) 2 | 3 | ## ```awaiter_wrapper.h``` 4 | 5 | ```awaiter_wrapper``` is a template class that enables writing wrappers 6 | around other awaitable objects. 7 | 8 | ```awaiter_wrapper``` can wrap any [```is_awaitable```](type_traits.md#is_awaitable) 9 | object. An ```awaiter_wrapper``` of [```is_awaiter```](type_traits.md#is_awaiter) 10 | directly wraps the awaiter object. An awaiter_wrapper of ```awaiter_wrapper``` of [```is_awaitable```](type_traits.md#is_awaiter) wraps the result of 11 | calling [```get_awaiter```](type_traits.md#get_awaiter). 12 | 13 | The ```awaiter_wrapper``` class provides members: 14 | 15 | ```c++ 16 | template 17 | class awaiter_wrapper 18 | { 19 | protected: 20 | // The type of the wrapper awaiter. 21 | typedef awaiter_type ...; 22 | 23 | // Get the awaiter 24 | awaiter_type& awaiter(); 25 | 26 | // Construct the wrapper, using the 27 | // awaitable object reference. 28 | explicit awaiter_wrapper( 29 | Awaiter awaiter 30 | ) noexcept 31 | requires std::is_reference_v; 32 | 33 | // Construct the wrapper, using the returned 34 | // awaitable object. 35 | template< 36 | std::invocable<> AwaitableFunc 37 | > awaiter_wrapper( 38 | AwaitableFunc&& awaitableFunc 39 | ) noexcept(...); 40 | 41 | public: 42 | // await_ready, await_suspend, and await_resume 43 | // which forward all arguments to underlying awaiter, 44 | // enabled for overload resolution only if the underlying 45 | // awaiter supports the method call, 46 | // and are noexcept if the underlying method call is noexcept. 47 | }; 48 | ``` 49 | 50 | Note that the constructor of ```awaiter_wrapper``` requires an ```std::invocable```. 51 | This is to prevent errors where non-copyable, non-movable awaiters are not supported 52 | by derived classes. The internal member variable representing the awaiter is 53 | initialized as the return value of a function, permitting the use of non-copyable 54 | non-movable awaiters. 55 | 56 | Semantics for particular specializations are as follows: 57 | 58 | * ```awaiter_wrapper``` 59 | 60 | The ```awaiter_wrapper``` stores a copy of the original awaitable 61 | object, and also the result of ```operator co_await``` on the 62 | original object. 63 | 64 | The intention is that the awaiter_wrapper ensures the lifetime 65 | of the awaitable object exceeds that of the awaiter. 66 | 67 | * ```awaiter_wrapper```, 68 | * ```awaiter_wrapper``` 69 | 70 | The ```awaiter_wrapper``` stores the result of ```operator co_await``` 71 | on the original object. The ```awaiter_wrapper``` does **NOT** store 72 | any reference or copy of the original awaitable object. 73 | 74 | The intention is that the caller already knows that the lifetime 75 | of the awaitable object will exceed that of the awaiter, so no 76 | extra storage needs to be done. Typically, this will occur 77 | if the awaitable object is an xvalue in a co_await expression 78 | or if the awaitable object is an lvalue. 79 | 80 | * ```awaiter_wrapper``` 81 | 82 | The ```awaiter_wrapper``` stores the actual awaiter object. 83 | 84 | * ```awaiter_wrapper```, 85 | * ```awaiter_wrapper``` 86 | 87 | The ```awaiter_wrapper``` stores an l-value reference to the original 88 | awaiter object. The intention is that the caller is using the awaiter_wrapper 89 | to wrap either an l-value or an xvalue in a co_await expression. 90 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Modules/Phantom.Coroutines.ixx: -------------------------------------------------------------------------------- 1 | export module Phantom.Coroutines; 2 | export import Phantom.Coroutines.aligned_array; 3 | export import Phantom.Coroutines.assert_same_thread; 4 | export import Phantom.Coroutines.async_atomic; 5 | export import Phantom.Coroutines.async_auto_reset_event; 6 | export import Phantom.Coroutines.async_generator; 7 | export import Phantom.Coroutines.async_latch; 8 | export import Phantom.Coroutines.async_manual_reset_event; 9 | export import Phantom.Coroutines.async_mutex; 10 | export import Phantom.Coroutines.async_promise; 11 | export import Phantom.Coroutines.async_reader_writer_lock; 12 | export import Phantom.Coroutines.async_scope; 13 | export import Phantom.Coroutines.async_sequence_barrier; 14 | export import Phantom.Coroutines.async_sharded_reader_writer_lock; 15 | export import Phantom.Coroutines.atomic_state; 16 | export import Phantom.Coroutines.awaiter_list; 17 | export import Phantom.Coroutines.awaiter_wrapper; 18 | export import Phantom.Coroutines.await_all_await_transform; 19 | export import Phantom.Coroutines.await_none_await_transform; 20 | export import Phantom.Coroutines.config_globals; 21 | export import Phantom.Coroutines.consecutive_global_id; 22 | export import Phantom.Coroutines.consecutive_thread_id; 23 | export import Phantom.Coroutines.contextual_promise; 24 | export import Phantom.Coroutines.core_task; 25 | export import Phantom.Coroutines.coroutine; 26 | export import Phantom.Coroutines.direct_initialized_optional; 27 | export import Phantom.Coroutines.double_wide_atomic; 28 | export import Phantom.Coroutines.early_termination_task; 29 | export import Phantom.Coroutines.error_condition_early_termination; 30 | export import Phantom.Coroutines.expected_early_termination; 31 | export import Phantom.Coroutines.extensible_promise; 32 | export import Phantom.Coroutines.fibonacci_heap; 33 | export import Phantom.Coroutines.final_suspend_transfer; 34 | export import Phantom.Coroutines.function_traits; 35 | export import Phantom.Coroutines.generator; 36 | export import Phantom.Coroutines.immovable_object; 37 | export import Phantom.Coroutines.inline_scheduler; 38 | export import Phantom.Coroutines.make_task; 39 | export import Phantom.Coroutines.nonatomic_shared_ptr; 40 | export import Phantom.Coroutines.non_copyable; 41 | export import Phantom.Coroutines.policies; 42 | export import Phantom.Coroutines.polymorphic_promise; 43 | export import Phantom.Coroutines.promise_allocator; 44 | export import Phantom.Coroutines.read_copy_update; 45 | export import Phantom.Coroutines.reusable_consecutive_global_id; 46 | export import Phantom.Coroutines.reusable_task; 47 | export import Phantom.Coroutines.scheduler; 48 | export import Phantom.Coroutines.scope_guard; 49 | export import Phantom.Coroutines.sequence_lock; 50 | export import Phantom.Coroutines.sharding; 51 | export import Phantom.Coroutines.shared_task; 52 | export import Phantom.Coroutines.static_thread_pool; 53 | export import Phantom.Coroutines.storage_for; 54 | export import Phantom.Coroutines.suspend_result; 55 | export import Phantom.Coroutines.sync_wait; 56 | export import Phantom.Coroutines.tagged_pointer; 57 | export import Phantom.Coroutines.task; 58 | export import Phantom.Coroutines.thread_local_context; 59 | export import Phantom.Coroutines.thread_local_contextual_promise; 60 | export import Phantom.Coroutines.thread_local_storage; 61 | export import Phantom.Coroutines.thread_pool_scheduler; 62 | export import Phantom.Coroutines.tracing; 63 | export import Phantom.Coroutines.type_traits; 64 | export import Phantom.Coroutines.value_awaiter; 65 | export import Phantom.Coroutines.variant_result_storage; 66 | -------------------------------------------------------------------------------- /Phantom.Coroutines.Test/reusable_consecutive_global_id_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #if defined(PHANTOM_COROUTINES_TESTING_SINGLE_MODULE) 4 | import Phantom.Coroutines; 5 | #elif defined(PHANTOM_COROUTINES_TESTING_MODULES) 6 | import Phantom.Coroutines.reusable_consecutive_global_id; 7 | #elif defined(PHANTOM_COROUTINES_TESTING_HEADERS) 8 | #include "Phantom.Coroutines/reusable_consecutive_global_id.h" 9 | #endif 10 | 11 | using namespace Phantom::Coroutines; 12 | 13 | TEST(reusable_consecutive_global_id_test, starts_at_zero_and_increments) 14 | { 15 | struct label; 16 | reusable_consecutive_global_id