├── .clang-format ├── .editorconfig ├── .gitignore ├── .gitmodules ├── .travis.yml ├── LICENSE.txt ├── README.md ├── appveyor.yml ├── args.cake ├── build-clang.sh ├── build-libcxx.sh ├── build.cake ├── cake.bat ├── config.cake ├── include └── cppcoro │ ├── async_auto_reset_event.hpp │ ├── async_generator.hpp │ ├── async_latch.hpp │ ├── async_manual_reset_event.hpp │ ├── async_mutex.hpp │ ├── async_scope.hpp │ ├── awaitable_traits.hpp │ ├── broken_promise.hpp │ ├── cancellation_registration.hpp │ ├── cancellation_source.hpp │ ├── cancellation_token.hpp │ ├── config.hpp │ ├── detail │ ├── any.hpp │ ├── get_awaiter.hpp │ ├── is_awaiter.hpp │ ├── lightweight_manual_reset_event.hpp │ ├── manual_lifetime.hpp │ ├── remove_rvalue_reference.hpp │ ├── sync_wait_task.hpp │ ├── unwrap_reference.hpp │ ├── void_value.hpp │ ├── when_all_counter.hpp │ ├── when_all_ready_awaitable.hpp │ ├── when_all_task.hpp │ ├── win32.hpp │ └── win32_overlapped_operation.hpp │ ├── file.hpp │ ├── file_buffering_mode.hpp │ ├── file_open_mode.hpp │ ├── file_read_operation.hpp │ ├── file_share_mode.hpp │ ├── file_write_operation.hpp │ ├── fmap.hpp │ ├── generator.hpp │ ├── inline_scheduler.hpp │ ├── io_service.hpp │ ├── is_awaitable.hpp │ ├── multi_producer_sequencer.hpp │ ├── net │ ├── ip_address.hpp │ ├── ip_endpoint.hpp │ ├── ipv4_address.hpp │ ├── ipv4_endpoint.hpp │ ├── ipv6_address.hpp │ ├── ipv6_endpoint.hpp │ ├── socket.hpp │ ├── socket_accept_operation.hpp │ ├── socket_connect_operation.hpp │ ├── socket_disconnect_operation.hpp │ ├── socket_recv_from_operation.hpp │ ├── socket_recv_operation.hpp │ ├── socket_send_operation.hpp │ └── socket_send_to_operation.hpp │ ├── on_scope_exit.hpp │ ├── operation_cancelled.hpp │ ├── read_only_file.hpp │ ├── read_write_file.hpp │ ├── readable_file.hpp │ ├── recursive_generator.hpp │ ├── resume_on.hpp │ ├── round_robin_scheduler.hpp │ ├── schedule_on.hpp │ ├── sequence_barrier.hpp │ ├── sequence_range.hpp │ ├── sequence_traits.hpp │ ├── shared_task.hpp │ ├── single_consumer_async_auto_reset_event.hpp │ ├── single_consumer_event.hpp │ ├── single_producer_sequencer.hpp │ ├── static_thread_pool.hpp │ ├── sync_wait.hpp │ ├── task.hpp │ ├── when_all.hpp │ ├── when_all_ready.hpp │ ├── writable_file.hpp │ └── write_only_file.hpp ├── init.sh ├── lib ├── async_auto_reset_event.cpp ├── async_manual_reset_event.cpp ├── async_mutex.cpp ├── auto_reset_event.cpp ├── auto_reset_event.hpp ├── build.cake ├── cancellation_registration.cpp ├── cancellation_source.cpp ├── cancellation_state.cpp ├── cancellation_state.hpp ├── cancellation_token.cpp ├── file.cpp ├── file_read_operation.cpp ├── file_write_operation.cpp ├── io_service.cpp ├── ip_address.cpp ├── ip_endpoint.cpp ├── ipv4_address.cpp ├── ipv4_endpoint.cpp ├── ipv6_address.cpp ├── ipv6_endpoint.cpp ├── lightweight_manual_reset_event.cpp ├── read_only_file.cpp ├── read_write_file.cpp ├── readable_file.cpp ├── socket.cpp ├── socket_accept_operation.cpp ├── socket_connect_operation.cpp ├── socket_disconnect_operation.cpp ├── socket_helpers.cpp ├── socket_helpers.hpp ├── socket_recv_from_operation.cpp ├── socket_recv_operation.cpp ├── socket_send_operation.cpp ├── socket_send_to_operation.cpp ├── spin_mutex.cpp ├── spin_mutex.hpp ├── spin_wait.cpp ├── spin_wait.hpp ├── static_thread_pool.cpp ├── use.cake ├── win32.cpp ├── writable_file.cpp └── write_only_file.cpp ├── test ├── async_auto_reset_event_tests.cpp ├── async_generator_tests.cpp ├── async_latch_tests.cpp ├── async_manual_reset_event_tests.cpp ├── async_mutex_tests.cpp ├── build.cake ├── cancellation_token_tests.cpp ├── counted.cpp ├── counted.hpp ├── doctest │ └── doctest.h ├── file_tests.cpp ├── generator_tests.cpp ├── io_service_fixture.hpp ├── io_service_tests.cpp ├── ip_address_tests.cpp ├── ip_endpoint_tests.cpp ├── ipv4_address_tests.cpp ├── ipv4_endpoint_tests.cpp ├── ipv6_address_tests.cpp ├── ipv6_endpoint_tests.cpp ├── main.cpp ├── multi_producer_sequencer_tests.cpp ├── recursive_generator_tests.cpp ├── scheduling_operator_tests.cpp ├── sequence_barrier_tests.cpp ├── shared_task_tests.cpp ├── single_consumer_async_auto_reset_event_tests.cpp ├── single_producer_sequencer_tests.cpp ├── socket_tests.cpp ├── static_thread_pool_tests.cpp ├── sync_wait_tests.cpp ├── task_tests.cpp ├── when_all_ready_tests.cpp └── when_all_tests.cpp └── tools └── cake_extensions └── testtool.py /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | --- 4 | Language: Cpp 5 | Standard: Cpp11 6 | ColumnLimit: 100 7 | TabWidth: 4 8 | IndentWidth: 4 9 | UseTab: ForContinuationAndIndentation 10 | AccessModifierOffset: -4 11 | AlignAfterOpenBracket: AlwaysBreak 12 | AlignConsecutiveAssignments: false 13 | AlignConsecutiveDeclarations: false 14 | AlignEscapedNewlines: Left 15 | AlignOperands: false 16 | AlignTrailingComments: true 17 | AllowAllParametersOfDeclarationOnNextLine: true 18 | AllowShortBlocksOnASingleLine: false 19 | AllowShortCaseLabelsOnASingleLine: false 20 | AllowShortIfStatementsOnASingleLine: false 21 | AllowShortFunctionsOnASingleLine: InlineOnly 22 | AllowShortLoopsOnASingleLine: false 23 | AlwaysBreakAfterReturnType: None 24 | AlwaysBreakTemplateDeclarations: true 25 | BinPackArguments: false 26 | BinPackParameters: false 27 | BreakBeforeBinaryOperators: None 28 | BreakBeforeBraces: Custom 29 | BraceWrapping: { 30 | AfterClass: true, 31 | AfterControlStatement: true, 32 | AfterEnum: true, 33 | AfterFunction: true, 34 | AfterNamespace: true, 35 | AfterStruct: true, 36 | AfterUnion: true, 37 | BeforeCatch: true, 38 | BeforeElse: true, 39 | IndentBraces: false, 40 | #SplitEmptyFunctionBody: false 41 | } 42 | BreakBeforeInheritanceComma: true 43 | BreakBeforeTernaryOperators: true 44 | BreakConstructorInitializers: BeforeComma 45 | ContinuationIndentWidth: 4 46 | Cpp11BracedListStyle: false 47 | IncludeCategories: 48 | - Regex: '^$' 49 | Priority: 1 50 | - Regex: '^ 9 | #include 10 | #include 11 | 12 | namespace cppcoro 13 | { 14 | class async_auto_reset_event_operation; 15 | 16 | /// An async auto-reset event is a coroutine synchronisation abstraction 17 | /// that allows one or more coroutines to wait until some thread calls 18 | /// set() on the event. 19 | /// 20 | /// When a coroutine awaits a 'set' event the event is automatically 21 | /// reset back to the 'not set' state, thus the name 'auto reset' event. 22 | class async_auto_reset_event 23 | { 24 | public: 25 | 26 | /// Initialise the event to either 'set' or 'not set' state. 27 | async_auto_reset_event(bool initiallySet = false) noexcept; 28 | 29 | ~async_auto_reset_event(); 30 | 31 | /// Wait for the event to enter the 'set' state. 32 | /// 33 | /// If the event is already 'set' then the event is set to the 'not set' 34 | /// state and the awaiting coroutine continues without suspending. 35 | /// Otherwise, the coroutine is suspended and later resumed when some 36 | /// thread calls 'set()'. 37 | /// 38 | /// Note that the coroutine may be resumed inside a call to 'set()' 39 | /// or inside another thread's call to 'operator co_await()'. 40 | async_auto_reset_event_operation operator co_await() const noexcept; 41 | 42 | /// Set the state of the event to 'set'. 43 | /// 44 | /// If there are pending coroutines awaiting the event then one 45 | /// pending coroutine is resumed and the state is immediately 46 | /// set back to the 'not set' state. 47 | /// 48 | /// This operation is a no-op if the event was already 'set'. 49 | void set() noexcept; 50 | 51 | /// Set the state of the event to 'not-set'. 52 | /// 53 | /// This is a no-op if the state was already 'not set'. 54 | void reset() noexcept; 55 | 56 | private: 57 | 58 | friend class async_auto_reset_event_operation; 59 | 60 | void resume_waiters(std::uint64_t initialState) const noexcept; 61 | 62 | // Bits 0-31 - Set count 63 | // Bits 32-63 - Waiter count 64 | mutable std::atomic m_state; 65 | 66 | mutable std::atomic m_newWaiters; 67 | 68 | mutable async_auto_reset_event_operation* m_waiters; 69 | 70 | }; 71 | 72 | class async_auto_reset_event_operation 73 | { 74 | public: 75 | 76 | async_auto_reset_event_operation() noexcept; 77 | 78 | explicit async_auto_reset_event_operation(const async_auto_reset_event& event) noexcept; 79 | 80 | async_auto_reset_event_operation(const async_auto_reset_event_operation& other) noexcept; 81 | 82 | bool await_ready() const noexcept { return m_event == nullptr; } 83 | bool await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept; 84 | void await_resume() const noexcept {} 85 | 86 | private: 87 | 88 | friend class async_auto_reset_event; 89 | 90 | const async_auto_reset_event* m_event; 91 | async_auto_reset_event_operation* m_next; 92 | std::experimental::coroutine_handle<> m_awaiter; 93 | std::atomic m_refCount; 94 | 95 | }; 96 | } 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /include/cppcoro/async_latch.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_ASYNC_LATCH_HPP_INCLUDED 6 | #define CPPCORO_ASYNC_LATCH_HPP_INCLUDED 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | namespace cppcoro 14 | { 15 | class async_latch 16 | { 17 | public: 18 | 19 | /// Construct the latch with the specified initial count. 20 | /// 21 | /// \param initialCount 22 | /// The initial count of the latch. The latch will become signalled once 23 | /// \c this->count_down() has been called \p initialCount times. 24 | /// The latch will be immediately signalled on construction if this 25 | /// parameter is zero or negative. 26 | async_latch(std::ptrdiff_t initialCount) noexcept 27 | : m_count(initialCount) 28 | , m_event(initialCount <= 0) 29 | {} 30 | 31 | /// Query if the latch has become signalled. 32 | /// 33 | /// The latch is marked as signalled once the count reaches zero. 34 | bool is_ready() const noexcept { return m_event.is_set(); } 35 | 36 | /// Decrement the count by n. 37 | /// 38 | /// Any coroutines awaiting this latch will be resumed once the count 39 | /// reaches zero. ie. when this method has been called at least 'initialCount' 40 | /// times. 41 | /// 42 | /// Any awaiting coroutines that are currently suspended waiting for the 43 | /// latch to become signalled will be resumed inside the last call to this 44 | /// method (ie. the call that decrements the count to zero). 45 | /// 46 | /// \param n 47 | /// The amount to decrement the count by. 48 | void count_down(std::ptrdiff_t n = 1) noexcept 49 | { 50 | if (m_count.fetch_sub(n, std::memory_order_acq_rel) <= n) 51 | { 52 | m_event.set(); 53 | } 54 | } 55 | 56 | /// Allows the latch to be awaited within a coroutine. 57 | /// 58 | /// If the latch is already signalled (ie. the count has been decremented 59 | /// to zero) then the awaiting coroutine will continue without suspending. 60 | /// Otherwise, the coroutine will suspend and will later be resumed inside 61 | /// a call to `count_down()`. 62 | auto operator co_await() const noexcept 63 | { 64 | return m_event.operator co_await(); 65 | } 66 | 67 | private: 68 | 69 | std::atomic m_count; 70 | async_manual_reset_event m_event; 71 | 72 | }; 73 | } 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /include/cppcoro/async_manual_reset_event.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_ASYNC_MANUAL_RESET_EVENT_HPP_INCLUDED 6 | #define CPPCORO_ASYNC_MANUAL_RESET_EVENT_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace cppcoro 13 | { 14 | class async_manual_reset_event_operation; 15 | 16 | /// An async manual-reset event is a coroutine synchronisation abstraction 17 | /// that allows one or more coroutines to wait until some thread calls 18 | /// set() on the event. 19 | /// 20 | /// When a coroutine awaits a 'set' event the coroutine continues without 21 | /// suspending. Otherwise, if it awaits a 'not set' event the coroutine is 22 | /// suspended and is later resumed inside the call to 'set()'. 23 | /// 24 | /// \seealso async_auto_reset_event 25 | class async_manual_reset_event 26 | { 27 | public: 28 | 29 | /// Initialise the event to either 'set' or 'not set' state. 30 | /// 31 | /// \param initiallySet 32 | /// If 'true' then initialises the event to the 'set' state, otherwise 33 | /// initialises the event to the 'not set' state. 34 | async_manual_reset_event(bool initiallySet = false) noexcept; 35 | 36 | ~async_manual_reset_event(); 37 | 38 | /// Wait for the event to enter the 'set' state. 39 | /// 40 | /// If the event is already 'set' then the coroutine continues without 41 | /// suspending. 42 | /// 43 | /// Otherwise, the coroutine is suspended and later resumed when some 44 | /// thread calls 'set()'. The coroutine will be resumed inside the next 45 | /// call to 'set()'. 46 | async_manual_reset_event_operation operator co_await() const noexcept; 47 | 48 | /// Query if the event is currently in the 'set' state. 49 | bool is_set() const noexcept; 50 | 51 | /// Set the state of the event to 'set'. 52 | /// 53 | /// If there are pending coroutines awaiting the event then all 54 | /// pending coroutines are resumed within this call. 55 | /// Any coroutines that subsequently await the event will continue 56 | /// without suspending. 57 | /// 58 | /// This operation is a no-op if the event was already 'set'. 59 | void set() noexcept; 60 | 61 | /// Set the state of the event to 'not-set'. 62 | /// 63 | /// Any coroutines that subsequently await the event will suspend 64 | /// until some thread calls 'set()'. 65 | /// 66 | /// This is a no-op if the state was already 'not set'. 67 | void reset() noexcept; 68 | 69 | private: 70 | 71 | friend class async_manual_reset_event_operation; 72 | 73 | // This variable has 3 states: 74 | // - this - The state is 'set'. 75 | // - nullptr - The state is 'not set' with no waiters. 76 | // - other - The state is 'not set'. 77 | // Points to an 'async_manual_reset_event_operation' that is 78 | // the head of a linked-list of waiters. 79 | mutable std::atomic m_state; 80 | 81 | }; 82 | 83 | class async_manual_reset_event_operation 84 | { 85 | public: 86 | 87 | explicit async_manual_reset_event_operation(const async_manual_reset_event& event) noexcept; 88 | 89 | bool await_ready() const noexcept; 90 | bool await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept; 91 | void await_resume() const noexcept {} 92 | 93 | private: 94 | 95 | friend class async_manual_reset_event; 96 | 97 | const async_manual_reset_event& m_event; 98 | async_manual_reset_event_operation* m_next; 99 | std::experimental::coroutine_handle<> m_awaiter; 100 | 101 | }; 102 | } 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /include/cppcoro/async_scope.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_ASYNC_SCOPE_HPP_INCLUDED 6 | #define CPPCORO_ASYNC_SCOPE_HPP_INCLUDED 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace cppcoro 16 | { 17 | class async_scope 18 | { 19 | public: 20 | 21 | async_scope() noexcept 22 | : m_count(1u) 23 | {} 24 | 25 | ~async_scope() 26 | { 27 | // scope must be co_awaited before it destructs. 28 | assert(m_continuation); 29 | } 30 | 31 | template 32 | void spawn(AWAITABLE&& awaitable) 33 | { 34 | [](async_scope* scope, std::decay_t awaitable) -> oneway_task 35 | { 36 | scope->on_work_started(); 37 | auto decrementOnCompletion = on_scope_exit([scope] { scope->on_work_finished(); }); 38 | co_await std::move(awaitable); 39 | }(this, std::forward(awaitable)); 40 | } 41 | 42 | [[nodiscard]] auto join() noexcept 43 | { 44 | class awaiter 45 | { 46 | async_scope* m_scope; 47 | public: 48 | awaiter(async_scope* scope) noexcept : m_scope(scope) {} 49 | 50 | bool await_ready() noexcept 51 | { 52 | return m_scope->m_count.load(std::memory_order_acquire) == 0; 53 | } 54 | 55 | bool await_suspend(std::experimental::coroutine_handle<> continuation) noexcept 56 | { 57 | m_scope->m_continuation = continuation; 58 | return m_scope->m_count.fetch_sub(1u, std::memory_order_acq_rel) > 1u; 59 | } 60 | 61 | void await_resume() noexcept 62 | {} 63 | }; 64 | 65 | return awaiter{ this }; 66 | } 67 | 68 | private: 69 | 70 | void on_work_finished() noexcept 71 | { 72 | if (m_count.fetch_sub(1u, std::memory_order_acq_rel) == 1) 73 | { 74 | m_continuation.resume(); 75 | } 76 | } 77 | 78 | void on_work_started() noexcept 79 | { 80 | assert(m_count.load(std::memory_order_relaxed) != 0); 81 | m_count.fetch_add(1, std::memory_order_relaxed); 82 | } 83 | 84 | struct oneway_task 85 | { 86 | struct promise_type 87 | { 88 | std::experimental::suspend_never initial_suspend() { return {}; } 89 | std::experimental::suspend_never final_suspend() { return {}; } 90 | void unhandled_exception() { std::terminate(); } 91 | oneway_task get_return_object() { return {}; } 92 | void return_void() {} 93 | }; 94 | }; 95 | 96 | std::atomic m_count; 97 | std::experimental::coroutine_handle<> m_continuation; 98 | 99 | }; 100 | } 101 | 102 | #endif 103 | -------------------------------------------------------------------------------- /include/cppcoro/awaitable_traits.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_AWAITABLE_TRAITS_HPP_INCLUDED 6 | #define CPPCORO_AWAITABLE_TRAITS_HPP_INCLUDED 7 | 8 | #include 9 | 10 | #include 11 | 12 | namespace cppcoro 13 | { 14 | template 15 | struct awaitable_traits 16 | {}; 17 | 18 | template 19 | struct awaitable_traits()))>> 20 | { 21 | using awaiter_t = decltype(cppcoro::detail::get_awaiter(std::declval())); 22 | 23 | using await_result_t = decltype(std::declval().await_resume()); 24 | }; 25 | } 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /include/cppcoro/broken_promise.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_BROKEN_PROMISE_HPP_INCLUDED 6 | #define CPPCORO_BROKEN_PROMISE_HPP_INCLUDED 7 | 8 | #include 9 | 10 | namespace cppcoro 11 | { 12 | /// \brief 13 | /// Exception thrown when you attempt to retrieve the result of 14 | /// a task that has been detached from its promise/coroutine. 15 | class broken_promise : public std::logic_error 16 | { 17 | public: 18 | broken_promise() 19 | : std::logic_error("broken promise") 20 | {} 21 | }; 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /include/cppcoro/cancellation_registration.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_CANCELLATION_REGISTRATION_HPP_INCLUDED 6 | #define CPPCORO_CANCELLATION_REGISTRATION_HPP_INCLUDED 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace cppcoro 17 | { 18 | namespace detail 19 | { 20 | class cancellation_state; 21 | struct cancellation_registration_list_chunk; 22 | struct cancellation_registration_state; 23 | } 24 | 25 | class cancellation_registration 26 | { 27 | public: 28 | 29 | /// Registers the callback to be executed when cancellation is requested 30 | /// on the cancellation_token. 31 | /// 32 | /// The callback will be executed if cancellation is requested for the 33 | /// specified cancellation token. If cancellation has already been requested 34 | /// then the callback will be executed immediately, before the constructor 35 | /// returns. If cancellation has not yet been requested then the callback 36 | /// will be executed on the first thread to request cancellation inside 37 | /// the call to cancellation_source::request_cancellation(). 38 | /// 39 | /// \param token 40 | /// The cancellation token to register the callback with. 41 | /// 42 | /// \param callback 43 | /// The callback to be executed when cancellation is requested on the 44 | /// the cancellation_token. Note that callback must not throw an exception 45 | /// if called when cancellation is requested otherwise std::terminate() 46 | /// will be called. 47 | /// 48 | /// \throw std::bad_alloc 49 | /// If registration failed due to insufficient memory available. 50 | template< 51 | typename FUNC, 52 | typename = std::enable_if_t, FUNC&&>>> 53 | cancellation_registration(cancellation_token token, FUNC&& callback) 54 | : m_callback(std::forward(callback)) 55 | { 56 | register_callback(std::move(token)); 57 | } 58 | 59 | cancellation_registration(const cancellation_registration& other) = delete; 60 | cancellation_registration& operator=(const cancellation_registration& other) = delete; 61 | 62 | /// Deregisters the callback. 63 | /// 64 | /// After the destructor returns it is guaranteed that the callback 65 | /// will not be subsequently called during a call to request_cancellation() 66 | /// on the cancellation_source. 67 | /// 68 | /// This may block if cancellation has been requested on another thread 69 | /// is it will need to wait until this callback has finished executing 70 | /// before the callback can be destroyed. 71 | ~cancellation_registration(); 72 | 73 | private: 74 | 75 | friend class detail::cancellation_state; 76 | friend struct detail::cancellation_registration_state; 77 | 78 | void register_callback(cancellation_token&& token); 79 | 80 | detail::cancellation_state* m_state; 81 | std::function m_callback; 82 | detail::cancellation_registration_list_chunk* m_chunk; 83 | std::uint32_t m_entryIndex; 84 | }; 85 | } 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /include/cppcoro/cancellation_source.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_CANCELLATION_SOURCE_HPP_INCLUDED 6 | #define CPPCORO_CANCELLATION_SOURCE_HPP_INCLUDED 7 | 8 | namespace cppcoro 9 | { 10 | class cancellation_token; 11 | 12 | namespace detail 13 | { 14 | class cancellation_state; 15 | } 16 | 17 | class cancellation_source 18 | { 19 | public: 20 | 21 | /// Construct to a new cancellation source. 22 | cancellation_source(); 23 | 24 | /// Create a new reference to the same underlying cancellation 25 | /// source as \p other. 26 | cancellation_source(const cancellation_source& other) noexcept; 27 | 28 | cancellation_source(cancellation_source&& other) noexcept; 29 | 30 | ~cancellation_source(); 31 | 32 | cancellation_source& operator=(const cancellation_source& other) noexcept; 33 | 34 | cancellation_source& operator=(cancellation_source&& other) noexcept; 35 | 36 | /// Query if this cancellation source can be cancelled. 37 | /// 38 | /// A cancellation source object will not be cancellable if it has 39 | /// previously been moved into another cancellation_source instance 40 | /// or was copied from a cancellation_source that was not cancellable. 41 | bool can_be_cancelled() const noexcept; 42 | 43 | /// Obtain a cancellation token that can be used to query if 44 | /// cancellation has been requested on this source. 45 | /// 46 | /// The cancellation token can be passed into functions that you 47 | /// may want to later be able to request cancellation. 48 | cancellation_token token() const noexcept; 49 | 50 | /// Request cancellation of operations that were passed an associated 51 | /// cancellation token. 52 | /// 53 | /// Any cancellation callback registered via a cancellation_registration 54 | /// object will be called inside this function by the first thread to 55 | /// call this method. 56 | /// 57 | /// This operation is a no-op if can_be_cancelled() returns false. 58 | void request_cancellation(); 59 | 60 | /// Query if some thread has called 'request_cancellation()' on this 61 | /// cancellation_source. 62 | bool is_cancellation_requested() const noexcept; 63 | 64 | private: 65 | 66 | detail::cancellation_state* m_state; 67 | 68 | }; 69 | } 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /include/cppcoro/cancellation_token.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_CANCELLATION_TOKEN_HPP_INCLUDED 6 | #define CPPCORO_CANCELLATION_TOKEN_HPP_INCLUDED 7 | 8 | namespace cppcoro 9 | { 10 | class cancellation_source; 11 | class cancellation_registration; 12 | 13 | namespace detail 14 | { 15 | class cancellation_state; 16 | } 17 | 18 | class cancellation_token 19 | { 20 | public: 21 | 22 | /// Construct to a cancellation token that can't be cancelled. 23 | cancellation_token() noexcept; 24 | 25 | /// Copy another cancellation token. 26 | /// 27 | /// New token will refer to the same underlying state. 28 | cancellation_token(const cancellation_token& other) noexcept; 29 | 30 | cancellation_token(cancellation_token&& other) noexcept; 31 | 32 | ~cancellation_token(); 33 | 34 | cancellation_token& operator=(const cancellation_token& other) noexcept; 35 | 36 | cancellation_token& operator=(cancellation_token&& other) noexcept; 37 | 38 | void swap(cancellation_token& other) noexcept; 39 | 40 | /// Query if it is possible that this operation will be cancelled 41 | /// or not. 42 | /// 43 | /// Cancellable operations may be able to take more efficient code-paths 44 | /// if they don't need to handle cancellation requests. 45 | bool can_be_cancelled() const noexcept; 46 | 47 | /// Query if some thread has requested cancellation on an associated 48 | /// cancellation_source object. 49 | bool is_cancellation_requested() const noexcept; 50 | 51 | /// Throws cppcoro::operation_cancelled exception if cancellation 52 | /// has been requested for the associated operation. 53 | void throw_if_cancellation_requested() const; 54 | 55 | private: 56 | 57 | friend class cancellation_source; 58 | friend class cancellation_registration; 59 | 60 | cancellation_token(detail::cancellation_state* state) noexcept; 61 | 62 | detail::cancellation_state* m_state; 63 | 64 | }; 65 | 66 | inline void swap(cancellation_token& a, cancellation_token& b) noexcept 67 | { 68 | a.swap(b); 69 | } 70 | } 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /include/cppcoro/detail/any.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_DETAIL_ANY_HPP_INCLUDED 6 | #define CPPCORO_DETAIL_ANY_HPP_INCLUDED 7 | 8 | namespace cppcoro 9 | { 10 | namespace detail 11 | { 12 | // Helper type that can be cast-to from any type. 13 | struct any 14 | { 15 | template 16 | any(T&&) noexcept 17 | {} 18 | }; 19 | } 20 | } 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /include/cppcoro/detail/get_awaiter.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_DETAIL_GET_AWAITER_HPP_INCLUDED 6 | #define CPPCORO_DETAIL_GET_AWAITER_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | 11 | namespace cppcoro 12 | { 13 | namespace detail 14 | { 15 | template 16 | auto get_awaiter_impl(T&& value, int) 17 | noexcept(noexcept(static_cast(value).operator co_await())) 18 | -> decltype(static_cast(value).operator co_await()) 19 | { 20 | return static_cast(value).operator co_await(); 21 | } 22 | 23 | template 24 | auto get_awaiter_impl(T&& value, long) 25 | noexcept(noexcept(operator co_await(static_cast(value)))) 26 | -> decltype(operator co_await(static_cast(value))) 27 | { 28 | return operator co_await(static_cast(value)); 29 | } 30 | 31 | template< 32 | typename T, 33 | std::enable_if_t::value, int> = 0> 34 | T&& get_awaiter_impl(T&& value, cppcoro::detail::any) noexcept 35 | { 36 | return static_cast(value); 37 | } 38 | 39 | template 40 | auto get_awaiter(T&& value) 41 | noexcept(noexcept(detail::get_awaiter_impl(static_cast(value), 123))) 42 | -> decltype(detail::get_awaiter_impl(static_cast(value), 123)) 43 | { 44 | return detail::get_awaiter_impl(static_cast(value), 123); 45 | } 46 | } 47 | } 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /include/cppcoro/detail/is_awaiter.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_DETAIL_IS_AWAITER_HPP_INCLUDED 6 | #define CPPCORO_DETAIL_IS_AWAITER_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | 11 | namespace cppcoro 12 | { 13 | namespace detail 14 | { 15 | template 16 | struct is_coroutine_handle 17 | : std::false_type 18 | {}; 19 | 20 | template 21 | struct is_coroutine_handle> 22 | : std::true_type 23 | {}; 24 | 25 | // NOTE: We're accepting a return value of coroutine_handle

here 26 | // which is an extension supported by Clang which is not yet part of 27 | // the C++ coroutines TS. 28 | template 29 | struct is_valid_await_suspend_return_value : std::disjunction< 30 | std::is_void, 31 | std::is_same, 32 | is_coroutine_handle> 33 | {}; 34 | 35 | template> 36 | struct is_awaiter : std::false_type {}; 37 | 38 | // NOTE: We're testing whether await_suspend() will be callable using an 39 | // arbitrary coroutine_handle here by checking if it supports being passed 40 | // a coroutine_handle. This may result in a false-result for some 41 | // types which are only awaitable within a certain context. 42 | template 43 | struct is_awaiter().await_ready()), 45 | decltype(std::declval().await_suspend(std::declval>())), 46 | decltype(std::declval().await_resume())>> : 47 | std::conjunction< 48 | std::is_constructible().await_ready())>, 49 | detail::is_valid_await_suspend_return_value< 50 | decltype(std::declval().await_suspend(std::declval>()))>> 51 | {}; 52 | } 53 | } 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /include/cppcoro/detail/lightweight_manual_reset_event.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_DETAIL_LIGHTWEIGHT_MANUAL_RESET_EVENT_HPP_INCLUDED 6 | #define CPPCORO_DETAIL_LIGHTWEIGHT_MANUAL_RESET_EVENT_HPP_INCLUDED 7 | 8 | #include 9 | 10 | #if CPPCORO_OS_LINUX || (CPPCORO_OS_WINNT >= 0x0602) 11 | # include 12 | # include 13 | #elif CPPCORO_OS_WINNT 14 | # include 15 | #else 16 | # include 17 | # include 18 | #endif 19 | 20 | namespace cppcoro 21 | { 22 | namespace detail 23 | { 24 | class lightweight_manual_reset_event 25 | { 26 | public: 27 | 28 | lightweight_manual_reset_event(bool initiallySet = false); 29 | 30 | ~lightweight_manual_reset_event(); 31 | 32 | void set() noexcept; 33 | 34 | void reset() noexcept; 35 | 36 | void wait() noexcept; 37 | 38 | private: 39 | 40 | #if CPPCORO_OS_LINUX 41 | std::atomic m_value; 42 | #elif CPPCORO_OS_WINNT >= 0x0602 43 | // Windows 8 or newer we can use WaitOnAddress() 44 | std::atomic m_value; 45 | #elif CPPCORO_OS_WINNT 46 | // Before Windows 8 we need to use a WIN32 manual reset event. 47 | cppcoro::detail::win32::handle_t m_eventHandle; 48 | #else 49 | // For other platforms that don't have a native futex 50 | // or manual reset event we can just use a std::mutex 51 | // and std::condition_variable to perform the wait. 52 | // Not so lightweight, but should be portable to all platforms. 53 | std::mutex m_mutex; 54 | std::condition_variable m_cv; 55 | bool m_isSet; 56 | #endif 57 | }; 58 | } 59 | } 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /include/cppcoro/detail/remove_rvalue_reference.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_DETAIL_REMOVE_RVALUE_REFERENCE_HPP_INCLUDED 6 | #define CPPCORO_DETAIL_REMOVE_RVALUE_REFERENCE_HPP_INCLUDED 7 | 8 | namespace cppcoro 9 | { 10 | namespace detail 11 | { 12 | template 13 | struct remove_rvalue_reference 14 | { 15 | using type = T; 16 | }; 17 | 18 | template 19 | struct remove_rvalue_reference 20 | { 21 | using type = T; 22 | }; 23 | 24 | template 25 | using remove_rvalue_reference_t = typename remove_rvalue_reference::type; 26 | } 27 | } 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /include/cppcoro/detail/unwrap_reference.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_DETAIL_UNWRAP_REFERENCE_HPP_INCLUDED 6 | #define CPPCORO_DETAIL_UNWRAP_REFERENCE_HPP_INCLUDED 7 | 8 | #include 9 | 10 | namespace cppcoro 11 | { 12 | namespace detail 13 | { 14 | template 15 | struct unwrap_reference 16 | { 17 | using type = T; 18 | }; 19 | 20 | template 21 | struct unwrap_reference> 22 | { 23 | using type = T; 24 | }; 25 | 26 | template 27 | using unwrap_reference_t = typename unwrap_reference::type; 28 | } 29 | } 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/cppcoro/detail/void_value.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_DETAIL_VOID_VALUE_HPP_INCLUDED 6 | #define CPPCORO_DETAIL_VOID_VALUE_HPP_INCLUDED 7 | 8 | namespace cppcoro 9 | { 10 | namespace detail 11 | { 12 | struct void_value {}; 13 | } 14 | } 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /include/cppcoro/detail/when_all_counter.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_DETAIL_WHEN_ALL_COUNTER_HPP_INCLUDED 6 | #define CPPCORO_DETAIL_WHEN_ALL_COUNTER_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace cppcoro 13 | { 14 | namespace detail 15 | { 16 | class when_all_counter 17 | { 18 | public: 19 | 20 | when_all_counter(std::size_t count) noexcept 21 | : m_count(count + 1) 22 | , m_awaitingCoroutine(nullptr) 23 | {} 24 | 25 | bool is_ready() const noexcept 26 | { 27 | // We consider this complete if we're asking whether it's ready 28 | // after a coroutine has already been registered. 29 | return static_cast(m_awaitingCoroutine); 30 | } 31 | 32 | bool try_await(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept 33 | { 34 | m_awaitingCoroutine = awaitingCoroutine; 35 | return m_count.fetch_sub(1, std::memory_order_acq_rel) > 1; 36 | } 37 | 38 | void notify_awaitable_completed() noexcept 39 | { 40 | if (m_count.fetch_sub(1, std::memory_order_acq_rel) == 1) 41 | { 42 | m_awaitingCoroutine.resume(); 43 | } 44 | } 45 | 46 | protected: 47 | 48 | std::atomic m_count; 49 | std::experimental::coroutine_handle<> m_awaitingCoroutine; 50 | 51 | }; 52 | } 53 | } 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /include/cppcoro/file.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_FILE_HPP_INCLUDED 6 | #define CPPCORO_FILE_HPP_INCLUDED 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #if CPPCORO_OS_WINNT 15 | # include 16 | #endif 17 | 18 | #include 19 | 20 | namespace cppcoro 21 | { 22 | class io_service; 23 | 24 | class file 25 | { 26 | public: 27 | 28 | file(file&& other) noexcept = default; 29 | 30 | virtual ~file(); 31 | 32 | /// Get the size of the file in bytes. 33 | std::uint64_t size() const; 34 | 35 | protected: 36 | 37 | #if CPPCORO_OS_WINNT 38 | file(detail::win32::safe_handle&& fileHandle) noexcept; 39 | 40 | static detail::win32::safe_handle open( 41 | detail::win32::dword_t fileAccess, 42 | io_service& ioService, 43 | const std::experimental::filesystem::path& path, 44 | file_open_mode openMode, 45 | file_share_mode shareMode, 46 | file_buffering_mode bufferingMode); 47 | 48 | detail::win32::safe_handle m_fileHandle; 49 | #endif 50 | 51 | }; 52 | } 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /include/cppcoro/file_buffering_mode.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_FILE_BUFFERING_MODE_HPP_INCLUDED 6 | #define CPPCORO_FILE_BUFFERING_MODE_HPP_INCLUDED 7 | 8 | namespace cppcoro 9 | { 10 | enum class file_buffering_mode 11 | { 12 | default_ = 0, 13 | sequential = 1, 14 | random_access = 2, 15 | unbuffered = 4, 16 | write_through = 8, 17 | temporary = 16 18 | }; 19 | 20 | constexpr file_buffering_mode operator&(file_buffering_mode a, file_buffering_mode b) 21 | { 22 | return static_cast( 23 | static_cast(a) & static_cast(b)); 24 | } 25 | 26 | constexpr file_buffering_mode operator|(file_buffering_mode a, file_buffering_mode b) 27 | { 28 | return static_cast(static_cast(a) | static_cast(b)); 29 | } 30 | } 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /include/cppcoro/file_open_mode.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_FILE_OPEN_MODE_HPP_INCLUDED 6 | #define CPPCORO_FILE_OPEN_MODE_HPP_INCLUDED 7 | 8 | namespace cppcoro 9 | { 10 | enum class file_open_mode 11 | { 12 | /// Open an existing file. 13 | /// 14 | /// If file does not already exist when opening the file then raises 15 | /// an exception. 16 | open_existing, 17 | 18 | /// Create a new file, overwriting an existing file if one exists. 19 | /// 20 | /// If a file exists at the path then it is overwitten with a new file. 21 | /// If no file exists at the path then a new one is created. 22 | create_always, 23 | 24 | /// Create a new file. 25 | /// 26 | /// If the file already exists then raises an exception. 27 | create_new, 28 | 29 | /// Open the existing file if one exists, otherwise create a new empty 30 | /// file. 31 | create_or_open, 32 | 33 | /// Open the existing file, truncating the file size to zero. 34 | /// 35 | /// If the file does not exist then raises an exception. 36 | truncate_existing 37 | }; 38 | } 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /include/cppcoro/file_read_operation.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_FILE_READ_OPERATION_HPP_INCLUDED 6 | #define CPPCORO_FILE_READ_OPERATION_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #if CPPCORO_OS_WINNT 17 | # include 18 | # include 19 | 20 | namespace cppcoro 21 | { 22 | class file_read_operation_impl 23 | { 24 | public: 25 | 26 | file_read_operation_impl( 27 | detail::win32::handle_t fileHandle, 28 | void* buffer, 29 | std::size_t byteCount) noexcept 30 | : m_fileHandle(fileHandle) 31 | , m_buffer(buffer) 32 | , m_byteCount(byteCount) 33 | {} 34 | 35 | bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; 36 | void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; 37 | 38 | private: 39 | 40 | detail::win32::handle_t m_fileHandle; 41 | void* m_buffer; 42 | std::size_t m_byteCount; 43 | 44 | }; 45 | 46 | class file_read_operation 47 | : public cppcoro::detail::win32_overlapped_operation 48 | { 49 | public: 50 | 51 | file_read_operation( 52 | detail::win32::handle_t fileHandle, 53 | std::uint64_t fileOffset, 54 | void* buffer, 55 | std::size_t byteCount) noexcept 56 | : cppcoro::detail::win32_overlapped_operation(fileOffset) 57 | , m_impl(fileHandle, buffer, byteCount) 58 | {} 59 | 60 | private: 61 | 62 | friend class cppcoro::detail::win32_overlapped_operation; 63 | 64 | bool try_start() noexcept { return m_impl.try_start(*this); } 65 | 66 | file_read_operation_impl m_impl; 67 | 68 | }; 69 | 70 | class file_read_operation_cancellable 71 | : public cppcoro::detail::win32_overlapped_operation_cancellable 72 | { 73 | public: 74 | 75 | file_read_operation_cancellable( 76 | detail::win32::handle_t fileHandle, 77 | std::uint64_t fileOffset, 78 | void* buffer, 79 | std::size_t byteCount, 80 | cancellation_token&& cancellationToken) noexcept 81 | : cppcoro::detail::win32_overlapped_operation_cancellable( 82 | fileOffset, std::move(cancellationToken)) 83 | , m_impl(fileHandle, buffer, byteCount) 84 | {} 85 | 86 | private: 87 | 88 | friend class cppcoro::detail::win32_overlapped_operation_cancellable; 89 | 90 | bool try_start() noexcept { return m_impl.try_start(*this); } 91 | void cancel() noexcept { m_impl.cancel(*this); } 92 | 93 | file_read_operation_impl m_impl; 94 | 95 | }; 96 | 97 | #endif 98 | } 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /include/cppcoro/file_share_mode.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_FILE_SHARE_MODE_HPP_INCLUDED 6 | #define CPPCORO_FILE_SHARE_MODE_HPP_INCLUDED 7 | 8 | namespace cppcoro 9 | { 10 | enum class file_share_mode 11 | { 12 | /// Don't allow any other processes to open the file concurrently. 13 | none = 0, 14 | 15 | /// Allow other processes to open the file in read-only mode 16 | /// concurrently with this process opening the file. 17 | read = 1, 18 | 19 | /// Allow other processes to open the file in write-only mode 20 | /// concurrently with this process opening the file. 21 | write = 2, 22 | 23 | /// Allow other processes to open the file in read and/or write mode 24 | /// concurrently with this process opening the file. 25 | read_write = read | write, 26 | 27 | /// Allow other processes to delete the file while this process 28 | /// has the file open. 29 | delete_ = 4 30 | }; 31 | 32 | constexpr file_share_mode operator|(file_share_mode a, file_share_mode b) 33 | { 34 | return static_cast( 35 | static_cast(a) | static_cast(b)); 36 | } 37 | 38 | constexpr file_share_mode operator&(file_share_mode a, file_share_mode b) 39 | { 40 | return static_cast( 41 | static_cast(a) & static_cast(b)); 42 | } 43 | } 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /include/cppcoro/file_write_operation.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_FILE_WRITE_OPERATION_HPP_INCLUDED 6 | #define CPPCORO_FILE_WRITE_OPERATION_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #if CPPCORO_OS_WINNT 17 | # include 18 | # include 19 | 20 | namespace cppcoro 21 | { 22 | class file_write_operation_impl 23 | { 24 | public: 25 | 26 | file_write_operation_impl( 27 | detail::win32::handle_t fileHandle, 28 | const void* buffer, 29 | std::size_t byteCount) noexcept 30 | : m_fileHandle(fileHandle) 31 | , m_buffer(buffer) 32 | , m_byteCount(byteCount) 33 | {} 34 | 35 | bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; 36 | void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; 37 | 38 | private: 39 | 40 | detail::win32::handle_t m_fileHandle; 41 | const void* m_buffer; 42 | std::size_t m_byteCount; 43 | 44 | }; 45 | 46 | class file_write_operation 47 | : public cppcoro::detail::win32_overlapped_operation 48 | { 49 | public: 50 | 51 | file_write_operation( 52 | detail::win32::handle_t fileHandle, 53 | std::uint64_t fileOffset, 54 | const void* buffer, 55 | std::size_t byteCount) noexcept 56 | : cppcoro::detail::win32_overlapped_operation(fileOffset) 57 | , m_impl(fileHandle, buffer, byteCount) 58 | {} 59 | 60 | private: 61 | 62 | friend class cppcoro::detail::win32_overlapped_operation; 63 | 64 | bool try_start() noexcept { return m_impl.try_start(*this); } 65 | 66 | file_write_operation_impl m_impl; 67 | 68 | }; 69 | 70 | class file_write_operation_cancellable 71 | : public cppcoro::detail::win32_overlapped_operation_cancellable 72 | { 73 | public: 74 | 75 | file_write_operation_cancellable( 76 | detail::win32::handle_t fileHandle, 77 | std::uint64_t fileOffset, 78 | const void* buffer, 79 | std::size_t byteCount, 80 | cancellation_token&& ct) noexcept 81 | : cppcoro::detail::win32_overlapped_operation_cancellable(fileOffset, std::move(ct)) 82 | , m_impl(fileHandle, buffer, byteCount) 83 | {} 84 | 85 | private: 86 | 87 | friend class cppcoro::detail::win32_overlapped_operation_cancellable; 88 | 89 | bool try_start() noexcept { return m_impl.try_start(*this); } 90 | void cancel() noexcept { m_impl.cancel(*this); } 91 | 92 | file_write_operation_impl m_impl; 93 | 94 | }; 95 | } 96 | 97 | #endif // CPPCORO_OS_WINNT 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /include/cppcoro/inline_scheduler.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_INLINE_SCHEDULER_HPP_INCLUDED 6 | #define CPPCORO_INLINE_SCHEDULER_HPP_INCLUDED 7 | 8 | #include 9 | 10 | namespace cppcoro 11 | { 12 | class inline_scheduler 13 | { 14 | public: 15 | 16 | inline_scheduler() noexcept = default; 17 | 18 | std::experimental::suspend_never schedule() const noexcept 19 | { 20 | return {}; 21 | } 22 | }; 23 | } 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /include/cppcoro/is_awaitable.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_IS_AWAITABLE_HPP_INCLUDED 6 | #define CPPCORO_IS_AWAITABLE_HPP_INCLUDED 7 | 8 | #include 9 | 10 | #include 11 | 12 | namespace cppcoro 13 | { 14 | template> 15 | struct is_awaitable : std::false_type {}; 16 | 17 | template 18 | struct is_awaitable()))>> 19 | : std::true_type 20 | {}; 21 | 22 | template 23 | constexpr bool is_awaitable_v = is_awaitable::value; 24 | } 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /include/cppcoro/net/ipv4_address.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_NET_IPV4_ADDRESS_HPP_INCLUDED 6 | #define CPPCORO_NET_IPV4_ADDRESS_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace cppcoro::net 14 | { 15 | class ipv4_address 16 | { 17 | using bytes_t = std::uint8_t[4]; 18 | 19 | public: 20 | 21 | constexpr ipv4_address() 22 | : m_bytes{ 0, 0, 0, 0 } 23 | {} 24 | 25 | explicit constexpr ipv4_address(std::uint32_t integer) 26 | : m_bytes{ 27 | static_cast(integer >> 24), 28 | static_cast(integer >> 16), 29 | static_cast(integer >> 8), 30 | static_cast(integer) } 31 | {} 32 | 33 | explicit constexpr ipv4_address(const std::uint8_t(&bytes)[4]) 34 | : m_bytes{ bytes[0], bytes[1], bytes[2], bytes[3] } 35 | {} 36 | 37 | explicit constexpr ipv4_address( 38 | std::uint8_t b0, 39 | std::uint8_t b1, 40 | std::uint8_t b2, 41 | std::uint8_t b3) 42 | : m_bytes{ b0, b1, b2, b3 } 43 | {} 44 | 45 | constexpr const bytes_t& bytes() const { return m_bytes; } 46 | 47 | constexpr std::uint32_t to_integer() const 48 | { 49 | return 50 | std::uint32_t(m_bytes[0]) << 24 | 51 | std::uint32_t(m_bytes[1]) << 16 | 52 | std::uint32_t(m_bytes[2]) << 8 | 53 | std::uint32_t(m_bytes[3]); 54 | } 55 | 56 | static constexpr ipv4_address loopback() 57 | { 58 | return ipv4_address(127, 0, 0, 1); 59 | } 60 | 61 | constexpr bool is_loopback() const 62 | { 63 | return m_bytes[0] == 127; 64 | } 65 | 66 | constexpr bool is_private_network() const 67 | { 68 | return m_bytes[0] == 10 || 69 | (m_bytes[0] == 172 && (m_bytes[1] & 0xF0) == 0x10) || 70 | (m_bytes[0] == 192 && m_bytes[2] == 168); 71 | } 72 | 73 | constexpr bool operator==(ipv4_address other) const 74 | { 75 | return 76 | m_bytes[0] == other.m_bytes[0] && 77 | m_bytes[1] == other.m_bytes[1] && 78 | m_bytes[2] == other.m_bytes[2] && 79 | m_bytes[3] == other.m_bytes[3]; 80 | } 81 | 82 | constexpr bool operator!=(ipv4_address other) const 83 | { 84 | return !(*this == other); 85 | } 86 | 87 | constexpr bool operator<(ipv4_address other) const 88 | { 89 | return to_integer() < other.to_integer(); 90 | } 91 | 92 | constexpr bool operator>(ipv4_address other) const 93 | { 94 | return other < *this; 95 | } 96 | 97 | constexpr bool operator<=(ipv4_address other) const 98 | { 99 | return !(other < *this); 100 | } 101 | 102 | constexpr bool operator>=(ipv4_address other) const 103 | { 104 | return !(*this < other); 105 | } 106 | 107 | /// Parse a string representation of an IP address. 108 | /// 109 | /// Parses strings of the form: 110 | /// - "num.num.num.num" where num is an integer in range [0, 255]. 111 | /// - A single integer value in range [0, 2^32). 112 | /// 113 | /// \param string 114 | /// The string to parse. 115 | /// Must be in ASCII, UTF-8 or Latin-1 encoding. 116 | /// 117 | /// \return 118 | /// The IP address if successful, otherwise std::nullopt if the string 119 | /// could not be parsed as an IPv4 address. 120 | static std::optional from_string(std::string_view string) noexcept; 121 | 122 | /// Convert the IP address to dotted decimal notation. 123 | /// 124 | /// eg. "12.67.190.23" 125 | std::string to_string() const; 126 | 127 | private: 128 | 129 | alignas(std::uint32_t) std::uint8_t m_bytes[4]; 130 | 131 | }; 132 | } 133 | 134 | #endif 135 | -------------------------------------------------------------------------------- /include/cppcoro/net/ipv4_endpoint.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_NET_IPV4_ENDPOINT_HPP_INCLUDED 6 | #define CPPCORO_NET_IPV4_ENDPOINT_HPP_INCLUDED 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace cppcoro 15 | { 16 | namespace net 17 | { 18 | class ipv4_endpoint 19 | { 20 | public: 21 | 22 | // Construct to 0.0.0.0:0 23 | ipv4_endpoint() noexcept 24 | : m_address() 25 | , m_port(0) 26 | {} 27 | 28 | explicit ipv4_endpoint(ipv4_address address, std::uint16_t port = 0) noexcept 29 | : m_address(address) 30 | , m_port(port) 31 | {} 32 | 33 | const ipv4_address& address() const noexcept { return m_address; } 34 | 35 | std::uint16_t port() const noexcept { return m_port; } 36 | 37 | std::string to_string() const; 38 | 39 | static std::optional from_string(std::string_view string) noexcept; 40 | 41 | private: 42 | 43 | ipv4_address m_address; 44 | std::uint16_t m_port; 45 | 46 | }; 47 | 48 | inline bool operator==(const ipv4_endpoint& a, const ipv4_endpoint& b) 49 | { 50 | return a.address() == b.address() && 51 | a.port() == b.port(); 52 | } 53 | 54 | inline bool operator!=(const ipv4_endpoint& a, const ipv4_endpoint& b) 55 | { 56 | return !(a == b); 57 | } 58 | 59 | inline bool operator<(const ipv4_endpoint& a, const ipv4_endpoint& b) 60 | { 61 | return a.address() < b.address() || 62 | (a.address() == b.address() && a.port() < b.port()); 63 | } 64 | 65 | inline bool operator>(const ipv4_endpoint& a, const ipv4_endpoint& b) 66 | { 67 | return b < a; 68 | } 69 | 70 | inline bool operator<=(const ipv4_endpoint& a, const ipv4_endpoint& b) 71 | { 72 | return !(b < a); 73 | } 74 | 75 | inline bool operator>=(const ipv4_endpoint& a, const ipv4_endpoint& b) 76 | { 77 | return !(a < b); 78 | } 79 | } 80 | } 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /include/cppcoro/net/ipv6_endpoint.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_NET_IPV6_ENDPOINT_HPP_INCLUDED 6 | #define CPPCORO_NET_IPV6_ENDPOINT_HPP_INCLUDED 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace cppcoro 15 | { 16 | namespace net 17 | { 18 | class ipv6_endpoint 19 | { 20 | public: 21 | 22 | // Construct to [::]:0 23 | ipv6_endpoint() noexcept 24 | : m_address() 25 | , m_port(0) 26 | {} 27 | 28 | explicit ipv6_endpoint(ipv6_address address, std::uint16_t port = 0) noexcept 29 | : m_address(address) 30 | , m_port(port) 31 | {} 32 | 33 | const ipv6_address& address() const noexcept { return m_address; } 34 | 35 | std::uint16_t port() const noexcept { return m_port; } 36 | 37 | std::string to_string() const; 38 | 39 | static std::optional from_string(std::string_view string) noexcept; 40 | 41 | private: 42 | 43 | ipv6_address m_address; 44 | std::uint16_t m_port; 45 | 46 | }; 47 | 48 | inline bool operator==(const ipv6_endpoint& a, const ipv6_endpoint& b) 49 | { 50 | return a.address() == b.address() && 51 | a.port() == b.port(); 52 | } 53 | 54 | inline bool operator!=(const ipv6_endpoint& a, const ipv6_endpoint& b) 55 | { 56 | return !(a == b); 57 | } 58 | 59 | inline bool operator<(const ipv6_endpoint& a, const ipv6_endpoint& b) 60 | { 61 | return a.address() < b.address() || 62 | (a.address() == b.address() && a.port() < b.port()); 63 | } 64 | 65 | inline bool operator>(const ipv6_endpoint& a, const ipv6_endpoint& b) 66 | { 67 | return b < a; 68 | } 69 | 70 | inline bool operator<=(const ipv6_endpoint& a, const ipv6_endpoint& b) 71 | { 72 | return !(b < a); 73 | } 74 | 75 | inline bool operator>=(const ipv6_endpoint& a, const ipv6_endpoint& b) 76 | { 77 | return !(a < b); 78 | } 79 | } 80 | } 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /include/cppcoro/net/socket_accept_operation.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_NET_SOCKET_ACCEPT_OPERATION_HPP_INCLUDED 6 | #define CPPCORO_NET_SOCKET_ACCEPT_OPERATION_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #if CPPCORO_OS_WINNT 13 | # include 14 | # include 15 | 16 | # include 17 | # include 18 | # include 19 | 20 | namespace cppcoro 21 | { 22 | namespace net 23 | { 24 | class socket; 25 | 26 | class socket_accept_operation_impl 27 | { 28 | public: 29 | 30 | socket_accept_operation_impl( 31 | socket& listeningSocket, 32 | socket& acceptingSocket) noexcept 33 | : m_listeningSocket(listeningSocket) 34 | , m_acceptingSocket(acceptingSocket) 35 | {} 36 | 37 | bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; 38 | void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; 39 | void get_result(cppcoro::detail::win32_overlapped_operation_base& operation); 40 | 41 | private: 42 | 43 | #if CPPCORO_COMPILER_MSVC 44 | # pragma warning(push) 45 | # pragma warning(disable : 4324) // Structure padded due to alignment 46 | #endif 47 | 48 | socket& m_listeningSocket; 49 | socket& m_acceptingSocket; 50 | alignas(8) std::uint8_t m_addressBuffer[88]; 51 | 52 | #if CPPCORO_COMPILER_MSVC 53 | # pragma warning(pop) 54 | #endif 55 | 56 | }; 57 | 58 | class socket_accept_operation 59 | : public cppcoro::detail::win32_overlapped_operation 60 | { 61 | public: 62 | 63 | socket_accept_operation( 64 | socket& listeningSocket, 65 | socket& acceptingSocket) noexcept 66 | : m_impl(listeningSocket, acceptingSocket) 67 | {} 68 | 69 | private: 70 | 71 | friend class cppcoro::detail::win32_overlapped_operation; 72 | 73 | bool try_start() noexcept { return m_impl.try_start(*this); } 74 | void get_result() { m_impl.get_result(*this); } 75 | 76 | socket_accept_operation_impl m_impl; 77 | 78 | }; 79 | 80 | class socket_accept_operation_cancellable 81 | : public cppcoro::detail::win32_overlapped_operation_cancellable 82 | { 83 | public: 84 | 85 | socket_accept_operation_cancellable( 86 | socket& listeningSocket, 87 | socket& acceptingSocket, 88 | cancellation_token&& ct) noexcept 89 | : cppcoro::detail::win32_overlapped_operation_cancellable(std::move(ct)) 90 | , m_impl(listeningSocket, acceptingSocket) 91 | {} 92 | 93 | private: 94 | 95 | friend class cppcoro::detail::win32_overlapped_operation_cancellable; 96 | 97 | bool try_start() noexcept { return m_impl.try_start(*this); } 98 | void cancel() noexcept { m_impl.cancel(*this); } 99 | void get_result() { m_impl.get_result(*this); } 100 | 101 | socket_accept_operation_impl m_impl; 102 | 103 | }; 104 | } 105 | } 106 | 107 | #endif // CPPCORO_OS_WINNT 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /include/cppcoro/net/socket_connect_operation.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_NET_SOCKET_CONNECT_OPERATION_HPP_INCLUDED 6 | #define CPPCORO_NET_SOCKET_CONNECT_OPERATION_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #if CPPCORO_OS_WINNT 13 | # include 14 | # include 15 | 16 | namespace cppcoro 17 | { 18 | namespace net 19 | { 20 | class socket; 21 | 22 | class socket_connect_operation_impl 23 | { 24 | public: 25 | 26 | socket_connect_operation_impl( 27 | socket& socket, 28 | const ip_endpoint& remoteEndPoint) noexcept 29 | : m_socket(socket) 30 | , m_remoteEndPoint(remoteEndPoint) 31 | {} 32 | 33 | bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; 34 | void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; 35 | void get_result(cppcoro::detail::win32_overlapped_operation_base& operation); 36 | 37 | private: 38 | 39 | socket& m_socket; 40 | ip_endpoint m_remoteEndPoint; 41 | 42 | }; 43 | 44 | class socket_connect_operation 45 | : public cppcoro::detail::win32_overlapped_operation 46 | { 47 | public: 48 | 49 | socket_connect_operation( 50 | socket& socket, 51 | const ip_endpoint& remoteEndPoint) noexcept 52 | : m_impl(socket, remoteEndPoint) 53 | {} 54 | 55 | private: 56 | 57 | friend class cppcoro::detail::win32_overlapped_operation; 58 | 59 | bool try_start() noexcept { return m_impl.try_start(*this); } 60 | decltype(auto) get_result() { return m_impl.get_result(*this); } 61 | 62 | socket_connect_operation_impl m_impl; 63 | 64 | }; 65 | 66 | class socket_connect_operation_cancellable 67 | : public cppcoro::detail::win32_overlapped_operation_cancellable 68 | { 69 | public: 70 | 71 | socket_connect_operation_cancellable( 72 | socket& socket, 73 | const ip_endpoint& remoteEndPoint, 74 | cancellation_token&& ct) noexcept 75 | : cppcoro::detail::win32_overlapped_operation_cancellable(std::move(ct)) 76 | , m_impl(socket, remoteEndPoint) 77 | {} 78 | 79 | private: 80 | 81 | friend class cppcoro::detail::win32_overlapped_operation_cancellable; 82 | 83 | bool try_start() noexcept { return m_impl.try_start(*this); } 84 | void cancel() noexcept { m_impl.cancel(*this); } 85 | void get_result() { m_impl.get_result(*this); } 86 | 87 | socket_connect_operation_impl m_impl; 88 | 89 | }; 90 | } 91 | } 92 | 93 | #endif // CPPCORO_OS_WINNT 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /include/cppcoro/net/socket_disconnect_operation.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_NET_SOCKET_DISCONNECT_OPERATION_HPP_INCLUDED 6 | #define CPPCORO_NET_SOCKET_DISCONNECT_OPERATION_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | 11 | #if CPPCORO_OS_WINNT 12 | # include 13 | # include 14 | 15 | namespace cppcoro 16 | { 17 | namespace net 18 | { 19 | class socket; 20 | 21 | class socket_disconnect_operation_impl 22 | { 23 | public: 24 | 25 | socket_disconnect_operation_impl(socket& socket) noexcept 26 | : m_socket(socket) 27 | {} 28 | 29 | bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; 30 | void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; 31 | void get_result(cppcoro::detail::win32_overlapped_operation_base& operation); 32 | 33 | private: 34 | 35 | socket& m_socket; 36 | 37 | }; 38 | 39 | class socket_disconnect_operation 40 | : public cppcoro::detail::win32_overlapped_operation 41 | { 42 | public: 43 | 44 | socket_disconnect_operation(socket& socket) noexcept 45 | : m_impl(socket) 46 | {} 47 | 48 | private: 49 | 50 | friend class cppcoro::detail::win32_overlapped_operation; 51 | 52 | bool try_start() noexcept { return m_impl.try_start(*this); } 53 | void get_result() { m_impl.get_result(*this); } 54 | 55 | socket_disconnect_operation_impl m_impl; 56 | 57 | }; 58 | 59 | class socket_disconnect_operation_cancellable 60 | : public cppcoro::detail::win32_overlapped_operation_cancellable 61 | { 62 | public: 63 | 64 | socket_disconnect_operation_cancellable(socket& socket, cancellation_token&& ct) noexcept 65 | : cppcoro::detail::win32_overlapped_operation_cancellable(std::move(ct)) 66 | , m_impl(socket) 67 | {} 68 | 69 | private: 70 | 71 | friend class cppcoro::detail::win32_overlapped_operation_cancellable; 72 | 73 | bool try_start() noexcept { return m_impl.try_start(*this); } 74 | void cancel() noexcept { m_impl.cancel(*this); } 75 | void get_result() { m_impl.get_result(*this); } 76 | 77 | socket_disconnect_operation_impl m_impl; 78 | 79 | }; 80 | } 81 | } 82 | 83 | #endif // CPPCORO_OS_WINNT 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /include/cppcoro/net/socket_recv_from_operation.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_NET_SOCKET_RECV_FROM_OPERATION_HPP_INCLUDED 6 | #define CPPCORO_NET_SOCKET_RECV_FROM_OPERATION_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #if CPPCORO_OS_WINNT 16 | # include 17 | # include 18 | 19 | namespace cppcoro::net 20 | { 21 | class socket; 22 | 23 | class socket_recv_from_operation_impl 24 | { 25 | public: 26 | 27 | socket_recv_from_operation_impl( 28 | socket& socket, 29 | void* buffer, 30 | std::size_t byteCount) noexcept 31 | : m_socket(socket) 32 | , m_buffer(buffer, byteCount) 33 | {} 34 | 35 | bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; 36 | void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; 37 | std::tuple get_result( 38 | cppcoro::detail::win32_overlapped_operation_base& operation); 39 | 40 | private: 41 | 42 | socket& m_socket; 43 | cppcoro::detail::win32::wsabuf m_buffer; 44 | 45 | static constexpr std::size_t sockaddrStorageAlignment = 4; 46 | 47 | // Storage suitable for either SOCKADDR_IN or SOCKADDR_IN6 48 | alignas(sockaddrStorageAlignment) std::uint8_t m_sourceSockaddrStorage[28]; 49 | int m_sourceSockaddrLength; 50 | 51 | }; 52 | 53 | class socket_recv_from_operation 54 | : public cppcoro::detail::win32_overlapped_operation 55 | { 56 | public: 57 | 58 | socket_recv_from_operation( 59 | socket& socket, 60 | void* buffer, 61 | std::size_t byteCount) noexcept 62 | : m_impl(socket, buffer, byteCount) 63 | {} 64 | 65 | private: 66 | 67 | friend class cppcoro::detail::win32_overlapped_operation; 68 | 69 | bool try_start() noexcept { return m_impl.try_start(*this); } 70 | decltype(auto) get_result() { return m_impl.get_result(*this); } 71 | 72 | socket_recv_from_operation_impl m_impl; 73 | 74 | }; 75 | 76 | class socket_recv_from_operation_cancellable 77 | : public cppcoro::detail::win32_overlapped_operation_cancellable 78 | { 79 | public: 80 | 81 | socket_recv_from_operation_cancellable( 82 | socket& socket, 83 | void* buffer, 84 | std::size_t byteCount, 85 | cancellation_token&& ct) noexcept 86 | : cppcoro::detail::win32_overlapped_operation_cancellable(std::move(ct)) 87 | , m_impl(socket, buffer, byteCount) 88 | {} 89 | 90 | private: 91 | 92 | friend class cppcoro::detail::win32_overlapped_operation_cancellable; 93 | 94 | bool try_start() noexcept { return m_impl.try_start(*this); } 95 | void cancel() noexcept { m_impl.cancel(*this); } 96 | decltype(auto) get_result() { return m_impl.get_result(*this); } 97 | 98 | socket_recv_from_operation_impl m_impl; 99 | 100 | }; 101 | 102 | } 103 | 104 | #endif // CPPCORO_OS_WINNT 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /include/cppcoro/net/socket_recv_operation.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_NET_SOCKET_RECV_OPERATION_HPP_INCLUDED 6 | #define CPPCORO_NET_SOCKET_RECV_OPERATION_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #if CPPCORO_OS_WINNT 14 | # include 15 | # include 16 | 17 | namespace cppcoro::net 18 | { 19 | class socket; 20 | 21 | class socket_recv_operation_impl 22 | { 23 | public: 24 | 25 | socket_recv_operation_impl( 26 | socket& s, 27 | void* buffer, 28 | std::size_t byteCount) noexcept 29 | : m_socket(s) 30 | , m_buffer(buffer, byteCount) 31 | {} 32 | 33 | bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; 34 | void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; 35 | 36 | private: 37 | 38 | socket& m_socket; 39 | cppcoro::detail::win32::wsabuf m_buffer; 40 | 41 | }; 42 | 43 | class socket_recv_operation 44 | : public cppcoro::detail::win32_overlapped_operation 45 | { 46 | public: 47 | 48 | socket_recv_operation( 49 | socket& s, 50 | void* buffer, 51 | std::size_t byteCount) noexcept 52 | : m_impl(s, buffer, byteCount) 53 | {} 54 | 55 | private: 56 | 57 | friend class cppcoro::detail::win32_overlapped_operation; 58 | 59 | bool try_start() noexcept { return m_impl.try_start(*this); } 60 | 61 | socket_recv_operation_impl m_impl; 62 | 63 | }; 64 | 65 | class socket_recv_operation_cancellable 66 | : public cppcoro::detail::win32_overlapped_operation_cancellable 67 | { 68 | public: 69 | 70 | socket_recv_operation_cancellable( 71 | socket& s, 72 | void* buffer, 73 | std::size_t byteCount, 74 | cancellation_token&& ct) noexcept 75 | : cppcoro::detail::win32_overlapped_operation_cancellable(std::move(ct)) 76 | , m_impl(s, buffer, byteCount) 77 | {} 78 | 79 | private: 80 | 81 | friend class cppcoro::detail::win32_overlapped_operation_cancellable; 82 | 83 | bool try_start() noexcept { return m_impl.try_start(*this); } 84 | void cancel() noexcept { m_impl.cancel(*this); } 85 | 86 | socket_recv_operation_impl m_impl; 87 | 88 | }; 89 | 90 | } 91 | 92 | #endif // CPPCORO_OS_WINNT 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /include/cppcoro/net/socket_send_operation.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_NET_SOCKET_SEND_OPERATION_HPP_INCLUDED 6 | #define CPPCORO_NET_SOCKET_SEND_OPERATION_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #if CPPCORO_OS_WINNT 14 | # include 15 | # include 16 | 17 | namespace cppcoro::net 18 | { 19 | class socket; 20 | 21 | class socket_send_operation_impl 22 | { 23 | public: 24 | 25 | socket_send_operation_impl( 26 | socket& s, 27 | const void* buffer, 28 | std::size_t byteCount) noexcept 29 | : m_socket(s) 30 | , m_buffer(const_cast(buffer), byteCount) 31 | {} 32 | 33 | bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; 34 | void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; 35 | 36 | private: 37 | 38 | socket& m_socket; 39 | cppcoro::detail::win32::wsabuf m_buffer; 40 | 41 | }; 42 | 43 | class socket_send_operation 44 | : public cppcoro::detail::win32_overlapped_operation 45 | { 46 | public: 47 | 48 | socket_send_operation( 49 | socket& s, 50 | const void* buffer, 51 | std::size_t byteCount) noexcept 52 | : m_impl(s, buffer, byteCount) 53 | {} 54 | 55 | private: 56 | 57 | friend class cppcoro::detail::win32_overlapped_operation; 58 | 59 | bool try_start() noexcept { return m_impl.try_start(*this); } 60 | 61 | socket_send_operation_impl m_impl; 62 | 63 | }; 64 | 65 | class socket_send_operation_cancellable 66 | : public cppcoro::detail::win32_overlapped_operation_cancellable 67 | { 68 | public: 69 | 70 | socket_send_operation_cancellable( 71 | socket& s, 72 | const void* buffer, 73 | std::size_t byteCount, 74 | cancellation_token&& ct) noexcept 75 | : cppcoro::detail::win32_overlapped_operation_cancellable(std::move(ct)) 76 | , m_impl(s, buffer, byteCount) 77 | {} 78 | 79 | private: 80 | 81 | friend class cppcoro::detail::win32_overlapped_operation_cancellable; 82 | 83 | bool try_start() noexcept { return m_impl.try_start(*this); } 84 | void cancel() noexcept { return m_impl.cancel(*this); } 85 | 86 | socket_send_operation_impl m_impl; 87 | 88 | }; 89 | 90 | } 91 | 92 | #endif // CPPCORO_OS_WINNT 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /include/cppcoro/net/socket_send_to_operation.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_NET_SOCKET_SEND_TO_OPERATION_HPP_INCLUDED 6 | #define CPPCORO_NET_SOCKET_SEND_TO_OPERATION_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #if CPPCORO_OS_WINNT 15 | # include 16 | # include 17 | 18 | namespace cppcoro::net 19 | { 20 | class socket; 21 | 22 | class socket_send_to_operation_impl 23 | { 24 | public: 25 | 26 | socket_send_to_operation_impl( 27 | socket& s, 28 | const ip_endpoint& destination, 29 | const void* buffer, 30 | std::size_t byteCount) noexcept 31 | : m_socket(s) 32 | , m_destination(destination) 33 | , m_buffer(const_cast(buffer), byteCount) 34 | {} 35 | 36 | bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; 37 | void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; 38 | 39 | private: 40 | 41 | socket& m_socket; 42 | ip_endpoint m_destination; 43 | cppcoro::detail::win32::wsabuf m_buffer; 44 | 45 | }; 46 | 47 | class socket_send_to_operation 48 | : public cppcoro::detail::win32_overlapped_operation 49 | { 50 | public: 51 | 52 | socket_send_to_operation( 53 | socket& s, 54 | const ip_endpoint& destination, 55 | const void* buffer, 56 | std::size_t byteCount) noexcept 57 | : m_impl(s, destination, buffer, byteCount) 58 | {} 59 | 60 | private: 61 | 62 | friend class cppcoro::detail::win32_overlapped_operation; 63 | 64 | bool try_start() noexcept { return m_impl.try_start(*this); } 65 | 66 | socket_send_to_operation_impl m_impl; 67 | 68 | }; 69 | 70 | class socket_send_to_operation_cancellable 71 | : public cppcoro::detail::win32_overlapped_operation_cancellable 72 | { 73 | public: 74 | 75 | socket_send_to_operation_cancellable( 76 | socket& s, 77 | const ip_endpoint& destination, 78 | const void* buffer, 79 | std::size_t byteCount, 80 | cancellation_token&& ct) noexcept 81 | : cppcoro::detail::win32_overlapped_operation_cancellable(std::move(ct)) 82 | , m_impl(s, destination, buffer, byteCount) 83 | {} 84 | 85 | private: 86 | 87 | friend class cppcoro::detail::win32_overlapped_operation_cancellable; 88 | 89 | bool try_start() noexcept { return m_impl.try_start(*this); } 90 | void cancel() noexcept { return m_impl.cancel(*this); } 91 | 92 | socket_send_to_operation_impl m_impl; 93 | 94 | }; 95 | 96 | } 97 | 98 | #endif // CPPCORO_OS_WINNT 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /include/cppcoro/operation_cancelled.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_OPERATION_CANCELLED_HPP_INCLUDED 6 | #define CPPCORO_OPERATION_CANCELLED_HPP_INCLUDED 7 | 8 | #include 9 | 10 | namespace cppcoro 11 | { 12 | class operation_cancelled : public std::exception 13 | { 14 | public: 15 | 16 | operation_cancelled() noexcept 17 | : std::exception() 18 | {} 19 | 20 | const char* what() const noexcept override { return "operation cancelled"; } 21 | }; 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /include/cppcoro/read_only_file.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_READ_ONLY_FILE_HPP_INCLUDED 6 | #define CPPCORO_READ_ONLY_FILE_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | namespace cppcoro 15 | { 16 | class read_only_file : public readable_file 17 | { 18 | public: 19 | 20 | /// Open a file for read-only access. 21 | /// 22 | /// \param ioContext 23 | /// The I/O context to use when dispatching I/O completion events. 24 | /// When asynchronous read operations on this file complete the 25 | /// completion events will be dispatched to an I/O thread associated 26 | /// with the I/O context. 27 | /// 28 | /// \param path 29 | /// Path of the file to open. 30 | /// 31 | /// \param shareMode 32 | /// Specifies the access to be allowed on the file concurrently with this file access. 33 | /// 34 | /// \param bufferingMode 35 | /// Specifies the modes/hints to provide to the OS that affects the behaviour 36 | /// of its file buffering. 37 | /// 38 | /// \return 39 | /// An object that can be used to read from the file. 40 | /// 41 | /// \throw std::system_error 42 | /// If the file could not be opened for read. 43 | [[nodiscard]] 44 | static read_only_file open( 45 | io_service& ioService, 46 | const std::experimental::filesystem::path& path, 47 | file_share_mode shareMode = file_share_mode::read, 48 | file_buffering_mode bufferingMode = file_buffering_mode::default_); 49 | 50 | protected: 51 | 52 | #if CPPCORO_OS_WINNT 53 | read_only_file(detail::win32::safe_handle&& fileHandle) noexcept; 54 | #endif 55 | 56 | }; 57 | } 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /include/cppcoro/read_write_file.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_READ_WRITE_FILE_HPP_INCLUDED 6 | #define CPPCORO_READ_WRITE_FILE_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | namespace cppcoro 17 | { 18 | class read_write_file : public readable_file, public writable_file 19 | { 20 | public: 21 | 22 | /// Open a file for read-write access. 23 | /// 24 | /// \param ioContext 25 | /// The I/O context to use when dispatching I/O completion events. 26 | /// When asynchronous write operations on this file complete the 27 | /// completion events will be dispatched to an I/O thread associated 28 | /// with the I/O context. 29 | /// 30 | /// \param pathMode 31 | /// Path of the file to open. 32 | /// 33 | /// \param openMode 34 | /// Specifies how the file should be opened and how to handle cases 35 | /// when the file exists or doesn't exist. 36 | /// 37 | /// \param shareMode 38 | /// Specifies the access to be allowed on the file concurrently with this file access. 39 | /// 40 | /// \param bufferingMode 41 | /// Specifies the modes/hints to provide to the OS that affects the behaviour 42 | /// of its file buffering. 43 | /// 44 | /// \return 45 | /// An object that can be used to write to the file. 46 | /// 47 | /// \throw std::system_error 48 | /// If the file could not be opened for write. 49 | [[nodiscard]] 50 | static read_write_file open( 51 | io_service& ioService, 52 | const std::experimental::filesystem::path& path, 53 | file_open_mode openMode = file_open_mode::create_or_open, 54 | file_share_mode shareMode = file_share_mode::none, 55 | file_buffering_mode bufferingMode = file_buffering_mode::default_); 56 | 57 | protected: 58 | 59 | #if CPPCORO_OS_WINNT 60 | read_write_file(detail::win32::safe_handle&& fileHandle) noexcept; 61 | #endif 62 | 63 | }; 64 | } 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /include/cppcoro/readable_file.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_READABLE_FILE_HPP_INCLUDED 6 | #define CPPCORO_READABLE_FILE_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace cppcoro 13 | { 14 | class readable_file : virtual public file 15 | { 16 | public: 17 | 18 | /// Read some data from the file. 19 | /// 20 | /// Reads \a byteCount bytes from the file starting at \a offset 21 | /// into the specified \a buffer. 22 | /// 23 | /// \param offset 24 | /// The offset within the file to start reading from. 25 | /// If the file has been opened using file_buffering_mode::unbuffered 26 | /// then the offset must be a multiple of the file-system's sector size. 27 | /// 28 | /// \param buffer 29 | /// The buffer to read the file contents into. 30 | /// If the file has been opened using file_buffering_mode::unbuffered 31 | /// then the address of the start of the buffer must be a multiple of 32 | /// the file-system's sector size. 33 | /// 34 | /// \param byteCount 35 | /// The number of bytes to read from the file. 36 | /// If the file has been opeend using file_buffering_mode::unbuffered 37 | /// then the byteCount must be a multiple of the file-system's sector size. 38 | /// 39 | /// \param ct 40 | /// An optional cancellation_token that can be used to cancel the 41 | /// read operation before it completes. 42 | /// 43 | /// \return 44 | /// An object that represents the read-operation. 45 | /// This object must be co_await'ed to start the read operation. 46 | [[nodiscard]] 47 | file_read_operation read( 48 | std::uint64_t offset, 49 | void* buffer, 50 | std::size_t byteCount) const noexcept; 51 | [[nodiscard]] 52 | file_read_operation_cancellable read( 53 | std::uint64_t offset, 54 | void* buffer, 55 | std::size_t byteCount, 56 | cancellation_token ct) const noexcept; 57 | 58 | protected: 59 | 60 | using file::file; 61 | 62 | }; 63 | } 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /include/cppcoro/round_robin_scheduler.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_ROUND_ROBIN_SCHEDULER_HPP_INCLUDED 6 | #define CPPCORO_ROUND_ROBIN_SCHEDULER_HPP_INCLUDED 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace cppcoro 17 | { 18 | #if CPPCORO_COMPILER_SUPPORTS_SYMMETRIC_TRANSFER 19 | /// This is a scheduler class that schedules coroutines in a round-robin 20 | /// fashion once N coroutines have been scheduled to it. 21 | /// 22 | /// Only supports access from a single thread at a time so 23 | /// 24 | /// This implementation was inspired by Gor Nishanov's CppCon 2018 talk 25 | /// about nano-coroutines. 26 | /// 27 | /// The implementation relies on symmetric transfer and noop_coroutine() 28 | /// and so only works with a relatively recent version of Clang and does 29 | /// not yet work with MSVC. 30 | template 31 | class round_robin_scheduler 32 | { 33 | static_assert( 34 | N >= 2, 35 | "Round robin scheduler must be configured to support at least two coroutines"); 36 | 37 | class schedule_operation 38 | { 39 | public: 40 | explicit schedule_operation(round_robin_scheduler& s) noexcept : m_scheduler(s) {} 41 | 42 | bool await_ready() noexcept 43 | { 44 | return false; 45 | } 46 | 47 | std::experimental::coroutine_handle<> await_suspend( 48 | std::experimental::coroutine_handle<> awaitingCoroutine) noexcept 49 | { 50 | return m_scheduler.exchange_next(awaitingCoroutine); 51 | } 52 | 53 | void await_resume() noexcept {} 54 | 55 | private: 56 | round_robin_scheduler& m_scheduler; 57 | }; 58 | 59 | friend class schedule_operation; 60 | 61 | public: 62 | round_robin_scheduler() noexcept 63 | : m_index(0) 64 | , m_noop(std::experimental::noop_coroutine()) 65 | { 66 | for (size_t i = 0; i < N - 1; ++i) 67 | { 68 | m_coroutines[i] = m_noop(); 69 | } 70 | } 71 | 72 | ~round_robin_scheduler() 73 | { 74 | // All tasks should have been joined before calling destructor. 75 | assert(std::all_of( 76 | m_coroutines.begin(), 77 | m_coroutines.end(), 78 | [&](auto h) { return h == m_noop; })); 79 | } 80 | 81 | schedule_operation schedule() noexcept 82 | { 83 | return schedule_operation{ *this }; 84 | } 85 | 86 | /// Resume any queued coroutines until there are no more coroutines. 87 | void drain() noexcept 88 | { 89 | size_t countRemaining = N - 1; 90 | do 91 | { 92 | auto nextToResume = exchange_next(m_noop); 93 | if (nextToResume != m_noop) 94 | { 95 | nextToResume.resume(); 96 | countRemaining = N - 1; 97 | } 98 | else 99 | { 100 | --countRemaining; 101 | } 102 | } while (countRemaining > 0); 103 | } 104 | 105 | private: 106 | 107 | std::experimental::coroutine_handle exchange_next( 108 | std::experimental::coroutine_handle<> coroutine) noexcept 109 | { 110 | auto coroutineToResume = std::exchange( 111 | m_scheduler.m_coroutines[m_scheduler.m_index], 112 | awaitingCoroutine); 113 | m_scheduler.m_index = m_scheduler.m_index < (N - 2) ? m_scheduler.m_index + 1 : 0; 114 | return coroutineToResume; 115 | } 116 | 117 | size_t m_index; 118 | const std::experimental::coroutine_handle<> m_noop; 119 | std::array, N - 1> m_coroutines; 120 | }; 121 | #endif 122 | } 123 | 124 | #endif 125 | -------------------------------------------------------------------------------- /include/cppcoro/schedule_on.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_SCHEDULE_ON_HPP_INCLUDED 6 | #define CPPCORO_SCHEDULE_ON_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | namespace cppcoro 16 | { 17 | template 18 | struct schedule_on_transform 19 | { 20 | explicit schedule_on_transform(SCHEDULER& scheduler) noexcept 21 | : scheduler(scheduler) 22 | {} 23 | 24 | SCHEDULER& scheduler; 25 | }; 26 | 27 | template 28 | schedule_on_transform schedule_on(SCHEDULER& scheduler) 29 | { 30 | return schedule_on_transform{ scheduler }; 31 | } 32 | 33 | template 34 | decltype(auto) operator|(T&& value, schedule_on_transform transform) 35 | { 36 | return schedule_on(transform.scheduler, std::forward(value)); 37 | } 38 | 39 | template 40 | auto schedule_on(SCHEDULER& scheduler, AWAITABLE awaitable) 41 | -> task::await_result_t>> 42 | { 43 | co_await scheduler.schedule(); 44 | co_return co_await std::move(awaitable); 45 | } 46 | 47 | template 48 | async_generator schedule_on(SCHEDULER& scheduler, async_generator source) 49 | { 50 | // Transfer exection to the scheduler before the implicit calls to 51 | // 'co_await begin()' or subsequent calls to `co_await iterator::operator++()` 52 | // below. This ensures that all calls to the generator's coroutine_handle<>::resume() 53 | // are executed on the execution context of the scheduler. 54 | co_await scheduler.schedule(); 55 | 56 | const auto itEnd = source.end(); 57 | auto it = co_await source.begin(); 58 | while (it != itEnd) 59 | { 60 | co_yield *it; 61 | 62 | co_await scheduler.schedule(); 63 | 64 | (void)co_await ++it; 65 | } 66 | } 67 | } 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /include/cppcoro/sequence_range.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_SEQUENCE_RANGE_HPP_INCLUDED 6 | #define CPPCORO_SEQUENCE_RANGE_HPP_INCLUDED 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | namespace cppcoro 14 | { 15 | template> 16 | class sequence_range 17 | { 18 | public: 19 | 20 | using value_type = SEQUENCE; 21 | using difference_type = typename TRAITS::difference_type; 22 | using size_type = typename TRAITS::size_type; 23 | 24 | class const_iterator 25 | { 26 | public: 27 | 28 | using iterator_category = std::random_access_iterator_tag; 29 | using value_type = SEQUENCE; 30 | using difference_type = typename TRAITS::difference_type; 31 | using reference = const SEQUENCE&; 32 | using pointer = const SEQUENCE*; 33 | 34 | explicit constexpr const_iterator(SEQUENCE value) noexcept : m_value(value) {} 35 | 36 | const SEQUENCE& operator*() const noexcept { return m_value; } 37 | const SEQUENCE* operator->() const noexcept { return std::addressof(m_value); } 38 | 39 | const_iterator& operator++() noexcept { ++m_value; return *this; } 40 | const_iterator& operator--() noexcept { --m_value; return *this; } 41 | 42 | const_iterator operator++(int) noexcept { return const_iterator(m_value++); } 43 | const_iterator operator--(int) noexcept { return const_iterator(m_value--); } 44 | 45 | constexpr difference_type operator-(const_iterator other) const noexcept { return TRAITS::difference(m_value, other.m_value); } 46 | constexpr const_iterator operator-(difference_type delta) const noexcept { return const_iterator{ static_cast(m_value - delta) }; } 47 | constexpr const_iterator operator+(difference_type delta) const noexcept { return const_iterator{ static_cast(m_value + delta) }; } 48 | 49 | constexpr bool operator==(const_iterator other) const noexcept { return m_value == other.m_value; } 50 | constexpr bool operator!=(const_iterator other) const noexcept { return m_value != other.m_value; } 51 | 52 | private: 53 | 54 | SEQUENCE m_value; 55 | 56 | }; 57 | 58 | constexpr sequence_range() noexcept 59 | : m_begin() 60 | , m_end() 61 | {} 62 | 63 | constexpr sequence_range(SEQUENCE begin, SEQUENCE end) noexcept 64 | : m_begin(begin) 65 | , m_end(end) 66 | {} 67 | 68 | constexpr const_iterator begin() const noexcept { return const_iterator(m_begin); } 69 | constexpr const_iterator end() const noexcept { return const_iterator(m_end); } 70 | 71 | constexpr SEQUENCE front() const noexcept { return m_begin; } 72 | constexpr SEQUENCE back() const noexcept { return m_end - 1; } 73 | 74 | constexpr size_type size() const noexcept 75 | { 76 | return static_cast(TRAITS::difference(m_end, m_begin)); 77 | } 78 | 79 | constexpr bool empty() const noexcept 80 | { 81 | return m_begin == m_end; 82 | } 83 | 84 | constexpr SEQUENCE operator[](size_type index) const noexcept 85 | { 86 | return m_begin + index; 87 | } 88 | 89 | constexpr sequence_range first(size_type count) const noexcept 90 | { 91 | return sequence_range{ m_begin, static_cast(m_begin + std::min(size(), count)) }; 92 | } 93 | 94 | constexpr sequence_range skip(size_type count) const noexcept 95 | { 96 | return sequence_range{ m_begin + std::min(size(), count), m_end }; 97 | } 98 | 99 | private: 100 | 101 | SEQUENCE m_begin; 102 | SEQUENCE m_end; 103 | 104 | }; 105 | } 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /include/cppcoro/sequence_traits.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_SEQUENCE_TRAITS_HPP_INCLUDED 6 | #define CPPCORO_SEQUENCE_TRAITS_HPP_INCLUDED 7 | 8 | #include 9 | 10 | namespace cppcoro 11 | { 12 | template 13 | struct sequence_traits 14 | { 15 | using value_type = SEQUENCE; 16 | using difference_type = std::make_signed_t; 17 | using size_type = std::make_unsigned_t; 18 | 19 | static constexpr value_type initial_sequence = static_cast(-1); 20 | 21 | static constexpr difference_type difference(value_type a, value_type b) 22 | { 23 | return static_cast(a - b); 24 | } 25 | 26 | static constexpr bool precedes(value_type a, value_type b) 27 | { 28 | return difference(a, b) < 0; 29 | } 30 | }; 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /include/cppcoro/single_consumer_async_auto_reset_event.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_SINGLE_CONSUMER_ASYNC_AUTO_RESET_EVENT_HPP_INCLUDED 6 | #define CPPCORO_SINGLE_CONSUMER_ASYNC_AUTO_RESET_EVENT_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace cppcoro 14 | { 15 | class single_consumer_async_auto_reset_event 16 | { 17 | public: 18 | 19 | single_consumer_async_auto_reset_event(bool initiallySet = false) noexcept 20 | : m_state(initiallySet ? this : nullptr) 21 | {} 22 | 23 | void set() noexcept 24 | { 25 | void* oldValue = m_state.exchange(this, std::memory_order_release); 26 | if (oldValue != nullptr && oldValue != this) 27 | { 28 | // There was a waiting coroutine that we now need to resume. 29 | auto handle = *static_cast*>(oldValue); 30 | 31 | // We also need to transition the state back to 'not set' before 32 | // resuming the coroutine. This operation needs to be 'acquire' 33 | // so that it synchronises with other calls to .set() that execute 34 | // concurrently with this call and execute the above m_state.exchange(this) 35 | // operation with 'release' semantics. 36 | // This needs to be an exchange() instead of a store() so that it can have 37 | // 'acquire' semantics. 38 | (void)m_state.exchange(nullptr, std::memory_order_acquire); 39 | 40 | // Finally, resume the waiting coroutine. 41 | handle.resume(); 42 | } 43 | } 44 | 45 | auto operator co_await() const noexcept 46 | { 47 | class awaiter 48 | { 49 | public: 50 | 51 | awaiter(const single_consumer_async_auto_reset_event& event) noexcept 52 | : m_event(event) 53 | {} 54 | 55 | bool await_ready() const noexcept { return false; } 56 | 57 | bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept 58 | { 59 | m_awaitingCoroutine = awaitingCoroutine; 60 | 61 | void* oldValue = nullptr; 62 | if (!m_event.m_state.compare_exchange_strong( 63 | oldValue, 64 | &m_awaitingCoroutine, 65 | std::memory_order_release, 66 | std::memory_order_relaxed)) 67 | { 68 | // This will only fail if the event was already 'set' 69 | // In which case we can just reset back to 'not set' 70 | // Need to use exchange() rather than store() here so we can make this 71 | // operation an 'acquire' operation so that we get visibility of all 72 | // writes prior to all preceding calls to .set(). 73 | assert(oldValue == &m_event); 74 | (void)m_event.m_state.exchange(nullptr, std::memory_order_acquire); 75 | return false; 76 | } 77 | 78 | return true; 79 | } 80 | 81 | void await_resume() noexcept {} 82 | 83 | private: 84 | const single_consumer_async_auto_reset_event& m_event; 85 | std::experimental::coroutine_handle<> m_awaitingCoroutine; 86 | }; 87 | 88 | return awaiter{ *this }; 89 | } 90 | 91 | private: 92 | 93 | // nullptr - not set, no waiter 94 | // this - set 95 | // other - not set, pointer is address of a coroutine_handle<> to resume. 96 | mutable std::atomic m_state; 97 | 98 | }; 99 | } 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /include/cppcoro/static_thread_pool.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_STATIC_THREAD_POOL_HPP_INCLUDED 6 | #define CPPCORO_STATIC_THREAD_POOL_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace cppcoro 17 | { 18 | class static_thread_pool 19 | { 20 | public: 21 | 22 | /// Initialise to a number of threads equal to the number of cores 23 | /// on the current machine. 24 | static_thread_pool(); 25 | 26 | /// Construct a thread pool with the specified number of threads. 27 | /// 28 | /// \param threadCount 29 | /// The number of threads in the pool that will be used to execute work. 30 | explicit static_thread_pool(std::uint32_t threadCount); 31 | 32 | ~static_thread_pool(); 33 | 34 | class schedule_operation 35 | { 36 | public: 37 | 38 | schedule_operation(static_thread_pool* tp) noexcept : m_threadPool(tp) {} 39 | 40 | bool await_ready() noexcept { return false; } 41 | void await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept; 42 | void await_resume() noexcept {} 43 | 44 | private: 45 | 46 | friend class static_thread_pool; 47 | 48 | static_thread_pool* m_threadPool; 49 | std::experimental::coroutine_handle<> m_awaitingCoroutine; 50 | schedule_operation* m_next; 51 | 52 | }; 53 | 54 | std::uint32_t thread_count() const noexcept { return m_threadCount; } 55 | 56 | [[nodiscard]] 57 | schedule_operation schedule() noexcept { return schedule_operation{ this }; } 58 | 59 | private: 60 | 61 | friend class schedule_operation; 62 | 63 | void run_worker_thread(std::uint32_t threadIndex) noexcept; 64 | 65 | void shutdown(); 66 | 67 | void schedule_impl(schedule_operation* operation) noexcept; 68 | 69 | void remote_enqueue(schedule_operation* operation) noexcept; 70 | 71 | bool has_any_queued_work_for(std::uint32_t threadIndex) noexcept; 72 | 73 | bool approx_has_any_queued_work_for(std::uint32_t threadIndex) const noexcept; 74 | 75 | bool is_shutdown_requested() const noexcept; 76 | 77 | void notify_intent_to_sleep(std::uint32_t threadIndex) noexcept; 78 | void try_clear_intent_to_sleep(std::uint32_t threadIndex) noexcept; 79 | 80 | schedule_operation* try_global_dequeue() noexcept; 81 | 82 | /// Try to steal a task from another thread. 83 | /// 84 | /// \return 85 | /// A pointer to the operation that was stolen if one could be stolen 86 | /// from another thread. Otherwise returns nullptr if none of the other 87 | /// threads had any tasks that could be stolen. 88 | schedule_operation* try_steal_from_other_thread(std::uint32_t thisThreadIndex) noexcept; 89 | 90 | void wake_one_thread() noexcept; 91 | 92 | class thread_state; 93 | 94 | static thread_local thread_state* s_currentState; 95 | static thread_local static_thread_pool* s_currentThreadPool; 96 | 97 | const std::uint32_t m_threadCount; 98 | const std::unique_ptr m_threadStates; 99 | 100 | std::vector m_threads; 101 | 102 | std::atomic m_stopRequested; 103 | 104 | std::mutex m_globalQueueMutex; 105 | std::atomic m_globalQueueHead; 106 | 107 | //alignas(std::hardware_destructive_interference_size) 108 | std::atomic m_globalQueueTail; 109 | 110 | //alignas(std::hardware_destructive_interference_size) 111 | std::atomic m_sleepingThreadCount; 112 | 113 | }; 114 | } 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /include/cppcoro/sync_wait.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_SYNC_WAIT_HPP_INCLUDED 6 | #define CPPCORO_SYNC_WAIT_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | namespace cppcoro 16 | { 17 | template 18 | auto sync_wait(AWAITABLE&& awaitable) 19 | -> typename cppcoro::awaitable_traits::await_result_t 20 | { 21 | #if CPPCORO_COMPILER_MSVC 22 | // HACK: Need to explicitly specify template argument to make_sync_wait_task 23 | // here to work around a bug in MSVC when passing parameters by universal 24 | // reference to a coroutine which causes the compiler to think it needs to 25 | // 'move' parameters passed by rvalue reference. 26 | auto task = detail::make_sync_wait_task(awaitable); 27 | #else 28 | auto task = detail::make_sync_wait_task(std::forward(awaitable)); 29 | #endif 30 | detail::lightweight_manual_reset_event event; 31 | task.start(event); 32 | event.wait(); 33 | return task.result(); 34 | } 35 | } 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /include/cppcoro/when_all.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_WHEN_ALL_HPP_INCLUDED 6 | #define CPPCORO_WHEN_ALL_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | namespace cppcoro 23 | { 24 | ////////// 25 | // Variadic when_all() 26 | 27 | template< 28 | typename... AWAITABLES, 29 | std::enable_if_t< 30 | std::conjunction_v>>...>, 31 | int> = 0> 32 | [[nodiscard]] auto when_all(AWAITABLES&&... awaitables) 33 | { 34 | return fmap([](auto&& taskTuple) 35 | { 36 | return std::apply([](auto&&... tasks) { 37 | return std::make_tuple(static_cast(tasks).non_void_result()...); 38 | }, static_cast(taskTuple)); 39 | }, when_all_ready(std::forward(awaitables)...)); 40 | } 41 | 42 | ////////// 43 | // when_all() with vector of awaitable 44 | 45 | template< 46 | typename AWAITABLE, 47 | typename RESULT = typename awaitable_traits>::await_result_t, 48 | std::enable_if_t, int> = 0> 49 | [[nodiscard]] 50 | auto when_all(std::vector awaitables) 51 | { 52 | return fmap([](auto&& taskVector) { 53 | for (auto& task : taskVector) 54 | { 55 | task.result(); 56 | } 57 | }, when_all_ready(std::move(awaitables))); 58 | } 59 | 60 | template< 61 | typename AWAITABLE, 62 | typename RESULT = typename awaitable_traits>::await_result_t, 63 | std::enable_if_t, int> = 0> 64 | [[nodiscard]] 65 | auto when_all(std::vector awaitables) 66 | { 67 | using result_t = std::conditional_t< 68 | std::is_lvalue_reference_v, 69 | std::reference_wrapper>, 70 | std::remove_reference_t>; 71 | 72 | return fmap([](auto&& taskVector) { 73 | std::vector results; 74 | results.reserve(taskVector.size()); 75 | for (auto& task : taskVector) 76 | { 77 | if constexpr (std::is_rvalue_reference_v) 78 | { 79 | results.emplace_back(std::move(task).result()); 80 | } 81 | else 82 | { 83 | results.emplace_back(task.result()); 84 | } 85 | } 86 | return results; 87 | }, when_all_ready(std::move(awaitables))); 88 | } 89 | } 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /include/cppcoro/when_all_ready.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_WHEN_ALL_READY_HPP_INCLUDED 6 | #define CPPCORO_WHEN_ALL_READY_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace cppcoro 22 | { 23 | template< 24 | typename... AWAITABLES, 25 | std::enable_if_t>>...>, int> = 0> 27 | [[nodiscard]] 28 | CPPCORO_FORCE_INLINE auto when_all_ready(AWAITABLES&&... awaitables) 29 | { 30 | return detail::when_all_ready_awaitable>>::await_result_t>...>>( 32 | std::make_tuple(detail::make_when_all_task(std::forward(awaitables))...)); 33 | } 34 | 35 | // TODO: Generalise this from vector to arbitrary sequence of awaitable. 36 | 37 | template< 38 | typename AWAITABLE, 39 | typename RESULT = typename awaitable_traits>::await_result_t> 40 | [[nodiscard]] auto when_all_ready(std::vector awaitables) 41 | { 42 | std::vector> tasks; 43 | 44 | tasks.reserve(awaitables.size()); 45 | 46 | for (auto& awaitable : awaitables) 47 | { 48 | tasks.emplace_back(detail::make_when_all_task(std::move(awaitable))); 49 | } 50 | 51 | return detail::when_all_ready_awaitable>>( 52 | std::move(tasks)); 53 | } 54 | } 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /include/cppcoro/writable_file.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_WRITABLE_FILE_HPP_INCLUDED 6 | #define CPPCORO_WRITABLE_FILE_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace cppcoro 13 | { 14 | class writable_file : virtual public file 15 | { 16 | public: 17 | 18 | /// Set the size of the file. 19 | /// 20 | /// \param fileSize 21 | /// The new size of the file in bytes. 22 | void set_size(std::uint64_t fileSize); 23 | 24 | /// Write some data to the file. 25 | /// 26 | /// Writes \a byteCount bytes from the file starting at \a offset 27 | /// into the specified \a buffer. 28 | /// 29 | /// \param offset 30 | /// The offset within the file to start writing from. 31 | /// If the file has been opened using file_buffering_mode::unbuffered 32 | /// then the offset must be a multiple of the file-system's sector size. 33 | /// 34 | /// \param buffer 35 | /// The buffer containing the data to be written to the file. 36 | /// If the file has been opened using file_buffering_mode::unbuffered 37 | /// then the address of the start of the buffer must be a multiple of 38 | /// the file-system's sector size. 39 | /// 40 | /// \param byteCount 41 | /// The number of bytes to write to the file. 42 | /// If the file has been opeend using file_buffering_mode::unbuffered 43 | /// then the byteCount must be a multiple of the file-system's sector size. 44 | /// 45 | /// \param ct 46 | /// An optional cancellation_token that can be used to cancel the 47 | /// write operation before it completes. 48 | /// 49 | /// \return 50 | /// An object that represents the write operation. 51 | /// This object must be co_await'ed to start the write operation. 52 | [[nodiscard]] 53 | file_write_operation write( 54 | std::uint64_t offset, 55 | const void* buffer, 56 | std::size_t byteCount) noexcept; 57 | [[nodiscard]] 58 | file_write_operation_cancellable write( 59 | std::uint64_t offset, 60 | const void* buffer, 61 | std::size_t byteCount, 62 | cancellation_token ct) noexcept; 63 | 64 | protected: 65 | 66 | using file::file; 67 | 68 | }; 69 | } 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /include/cppcoro/write_only_file.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_WRITE_ONLY_FILE_HPP_INCLUDED 6 | #define CPPCORO_WRITE_ONLY_FILE_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | namespace cppcoro 16 | { 17 | class write_only_file : public writable_file 18 | { 19 | public: 20 | 21 | /// Open a file for write-only access. 22 | /// 23 | /// \param ioContext 24 | /// The I/O context to use when dispatching I/O completion events. 25 | /// When asynchronous write operations on this file complete the 26 | /// completion events will be dispatched to an I/O thread associated 27 | /// with the I/O context. 28 | /// 29 | /// \param pathMode 30 | /// Path of the file to open. 31 | /// 32 | /// \param openMode 33 | /// Specifies how the file should be opened and how to handle cases 34 | /// when the file exists or doesn't exist. 35 | /// 36 | /// \param shareMode 37 | /// Specifies the access to be allowed on the file concurrently with this file access. 38 | /// 39 | /// \param bufferingMode 40 | /// Specifies the modes/hints to provide to the OS that affects the behaviour 41 | /// of its file buffering. 42 | /// 43 | /// \return 44 | /// An object that can be used to write to the file. 45 | /// 46 | /// \throw std::system_error 47 | /// If the file could not be opened for write. 48 | [[nodiscard]] 49 | static write_only_file open( 50 | io_service& ioService, 51 | const std::experimental::filesystem::path& path, 52 | file_open_mode openMode = file_open_mode::create_or_open, 53 | file_share_mode shareMode = file_share_mode::none, 54 | file_buffering_mode bufferingMode = file_buffering_mode::default_); 55 | 56 | protected: 57 | 58 | #if CPPCORO_OS_WINNT 59 | write_only_file(detail::win32::safe_handle&& fileHandle) noexcept; 60 | #endif 61 | 62 | }; 63 | } 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export CAKE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/tools/cake/src/run.py" 3 | 4 | function cake() 5 | { 6 | python2.7 "$CAKE" "$@" 7 | } 8 | -------------------------------------------------------------------------------- /lib/async_manual_reset_event.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | 12 | cppcoro::async_manual_reset_event::async_manual_reset_event(bool initiallySet) noexcept 13 | : m_state(initiallySet ? static_cast(this) : nullptr) 14 | {} 15 | 16 | cppcoro::async_manual_reset_event::~async_manual_reset_event() 17 | { 18 | // There should be no coroutines still awaiting the event. 19 | assert( 20 | m_state.load(std::memory_order_relaxed) == nullptr || 21 | m_state.load(std::memory_order_relaxed) == static_cast(this)); 22 | } 23 | 24 | bool cppcoro::async_manual_reset_event::is_set() const noexcept 25 | { 26 | return m_state.load(std::memory_order_acquire) == static_cast(this); 27 | } 28 | 29 | cppcoro::async_manual_reset_event_operation 30 | cppcoro::async_manual_reset_event::operator co_await() const noexcept 31 | { 32 | return async_manual_reset_event_operation{ *this }; 33 | } 34 | 35 | void cppcoro::async_manual_reset_event::set() noexcept 36 | { 37 | void* const setState = static_cast(this); 38 | 39 | // Needs 'release' semantics so that prior writes are visible to event awaiters 40 | // that synchronise either via 'is_set()' or 'operator co_await()'. 41 | // Needs 'acquire' semantics in case there are any waiters so that we see 42 | // prior writes to the waiting coroutine's state and to the contents of 43 | // the queued async_manual_reset_event_operation objects. 44 | void* oldState = m_state.exchange(setState, std::memory_order_acq_rel); 45 | if (oldState != setState) 46 | { 47 | auto* current = static_cast(oldState); 48 | while (current != nullptr) 49 | { 50 | auto* next = current->m_next; 51 | current->m_awaiter.resume(); 52 | current = next; 53 | } 54 | } 55 | } 56 | 57 | void cppcoro::async_manual_reset_event::reset() noexcept 58 | { 59 | void* oldState = static_cast(this); 60 | m_state.compare_exchange_strong(oldState, nullptr, std::memory_order_relaxed); 61 | } 62 | 63 | cppcoro::async_manual_reset_event_operation::async_manual_reset_event_operation( 64 | const async_manual_reset_event& event) noexcept 65 | : m_event(event) 66 | { 67 | } 68 | 69 | bool cppcoro::async_manual_reset_event_operation::await_ready() const noexcept 70 | { 71 | return m_event.is_set(); 72 | } 73 | 74 | bool cppcoro::async_manual_reset_event_operation::await_suspend( 75 | std::experimental::coroutine_handle<> awaiter) noexcept 76 | { 77 | m_awaiter = awaiter; 78 | 79 | const void* const setState = static_cast(&m_event); 80 | 81 | void* oldState = m_event.m_state.load(std::memory_order_acquire); 82 | do 83 | { 84 | if (oldState == setState) 85 | { 86 | // State is now 'set' no need to suspend. 87 | return false; 88 | } 89 | 90 | m_next = static_cast(oldState); 91 | } while (!m_event.m_state.compare_exchange_weak( 92 | oldState, 93 | static_cast(this), 94 | std::memory_order_release, 95 | std::memory_order_acquire)); 96 | 97 | // Successfully queued this waiter to the list. 98 | return true; 99 | } 100 | -------------------------------------------------------------------------------- /lib/async_mutex.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #include 9 | 10 | cppcoro::async_mutex::async_mutex() noexcept 11 | : m_state(not_locked) 12 | , m_waiters(nullptr) 13 | {} 14 | 15 | cppcoro::async_mutex::~async_mutex() 16 | { 17 | [[maybe_unused]] auto state = m_state.load(std::memory_order_relaxed); 18 | assert(state == not_locked || state == locked_no_waiters); 19 | assert(m_waiters == nullptr); 20 | } 21 | 22 | bool cppcoro::async_mutex::try_lock() noexcept 23 | { 24 | // Try to atomically transition from nullptr (not-locked) -> this (locked-no-waiters). 25 | auto oldState = not_locked; 26 | return m_state.compare_exchange_strong( 27 | oldState, 28 | locked_no_waiters, 29 | std::memory_order_acquire, 30 | std::memory_order_relaxed); 31 | } 32 | 33 | cppcoro::async_mutex_lock_operation cppcoro::async_mutex::lock_async() noexcept 34 | { 35 | return async_mutex_lock_operation{ *this }; 36 | } 37 | 38 | cppcoro::async_mutex_scoped_lock_operation cppcoro::async_mutex::scoped_lock_async() noexcept 39 | { 40 | return async_mutex_scoped_lock_operation{ *this }; 41 | } 42 | 43 | void cppcoro::async_mutex::unlock() 44 | { 45 | assert(m_state.load(std::memory_order_relaxed) != not_locked); 46 | 47 | async_mutex_lock_operation* waitersHead = m_waiters; 48 | if (waitersHead == nullptr) 49 | { 50 | auto oldState = locked_no_waiters; 51 | const bool releasedLock = m_state.compare_exchange_strong( 52 | oldState, 53 | not_locked, 54 | std::memory_order_release, 55 | std::memory_order_relaxed); 56 | if (releasedLock) 57 | { 58 | return; 59 | } 60 | 61 | // At least one new waiter. 62 | // Acquire the list of new waiter operations atomically. 63 | oldState = m_state.exchange(locked_no_waiters, std::memory_order_acquire); 64 | 65 | assert(oldState != locked_no_waiters && oldState != not_locked); 66 | 67 | // Transfer the list to m_waiters, reversing the list in the process so 68 | // that the head of the list is the first to be resumed. 69 | auto* next = reinterpret_cast(oldState); 70 | do 71 | { 72 | auto* temp = next->m_next; 73 | next->m_next = waitersHead; 74 | waitersHead = next; 75 | next = temp; 76 | } while (next != nullptr); 77 | } 78 | 79 | assert(waitersHead != nullptr); 80 | 81 | m_waiters = waitersHead->m_next; 82 | 83 | // Resume the waiter. 84 | // This will pass the ownership of the lock on to that operation/coroutine. 85 | waitersHead->m_awaiter.resume(); 86 | } 87 | 88 | bool cppcoro::async_mutex_lock_operation::await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept 89 | { 90 | m_awaiter = awaiter; 91 | 92 | std::uintptr_t oldState = m_mutex.m_state.load(std::memory_order_acquire); 93 | while (true) 94 | { 95 | if (oldState == async_mutex::not_locked) 96 | { 97 | if (m_mutex.m_state.compare_exchange_weak( 98 | oldState, 99 | async_mutex::locked_no_waiters, 100 | std::memory_order_acquire, 101 | std::memory_order_relaxed)) 102 | { 103 | // Acquired lock, don't suspend. 104 | return false; 105 | } 106 | } 107 | else 108 | { 109 | // Try to push this operation onto the head of the waiter stack. 110 | m_next = reinterpret_cast(oldState); 111 | if (m_mutex.m_state.compare_exchange_weak( 112 | oldState, 113 | reinterpret_cast(this), 114 | std::memory_order_release, 115 | std::memory_order_relaxed)) 116 | { 117 | // Queued operation to waiters list, suspend now. 118 | return true; 119 | } 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/auto_reset_event.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include "auto_reset_event.hpp" 7 | 8 | #if CPPCORO_OS_WINNT 9 | # define WIN32_LEAN_AND_MEAN 10 | # include 11 | # include 12 | #endif 13 | 14 | namespace cppcoro 15 | { 16 | #if CPPCORO_OS_WINNT 17 | 18 | auto_reset_event::auto_reset_event(bool initiallySet) 19 | : m_event(::CreateEventW(NULL, FALSE, initiallySet ? TRUE : FALSE, NULL)) 20 | { 21 | if (m_event.handle() == NULL) 22 | { 23 | DWORD errorCode = ::GetLastError(); 24 | throw std::system_error 25 | { 26 | static_cast(errorCode), 27 | std::system_category(), 28 | "auto_reset_event: CreateEvent failed" 29 | }; 30 | } 31 | } 32 | 33 | auto_reset_event::~auto_reset_event() 34 | { 35 | } 36 | 37 | void auto_reset_event::set() 38 | { 39 | BOOL ok =::SetEvent(m_event.handle()); 40 | if (!ok) 41 | { 42 | DWORD errorCode = ::GetLastError(); 43 | throw std::system_error 44 | { 45 | static_cast(errorCode), 46 | std::system_category(), 47 | "auto_reset_event: SetEvent failed" 48 | }; 49 | } 50 | } 51 | 52 | void auto_reset_event::wait() 53 | { 54 | DWORD result = ::WaitForSingleObjectEx(m_event.handle(), INFINITE, FALSE); 55 | if (result != WAIT_OBJECT_0) 56 | { 57 | DWORD errorCode = ::GetLastError(); 58 | throw std::system_error 59 | { 60 | static_cast(errorCode), 61 | std::system_category(), 62 | "auto_reset_event: WaitForSingleObjectEx failed" 63 | }; 64 | } 65 | } 66 | 67 | #else 68 | 69 | auto_reset_event::auto_reset_event(bool initiallySet) 70 | : m_isSet(initiallySet) 71 | {} 72 | 73 | auto_reset_event::~auto_reset_event() 74 | {} 75 | 76 | void auto_reset_event::set() 77 | { 78 | std::unique_lock lock{ m_mutex }; 79 | if (!m_isSet) 80 | { 81 | m_isSet = true; 82 | m_cv.notify_one(); 83 | } 84 | } 85 | 86 | void auto_reset_event::wait() 87 | { 88 | std::unique_lock lock{ m_mutex }; 89 | while (!m_isSet) 90 | { 91 | m_cv.wait(lock); 92 | } 93 | m_isSet = false; 94 | } 95 | 96 | #endif 97 | } 98 | -------------------------------------------------------------------------------- /lib/auto_reset_event.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_AUTO_RESET_EVENT_HPP_INCLUDED 6 | #define CPPCORO_AUTO_RESET_EVENT_HPP_INCLUDED 7 | 8 | #include 9 | 10 | #if CPPCORO_OS_WINNT 11 | # include 12 | #else 13 | # include 14 | # include 15 | #endif 16 | 17 | namespace cppcoro 18 | { 19 | class auto_reset_event 20 | { 21 | public: 22 | 23 | auto_reset_event(bool initiallySet = false); 24 | 25 | ~auto_reset_event(); 26 | 27 | void set(); 28 | 29 | void wait(); 30 | 31 | private: 32 | 33 | #if CPPCORO_OS_WINNT 34 | cppcoro::detail::win32::safe_handle m_event; 35 | #else 36 | std::mutex m_mutex; 37 | std::condition_variable m_cv; 38 | bool m_isSet; 39 | #endif 40 | 41 | }; 42 | } 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /lib/cancellation_registration.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #include "cancellation_state.hpp" 9 | 10 | #include 11 | 12 | cppcoro::cancellation_registration::~cancellation_registration() 13 | { 14 | if (m_state != nullptr) 15 | { 16 | m_state->deregister_callback(this); 17 | m_state->release_token_ref(); 18 | } 19 | } 20 | 21 | void cppcoro::cancellation_registration::register_callback(cancellation_token&& token) 22 | { 23 | auto* state = token.m_state; 24 | if (state != nullptr && state->can_be_cancelled()) 25 | { 26 | m_state = state; 27 | if (state->try_register_callback(this)) 28 | { 29 | token.m_state = nullptr; 30 | } 31 | else 32 | { 33 | m_state = nullptr; 34 | m_callback(); 35 | } 36 | } 37 | else 38 | { 39 | m_state = nullptr; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/cancellation_source.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #include "cancellation_state.hpp" 9 | 10 | #include 11 | 12 | cppcoro::cancellation_source::cancellation_source() 13 | : m_state(detail::cancellation_state::create()) 14 | { 15 | } 16 | 17 | cppcoro::cancellation_source::cancellation_source(const cancellation_source& other) noexcept 18 | : m_state(other.m_state) 19 | { 20 | if (m_state != nullptr) 21 | { 22 | m_state->add_source_ref(); 23 | } 24 | } 25 | 26 | cppcoro::cancellation_source::cancellation_source(cancellation_source&& other) noexcept 27 | : m_state(other.m_state) 28 | { 29 | other.m_state = nullptr; 30 | } 31 | 32 | cppcoro::cancellation_source::~cancellation_source() 33 | { 34 | if (m_state != nullptr) 35 | { 36 | m_state->release_source_ref(); 37 | } 38 | } 39 | 40 | cppcoro::cancellation_source& cppcoro::cancellation_source::operator=(const cancellation_source& other) noexcept 41 | { 42 | if (m_state != other.m_state) 43 | { 44 | if (m_state != nullptr) 45 | { 46 | m_state->release_source_ref(); 47 | } 48 | 49 | m_state = other.m_state; 50 | 51 | if (m_state != nullptr) 52 | { 53 | m_state->add_source_ref(); 54 | } 55 | } 56 | 57 | return *this; 58 | } 59 | 60 | cppcoro::cancellation_source& cppcoro::cancellation_source::operator=(cancellation_source&& other) noexcept 61 | { 62 | if (this != &other) 63 | { 64 | if (m_state != nullptr) 65 | { 66 | m_state->release_source_ref(); 67 | } 68 | 69 | m_state = other.m_state; 70 | other.m_state = nullptr; 71 | } 72 | 73 | return *this; 74 | } 75 | 76 | bool cppcoro::cancellation_source::can_be_cancelled() const noexcept 77 | { 78 | return m_state != nullptr; 79 | } 80 | 81 | cppcoro::cancellation_token cppcoro::cancellation_source::token() const noexcept 82 | { 83 | return cancellation_token(m_state); 84 | } 85 | 86 | void cppcoro::cancellation_source::request_cancellation() 87 | { 88 | if (m_state != nullptr) 89 | { 90 | m_state->request_cancellation(); 91 | } 92 | } 93 | 94 | bool cppcoro::cancellation_source::is_cancellation_requested() const noexcept 95 | { 96 | return m_state != nullptr && m_state->is_cancellation_requested(); 97 | } 98 | -------------------------------------------------------------------------------- /lib/cancellation_token.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | #include 8 | 9 | #include "cancellation_state.hpp" 10 | 11 | #include 12 | #include 13 | 14 | cppcoro::cancellation_token::cancellation_token() noexcept 15 | : m_state(nullptr) 16 | { 17 | } 18 | 19 | cppcoro::cancellation_token::cancellation_token(const cancellation_token& other) noexcept 20 | : m_state(other.m_state) 21 | { 22 | if (m_state != nullptr) 23 | { 24 | m_state->add_token_ref(); 25 | } 26 | } 27 | 28 | cppcoro::cancellation_token::cancellation_token(cancellation_token&& other) noexcept 29 | : m_state(other.m_state) 30 | { 31 | other.m_state = nullptr; 32 | } 33 | 34 | cppcoro::cancellation_token::~cancellation_token() 35 | { 36 | if (m_state != nullptr) 37 | { 38 | m_state->release_token_ref(); 39 | } 40 | } 41 | 42 | cppcoro::cancellation_token& cppcoro::cancellation_token::operator=(const cancellation_token& other) noexcept 43 | { 44 | if (other.m_state != m_state) 45 | { 46 | if (m_state != nullptr) 47 | { 48 | m_state->release_token_ref(); 49 | } 50 | 51 | m_state = other.m_state; 52 | 53 | if (m_state != nullptr) 54 | { 55 | m_state->add_token_ref(); 56 | } 57 | } 58 | 59 | return *this; 60 | } 61 | 62 | cppcoro::cancellation_token& cppcoro::cancellation_token::operator=(cancellation_token&& other) noexcept 63 | { 64 | if (this != &other) 65 | { 66 | if (m_state != nullptr) 67 | { 68 | m_state->release_token_ref(); 69 | } 70 | 71 | m_state = other.m_state; 72 | other.m_state = nullptr; 73 | } 74 | 75 | return *this; 76 | } 77 | 78 | void cppcoro::cancellation_token::swap(cancellation_token& other) noexcept 79 | { 80 | std::swap(m_state, other.m_state); 81 | } 82 | 83 | bool cppcoro::cancellation_token::can_be_cancelled() const noexcept 84 | { 85 | return m_state != nullptr && m_state->can_be_cancelled(); 86 | } 87 | 88 | bool cppcoro::cancellation_token::is_cancellation_requested() const noexcept 89 | { 90 | return m_state != nullptr && m_state->is_cancellation_requested(); 91 | } 92 | 93 | void cppcoro::cancellation_token::throw_if_cancellation_requested() const 94 | { 95 | if (is_cancellation_requested()) 96 | { 97 | throw operation_cancelled{}; 98 | } 99 | } 100 | 101 | cppcoro::cancellation_token::cancellation_token(detail::cancellation_state* state) noexcept 102 | : m_state(state) 103 | { 104 | if (m_state != nullptr) 105 | { 106 | m_state->add_token_ref(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /lib/file_read_operation.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #if CPPCORO_OS_WINNT 9 | # ifndef WIN32_LEAN_AND_MEAN 10 | # define WIN32_LEAN_AND_MEAN 11 | # endif 12 | # include 13 | 14 | bool cppcoro::file_read_operation_impl::try_start( 15 | cppcoro::detail::win32_overlapped_operation_base& operation) noexcept 16 | { 17 | const DWORD numberOfBytesToRead = 18 | m_byteCount <= 0xFFFFFFFF ? 19 | static_cast(m_byteCount) : DWORD(0xFFFFFFFF); 20 | 21 | DWORD numberOfBytesRead = 0; 22 | BOOL ok = ::ReadFile( 23 | m_fileHandle, 24 | m_buffer, 25 | numberOfBytesToRead, 26 | &numberOfBytesRead, 27 | operation.get_overlapped()); 28 | const DWORD errorCode = ok ? ERROR_SUCCESS : ::GetLastError(); 29 | if (errorCode != ERROR_IO_PENDING) 30 | { 31 | // Completed synchronously. 32 | // 33 | // We are assuming that the file-handle has been set to the 34 | // mode where synchronous completions do not post a completion 35 | // event to the I/O completion port and thus can return without 36 | // suspending here. 37 | 38 | operation.m_errorCode = errorCode; 39 | operation.m_numberOfBytesTransferred = numberOfBytesRead; 40 | 41 | return false; 42 | } 43 | 44 | return true; 45 | } 46 | 47 | void cppcoro::file_read_operation_impl::cancel( 48 | cppcoro::detail::win32_overlapped_operation_base& operation) noexcept 49 | { 50 | (void)::CancelIoEx(m_fileHandle, operation.get_overlapped()); 51 | } 52 | 53 | #endif // CPPCORO_OS_WINNT 54 | -------------------------------------------------------------------------------- /lib/file_write_operation.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #if CPPCORO_OS_WINNT 9 | # ifndef WIN32_LEAN_AND_MEAN 10 | # define WIN32_LEAN_AND_MEAN 11 | # endif 12 | # include 13 | 14 | bool cppcoro::file_write_operation_impl::try_start( 15 | cppcoro::detail::win32_overlapped_operation_base& operation) noexcept 16 | { 17 | const DWORD numberOfBytesToWrite = 18 | m_byteCount <= 0xFFFFFFFF ? 19 | static_cast(m_byteCount) : DWORD(0xFFFFFFFF); 20 | 21 | DWORD numberOfBytesWritten = 0; 22 | BOOL ok = ::WriteFile( 23 | m_fileHandle, 24 | m_buffer, 25 | numberOfBytesToWrite, 26 | &numberOfBytesWritten, 27 | operation.get_overlapped()); 28 | const DWORD errorCode = ok ? ERROR_SUCCESS : ::GetLastError(); 29 | if (errorCode != ERROR_IO_PENDING) 30 | { 31 | // Completed synchronously. 32 | // 33 | // We are assuming that the file-handle has been set to the 34 | // mode where synchronous completions do not post a completion 35 | // event to the I/O completion port and thus can return without 36 | // suspending here. 37 | 38 | operation.m_errorCode = errorCode; 39 | operation.m_numberOfBytesTransferred = numberOfBytesWritten; 40 | 41 | return false; 42 | } 43 | 44 | return true; 45 | } 46 | 47 | void cppcoro::file_write_operation_impl::cancel( 48 | cppcoro::detail::win32_overlapped_operation_base& operation) noexcept 49 | { 50 | (void)::CancelIoEx(m_fileHandle, operation.get_overlapped()); 51 | } 52 | 53 | #endif // CPPCORO_OS_WINNT 54 | -------------------------------------------------------------------------------- /lib/ip_address.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | std::string cppcoro::net::ip_address::to_string() const 9 | { 10 | return is_ipv4() ? m_ipv4.to_string() : m_ipv6.to_string(); 11 | } 12 | 13 | std::optional 14 | cppcoro::net::ip_address::from_string(std::string_view string) noexcept 15 | { 16 | if (auto ipv4 = ipv4_address::from_string(string); ipv4) 17 | { 18 | return *ipv4; 19 | } 20 | 21 | if (auto ipv6 = ipv6_address::from_string(string); ipv6) 22 | { 23 | return *ipv6; 24 | } 25 | 26 | return std::nullopt; 27 | } 28 | -------------------------------------------------------------------------------- /lib/ip_endpoint.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | std::string cppcoro::net::ip_endpoint::to_string() const 9 | { 10 | return is_ipv4() ? m_ipv4.to_string() : m_ipv6.to_string(); 11 | } 12 | 13 | std::optional 14 | cppcoro::net::ip_endpoint::from_string(std::string_view string) noexcept 15 | { 16 | if (auto ipv4 = ipv4_endpoint::from_string(string); ipv4) 17 | { 18 | return *ipv4; 19 | } 20 | 21 | if (auto ipv6 = ipv6_endpoint::from_string(string); ipv6) 22 | { 23 | return *ipv6; 24 | } 25 | 26 | return std::nullopt; 27 | } 28 | -------------------------------------------------------------------------------- /lib/ipv4_endpoint.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Kt C++ Library 3 | // Copyright (c) 2015 Lewis Baker 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #include 9 | 10 | namespace 11 | { 12 | namespace local 13 | { 14 | bool is_digit(char c) 15 | { 16 | return c >= '0' && c <= '9'; 17 | } 18 | 19 | std::uint8_t digit_value(char c) 20 | { 21 | return static_cast(c - '0'); 22 | } 23 | 24 | std::optional parse_port(std::string_view string) 25 | { 26 | if (string.empty()) return std::nullopt; 27 | 28 | std::uint32_t value = 0; 29 | for (auto c : string) 30 | { 31 | if (!is_digit(c)) return std::nullopt; 32 | value = value * 10 + digit_value(c); 33 | if (value > 0xFFFFu) return std::nullopt; 34 | } 35 | 36 | return static_cast(value); 37 | } 38 | } 39 | } 40 | 41 | std::string cppcoro::net::ipv4_endpoint::to_string() const 42 | { 43 | auto s = m_address.to_string(); 44 | s.push_back(':'); 45 | s.append(std::to_string(m_port)); 46 | return s; 47 | } 48 | 49 | std::optional 50 | cppcoro::net::ipv4_endpoint::from_string(std::string_view string) noexcept 51 | { 52 | auto colonPos = string.find(':'); 53 | if (colonPos == std::string_view::npos) 54 | { 55 | return std::nullopt; 56 | } 57 | 58 | auto address = ipv4_address::from_string(string.substr(0, colonPos)); 59 | if (!address) 60 | { 61 | return std::nullopt; 62 | } 63 | 64 | auto port = local::parse_port(string.substr(colonPos + 1)); 65 | if (!port) 66 | { 67 | return std::nullopt; 68 | } 69 | 70 | return ipv4_endpoint{ *address, *port }; 71 | } 72 | -------------------------------------------------------------------------------- /lib/ipv6_endpoint.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Kt C++ Library 3 | // Copyright (c) 2015 Lewis Baker 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #include 9 | 10 | namespace 11 | { 12 | namespace local 13 | { 14 | bool is_digit(char c) 15 | { 16 | return c >= '0' && c <= '9'; 17 | } 18 | 19 | std::uint8_t digit_value(char c) 20 | { 21 | return static_cast(c - '0'); 22 | } 23 | 24 | std::optional parse_port(std::string_view string) 25 | { 26 | if (string.empty()) return std::nullopt; 27 | 28 | std::uint32_t value = 0; 29 | for (auto c : string) 30 | { 31 | if (!is_digit(c)) return std::nullopt; 32 | value = value * 10 + digit_value(c); 33 | if (value > 0xFFFFu) return std::nullopt; 34 | } 35 | 36 | return static_cast(value); 37 | } 38 | } 39 | } 40 | 41 | std::string cppcoro::net::ipv6_endpoint::to_string() const 42 | { 43 | std::string result; 44 | result.push_back('['); 45 | result += m_address.to_string(); 46 | result += "]:"; 47 | result += std::to_string(m_port); 48 | return result; 49 | } 50 | 51 | std::optional 52 | cppcoro::net::ipv6_endpoint::from_string(std::string_view string) noexcept 53 | { 54 | // Shortest valid endpoint is "[::]:0" 55 | if (string.size() < 6) 56 | { 57 | return std::nullopt; 58 | } 59 | 60 | if (string[0] != '[') 61 | { 62 | return std::nullopt; 63 | } 64 | 65 | auto closeBracketPos = string.find("]:", 1); 66 | if (closeBracketPos == std::string_view::npos) 67 | { 68 | return std::nullopt; 69 | } 70 | 71 | auto address = ipv6_address::from_string(string.substr(1, closeBracketPos - 1)); 72 | if (!address) 73 | { 74 | return std::nullopt; 75 | } 76 | 77 | auto port = local::parse_port(string.substr(closeBracketPos + 2)); 78 | if (!port) 79 | { 80 | return std::nullopt; 81 | } 82 | 83 | return ipv6_endpoint{ *address, *port }; 84 | } 85 | -------------------------------------------------------------------------------- /lib/read_only_file.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #if CPPCORO_OS_WINNT 9 | # ifndef WIN32_LEAN_AND_MEAN 10 | # define WIN32_LEAN_AND_MEAN 11 | # endif 12 | # include 13 | 14 | cppcoro::read_only_file cppcoro::read_only_file::open( 15 | io_service& ioService, 16 | const std::experimental::filesystem::path& path, 17 | file_share_mode shareMode, 18 | file_buffering_mode bufferingMode) 19 | { 20 | return read_only_file(file::open( 21 | GENERIC_READ, 22 | ioService, 23 | path, 24 | file_open_mode::open_existing, 25 | shareMode, 26 | bufferingMode)); 27 | } 28 | 29 | cppcoro::read_only_file::read_only_file( 30 | detail::win32::safe_handle&& fileHandle) noexcept 31 | : file(std::move(fileHandle)) 32 | , readable_file(detail::win32::safe_handle{}) 33 | { 34 | } 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /lib/read_write_file.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #if CPPCORO_OS_WINNT 9 | # ifndef WIN32_LEAN_AND_MEAN 10 | # define WIN32_LEAN_AND_MEAN 11 | # endif 12 | # include 13 | 14 | cppcoro::read_write_file cppcoro::read_write_file::open( 15 | io_service& ioService, 16 | const std::experimental::filesystem::path& path, 17 | file_open_mode openMode, 18 | file_share_mode shareMode, 19 | file_buffering_mode bufferingMode) 20 | { 21 | return read_write_file(file::open( 22 | GENERIC_READ | GENERIC_WRITE, 23 | ioService, 24 | path, 25 | openMode, 26 | shareMode, 27 | bufferingMode)); 28 | } 29 | 30 | cppcoro::read_write_file::read_write_file( 31 | detail::win32::safe_handle&& fileHandle) noexcept 32 | : file(std::move(fileHandle)) 33 | , readable_file(detail::win32::safe_handle{}) 34 | , writable_file(detail::win32::safe_handle{}) 35 | { 36 | } 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /lib/readable_file.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #if CPPCORO_OS_WINNT 9 | 10 | cppcoro::file_read_operation cppcoro::readable_file::read( 11 | std::uint64_t offset, 12 | void* buffer, 13 | std::size_t byteCount) const noexcept 14 | { 15 | return file_read_operation( 16 | m_fileHandle.handle(), 17 | offset, 18 | buffer, 19 | byteCount); 20 | } 21 | 22 | cppcoro::file_read_operation_cancellable cppcoro::readable_file::read( 23 | std::uint64_t offset, 24 | void* buffer, 25 | std::size_t byteCount, 26 | cancellation_token ct) const noexcept 27 | { 28 | return file_read_operation_cancellable( 29 | m_fileHandle.handle(), 30 | offset, 31 | buffer, 32 | byteCount, 33 | std::move(ct)); 34 | } 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /lib/socket_disconnect_operation.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | #include 8 | 9 | #include "socket_helpers.hpp" 10 | 11 | #include 12 | 13 | #if CPPCORO_OS_WINNT 14 | # include 15 | # include 16 | # include 17 | # include 18 | 19 | bool cppcoro::net::socket_disconnect_operation_impl::try_start( 20 | cppcoro::detail::win32_overlapped_operation_base& operation) noexcept 21 | { 22 | // Lookup the address of the DisconnectEx function pointer for this socket. 23 | LPFN_DISCONNECTEX disconnectExPtr; 24 | { 25 | GUID disconnectExGuid = WSAID_DISCONNECTEX; 26 | DWORD byteCount = 0; 27 | const int result = ::WSAIoctl( 28 | m_socket.native_handle(), 29 | SIO_GET_EXTENSION_FUNCTION_POINTER, 30 | static_cast(&disconnectExGuid), 31 | sizeof(disconnectExGuid), 32 | static_cast(&disconnectExPtr), 33 | sizeof(disconnectExPtr), 34 | &byteCount, 35 | nullptr, 36 | nullptr); 37 | if (result == SOCKET_ERROR) 38 | { 39 | operation.m_errorCode = static_cast(::WSAGetLastError()); 40 | return false; 41 | } 42 | } 43 | 44 | // Need to read this flag before starting the operation, otherwise 45 | // it may be possible that the operation will complete immediately 46 | // on another thread and then destroy the socket before we get a 47 | // chance to read it. 48 | const bool skipCompletionOnSuccess = m_socket.skip_completion_on_success(); 49 | 50 | // Need to add TF_REUSE_SOCKET to these flags if we want to allow reusing 51 | // a socket for subsequent connections once the disconnect operation 52 | // completes. 53 | const DWORD flags = 0; 54 | 55 | const BOOL ok = disconnectExPtr( 56 | m_socket.native_handle(), 57 | operation.get_overlapped(), 58 | flags, 59 | 0); 60 | if (!ok) 61 | { 62 | const int errorCode = ::WSAGetLastError(); 63 | if (errorCode != ERROR_IO_PENDING) 64 | { 65 | // Failed synchronously. 66 | operation.m_errorCode = static_cast(errorCode); 67 | return false; 68 | } 69 | } 70 | else if (skipCompletionOnSuccess) 71 | { 72 | // Successfully completed synchronously and no completion event 73 | // will be posted to an I/O thread so we can return without suspending. 74 | operation.m_errorCode = ERROR_SUCCESS; 75 | return false; 76 | } 77 | 78 | return true; 79 | } 80 | 81 | void cppcoro::net::socket_disconnect_operation_impl::cancel( 82 | cppcoro::detail::win32_overlapped_operation_base& operation) noexcept 83 | { 84 | (void)::CancelIoEx( 85 | reinterpret_cast(m_socket.native_handle()), 86 | operation.get_overlapped()); 87 | } 88 | 89 | void cppcoro::net::socket_disconnect_operation_impl::get_result( 90 | cppcoro::detail::win32_overlapped_operation_base& operation) 91 | { 92 | if (operation.m_errorCode != ERROR_SUCCESS) 93 | { 94 | if (operation.m_errorCode == ERROR_OPERATION_ABORTED) 95 | { 96 | throw operation_cancelled{}; 97 | } 98 | 99 | throw std::system_error{ 100 | static_cast(operation.m_errorCode), 101 | std::system_category(), 102 | "Disconnect operation failed: DisconnectEx" 103 | }; 104 | } 105 | } 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /lib/socket_helpers.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include "socket_helpers.hpp" 7 | 8 | #include 9 | 10 | #if CPPCORO_OS_WINNT 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | cppcoro::net::ip_endpoint 21 | cppcoro::net::detail::sockaddr_to_ip_endpoint(const sockaddr& address) noexcept 22 | { 23 | if (address.sa_family == AF_INET) 24 | { 25 | SOCKADDR_IN ipv4Address; 26 | std::memcpy(&ipv4Address, &address, sizeof(ipv4Address)); 27 | 28 | std::uint8_t addressBytes[4]; 29 | std::memcpy(addressBytes, &ipv4Address.sin_addr, 4); 30 | 31 | return ipv4_endpoint{ 32 | ipv4_address{ addressBytes }, 33 | ntohs(ipv4Address.sin_port) 34 | }; 35 | } 36 | else 37 | { 38 | assert(address.sa_family == AF_INET6); 39 | 40 | SOCKADDR_IN6 ipv6Address; 41 | std::memcpy(&ipv6Address, &address, sizeof(ipv6Address)); 42 | 43 | return ipv6_endpoint{ 44 | ipv6_address{ ipv6Address.sin6_addr.u.Byte }, 45 | ntohs(ipv6Address.sin6_port) 46 | }; 47 | } 48 | } 49 | 50 | int cppcoro::net::detail::ip_endpoint_to_sockaddr( 51 | const ip_endpoint& endPoint, 52 | std::reference_wrapper address) noexcept 53 | { 54 | if (endPoint.is_ipv4()) 55 | { 56 | const auto& ipv4EndPoint = endPoint.to_ipv4(); 57 | 58 | SOCKADDR_IN ipv4Address; 59 | ipv4Address.sin_family = AF_INET; 60 | std::memcpy(&ipv4Address.sin_addr, ipv4EndPoint.address().bytes(), 4); 61 | ipv4Address.sin_port = htons(ipv4EndPoint.port()); 62 | std::memset(&ipv4Address.sin_zero, 0, sizeof(ipv4Address.sin_zero)); 63 | 64 | std::memcpy(&address.get(), &ipv4Address, sizeof(ipv4Address)); 65 | 66 | return sizeof(SOCKADDR_IN); 67 | } 68 | else 69 | { 70 | const auto& ipv6EndPoint = endPoint.to_ipv6(); 71 | 72 | SOCKADDR_IN6 ipv6Address; 73 | ipv6Address.sin6_family = AF_INET6; 74 | std::memcpy(&ipv6Address.sin6_addr, ipv6EndPoint.address().bytes(), 16); 75 | ipv6Address.sin6_port = htons(ipv6EndPoint.port()); 76 | ipv6Address.sin6_flowinfo = 0; 77 | ipv6Address.sin6_scope_struct = SCOPEID_UNSPECIFIED_INIT; 78 | 79 | std::memcpy(&address.get(), &ipv6Address, sizeof(ipv6Address)); 80 | 81 | return sizeof(SOCKADDR_IN6); 82 | } 83 | } 84 | 85 | #endif // CPPCORO_OS_WINNT 86 | -------------------------------------------------------------------------------- /lib/socket_helpers.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_PRIVATE_SOCKET_HELPERS_HPP_INCLUDED 6 | #define CPPCORO_PRIVATE_SOCKET_HELPERS_HPP_INCLUDED 7 | 8 | #include 9 | 10 | #if CPPCORO_OS_WINNT 11 | # include 12 | struct sockaddr; 13 | struct sockaddr_storage; 14 | #endif 15 | 16 | namespace cppcoro 17 | { 18 | namespace net 19 | { 20 | class ip_endpoint; 21 | 22 | namespace detail 23 | { 24 | #if CPPCORO_OS_WINNT 25 | /// Convert a sockaddr to an IP endpoint. 26 | ip_endpoint sockaddr_to_ip_endpoint(const sockaddr& address) noexcept; 27 | 28 | /// Converts an ip_endpoint to a sockaddr structure. 29 | /// 30 | /// \param endPoint 31 | /// The IP endpoint to convert to a sockaddr structure. 32 | /// 33 | /// \param address 34 | /// The sockaddr structure to populate. 35 | /// 36 | /// \return 37 | /// The length of the sockaddr structure that was populated. 38 | int ip_endpoint_to_sockaddr( 39 | const ip_endpoint& endPoint, 40 | std::reference_wrapper address) noexcept; 41 | 42 | #endif 43 | } 44 | } 45 | } 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /lib/socket_recv_from_operation.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | #include 8 | 9 | #if CPPCORO_OS_WINNT 10 | # include "socket_helpers.hpp" 11 | 12 | # include 13 | # include 14 | # include 15 | # include 16 | 17 | bool cppcoro::net::socket_recv_from_operation_impl::try_start( 18 | cppcoro::detail::win32_overlapped_operation_base& operation) noexcept 19 | { 20 | static_assert( 21 | sizeof(m_sourceSockaddrStorage) >= sizeof(SOCKADDR_IN) && 22 | sizeof(m_sourceSockaddrStorage) >= sizeof(SOCKADDR_IN6)); 23 | static_assert( 24 | sockaddrStorageAlignment >= alignof(SOCKADDR_IN) && 25 | sockaddrStorageAlignment >= alignof(SOCKADDR_IN6)); 26 | 27 | // Need to read this flag before starting the operation, otherwise 28 | // it may be possible that the operation will complete immediately 29 | // on another thread, resume the coroutine and then destroy the 30 | // socket before we get a chance to read it. 31 | const bool skipCompletionOnSuccess = m_socket.skip_completion_on_success(); 32 | 33 | m_sourceSockaddrLength = sizeof(m_sourceSockaddrStorage); 34 | 35 | DWORD numberOfBytesReceived = 0; 36 | DWORD flags = 0; 37 | int result = ::WSARecvFrom( 38 | m_socket.native_handle(), 39 | reinterpret_cast(&m_buffer), 40 | 1, // buffer count 41 | &numberOfBytesReceived, 42 | &flags, 43 | reinterpret_cast(&m_sourceSockaddrStorage), 44 | &m_sourceSockaddrLength, 45 | operation.get_overlapped(), 46 | nullptr); 47 | if (result == SOCKET_ERROR) 48 | { 49 | int errorCode = ::WSAGetLastError(); 50 | if (errorCode != WSA_IO_PENDING) 51 | { 52 | // Failed synchronously. 53 | operation.m_errorCode = static_cast(errorCode); 54 | operation.m_numberOfBytesTransferred = numberOfBytesReceived; 55 | return false; 56 | } 57 | } 58 | else if (skipCompletionOnSuccess) 59 | { 60 | // Completed synchronously, no completion event will be posted to the IOCP. 61 | operation.m_errorCode = ERROR_SUCCESS; 62 | operation.m_numberOfBytesTransferred = numberOfBytesReceived; 63 | return false; 64 | } 65 | 66 | // Operation will complete asynchronously. 67 | return true; 68 | } 69 | 70 | void cppcoro::net::socket_recv_from_operation_impl::cancel( 71 | cppcoro::detail::win32_overlapped_operation_base& operation) noexcept 72 | { 73 | (void)::CancelIoEx( 74 | reinterpret_cast(m_socket.native_handle()), 75 | operation.get_overlapped()); 76 | } 77 | 78 | std::tuple 79 | cppcoro::net::socket_recv_from_operation_impl::get_result( 80 | cppcoro::detail::win32_overlapped_operation_base& operation) 81 | { 82 | if (operation.m_errorCode != ERROR_SUCCESS) 83 | { 84 | throw std::system_error( 85 | static_cast(operation.m_errorCode), 86 | std::system_category(), 87 | "Error receiving message on socket: WSARecvFrom"); 88 | } 89 | 90 | return std::make_tuple( 91 | static_cast(operation.m_numberOfBytesTransferred), 92 | detail::sockaddr_to_ip_endpoint( 93 | *reinterpret_cast(&m_sourceSockaddrStorage))); 94 | } 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /lib/socket_recv_operation.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | #include 8 | 9 | #if CPPCORO_OS_WINNT 10 | # include 11 | # include 12 | # include 13 | # include 14 | 15 | bool cppcoro::net::socket_recv_operation_impl::try_start( 16 | cppcoro::detail::win32_overlapped_operation_base& operation) noexcept 17 | { 18 | // Need to read this flag before starting the operation, otherwise 19 | // it may be possible that the operation will complete immediately 20 | // on another thread and then destroy the socket before we get a 21 | // chance to read it. 22 | const bool skipCompletionOnSuccess = m_socket.skip_completion_on_success(); 23 | 24 | DWORD numberOfBytesReceived = 0; 25 | DWORD flags = 0; 26 | int result = ::WSARecv( 27 | m_socket.native_handle(), 28 | reinterpret_cast(&m_buffer), 29 | 1, // buffer count 30 | &numberOfBytesReceived, 31 | &flags, 32 | operation.get_overlapped(), 33 | nullptr); 34 | if (result == SOCKET_ERROR) 35 | { 36 | int errorCode = ::WSAGetLastError(); 37 | if (errorCode != WSA_IO_PENDING) 38 | { 39 | // Failed synchronously. 40 | operation.m_errorCode = static_cast(errorCode); 41 | operation.m_numberOfBytesTransferred = numberOfBytesReceived; 42 | return false; 43 | } 44 | } 45 | else if (skipCompletionOnSuccess) 46 | { 47 | // Completed synchronously, no completion event will be posted to the IOCP. 48 | operation.m_errorCode = ERROR_SUCCESS; 49 | operation.m_numberOfBytesTransferred = numberOfBytesReceived; 50 | return false; 51 | } 52 | 53 | // Operation will complete asynchronously. 54 | return true; 55 | } 56 | 57 | 58 | void cppcoro::net::socket_recv_operation_impl::cancel( 59 | cppcoro::detail::win32_overlapped_operation_base& operation) noexcept 60 | { 61 | (void)::CancelIoEx( 62 | reinterpret_cast(m_socket.native_handle()), 63 | operation.get_overlapped()); 64 | } 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /lib/socket_send_operation.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | #include 8 | 9 | #if CPPCORO_OS_WINNT 10 | # include 11 | # include 12 | # include 13 | # include 14 | 15 | bool cppcoro::net::socket_send_operation_impl::try_start( 16 | cppcoro::detail::win32_overlapped_operation_base& operation) noexcept 17 | { 18 | // Need to read this flag before starting the operation, otherwise 19 | // it may be possible that the operation will complete immediately 20 | // on another thread and then destroy the socket before we get a 21 | // chance to read it. 22 | const bool skipCompletionOnSuccess = m_socket.skip_completion_on_success(); 23 | 24 | DWORD numberOfBytesSent = 0; 25 | int result = ::WSASend( 26 | m_socket.native_handle(), 27 | reinterpret_cast(&m_buffer), 28 | 1, // buffer count 29 | &numberOfBytesSent, 30 | 0, // flags 31 | operation.get_overlapped(), 32 | nullptr); 33 | if (result == SOCKET_ERROR) 34 | { 35 | int errorCode = ::WSAGetLastError(); 36 | if (errorCode != WSA_IO_PENDING) 37 | { 38 | // Failed synchronously. 39 | operation.m_errorCode = static_cast(errorCode); 40 | operation.m_numberOfBytesTransferred = numberOfBytesSent; 41 | return false; 42 | } 43 | } 44 | else if (skipCompletionOnSuccess) 45 | { 46 | // Completed synchronously, no completion event will be posted to the IOCP. 47 | operation.m_errorCode = ERROR_SUCCESS; 48 | operation.m_numberOfBytesTransferred = numberOfBytesSent; 49 | return false; 50 | } 51 | 52 | // Operation will complete asynchronously. 53 | return true; 54 | } 55 | 56 | void cppcoro::net::socket_send_operation_impl::cancel( 57 | cppcoro::detail::win32_overlapped_operation_base& operation) noexcept 58 | { 59 | (void)::CancelIoEx( 60 | reinterpret_cast(m_socket.native_handle()), 61 | operation.get_overlapped()); 62 | } 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /lib/socket_send_to_operation.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | #include 8 | 9 | #if CPPCORO_OS_WINNT 10 | # include "socket_helpers.hpp" 11 | 12 | # include 13 | # include 14 | # include 15 | # include 16 | 17 | bool cppcoro::net::socket_send_to_operation_impl::try_start( 18 | cppcoro::detail::win32_overlapped_operation_base& operation) noexcept 19 | { 20 | // Need to read this flag before starting the operation, otherwise 21 | // it may be possible that the operation will complete immediately 22 | // on another thread and then destroy the socket before we get a 23 | // chance to read it. 24 | const bool skipCompletionOnSuccess = m_socket.skip_completion_on_success(); 25 | 26 | SOCKADDR_STORAGE destinationAddress; 27 | const int destinationLength = detail::ip_endpoint_to_sockaddr( 28 | m_destination, std::ref(destinationAddress)); 29 | 30 | DWORD numberOfBytesSent = 0; 31 | int result = ::WSASendTo( 32 | m_socket.native_handle(), 33 | reinterpret_cast(&m_buffer), 34 | 1, // buffer count 35 | &numberOfBytesSent, 36 | 0, // flags 37 | reinterpret_cast(&destinationAddress), 38 | destinationLength, 39 | operation.get_overlapped(), 40 | nullptr); 41 | if (result == SOCKET_ERROR) 42 | { 43 | int errorCode = ::WSAGetLastError(); 44 | if (errorCode != WSA_IO_PENDING) 45 | { 46 | // Failed synchronously. 47 | operation.m_errorCode = static_cast(errorCode); 48 | operation.m_numberOfBytesTransferred = numberOfBytesSent; 49 | return false; 50 | } 51 | } 52 | else if (skipCompletionOnSuccess) 53 | { 54 | // Completed synchronously, no completion event will be posted to the IOCP. 55 | operation.m_errorCode = ERROR_SUCCESS; 56 | operation.m_numberOfBytesTransferred = numberOfBytesSent; 57 | return false; 58 | } 59 | 60 | // Operation will complete asynchronously. 61 | return true; 62 | } 63 | 64 | void cppcoro::net::socket_send_to_operation_impl::cancel( 65 | cppcoro::detail::win32_overlapped_operation_base& operation) noexcept 66 | { 67 | (void)::CancelIoEx( 68 | reinterpret_cast(m_socket.native_handle()), 69 | operation.get_overlapped()); 70 | } 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /lib/spin_mutex.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include "spin_mutex.hpp" 7 | #include "spin_wait.hpp" 8 | 9 | namespace cppcoro 10 | { 11 | spin_mutex::spin_mutex() noexcept 12 | : m_isLocked(false) 13 | { 14 | } 15 | 16 | bool spin_mutex::try_lock() noexcept 17 | { 18 | return !m_isLocked.exchange(true, std::memory_order_acquire); 19 | } 20 | 21 | void spin_mutex::lock() noexcept 22 | { 23 | spin_wait wait; 24 | while (!try_lock()) 25 | { 26 | while (m_isLocked.load(std::memory_order_relaxed)) 27 | { 28 | wait.spin_one(); 29 | } 30 | } 31 | } 32 | 33 | void spin_mutex::unlock() noexcept 34 | { 35 | m_isLocked.store(false, std::memory_order_release); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/spin_mutex.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_SPIN_MUTEX_HPP_INCLUDED 6 | #define CPPCORO_SPIN_MUTEX_HPP_INCLUDED 7 | 8 | #include 9 | 10 | namespace cppcoro 11 | { 12 | class spin_mutex 13 | { 14 | public: 15 | 16 | /// Initialise the mutex to the unlocked state. 17 | spin_mutex() noexcept; 18 | 19 | /// Attempt to lock the mutex without blocking 20 | /// 21 | /// \return 22 | /// true if the lock was acquired, false if the lock was already held 23 | /// and could not be immediately acquired. 24 | bool try_lock() noexcept; 25 | 26 | /// Block the current thread until the lock is acquired. 27 | /// 28 | /// This will busy-wait until it acquires the lock. 29 | /// 30 | /// This has 'acquire' memory semantics and synchronises 31 | /// with prior calls to unlock(). 32 | void lock() noexcept; 33 | 34 | /// Release the lock. 35 | /// 36 | /// This has 'release' memory semantics and synchronises with 37 | /// lock() and try_lock(). 38 | void unlock() noexcept; 39 | 40 | private: 41 | 42 | std::atomic m_isLocked; 43 | 44 | }; 45 | } 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /lib/spin_wait.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include "spin_wait.hpp" 7 | 8 | #include 9 | #include 10 | 11 | #if CPPCORO_OS_WINNT 12 | # define WIN32_LEAN_AND_MEAN 13 | # include 14 | #endif 15 | 16 | namespace 17 | { 18 | namespace local 19 | { 20 | constexpr std::uint32_t yield_threshold = 10; 21 | } 22 | } 23 | 24 | namespace cppcoro 25 | { 26 | spin_wait::spin_wait() noexcept 27 | { 28 | reset(); 29 | } 30 | 31 | bool spin_wait::next_spin_will_yield() const noexcept 32 | { 33 | return m_count >= local::yield_threshold; 34 | } 35 | 36 | void spin_wait::reset() noexcept 37 | { 38 | static const std::uint32_t initialCount = 39 | std::thread::hardware_concurrency() > 1 ? 0 : local::yield_threshold; 40 | m_count = initialCount; 41 | } 42 | 43 | void spin_wait::spin_one() noexcept 44 | { 45 | #if CPPCORO_OS_WINNT 46 | // Spin strategy taken from .NET System.SpinWait class. 47 | // I assume the Microsoft developers knew what they're doing. 48 | if (!next_spin_will_yield()) 49 | { 50 | // CPU-level pause 51 | // Allow other hyper-threads to run while we busy-wait. 52 | 53 | // Make each busy-spin exponentially longer 54 | const std::uint32_t loopCount = 2u << m_count; 55 | for (std::uint32_t i = 0; i < loopCount; ++i) 56 | { 57 | ::YieldProcessor(); 58 | ::YieldProcessor(); 59 | } 60 | } 61 | else 62 | { 63 | // We've already spun a number of iterations. 64 | // 65 | const auto yieldCount = m_count - local::yield_threshold; 66 | if (yieldCount % 20 == 19) 67 | { 68 | // Yield remainder of time slice to another thread and 69 | // don't schedule this thread for a little while. 70 | ::SleepEx(1, FALSE); 71 | } 72 | else if (yieldCount % 5 == 4) 73 | { 74 | // Yield remainder of time slice to another thread 75 | // that is ready to run (possibly from another processor?). 76 | ::SleepEx(0, FALSE); 77 | } 78 | else 79 | { 80 | // Yield to another thread that is ready to run on the 81 | // current processor. 82 | ::SwitchToThread(); 83 | } 84 | } 85 | #else 86 | if (next_spin_will_yield()) 87 | { 88 | std::this_thread::yield(); 89 | } 90 | #endif 91 | 92 | ++m_count; 93 | if (m_count == 0) 94 | { 95 | // Don't wrap around to zero as this would go back to 96 | // busy-waiting. 97 | m_count = local::yield_threshold; 98 | } 99 | } 100 | } 101 | 102 | -------------------------------------------------------------------------------- /lib/spin_wait.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_SPIN_WAIT_HPP_INCLUDED 6 | #define CPPCORO_SPIN_WAIT_HPP_INCLUDED 7 | 8 | #include 9 | 10 | namespace cppcoro 11 | { 12 | class spin_wait 13 | { 14 | public: 15 | 16 | spin_wait() noexcept; 17 | 18 | bool next_spin_will_yield() const noexcept; 19 | 20 | void spin_one() noexcept; 21 | 22 | void reset() noexcept; 23 | 24 | private: 25 | 26 | std::uint32_t m_count; 27 | 28 | }; 29 | } 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /lib/use.cake: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Copyright (c) Lewis Baker 3 | # Licenced under MIT license. See LICENSE.txt for details. 4 | ############################################################################### 5 | 6 | import cake.path 7 | 8 | from cake.tools import script, env, compiler, variant 9 | 10 | compiler.addIncludePath(env.expand('${CPPCORO}/include')) 11 | 12 | buildScript = script.get(script.cwd('build.cake')) 13 | compiler.addLibrary(buildScript.getResult('library')) 14 | 15 | if variant.platform == "windows": 16 | compiler.addLibrary("Synchronization") 17 | compiler.addLibrary("kernel32") 18 | compiler.addLibrary("WS2_32") 19 | compiler.addLibrary("Mswsock") 20 | 21 | -------------------------------------------------------------------------------- /lib/win32.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #ifndef WIN32_LEAN_AND_MEAN 9 | # define WIN32_LEAN_AND_MEAN 10 | #endif 11 | #include 12 | 13 | void cppcoro::detail::win32::safe_handle::close() noexcept 14 | { 15 | if (m_handle != nullptr && m_handle != INVALID_HANDLE_VALUE) 16 | { 17 | ::CloseHandle(m_handle); 18 | m_handle = nullptr; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/writable_file.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #include 9 | 10 | #if CPPCORO_OS_WINNT 11 | # ifndef WIN32_LEAN_AND_MEAN 12 | # define WIN32_LEAN_AND_MEAN 13 | # endif 14 | # include 15 | 16 | void cppcoro::writable_file::set_size( 17 | std::uint64_t fileSize) 18 | { 19 | LARGE_INTEGER position; 20 | position.QuadPart = fileSize; 21 | 22 | BOOL ok = ::SetFilePointerEx(m_fileHandle.handle(), position, nullptr, FILE_BEGIN); 23 | if (!ok) 24 | { 25 | DWORD errorCode = ::GetLastError(); 26 | throw std::system_error 27 | { 28 | static_cast(errorCode), 29 | std::system_category(), 30 | "error setting file size: SetFilePointerEx" 31 | }; 32 | } 33 | 34 | ok = ::SetEndOfFile(m_fileHandle.handle()); 35 | if (!ok) 36 | { 37 | DWORD errorCode = ::GetLastError(); 38 | throw std::system_error 39 | { 40 | static_cast(errorCode), 41 | std::system_category(), 42 | "error setting file size: SetEndOfFile" 43 | }; 44 | } 45 | } 46 | 47 | cppcoro::file_write_operation cppcoro::writable_file::write( 48 | std::uint64_t offset, 49 | const void* buffer, 50 | std::size_t byteCount) noexcept 51 | { 52 | return file_write_operation{ 53 | m_fileHandle.handle(), 54 | offset, 55 | buffer, 56 | byteCount 57 | }; 58 | } 59 | 60 | cppcoro::file_write_operation_cancellable cppcoro::writable_file::write( 61 | std::uint64_t offset, 62 | const void* buffer, 63 | std::size_t byteCount, 64 | cancellation_token ct) noexcept 65 | { 66 | return file_write_operation_cancellable{ 67 | m_fileHandle.handle(), 68 | offset, 69 | buffer, 70 | byteCount, 71 | std::move(ct) 72 | }; 73 | } 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /lib/write_only_file.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #if CPPCORO_OS_WINNT 9 | # ifndef WIN32_LEAN_AND_MEAN 10 | # define WIN32_LEAN_AND_MEAN 11 | # endif 12 | # include 13 | 14 | cppcoro::write_only_file cppcoro::write_only_file::open( 15 | io_service& ioService, 16 | const std::experimental::filesystem::path& path, 17 | file_open_mode openMode, 18 | file_share_mode shareMode, 19 | file_buffering_mode bufferingMode) 20 | { 21 | return write_only_file(file::open( 22 | GENERIC_WRITE, 23 | ioService, 24 | path, 25 | openMode, 26 | shareMode, 27 | bufferingMode)); 28 | } 29 | 30 | cppcoro::write_only_file::write_only_file( 31 | detail::win32::safe_handle&& fileHandle) noexcept 32 | : file(std::move(fileHandle)) 33 | , writable_file(detail::win32::safe_handle{}) 34 | { 35 | } 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /test/async_auto_reset_event_tests.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include "doctest/doctest.h" 22 | 23 | TEST_SUITE_BEGIN("async_auto_reset_event"); 24 | 25 | TEST_CASE("single waiter") 26 | { 27 | cppcoro::async_auto_reset_event event; 28 | 29 | bool started = false; 30 | bool finished = false; 31 | auto run = [&]() -> cppcoro::task<> 32 | { 33 | started = true; 34 | co_await event; 35 | finished = true; 36 | }; 37 | 38 | auto check = [&]() -> cppcoro::task<> 39 | { 40 | CHECK(started); 41 | CHECK(!finished); 42 | 43 | event.set(); 44 | 45 | CHECK(finished); 46 | 47 | co_return; 48 | }; 49 | 50 | cppcoro::sync_wait(cppcoro::when_all_ready(run(), check())); 51 | } 52 | 53 | TEST_CASE("multiple waiters") 54 | { 55 | cppcoro::async_auto_reset_event event; 56 | 57 | 58 | auto run = [&](bool& flag) -> cppcoro::task<> 59 | { 60 | co_await event; 61 | flag = true; 62 | }; 63 | 64 | bool completed1 = false; 65 | bool completed2 = false; 66 | 67 | auto check = [&]() -> cppcoro::task<> 68 | { 69 | CHECK(!completed1); 70 | CHECK(!completed2); 71 | 72 | event.set(); 73 | 74 | CHECK(completed1); 75 | CHECK(!completed2); 76 | 77 | event.set(); 78 | 79 | CHECK(completed2); 80 | 81 | co_return; 82 | }; 83 | 84 | cppcoro::sync_wait(cppcoro::when_all_ready( 85 | run(completed1), 86 | run(completed2), 87 | check())); 88 | } 89 | 90 | TEST_CASE("multi-threaded") 91 | { 92 | cppcoro::static_thread_pool tp{ 3 }; 93 | 94 | auto run = [&]() -> cppcoro::task<> 95 | { 96 | cppcoro::async_auto_reset_event event; 97 | 98 | int value = 0; 99 | 100 | auto startWaiter = [&]() -> cppcoro::task<> 101 | { 102 | co_await tp.schedule(); 103 | co_await event; 104 | ++value; 105 | event.set(); 106 | }; 107 | 108 | auto startSignaller = [&]() -> cppcoro::task<> 109 | { 110 | co_await tp.schedule(); 111 | value = 5; 112 | event.set(); 113 | }; 114 | 115 | std::vector> tasks; 116 | 117 | tasks.emplace_back(startSignaller()); 118 | 119 | for (int i = 0; i < 1000; ++i) 120 | { 121 | tasks.emplace_back(startWaiter()); 122 | } 123 | 124 | co_await cppcoro::when_all(std::move(tasks)); 125 | 126 | // NOTE: Can't use CHECK() here because it's not thread-safe 127 | assert(value == 1005); 128 | }; 129 | 130 | std::vector> tasks; 131 | 132 | for (int i = 0; i < 1000; ++i) 133 | { 134 | tasks.emplace_back(run()); 135 | } 136 | 137 | cppcoro::sync_wait(cppcoro::when_all(std::move(tasks))); 138 | } 139 | 140 | TEST_SUITE_END(); 141 | -------------------------------------------------------------------------------- /test/async_latch_tests.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include "doctest/doctest.h" 14 | 15 | TEST_SUITE_BEGIN("async_latch"); 16 | 17 | using namespace cppcoro; 18 | 19 | TEST_CASE("latch constructed with zero count is initially ready") 20 | { 21 | async_latch latch(0); 22 | CHECK(latch.is_ready()); 23 | } 24 | 25 | TEST_CASE("latch constructed with negative count is initially ready") 26 | { 27 | async_latch latch(-3); 28 | CHECK(latch.is_ready()); 29 | } 30 | 31 | TEST_CASE("count_down and is_ready") 32 | { 33 | async_latch latch(3); 34 | CHECK(!latch.is_ready()); 35 | latch.count_down(); 36 | CHECK(!latch.is_ready()); 37 | latch.count_down(); 38 | CHECK(!latch.is_ready()); 39 | latch.count_down(); 40 | CHECK(latch.is_ready()); 41 | } 42 | 43 | TEST_CASE("count_down by n") 44 | { 45 | async_latch latch(5); 46 | latch.count_down(3); 47 | CHECK(!latch.is_ready()); 48 | latch.count_down(2); 49 | CHECK(latch.is_ready()); 50 | } 51 | 52 | TEST_CASE("single awaiter") 53 | { 54 | async_latch latch(2); 55 | bool after = false; 56 | sync_wait(when_all_ready( 57 | [&]() -> task<> 58 | { 59 | co_await latch; 60 | after = true; 61 | }(), 62 | [&]() -> task<> 63 | { 64 | CHECK(!after); 65 | latch.count_down(); 66 | CHECK(!after); 67 | latch.count_down(); 68 | CHECK(after); 69 | co_return; 70 | }() 71 | )); 72 | } 73 | 74 | TEST_CASE("multiple awaiters") 75 | { 76 | async_latch latch(2); 77 | bool after1 = false; 78 | bool after2 = false; 79 | bool after3 = false; 80 | sync_wait(when_all_ready( 81 | [&]() -> task<> 82 | { 83 | co_await latch; 84 | after1 = true; 85 | }(), 86 | [&]() -> task<> 87 | { 88 | co_await latch; 89 | after2 = true; 90 | }(), 91 | [&]() -> task<> 92 | { 93 | co_await latch; 94 | after3 = true; 95 | }(), 96 | [&]() -> task<> 97 | { 98 | CHECK(!after1); 99 | CHECK(!after2); 100 | CHECK(!after3); 101 | latch.count_down(); 102 | CHECK(!after1); 103 | CHECK(!after2); 104 | CHECK(!after3); 105 | latch.count_down(); 106 | CHECK(after1); 107 | CHECK(after2); 108 | CHECK(after3); 109 | co_return; 110 | }())); 111 | } 112 | 113 | TEST_SUITE_END(); 114 | -------------------------------------------------------------------------------- /test/async_manual_reset_event_tests.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "doctest/doctest.h" 13 | 14 | TEST_SUITE_BEGIN("async_manual_reset_event"); 15 | 16 | TEST_CASE("default constructor initially not set") 17 | { 18 | cppcoro::async_manual_reset_event event; 19 | CHECK(!event.is_set()); 20 | } 21 | 22 | TEST_CASE("construct event initially set") 23 | { 24 | cppcoro::async_manual_reset_event event{ true }; 25 | CHECK(event.is_set()); 26 | } 27 | 28 | TEST_CASE("set and reset") 29 | { 30 | cppcoro::async_manual_reset_event event; 31 | CHECK(!event.is_set()); 32 | event.set(); 33 | CHECK(event.is_set()); 34 | event.set(); 35 | CHECK(event.is_set()); 36 | event.reset(); 37 | CHECK(!event.is_set()); 38 | event.reset(); 39 | CHECK(!event.is_set()); 40 | event.set(); 41 | CHECK(event.is_set()); 42 | } 43 | 44 | TEST_CASE("await not set event") 45 | { 46 | cppcoro::async_manual_reset_event event; 47 | 48 | auto createWaiter = [&](bool& flag) -> cppcoro::task<> 49 | { 50 | co_await event; 51 | flag = true; 52 | }; 53 | 54 | bool completed1 = false; 55 | bool completed2 = false; 56 | 57 | auto check = [&]() -> cppcoro::task<> 58 | { 59 | CHECK(!completed1); 60 | CHECK(!completed2); 61 | 62 | event.reset(); 63 | 64 | CHECK(!completed1); 65 | CHECK(!completed2); 66 | 67 | event.set(); 68 | 69 | CHECK(completed1); 70 | CHECK(completed2); 71 | 72 | co_return; 73 | }; 74 | 75 | cppcoro::sync_wait(cppcoro::when_all_ready( 76 | createWaiter(completed1), 77 | createWaiter(completed2), 78 | check())); 79 | } 80 | 81 | TEST_CASE("awaiting already set event doesn't suspend") 82 | { 83 | cppcoro::async_manual_reset_event event{ true }; 84 | 85 | auto createWaiter = [&]() -> cppcoro::task<> 86 | { 87 | co_await event; 88 | }; 89 | 90 | // Should complete without blocking. 91 | cppcoro::sync_wait(cppcoro::when_all_ready( 92 | createWaiter(), 93 | createWaiter())); 94 | } 95 | 96 | TEST_SUITE_END(); 97 | -------------------------------------------------------------------------------- /test/async_mutex_tests.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include "doctest/doctest.h" 14 | 15 | TEST_SUITE_BEGIN("async_mutex"); 16 | 17 | TEST_CASE("try_lock") 18 | { 19 | cppcoro::async_mutex mutex; 20 | 21 | CHECK(mutex.try_lock()); 22 | 23 | CHECK_FALSE(mutex.try_lock()); 24 | 25 | mutex.unlock(); 26 | 27 | CHECK(mutex.try_lock()); 28 | } 29 | 30 | #if 0 31 | TEST_CASE("multiple lockers") 32 | { 33 | int value = 0; 34 | cppcoro::async_mutex mutex; 35 | cppcoro::single_consumer_event a; 36 | cppcoro::single_consumer_event b; 37 | cppcoro::single_consumer_event c; 38 | cppcoro::single_consumer_event d; 39 | 40 | auto f = [&](cppcoro::single_consumer_event& e) -> cppcoro::task<> 41 | { 42 | auto lock = co_await mutex.scoped_lock_async(); 43 | co_await e; 44 | ++value; 45 | }; 46 | 47 | auto check = [&]() -> cppcoro::task<> 48 | { 49 | CHECK(value == 0); 50 | 51 | a.set(); 52 | 53 | CHECK(value == 1); 54 | 55 | auto check2 = [&]() -> cppcoro::task<> 56 | { 57 | b.set(); 58 | 59 | CHECK(value == 2); 60 | 61 | c.set(); 62 | 63 | CHECK(value == 3); 64 | 65 | d.set(); 66 | 67 | CHECK(value == 4); 68 | 69 | co_return; 70 | }; 71 | 72 | // Now that we've queued some waiters and released one waiter this will 73 | // have acquired the list of pending waiters in the local cache. 74 | // We'll now queue up another one before releasing any more waiters 75 | // to test the code-path that looks at the newly queued waiter list 76 | // when the cache of waiters is exhausted. 77 | (void)co_await cppcoro::when_all_ready(f(d), check2()); 78 | }; 79 | 80 | cppcoro::sync_wait(cppcoro::when_all_ready( 81 | f(a), 82 | f(b), 83 | f(c), 84 | check())); 85 | 86 | CHECK(value == 4); 87 | } 88 | #endif 89 | 90 | TEST_SUITE_END(); 91 | -------------------------------------------------------------------------------- /test/build.cake: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Copyright (c) Lewis Baker 3 | # Licenced under MIT license. See LICENSE.txt for details. 4 | ############################################################################### 5 | 6 | import cake.path 7 | 8 | from cake.tools import script, env, compiler, project, variant, test 9 | 10 | script.include([ 11 | env.expand('${CPPCORO}/lib/use.cake'), 12 | ]) 13 | 14 | headers = script.cwd([ 15 | "counted.hpp", 16 | "io_service_fixture.hpp", 17 | ]) 18 | 19 | sources = script.cwd([ 20 | 'main.cpp', 21 | 'counted.cpp', 22 | 'generator_tests.cpp', 23 | 'recursive_generator_tests.cpp', 24 | 'async_generator_tests.cpp', 25 | 'async_auto_reset_event_tests.cpp', 26 | 'async_manual_reset_event_tests.cpp', 27 | 'async_mutex_tests.cpp', 28 | 'async_latch_tests.cpp', 29 | 'cancellation_token_tests.cpp', 30 | 'task_tests.cpp', 31 | 'sequence_barrier_tests.cpp', 32 | 'shared_task_tests.cpp', 33 | 'sync_wait_tests.cpp', 34 | 'single_consumer_async_auto_reset_event_tests.cpp', 35 | 'single_producer_sequencer_tests.cpp', 36 | 'multi_producer_sequencer_tests.cpp', 37 | 'when_all_tests.cpp', 38 | 'when_all_ready_tests.cpp', 39 | 'ip_address_tests.cpp', 40 | 'ip_endpoint_tests.cpp', 41 | 'ipv4_address_tests.cpp', 42 | 'ipv4_endpoint_tests.cpp', 43 | 'ipv6_address_tests.cpp', 44 | 'ipv6_endpoint_tests.cpp', 45 | 'static_thread_pool_tests.cpp', 46 | ]) 47 | 48 | if variant.platform == 'windows': 49 | sources += script.cwd([ 50 | 'scheduling_operator_tests.cpp', 51 | 'io_service_tests.cpp', 52 | 'file_tests.cpp', 53 | 'socket_tests.cpp', 54 | ]) 55 | 56 | extras = script.cwd([ 57 | 'build.cake', 58 | ]) 59 | 60 | intermediateBuildDir = cake.path.join(env.expand('${CPPCORO_BUILD}'), 'test', 'obj') 61 | 62 | compiler.addDefine('CPPCORO_RELEASE_' + variant.release.upper()) 63 | 64 | objects = compiler.objects( 65 | targetDir=intermediateBuildDir, 66 | sources=sources, 67 | ) 68 | 69 | testExe = compiler.program( 70 | target=env.expand('${CPPCORO_BUILD}/test/run'), 71 | sources=objects, 72 | ) 73 | 74 | test.alwaysRun = True 75 | testResult = test.run( 76 | program=testExe, 77 | results=env.expand('${CPPCORO_BUILD}/test/run.results'), 78 | ) 79 | script.addTarget('testresult', testResult) 80 | 81 | vcproj = project.project( 82 | target=env.expand('${CPPCORO_PROJECT}/cppcoro_tests'), 83 | items={ 84 | 'Source': sources + headers, 85 | '': extras, 86 | }, 87 | output=testExe, 88 | ) 89 | 90 | script.setResult( 91 | project=vcproj, 92 | test=testExe, 93 | ) 94 | -------------------------------------------------------------------------------- /test/counted.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include "counted.hpp" 7 | 8 | int counted::default_construction_count; 9 | int counted::copy_construction_count; 10 | int counted::move_construction_count; 11 | int counted::destruction_count; 12 | -------------------------------------------------------------------------------- /test/counted.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_TESTS_COUNTED_HPP_INCLUDED 6 | #define CPPCORO_TESTS_COUNTED_HPP_INCLUDED 7 | 8 | struct counted 9 | { 10 | static int default_construction_count; 11 | static int copy_construction_count; 12 | static int move_construction_count; 13 | static int destruction_count; 14 | 15 | int id; 16 | 17 | static void reset_counts() 18 | { 19 | default_construction_count = 0; 20 | copy_construction_count = 0; 21 | move_construction_count = 0; 22 | destruction_count = 0; 23 | } 24 | 25 | static int construction_count() 26 | { 27 | return default_construction_count + copy_construction_count + move_construction_count; 28 | } 29 | 30 | static int active_count() 31 | { 32 | return construction_count() - destruction_count; 33 | } 34 | 35 | counted() : id(default_construction_count++) {} 36 | counted(const counted& other) : id(other.id) { ++copy_construction_count; } 37 | counted(counted&& other) : id(other.id) { ++move_construction_count; other.id = -1; } 38 | ~counted() { ++destruction_count; } 39 | 40 | }; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /test/io_service_fixture.hpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | #ifndef CPPCORO_TESTS_IO_SERVICE_FIXTURE_HPP_INCLUDED 6 | #define CPPCORO_TESTS_IO_SERVICE_FIXTURE_HPP_INCLUDED 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | /// \brief 14 | /// Test fixture that creates an io_service and starts up a background thread 15 | /// to process I/O completion events. 16 | /// 17 | /// Thread and io_service are shutdown on destruction. 18 | struct io_service_fixture 19 | { 20 | public: 21 | 22 | io_service_fixture(std::uint32_t threadCount = 1) 23 | : m_ioService() 24 | { 25 | m_ioThreads.reserve(threadCount); 26 | try 27 | { 28 | for (std::uint32_t i = 0; i < threadCount; ++i) 29 | { 30 | m_ioThreads.emplace_back([this] { m_ioService.process_events(); }); 31 | } 32 | } 33 | catch (...) 34 | { 35 | stop(); 36 | throw; 37 | } 38 | } 39 | 40 | ~io_service_fixture() 41 | { 42 | stop(); 43 | } 44 | 45 | cppcoro::io_service& io_service() { return m_ioService; } 46 | 47 | private: 48 | 49 | void stop() 50 | { 51 | m_ioService.stop(); 52 | for (auto& thread : m_ioThreads) 53 | { 54 | thread.join(); 55 | } 56 | } 57 | 58 | cppcoro::io_service m_ioService; 59 | std::vector m_ioThreads; 60 | 61 | }; 62 | 63 | template 64 | struct io_service_fixture_with_threads : io_service_fixture 65 | { 66 | io_service_fixture_with_threads() 67 | : io_service_fixture(thread_count) 68 | {} 69 | }; 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /test/ip_address_tests.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #include "doctest/doctest.h" 9 | 10 | TEST_SUITE_BEGIN("ip_address"); 11 | 12 | using cppcoro::net::ip_address; 13 | using cppcoro::net::ipv4_address; 14 | using cppcoro::net::ipv6_address; 15 | 16 | TEST_CASE("default constructor") 17 | { 18 | ip_address x; 19 | CHECK(x.is_ipv4()); 20 | CHECK(x.to_ipv4() == ipv4_address{}); 21 | } 22 | 23 | TEST_CASE("to_string") 24 | { 25 | ip_address a = ipv6_address{ 0xAABBCCDD00112233, 0x0102030405060708 }; 26 | ip_address b = ipv4_address{ 192, 168, 0, 1 }; 27 | 28 | CHECK(a.to_string() == "aabb:ccdd:11:2233:102:304:506:708"); 29 | CHECK(b.to_string() == "192.168.0.1"); 30 | } 31 | 32 | TEST_CASE("from_string") 33 | { 34 | CHECK(ip_address::from_string("") == std::nullopt); 35 | CHECK(ip_address::from_string("foo") == std::nullopt); 36 | CHECK(ip_address::from_string(" 192.168.0.1") == std::nullopt); 37 | CHECK(ip_address::from_string("192.168.0.1asdf") == std::nullopt); 38 | 39 | CHECK(ip_address::from_string("192.168.0.1") == ipv4_address(192, 168, 0, 1)); 40 | CHECK(ip_address::from_string("::192.168.0.1") == ipv6_address(0, 0, 0, 0, 0, 0, 0xc0a8, 0x1)); 41 | CHECK(ip_address::from_string("aabb:ccdd:11:2233:102:304:506:708") == 42 | ipv6_address{ 0xAABBCCDD00112233, 0x0102030405060708 }); 43 | } 44 | 45 | TEST_SUITE_END(); 46 | -------------------------------------------------------------------------------- /test/ip_endpoint_tests.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | #include 8 | 9 | #include "doctest/doctest.h" 10 | 11 | TEST_SUITE_BEGIN("ip_endpoint"); 12 | 13 | using namespace cppcoro::net; 14 | 15 | namespace 16 | { 17 | constexpr bool isMsvc15_5X86Optimised = 18 | #if CPPCORO_COMPILER_MSVC && CPPCORO_CPU_X86 && _MSC_VER == 1912 && defined(CPPCORO_RELEASE_OPTIMISED) 19 | true; 20 | #else 21 | false; 22 | #endif 23 | } 24 | 25 | // BUG: Skip this test under MSVC 15.5 x86 optimised builds due to a compiler bug 26 | // that generates bad code. 27 | // See https://developercommunity.visualstudio.com/content/problem/177151/bad-code-generation-under-x86-optimised-for-stdopt.html 28 | TEST_CASE("to_string" * doctest::skip{ isMsvc15_5X86Optimised }) 29 | { 30 | ip_endpoint a = ipv4_endpoint{ ipv4_address{ 192, 168, 2, 254 }, 80 }; 31 | ip_endpoint b = ipv6_endpoint{ 32 | *ipv6_address::from_string("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), 33 | 22 }; 34 | 35 | CHECK(a.to_string() == "192.168.2.254:80"); 36 | CHECK(b.to_string() == "[2001:db8:85a3::8a2e:370:7334]:22"); 37 | } 38 | 39 | TEST_CASE("from_string" * doctest::skip{ isMsvc15_5X86Optimised }) 40 | { 41 | CHECK(ip_endpoint::from_string("") == std::nullopt); 42 | CHECK(ip_endpoint::from_string("[foo]:123") == std::nullopt); 43 | CHECK(ip_endpoint::from_string("[123]:1000") == std::nullopt); 44 | CHECK(ip_endpoint::from_string("[10.11.12.13]:1000") == std::nullopt); 45 | 46 | CHECK(ip_endpoint::from_string("192.168.2.254:80") == 47 | ipv4_endpoint{ 48 | ipv4_address{ 192, 168, 2, 254 }, 80 }); 49 | CHECK(ip_endpoint::from_string("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:443") == 50 | ipv6_endpoint{ 51 | ipv6_address{ 0x2001, 0xdb8, 0x85a3, 0x0, 0x0, 0x8a2e, 0x370, 0x7334 }, 52 | 443 }); 53 | } 54 | 55 | TEST_SUITE_END(); 56 | 57 | -------------------------------------------------------------------------------- /test/ipv4_address_tests.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #include "doctest/doctest.h" 9 | 10 | 11 | TEST_SUITE_BEGIN("ipv4_address"); 12 | 13 | using cppcoro::net::ipv4_address; 14 | 15 | TEST_CASE("DefaultConstructToZeroes") 16 | { 17 | CHECK(ipv4_address{}.to_integer() == 0u); 18 | } 19 | 20 | TEST_CASE("to_integer() is BigEndian") 21 | { 22 | ipv4_address address{ 10, 11, 12, 13 }; 23 | CHECK(address.to_integer() == 0x0A0B0C0Du); 24 | } 25 | 26 | TEST_CASE("is_loopback()") 27 | { 28 | CHECK(ipv4_address{ 127, 0, 0, 1 }.is_loopback()); 29 | CHECK(ipv4_address{ 127, 0, 0, 50 }.is_loopback()); 30 | CHECK(ipv4_address{ 127, 5, 10, 15 }.is_loopback()); 31 | CHECK(!ipv4_address{ 10, 11, 12, 13 }.is_loopback()); 32 | } 33 | 34 | TEST_CASE("bytes()") 35 | { 36 | ipv4_address ip{ 19, 63, 129, 200 }; 37 | CHECK(ip.bytes()[0] == 19); 38 | CHECK(ip.bytes()[1] == 63); 39 | CHECK(ip.bytes()[2] == 129); 40 | CHECK(ip.bytes()[3] == 200); 41 | } 42 | 43 | TEST_CASE("to_string()") 44 | { 45 | CHECK(ipv4_address(0, 0, 0, 0).to_string() == "0.0.0.0"); 46 | CHECK(ipv4_address(10, 125, 255, 7).to_string() == "10.125.255.7"); 47 | CHECK(ipv4_address(123, 234, 101, 255).to_string() == "123.234.101.255"); 48 | } 49 | 50 | TEST_CASE("from_string") 51 | { 52 | // Check for some invalid strings. 53 | CHECK(ipv4_address::from_string("") == std::nullopt); 54 | CHECK(ipv4_address::from_string("asdf") == std::nullopt); 55 | CHECK(ipv4_address::from_string(" 123.34.56.8") == std::nullopt); 56 | CHECK(ipv4_address::from_string("123.34.56.8 ") == std::nullopt); 57 | CHECK(ipv4_address::from_string("123.") == std::nullopt); 58 | CHECK(ipv4_address::from_string("123.1") == std::nullopt); 59 | CHECK(ipv4_address::from_string("123.12") == std::nullopt); 60 | CHECK(ipv4_address::from_string("123.12.") == std::nullopt); 61 | CHECK(ipv4_address::from_string("123.12.4") == std::nullopt); 62 | CHECK(ipv4_address::from_string("123.12.45") == std::nullopt); 63 | CHECK(ipv4_address::from_string("123.12.45.") == std::nullopt); 64 | 65 | // Overflow of individual parts 66 | CHECK(ipv4_address::from_string("456.12.45.30") == std::nullopt); 67 | CHECK(ipv4_address::from_string("45.256.45.30") == std::nullopt); 68 | CHECK(ipv4_address::from_string("45.25.677.30") == std::nullopt); 69 | CHECK(ipv4_address::from_string("123.12.45.301") == std::nullopt); 70 | 71 | // Can't parse octal yet. 72 | CHECK(ipv4_address::from_string("00") == std::nullopt); 73 | CHECK(ipv4_address::from_string("012345") == std::nullopt); 74 | CHECK(ipv4_address::from_string("045.25.67.30") == std::nullopt); 75 | CHECK(ipv4_address::from_string("45.025.67.30") == std::nullopt); 76 | CHECK(ipv4_address::from_string("45.25.067.30") == std::nullopt); 77 | CHECK(ipv4_address::from_string("45.25.67.030") == std::nullopt); 78 | 79 | // Parse single integer format 80 | CHECK(ipv4_address::from_string("0") == ipv4_address(0)); 81 | CHECK(ipv4_address::from_string("1") == ipv4_address(0, 0, 0, 1)); 82 | CHECK(ipv4_address::from_string("255") == ipv4_address(0, 0, 0, 255)); 83 | CHECK(ipv4_address::from_string("43534243") == ipv4_address(43534243)); 84 | 85 | // Parse dotted decimal format 86 | CHECK(ipv4_address::from_string("45.25.67.30") == ipv4_address(45, 25, 67, 30)); 87 | CHECK(ipv4_address::from_string("0.0.0.0") == ipv4_address(0, 0, 0, 0)); 88 | CHECK(ipv4_address::from_string("1.2.3.4") == ipv4_address(1, 2, 3, 4)); 89 | } 90 | TEST_SUITE_END(); 91 | -------------------------------------------------------------------------------- /test/ipv4_endpoint_tests.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #include "doctest/doctest.h" 9 | 10 | TEST_SUITE_BEGIN("ip_endpoint"); 11 | 12 | using namespace cppcoro::net; 13 | 14 | TEST_CASE("to_string") 15 | { 16 | CHECK(ipv4_endpoint{ ipv4_address{ 192, 168, 2, 254 }, 80 }.to_string() == "192.168.2.254:80"); 17 | } 18 | 19 | TEST_CASE("from_string") 20 | { 21 | CHECK(ipv4_endpoint::from_string("") == std::nullopt); 22 | CHECK(ipv4_endpoint::from_string(" ") == std::nullopt); 23 | CHECK(ipv4_endpoint::from_string("100") == std::nullopt); 24 | CHECK(ipv4_endpoint::from_string("100.10.200.20") == std::nullopt); 25 | CHECK(ipv4_endpoint::from_string("100.10.200.20:") == std::nullopt); 26 | CHECK(ipv4_endpoint::from_string("100.10.200.20::80") == std::nullopt); 27 | CHECK(ipv4_endpoint::from_string("100.10.200.20 80") == std::nullopt); 28 | 29 | CHECK(ipv4_endpoint::from_string("192.168.2.254:80") == 30 | ipv4_endpoint{ ipv4_address{ 192, 168, 2, 254 }, 80 }); 31 | } 32 | 33 | TEST_SUITE_END(); 34 | -------------------------------------------------------------------------------- /test/ipv6_endpoint_tests.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | #include 8 | 9 | #include "doctest/doctest.h" 10 | 11 | TEST_SUITE_BEGIN("ipv6_endpoint"); 12 | 13 | using namespace cppcoro::net; 14 | 15 | namespace 16 | { 17 | constexpr bool isMsvc15_5X86Optimised = 18 | #if CPPCORO_COMPILER_MSVC && CPPCORO_CPU_X86 && _MSC_VER == 1912 && defined(CPPCORO_RELEASE_OPTIMISED) 19 | true; 20 | #else 21 | false; 22 | #endif 23 | } 24 | 25 | // BUG: MSVC 15.5 x86 optimised builds generates bad code 26 | TEST_CASE("to_string" * doctest::skip{ isMsvc15_5X86Optimised }) 27 | { 28 | CHECK(ipv6_endpoint{ ipv6_address{ 0x20010db885a30000, 0x00008a2e03707334 }, 80 }.to_string() == 29 | "[2001:db8:85a3::8a2e:370:7334]:80"); 30 | } 31 | 32 | // BUG: MSVC 15.5 x86 optimised builds generates bad code 33 | TEST_CASE("from_string" * doctest::skip{ isMsvc15_5X86Optimised }) 34 | { 35 | CHECK(ipv6_endpoint::from_string("") == std::nullopt); 36 | CHECK(ipv6_endpoint::from_string(" ") == std::nullopt); 37 | CHECK(ipv6_endpoint::from_string("asdf") == std::nullopt); 38 | CHECK(ipv6_endpoint::from_string("100:100") == std::nullopt); 39 | CHECK(ipv6_endpoint::from_string("100.10.200.20:100") == std::nullopt); 40 | CHECK(ipv6_endpoint::from_string("2001:0db8:85a3:0000:0000:8a2e:0370:7334") == std::nullopt); 41 | CHECK(ipv6_endpoint::from_string("[2001:0db8:85a3:0000:0000:8a2e:0370:7334") == std::nullopt); 42 | CHECK(ipv6_endpoint::from_string("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]") == std::nullopt); 43 | CHECK(ipv6_endpoint::from_string("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:") == std::nullopt); 44 | CHECK(ipv6_endpoint::from_string("[2001:0db8:85a3:0000:0000:8a2e:0370:7334] :123") == std::nullopt); 45 | CHECK(ipv6_endpoint::from_string("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:65536") == std::nullopt); 46 | CHECK(ipv6_endpoint::from_string("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:6553600") == std::nullopt); 47 | 48 | CHECK(ipv6_endpoint::from_string("[::]:0") == ipv6_endpoint{}); 49 | CHECK(ipv6_endpoint::from_string("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80") == 50 | ipv6_endpoint{ ipv6_address{ 0x20010db885a30000, 0x00008a2e03707334 }, 80 }); 51 | CHECK(ipv6_endpoint::from_string("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:65535") == 52 | ipv6_endpoint{ ipv6_address{ 0x20010db885a30000, 0x00008a2e03707334 }, 65535 }); 53 | } 54 | 55 | TEST_SUITE_END(); 56 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 7 | #include "doctest/doctest.h" 8 | -------------------------------------------------------------------------------- /test/single_consumer_async_auto_reset_event_tests.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "doctest/doctest.h" 21 | 22 | TEST_SUITE_BEGIN("single_consumer_async_auto_reset_event"); 23 | 24 | TEST_CASE("single waiter") 25 | { 26 | cppcoro::single_consumer_async_auto_reset_event event; 27 | 28 | bool started = false; 29 | bool finished = false; 30 | auto run = [&]() -> cppcoro::task<> 31 | { 32 | started = true; 33 | co_await event; 34 | finished = true; 35 | }; 36 | 37 | auto check = [&]() -> cppcoro::task<> 38 | { 39 | CHECK(started); 40 | CHECK(!finished); 41 | 42 | event.set(); 43 | 44 | CHECK(finished); 45 | 46 | co_return; 47 | }; 48 | 49 | cppcoro::sync_wait(cppcoro::when_all_ready(run(), check())); 50 | } 51 | 52 | TEST_CASE("multi-threaded") 53 | { 54 | cppcoro::static_thread_pool tp; 55 | 56 | cppcoro::sync_wait([&]() -> cppcoro::task<> 57 | { 58 | cppcoro::single_consumer_async_auto_reset_event valueChangedEvent; 59 | 60 | std::atomic value; 61 | 62 | auto consumer = [&]() -> cppcoro::task 63 | { 64 | while (value.load(std::memory_order_relaxed) < 10'000) 65 | { 66 | co_await valueChangedEvent; 67 | } 68 | 69 | co_return 0; 70 | }; 71 | 72 | auto modifier = [&](int count) -> cppcoro::task 73 | { 74 | co_await tp.schedule(); 75 | for (int i = 0; i < count; ++i) 76 | { 77 | value.fetch_add(1, std::memory_order_relaxed); 78 | valueChangedEvent.set(); 79 | } 80 | co_return 0; 81 | }; 82 | 83 | for (int i = 0; i < 1000; ++i) 84 | { 85 | value.store(0, std::memory_order_relaxed); 86 | 87 | // Really just checking that we don't deadlock here due to a missed wake-up. 88 | (void)co_await cppcoro::when_all(consumer(), modifier(5'000), modifier(5'000)); 89 | } 90 | }()); 91 | } 92 | 93 | TEST_SUITE_END(); 94 | -------------------------------------------------------------------------------- /test/single_producer_sequencer_tests.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | #include "doctest/doctest.h" 21 | 22 | DOCTEST_TEST_SUITE_BEGIN("single_producer_sequencer"); 23 | 24 | using namespace cppcoro; 25 | 26 | DOCTEST_TEST_CASE("multi-threaded usage single consumer") 27 | { 28 | static_thread_pool tp{ 2 }; 29 | 30 | constexpr std::size_t bufferSize = 256; 31 | 32 | sequence_barrier readBarrier; 33 | single_producer_sequencer sequencer(readBarrier, bufferSize); 34 | 35 | constexpr std::size_t iterationCount = 1'000'000; 36 | 37 | std::uint64_t buffer[bufferSize]; 38 | 39 | auto[result, dummy] = sync_wait(when_all( 40 | [&]() -> task 41 | { 42 | // Consumer 43 | std::uint64_t sum = 0; 44 | 45 | bool reachedEnd = false; 46 | std::size_t nextToRead = 0; 47 | do 48 | { 49 | const std::size_t available = co_await sequencer.wait_until_published(nextToRead, tp); 50 | do 51 | { 52 | sum += buffer[nextToRead % bufferSize]; 53 | } while (nextToRead++ != available); 54 | 55 | // Zero value is sentinel that indicates the end of the stream. 56 | reachedEnd = buffer[available % bufferSize] == 0; 57 | 58 | // Notify that we've finished processing up to 'available'. 59 | readBarrier.publish(available); 60 | } while (!reachedEnd); 61 | 62 | co_return sum; 63 | }(), 64 | [&]() -> task<> 65 | { 66 | // Producer 67 | constexpr std::size_t maxBatchSize = 10; 68 | 69 | std::size_t i = 0; 70 | while (i < iterationCount) 71 | { 72 | const std::size_t batchSize = std::min(maxBatchSize, iterationCount - i); 73 | auto sequences = co_await sequencer.claim_up_to(batchSize, tp); 74 | for (auto seq : sequences) 75 | { 76 | buffer[seq % bufferSize] = ++i; 77 | } 78 | sequencer.publish(sequences.back()); 79 | } 80 | 81 | auto finalSeq = co_await sequencer.claim_one(tp); 82 | buffer[finalSeq % bufferSize] = 0; 83 | sequencer.publish(finalSeq); 84 | }())); 85 | 86 | // Suppress unused variable warning. 87 | (void)dummy; 88 | 89 | constexpr std::uint64_t expectedResult = 90 | std::uint64_t(iterationCount) * std::uint64_t(iterationCount + 1) / 2; 91 | 92 | CHECK(result == expectedResult); 93 | } 94 | 95 | DOCTEST_TEST_SUITE_END(); 96 | -------------------------------------------------------------------------------- /test/sync_wait_tests.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Lewis Baker 3 | // Licenced under MIT license. See LICENSE.txt for details. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | #include "doctest/doctest.h" 18 | 19 | TEST_SUITE_BEGIN("sync_wait"); 20 | 21 | static_assert(std::is_same< 22 | decltype(cppcoro::sync_wait(std::declval>())), 23 | std::string&&>::value); 24 | static_assert(std::is_same< 25 | decltype(cppcoro::sync_wait(std::declval&>())), 26 | std::string&>::value); 27 | 28 | TEST_CASE("sync_wait(task)") 29 | { 30 | auto makeTask = []() -> cppcoro::task 31 | { 32 | co_return "foo"; 33 | }; 34 | 35 | auto task = makeTask(); 36 | CHECK(cppcoro::sync_wait(task) == "foo"); 37 | 38 | CHECK(cppcoro::sync_wait(makeTask()) == "foo"); 39 | } 40 | 41 | TEST_CASE("sync_wait(shared_task)") 42 | { 43 | auto makeTask = []() -> cppcoro::shared_task 44 | { 45 | co_return "foo"; 46 | }; 47 | 48 | auto task = makeTask(); 49 | 50 | CHECK(cppcoro::sync_wait(task) == "foo"); 51 | CHECK(cppcoro::sync_wait(makeTask()) == "foo"); 52 | } 53 | 54 | TEST_CASE("multiple threads") 55 | { 56 | // We are creating a new task and starting it inside the sync_wait(). 57 | // The task will reschedule itself for resumption on a thread-pool thread 58 | // which will sometimes complete before this thread calls event.wait() 59 | // inside sync_wait(). Thus we're roughly testing the thread-safety of 60 | // sync_wait(). 61 | cppcoro::static_thread_pool tp{ 1 }; 62 | 63 | int value = 0; 64 | auto createLazyTask = [&]() -> cppcoro::task 65 | { 66 | co_await tp.schedule(); 67 | co_return value++; 68 | }; 69 | 70 | for (int i = 0; i < 10'000; ++i) 71 | { 72 | CHECK(cppcoro::sync_wait(createLazyTask()) == i); 73 | } 74 | } 75 | 76 | TEST_SUITE_END(); 77 | --------------------------------------------------------------------------------