├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── docs └── dummy.cc ├── example ├── and_or.cc ├── and_or.exe ├── awaitable.cc ├── awaitable_group.cc ├── awaitable_group.exe ├── channel.cc ├── channel.exe ├── co_bind.cc ├── co_bind.exe ├── co_mutex.cc ├── co_mutex.exe ├── co_signal.cc ├── co_signal.exe ├── co_spawn.cc ├── condition_variable.cc ├── condition_variable.exe ├── deadline_timer.cc ├── deadline_timer.exe ├── functional.cc ├── functional.exe ├── generator.cc ├── generator.exe ├── net │ ├── cancel.cc │ ├── cancel.exe │ ├── client.cc │ ├── client.exe │ ├── deserialize.cc │ ├── deserialize_struct.cc │ ├── deserialize_struct.exe │ ├── network_v4.cc │ ├── network_v4.exe │ ├── read.cc │ ├── read.exe │ ├── resolver.cc │ ├── resolver.exe │ ├── send.cc │ ├── send.exe │ ├── serialize.cc │ ├── serialize_struct.cc │ └── serialize_struct.exe ├── pipeline.cc ├── pipeline.exe ├── rpc │ ├── rpc_client1.cc │ ├── rpc_client1.exe │ ├── rpc_server1.cc │ └── rpc_server1.exe ├── sleep.cc ├── timeout.cc ├── timeout.exe ├── when_any_all.cc ├── when_any_all.exe └── yield.cc ├── include ├── coasync.hpp └── coasync │ ├── async_fn.hpp │ ├── awaitable.hpp │ ├── awaitable_group.hpp │ ├── cancellation_error.hpp │ ├── channel.hpp │ ├── co_bind.hpp │ ├── co_condition_variable.hpp │ ├── co_latch.hpp │ ├── co_mutex.hpp │ ├── co_semaphore.hpp │ ├── co_signal.hpp │ ├── co_spawn.hpp │ ├── deadline_timer.hpp │ ├── detail │ ├── awaitable_frame.hpp │ ├── awaitable_frame_alloc.hpp │ ├── awaitable_frame_base.hpp │ ├── basic_lockable.hpp │ ├── config.hpp │ ├── frame_delegate.hpp │ ├── frame_executor.hpp │ ├── frame_lifetime.hpp │ ├── get_context.hpp │ ├── get_frame.hpp │ ├── get_id.hpp │ ├── get_stop_token.hpp │ ├── manual_lifetime.hpp │ ├── meta │ │ ├── function_traits.hpp │ │ ├── magic_get.hpp │ │ ├── member_field.hpp │ │ ├── serde_stream_base.hpp │ │ └── signature.hpp │ ├── networking.hpp │ ├── object_deduce.hpp │ ├── remote_queue.hpp │ ├── ring_container.hpp │ ├── service │ │ ├── dequeue_service.hpp │ │ ├── enqueue_service.hpp │ │ ├── epoll_socket_service.hpp │ │ ├── flag_service.hpp │ │ ├── future_service.hpp │ │ ├── iocp_socket_service.hpp │ │ ├── latch_service.hpp │ │ ├── select_socket_service.hpp │ │ ├── service_traits.hpp │ │ ├── socket_service.hpp │ │ ├── time_service.hpp │ │ └── yield_service.hpp │ ├── service_registry.hpp │ ├── signal_condition.hpp │ ├── spin_loop_pause.hpp │ ├── suspendible.hpp │ ├── varint.hpp │ ├── windows_initiate.hpp │ └── workstealing.hpp │ ├── execution_context.hpp │ ├── functional.hpp │ ├── generator.hpp │ ├── net │ ├── acceptor.hpp │ ├── address.hpp │ ├── address_v4.hpp │ ├── address_v4_iterator.hpp │ ├── address_v6.hpp │ ├── address_v6_iterator.hpp │ ├── endpoint.hpp │ ├── message_flags.hpp │ ├── network_v4.hpp │ ├── network_v6.hpp │ ├── option.hpp │ ├── port.hpp │ ├── protocol.hpp │ ├── receive.hpp │ ├── resolver.hpp │ ├── resolver_flags.hpp │ ├── scope_id.hpp │ ├── send.hpp │ ├── serde_stream.hpp │ ├── socket.hpp │ └── socket_base.hpp │ ├── rpc │ ├── rpc_client.hpp │ ├── rpc_control_variable.hpp │ └── rpc_server.hpp │ ├── set_stop_source.hpp │ ├── this_coro.hpp │ ├── when_all.hpp │ └── when_any.hpp ├── perf └── coasync │ └── dummy.cc ├── test └── coasync │ └── dummy.cc ├── tool-scripts └── coasync │ └── dummy.cc └── xmake.lua /.gitignore: -------------------------------------------------------------------------------- 1 | test/*.exe 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(coasync 3 | VERSION 0.0.1 4 | LANGUAGES CXX 5 | ) 6 | 7 | add_library(coasync INTERFACE) 8 | add_library(coasync::coasync ALIAS coasync) 9 | 10 | target_compile_features(coasync INTERFACE cxx_std_20) 11 | 12 | target_include_directories(coasync INTERFACE 13 | $ 14 | $ 15 | ) 16 | 17 | set(COASYNC_MASTER_PROJECT OFF) 18 | if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 19 | set(COASYNC_MASTER_PROJECT ON) 20 | endif() 21 | 22 | option(COASYNC_TEST "Build the tests" ${COASYNC_MASTER_PROJECT}) 23 | 24 | add_compile_options(-fcoroutines -fconcepts -freport-bug -Wno-interference-size) 25 | 26 | # only override the warning options if we're build as the master 27 | # project, in other cases leave them alone since we might be added 28 | # to a project with laxer standards for dealing with warnings 29 | if (COASYNC_MASTER_PROJECT) 30 | # msvc does not support normal options 31 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 32 | # set warning level to 4 33 | target_compile_options(coasync INTERFACE /W4) 34 | else() 35 | # include helper to enable only options which are supported 36 | include(cmake/target_supported_compile_options.cmake) 37 | 38 | # try all the regular options and enable them if possible 39 | target_supported_compile_options(coasync INTERFACE -Wall) 40 | target_supported_compile_options(coasync INTERFACE -Wextra) 41 | target_supported_compile_options(coasync INTERFACE -Wdeprecated) 42 | target_supported_compile_options(coasync INTERFACE -Wdocumentation) 43 | endif() 44 | endif() 45 | 46 | install( 47 | DIRECTORY include/coasync 48 | DESTINATION include 49 | ) 50 | 51 | install( 52 | FILES include/coasync.hpp 53 | DESTINATION include 54 | ) 55 | 56 | install( 57 | TARGETS coasync 58 | EXPORT coasync-targets 59 | DESTINATION lib 60 | ) 61 | 62 | install( 63 | EXPORT coasync-targets 64 | NAMESPACE coasync:: 65 | DESTINATION lib/cmake/coasync 66 | ) 67 | 68 | include(CMakePackageConfigHelpers) 69 | write_basic_package_version_file( 70 | "${CMAKE_CURRENT_BINARY_DIR}/coasync/coasync-config-version.cmake" 71 | VERSION ${PROJECT_VERSION} 72 | COMPATIBILITY AnyNewerVersion 73 | ) 74 | 75 | export( 76 | EXPORT coasync-targets 77 | FILE "${CMAKE_CURRENT_BINARY_DIR}/coasync/coasync-targets.cmake" 78 | NAMESPACE coasync:: 79 | ) 80 | 81 | configure_file(cmake/coasync-config.cmake 82 | "${CMAKE_CURRENT_BINARY_DIR}/coasync/coasync-config.cmake" 83 | COPYONLY 84 | ) 85 | 86 | install( 87 | FILES 88 | cmake/coasync-config.cmake 89 | "${CMAKE_CURRENT_BINARY_DIR}/coasync/coasync-config-version.cmake" 90 | DESTINATION 91 | lib/cmake/coasync 92 | ) 93 | 94 | if (COASYNC_TEST) 95 | add_subdirectory(tests) 96 | endif() 97 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 RFoe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/dummy.cc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/docs/dummy.cc -------------------------------------------------------------------------------- /example/and_or.cc: -------------------------------------------------------------------------------- 1 | #include "../include/coasync/functional.hpp" 2 | #include "../include/coasync/this_coro.hpp" 3 | #include "../include/coasync/co_spawn.hpp" 4 | 5 | using namespace coasync; 6 | using std::chrono::operator""s; 7 | 8 | awaitable predicate_X() { 9 | co_await sleep_for(1s); 10 | std::puts("predicate X"); 11 | co_return 999; 12 | } 13 | awaitable predicate_Y() { 14 | co_await sleep_for(2s); 15 | std::puts("predicate Y"); 16 | co_return 888; 17 | } 18 | awaitable test() 19 | { 20 | auto [x, y] = co_await (predicate_X() && predicate_Y()); 21 | std::printf("[%d, %d]\n", x, y); 22 | auto z = co_await (predicate_X() || predicate_Y()); 23 | std::visit([](int v) { std::printf("%d\n", v); }, z); 24 | } 25 | 26 | 27 | int main() { 28 | execution_context context{concurrency_arg(0)}; 29 | /// Initiate no child threads 30 | co_spawn(context, test(), use_detach); 31 | context.loop(); 32 | } 33 | -------------------------------------------------------------------------------- /example/and_or.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/and_or.exe -------------------------------------------------------------------------------- /example/awaitable.cc: -------------------------------------------------------------------------------- 1 | #include "../include/coasync/awaitable.hpp" 2 | #include 3 | 4 | using namespace coasync; 5 | 6 | template struct function; 7 | template 8 | struct function(Args ...)> { 9 | struct base { 10 | virtual awaitable call(Args ...) = 0; 11 | }; 12 | template 13 | struct impl: base{ 14 | impl(F&& f): _M_f((F&&) f) {} 15 | F _M_f; 16 | virtual awaitable call(Args ...args) override { 17 | std::puts("call"); 18 | return _M_f((Args) args ...); 19 | } 20 | }; 21 | awaitable operator()(Args ...args) { 22 | std::puts("operator()"); 23 | return _M_ptr -> call((Args) args ...); 24 | }; 25 | template 26 | function(F&& f) : _M_ptr(std::make_unique>(((F &&)f))) {} 27 | std::unique_ptr _M_ptr; 28 | }; 29 | 30 | awaitable async_main() { 31 | function(int)> f { [value = 8](int x) mutable -> awaitable { co_return x + value ++; } }; 32 | std::printf("%d\n", co_await f(1)); 33 | std::printf("%d\n", co_await f(2)); 34 | std::printf("%d\n", co_await f(3)); 35 | } 36 | int main() { 37 | async_main().get_coroutine()(); 38 | } 39 | -------------------------------------------------------------------------------- /example/awaitable_group.cc: -------------------------------------------------------------------------------- 1 | #include "../include/coasync/when_all.hpp" 2 | #include "../include/coasync/when_any.hpp" 3 | #include "../include/coasync/execution_context.hpp" 4 | #include "../include/coasync/functional.hpp" 5 | #include "../include/coasync/this_coro.hpp" 6 | 7 | using namespace coasync; 8 | using std::chrono::operator""s; 9 | 10 | awaitable delay(int seconds) 11 | { 12 | co_await sleep_for(std::chrono::seconds(seconds)); 13 | std::puts("sleep awaiken"); 14 | co_return co_await this_coro::id; 15 | } 16 | awaitable test() 17 | { 18 | for(unsigned int i {}; i < 10; i ++) 19 | { 20 | awaitable_group group; 21 | for(unsigned int i {}; i < 5; i ++) 22 | group.emplace_back(delay(i)); 23 | auto results = co_await when_all(std::move(group)); 24 | std::printf("when_all return, size: %llu\n", results.size()); 25 | for(int res: results) 26 | std::printf("res: %d\n", res); 27 | } 28 | co_return; 29 | } 30 | int main() 31 | { 32 | execution_context context{concurrency_arg(3)}; 33 | /// Initiate three child threads 34 | co_spawn(context, test(), use_detach); 35 | context.loop(); 36 | } 37 | -------------------------------------------------------------------------------- /example/awaitable_group.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/awaitable_group.exe -------------------------------------------------------------------------------- /example/channel.cc: -------------------------------------------------------------------------------- 1 | #include "../include/coasync/execution_context.hpp" 2 | #include "../include/coasync/functional.hpp" 3 | #include "../include/coasync/channel.hpp" 4 | #include "../include/coasync/co_spawn.hpp" 5 | using namespace coasync; 6 | using std::chrono::operator""s; 7 | awaitable do_send(channel& channel) { 8 | for(unsigned int count {}; count < 8; count ++) { 9 | co_await sleep_for(1s); 10 | co_await channel.send(std::rand()); 11 | } 12 | } 13 | awaitable do_receive(channel& channel) { 14 | for(unsigned int count {}; count < 8; count ++) 15 | std::printf("receive: %d\n", co_await channel.receive()); 16 | } 17 | int main() { 18 | execution_context context{concurrency_arg(2)}; 19 | channel channel{context}; 20 | co_spawn(context, do_send(channel), use_detach); 21 | co_spawn(context, do_send(channel), use_detach); 22 | co_spawn(context, do_send(channel), use_detach); 23 | co_spawn(context, do_receive(channel), use_detach); 24 | co_spawn(context, do_receive(channel), use_detach); 25 | co_spawn(context, do_receive(channel), use_detach); 26 | context.loop(); 27 | } 28 | -------------------------------------------------------------------------------- /example/channel.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/channel.exe -------------------------------------------------------------------------------- /example/co_bind.cc: -------------------------------------------------------------------------------- 1 | #include "../include/coasync/co_bind.hpp" 2 | #include "../include/coasync/execution_context.hpp" 3 | #include "../include/coasync/when_any.hpp" 4 | #include "../include/coasync/when_all.hpp" 5 | #include "../include/coasync/functional.hpp" 6 | #include "../include/coasync/this_coro.hpp" 7 | using namespace coasync; 8 | using std::chrono::operator""s; 9 | awaitable co_do_sleep(int seconds) noexcept 10 | { 11 | co_await sleep_for(std::chrono::seconds(seconds)); 12 | std::puts("co_await sleep_for"); 13 | } 14 | void do_sleep(int seconds) noexcept 15 | { 16 | std::this_thread::sleep_for(std::chrono::seconds(seconds)); 17 | std::puts("this_thread::sleep_for"); 18 | } 19 | awaitable test() noexcept { 20 | execution_context& ctx = co_await this_coro::context; 21 | for(unsigned int i {}; i < 10; i ++) { 22 | co_await when_any(co_do_sleep(1), co_do_sleep(3), co_bind(ctx, do_sleep, 2), co_bind(ctx, do_sleep, 4)); 23 | std::puts("----------when_any done----------"); 24 | co_await when_all(co_do_sleep(1), co_do_sleep(3), co_bind(ctx, do_sleep, 2), co_bind(ctx, do_sleep, 4)); 25 | std::puts("----------when_all done----------"); 26 | } 27 | } 28 | int main() 29 | { 30 | execution_context context; 31 | co_spawn(context, test(), use_detach); 32 | context.loop(); 33 | } 34 | -------------------------------------------------------------------------------- /example/co_bind.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/co_bind.exe -------------------------------------------------------------------------------- /example/co_mutex.cc: -------------------------------------------------------------------------------- 1 | #include "../include/coasync/co_spawn.hpp" 2 | #include "../include/coasync/co_mutex.hpp" 3 | #include "../include/coasync/execution_context.hpp" 4 | #include "../include/coasync/functional.hpp" 5 | #include "../include/coasync/this_coro.hpp" 6 | #include 7 | 8 | using namespace coasync; 9 | using std::chrono::operator""s; 10 | 11 | 12 | int data; /// unprotected raw data 13 | 14 | awaitable access(co_mutex& mutex) noexcept 15 | { 16 | std::puts("access"); 17 | auto _l = co_await mutex.scoped(); 18 | // co_await mutex.lock(); 19 | std::printf("data: %d\n", data ++); 20 | std::cout << "thread: " << std::this_thread::get_id() << "\n"; 21 | co_await sleep_for(1s); 22 | // mutex.unlock(); 23 | co_return; 24 | } 25 | int main() 26 | { 27 | execution_context context {concurrency_arg(8)}; /// data race 28 | co_mutex mutex{context}; 29 | for(unsigned int i {}; i < 16; i ++) 30 | co_spawn(context, access(mutex), use_detach); 31 | context.loop(); 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /example/co_mutex.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/co_mutex.exe -------------------------------------------------------------------------------- /example/co_signal.cc: -------------------------------------------------------------------------------- 1 | #include "../include/coasync/co_signal.hpp" 2 | #include "../include/coasync/execution_context.hpp" 3 | #include "../include/coasync/functional.hpp" 4 | #include "../include/coasync/this_coro.hpp" 5 | 6 | #include /// for extern std::clog 7 | 8 | using namespace coasync; 9 | using std::chrono::operator""s; 10 | 11 | awaitable do_raise(int seconds) noexcept 12 | { 13 | co_await sleep_for(std::chrono::seconds(seconds)); 14 | std::puts("std::raise"); 15 | std::raise(SIGINT); 16 | } 17 | int main() 18 | { 19 | execution_context context {concurrency_arg(0)}; 20 | co_signal(context, SIGINT, [] 21 | ([[maybe_unused]]int) noexcept { 22 | std::puts("interpreted!!! callback 001"); 23 | }); 24 | co_signal(context, SIGINT, [] 25 | ([[maybe_unused]]int) noexcept { 26 | std::puts("interpreted!!! callback 002"); 27 | }); 28 | 29 | co_spawn(context, do_raise(1), use_detach); 30 | co_spawn(context, do_raise(2), use_detach); 31 | context.loop(); 32 | 33 | std::puts("final raise"); 34 | std::raise(SIGINT); /// see what will happen 35 | } 36 | -------------------------------------------------------------------------------- /example/co_signal.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/co_signal.exe -------------------------------------------------------------------------------- /example/co_spawn.cc: -------------------------------------------------------------------------------- 1 | #include "../include/coasync/co_spawn.hpp" 2 | #include "../include/coasync/execution_context.hpp" 3 | #include "../include/coasync/functional.hpp" 4 | #include "../include/coasync/this_coro.hpp" 5 | using namespace coasync; 6 | using std::chrono::operator""s; 7 | awaitable test() noexcept 8 | { 9 | std::puts("test"); 10 | auto s = co_await this_coro::stop_token; 11 | auto a = co_spawn(co_await this_coro::context, sleep_for(2s), use_awaitable); 12 | co_await a; 13 | std::puts("hello world"); 14 | co_return; 15 | } 16 | int main() 17 | { 18 | execution_context context {concurrency_arg(0)}; 19 | co_spawn(context, test(), use_detach); 20 | std::puts("co_spawn"); 21 | context.loop(); 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /example/condition_variable.cc: -------------------------------------------------------------------------------- 1 | #include "../include/coasync/co_spawn.hpp" 2 | #include "../include/coasync/co_mutex.hpp" 3 | #include "../include/coasync/co_condition_variable.hpp" 4 | #include "../include/coasync/execution_context.hpp" 5 | #include "../include/coasync/functional.hpp" 6 | #include "../include/coasync/this_coro.hpp" 7 | #include 8 | #include 9 | 10 | using namespace coasync; 11 | using std::chrono::operator""s; 12 | 13 | std::queue data; /// unprotected raw data 14 | 15 | awaitable producer(co_mutex& mutex, co_condition_variable& condition) 16 | { 17 | co_await mutex.lock(); 18 | std::puts("producer"); 19 | data.emplace(co_await this_coro::id); 20 | co_await sleep_for(1s); 21 | mutex.unlock(); 22 | condition.notify_one(); 23 | co_return; 24 | } 25 | 26 | awaitable consumer(co_mutex& mutex, co_condition_variable& condition) 27 | { 28 | co_await mutex.lock(); 29 | co_await condition.wait(mutex, [&] { return not data.empty(); }); 30 | assert(not data.empty()); 31 | std::printf("consumer: %d\n", data.front()); 32 | data.pop(); 33 | mutex.unlock(); 34 | co_return; 35 | } 36 | 37 | int main() 38 | { 39 | execution_context context {concurrency_arg(8)}; /// data race 40 | co_condition_variable condition {context}; 41 | co_mutex mutex{context}; 42 | for(unsigned int i {}; i < 16; i ++) 43 | co_spawn(context, producer(mutex, condition), use_detach); 44 | for(unsigned int i {}; i < 16; i ++) 45 | co_spawn(context, consumer(mutex, condition), use_detach); 46 | 47 | context.loop(); 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /example/condition_variable.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/condition_variable.exe -------------------------------------------------------------------------------- /example/deadline_timer.cc: -------------------------------------------------------------------------------- 1 | #include "../include/coasync/execution_context.hpp" 2 | #include "../include/coasync/deadline_timer.hpp" 3 | #include "../include/coasync/functional.hpp" 4 | #include "../include/coasync/this_coro.hpp" 5 | #include "../include/coasync/co_spawn.hpp" 6 | 7 | using namespace coasync; 8 | using std::chrono::operator""s; 9 | 10 | awaitable expire(deadline_timer<>& timer) 11 | { 12 | auto result = co_await timer.wait(); 13 | if(result == deadline_timer_status::timeout) 14 | std::puts("wait done"); 15 | else 16 | std::puts("wait cancel"); 17 | } 18 | awaitable cancel(deadline_timer<>& timer) { 19 | co_await sleep_for(1s); 20 | timer.cancel(); 21 | } 22 | 23 | int main() { 24 | execution_context context{concurrency_arg(0)}; 25 | /// Initiate no child threads 26 | deadline_timer timer(context, 2s); 27 | co_spawn(context, expire(timer), use_detach); 28 | co_spawn(context, cancel(timer), use_detach); 29 | context.loop(); 30 | } 31 | -------------------------------------------------------------------------------- /example/deadline_timer.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/deadline_timer.exe -------------------------------------------------------------------------------- /example/functional.cc: -------------------------------------------------------------------------------- 1 | #include "../include/coasync/functional.hpp" 2 | #include "../include/coasync/this_coro.hpp" 3 | #include "../include/coasync/co_spawn.hpp" 4 | 5 | using namespace coasync; 6 | using std::chrono::operator""s; 7 | std::atomic_flag flag; 8 | std::promise promise; 9 | std::future future = promise.get_future(); 10 | std::latch latch{1}; 11 | awaitable waiting() 12 | { 13 | co_await flag; 14 | std::puts("atomic_flag is set"); 15 | co_await future; 16 | std::puts("future is set"); 17 | co_await latch; 18 | std::puts("latch is set"); 19 | } 20 | awaitable calling() 21 | { 22 | co_await 1s; 23 | flag.test_and_set(std::memory_order_release); 24 | co_await 1s; 25 | promise.set_value(); 26 | co_await 1s; 27 | latch.count_down(); 28 | } 29 | 30 | int main() { 31 | execution_context context{concurrency_arg(0)}; 32 | /// Initiate no child threads 33 | co_spawn(context, waiting(), use_detach); 34 | co_spawn(context, calling(), use_detach); 35 | context.loop(); 36 | } 37 | -------------------------------------------------------------------------------- /example/functional.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/functional.exe -------------------------------------------------------------------------------- /example/generator.cc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/generator.cc -------------------------------------------------------------------------------- /example/generator.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/generator.exe -------------------------------------------------------------------------------- /example/net/cancel.cc: -------------------------------------------------------------------------------- 1 | #include "../../include/coasync/execution_context.hpp" 2 | #include "../../include/coasync/co_spawn.hpp" 3 | #include "../../include/coasync/net/endpoint.hpp" 4 | #include "../../include/coasync/net/acceptor.hpp" 5 | #include "../../include/coasync/net/receive.hpp" 6 | #include "../../include/coasync/net/protocol.hpp" 7 | #include "../../include/coasync/functional.hpp" 8 | #include "../../include/coasync/this_coro.hpp" 9 | #include 10 | 11 | bool ignore_case_equal(char a, char b) 12 | { 13 | return std::tolower(a) == std::tolower(b); 14 | } 15 | 16 | using namespace coasync; 17 | using std::chrono::operator""s; 18 | awaitable connetion(net::tcp::socket client) noexcept 19 | { 20 | std::puts("hello client"); 21 | co_spawn(co_await this_coro::context, 22 | [](net::tcp::socket* _client) -> awaitable 23 | { 24 | co_await sleep_for(3s); /// after 3s actively close connection 25 | _client->cancel(); 26 | co_return; 27 | }(&client), 28 | use_detach); 29 | while(true) 30 | { 31 | try 32 | { 33 | std::cout << co_await net::receive_until(client, "fuck", ignore_case_equal) << "\n"; 34 | } 35 | catch(std::exception& error) 36 | { 37 | std::cout << error.what() << "\n"; 38 | co_return; 39 | } 40 | 41 | } 42 | } 43 | awaitable acceptance() noexcept 44 | { 45 | net::tcp::acceptor acceptor 46 | { 47 | co_await this_coro::context, 48 | net::tcp::endpoint(net::address_v4::loopback(), 10086) 49 | }; 50 | while(true) 51 | { 52 | co_spawn(co_await this_coro::context, connetion(co_await acceptor.accept()), use_detach); 53 | std::puts("new client found"); 54 | } 55 | } 56 | int main() 57 | { 58 | execution_context context {concurrency_arg(0)}; 59 | co_spawn(context, acceptance(), use_detach); 60 | context.loop(); 61 | } 62 | -------------------------------------------------------------------------------- /example/net/cancel.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/net/cancel.exe -------------------------------------------------------------------------------- /example/net/client.cc: -------------------------------------------------------------------------------- 1 | #include "../../include/coasync/execution_context.hpp" 2 | #include "../../include/coasync/co_spawn.hpp" 3 | #include "../../include/coasync/net/socket.hpp" 4 | #include "../../include/coasync/net/protocol.hpp" 5 | #include "../../include/coasync/net/endpoint.hpp" 6 | #include "../../include/coasync/this_coro.hpp" 7 | #include 8 | using namespace coasync; 9 | 10 | awaitable do_send() { 11 | net::tcp::socket socket { co_await this_coro::context, net::tcp::v4() }; 12 | co_await socket.connect(net::tcp::endpoint{net::address_v4::loopback(), 10086}); 13 | std::string send_bytes; 14 | while(true) { 15 | std::getline(std::cin, send_bytes); 16 | if(send_bytes.compare("quit") == 0) co_return; 17 | co_await socket.send(send_bytes); 18 | } 19 | } 20 | 21 | int main() { 22 | execution_context context {concurrency_arg(1)}; 23 | co_spawn(context, do_send(), use_detach); 24 | context.loop(); 25 | } 26 | -------------------------------------------------------------------------------- /example/net/client.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/net/client.exe -------------------------------------------------------------------------------- /example/net/deserialize.cc: -------------------------------------------------------------------------------- 1 | #include "../../include/coasync/execution_context.hpp" 2 | #include "../../include/coasync/co_spawn.hpp" 3 | #include "../../include/coasync/this_coro.hpp" 4 | #include "../../include/coasync/net/endpoint.hpp" 5 | #include "../../include/coasync/net/acceptor.hpp" 6 | #include "../../include/coasync/net/protocol.hpp" 7 | #include "../../include/coasync/net/serde_stream.hpp" 8 | 9 | using namespace coasync; 10 | 11 | awaitable deserial(net::tcp::socket socket) { 12 | net::serde_stream s { std::move(socket) }; 13 | std::vector vec; 14 | std::queue que; 15 | co_await s.deserialize(vec); 16 | for(auto value: vec) 17 | std::printf("vector %d\n", value); 18 | co_await s.deserialize(que); 19 | while(not que.empty()) { 20 | printf("queue: %d\n", que.front()); 21 | que.pop(); 22 | } 23 | } 24 | 25 | awaitable acceptance() noexcept 26 | { 27 | net::tcp::acceptor acceptor { 28 | co_await this_coro::context, 29 | net::tcp::endpoint(net::address_v4::loopback(), 10086) 30 | }; 31 | while(true) 32 | co_spawn(co_await this_coro::context, deserial(co_await acceptor.accept()), use_detach); 33 | } 34 | 35 | int main() { 36 | execution_context context {concurrency_arg(2)}; 37 | co_spawn(context, acceptance(), use_detach); 38 | context.loop(); 39 | } 40 | -------------------------------------------------------------------------------- /example/net/deserialize_struct.cc: -------------------------------------------------------------------------------- 1 | #include "../../include/coasync/execution_context.hpp" 2 | #include "../../include/coasync/co_spawn.hpp" 3 | #include "../../include/coasync/this_coro.hpp" 4 | #include "../../include/coasync/net/endpoint.hpp" 5 | #include "../../include/coasync/net/acceptor.hpp" 6 | #include "../../include/coasync/net/protocol.hpp" 7 | #include "../../include/coasync/net/serde_stream.hpp" 8 | 9 | #include 10 | 11 | using namespace coasync; 12 | 13 | struct example 14 | { 15 | int ivalue; 16 | double fvalue; 17 | std::vector ivector; 18 | }; 19 | struct_meta(example, ivalue, fvalue, ivector); 20 | 21 | awaitable deserial(net::tcp::socket socket) 22 | { 23 | std::puts("new client"); 24 | net::serde_stream s { std::move(socket) }; 25 | [[gnu::uninitialized]] example ex; 26 | co_await s.deserialize(ex); 27 | std::cout << "ivalue: " << ex.ivalue << "\n"; 28 | std::cout << "fvalue: " << ex.fvalue << "\n"; 29 | for(auto v: ex.ivector) 30 | std::cout << "ivector element: " << v << "\n"; 31 | } 32 | 33 | awaitable acceptance() noexcept 34 | { 35 | net::tcp::acceptor acceptor 36 | { 37 | co_await this_coro::context, 38 | net::tcp::endpoint(net::address_v4::loopback(), 10086) 39 | }; 40 | while(true) 41 | co_spawn(co_await this_coro::context, deserial(co_await acceptor.accept()), use_detach); 42 | } 43 | 44 | int main() 45 | { 46 | execution_context context {concurrency_arg(2)}; 47 | co_spawn(context, acceptance(), use_detach); 48 | context.loop(); 49 | } 50 | -------------------------------------------------------------------------------- /example/net/deserialize_struct.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/net/deserialize_struct.exe -------------------------------------------------------------------------------- /example/net/network_v4.cc: -------------------------------------------------------------------------------- 1 | #include "../../include/coasync/net/network_v4.hpp" 2 | #include 3 | using namespace coasync; 4 | int main() 5 | { 6 | net::network_v4 nw(net::make_address_v4("192.0.2.0"), 24); 7 | 8 | std::cout << "Network address: " << nw.network() << std::endl; 9 | std::cout << "Broadcast address: " << nw.broadcast() << std::endl; 10 | 11 | std::cout << "Network address mask: " << nw.netmask() << std::endl; 12 | 13 | std::cout << "Prefix length: " << nw.prefix_length() << std::endl; 14 | 15 | for(auto addr: nw.hosts()) 16 | std::cout << "network host: " << addr << std::endl; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /example/net/network_v4.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/net/network_v4.exe -------------------------------------------------------------------------------- /example/net/read.cc: -------------------------------------------------------------------------------- 1 | #include "../../include/coasync/execution_context.hpp" 2 | #include "../../include/coasync/co_spawn.hpp" 3 | #include "../../include/coasync/net/endpoint.hpp" 4 | #include "../../include/coasync/net/acceptor.hpp" 5 | #include "../../include/coasync/net/receive.hpp" 6 | #include "../../include/coasync/net/protocol.hpp" 7 | #include "../../include/coasync/functional.hpp" 8 | #include "../../include/coasync/this_coro.hpp" 9 | #include 10 | 11 | bool ignore_case_equal(char a, char b) 12 | { 13 | return std::tolower(a) == std::tolower(b); 14 | } 15 | 16 | using namespace coasync; 17 | awaitable connetion(net::tcp::socket client) noexcept 18 | { 19 | std::puts("hello client"); 20 | while(true) 21 | std::cout << co_await net::receive_until(client, "fuck", ignore_case_equal) << "\n"; 22 | } 23 | awaitable acceptance() noexcept 24 | { 25 | net::tcp::acceptor acceptor 26 | { 27 | co_await this_coro::context, 28 | net::tcp::endpoint(net::address_v4::loopback(), 10086) 29 | }; 30 | while(true) 31 | { 32 | co_spawn(co_await this_coro::context, connetion(co_await acceptor.accept()), use_detach); 33 | std::puts("new client found"); 34 | } 35 | } 36 | int main() 37 | { 38 | execution_context context {concurrency_arg(0)}; 39 | co_spawn(context, acceptance(), use_detach); 40 | context.loop(); 41 | } 42 | -------------------------------------------------------------------------------- /example/net/read.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/net/read.exe -------------------------------------------------------------------------------- /example/net/resolver.cc: -------------------------------------------------------------------------------- 1 | #include "../../include/coasync/execution_context.hpp" 2 | #include "../../include/coasync/net/protocol.hpp" 3 | #include "../../include/coasync/net/resolver.hpp" 4 | #include "../../include/coasync/net/endpoint.hpp" 5 | #include "../../include/coasync/this_coro.hpp" 6 | #include 7 | using namespace coasync; 8 | 9 | awaitable test() noexcept { 10 | net::tcp::resolver resolver { co_await this_coro::context }; 11 | auto results = co_await resolver.resolve(net::tcp::endpoint(net::address_v4::loopback(), 10086), use_awaitable); 12 | for(auto& entry: results) { 13 | std::cout << entry.host_name() << "\n"; 14 | std::cout << entry.service_name() << "\n"; 15 | std::cout << entry.endpoint() << "\n"; 16 | } 17 | } 18 | 19 | int main() { 20 | execution_context context{concurrency_arg(0)}; 21 | co_spawn(context, test(), use_detach); 22 | context.loop(); 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /example/net/resolver.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/net/resolver.exe -------------------------------------------------------------------------------- /example/net/send.cc: -------------------------------------------------------------------------------- 1 | #include "../../include/coasync/execution_context.hpp" 2 | #include "../../include/coasync/co_spawn.hpp" 3 | #include "../../include/coasync/net/socket.hpp" 4 | #include "../../include/coasync/net/send.hpp" 5 | #include "../../include/coasync/net/protocol.hpp" 6 | #include "../../include/coasync/net/endpoint.hpp" 7 | #include "../../include/coasync/this_coro.hpp" 8 | #include 9 | using namespace coasync; 10 | 11 | awaitable do_send() { 12 | net::tcp::socket socket { co_await this_coro::context, net::tcp::v4() }; 13 | co_await socket.connect(net::tcp::endpoint{net::address_v4::loopback(), 10086}); 14 | std::puts("connected."); 15 | std::string send_bytes; 16 | while(true) { 17 | std::puts("print message"); 18 | std::getline(std::cin, send_bytes); 19 | if(send_bytes.compare("quit") == 0) co_return; 20 | co_await send_exactly(socket, send_bytes); 21 | } 22 | } 23 | 24 | int main() { 25 | execution_context context {concurrency_arg(0)}; 26 | co_spawn(context, do_send(), use_detach); 27 | context.loop(); 28 | } 29 | -------------------------------------------------------------------------------- /example/net/send.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/net/send.exe -------------------------------------------------------------------------------- /example/net/serialize.cc: -------------------------------------------------------------------------------- 1 | #include "../../include/coasync/execution_context.hpp" 2 | #include "../../include/coasync/co_spawn.hpp" 3 | #include "../../include/coasync/this_coro.hpp" 4 | #include "../../include/coasync/net/socket.hpp" 5 | #include "../../include/coasync/net/protocol.hpp" 6 | #include "../../include/coasync/net/endpoint.hpp" 7 | #include "../../include/coasync/net/serde_stream.hpp" 8 | #include 9 | using namespace coasync; 10 | awaitable serial() 11 | { 12 | net::tcp::socket socket { co_await this_coro::context, net::tcp::v4() }; 13 | co_await socket.connect(net::tcp::endpoint{net::address_v4::loopback(), 10086}); 14 | net::serde_stream s { std::move(socket) }; 15 | std::vector vec {}; 16 | for(unsigned int count {}; count < 10; count ++) vec.push_back(count); 17 | co_await s.serialize(vec); 18 | std::queue que {}; 19 | for(unsigned int count {}; count < 10; count ++) que.push(count); 20 | co_await s.serialize(que); 21 | } 22 | int main() 23 | { 24 | execution_context context {concurrency_arg(2)}; 25 | co_spawn(context, serial(), use_detach); 26 | context.loop(); 27 | } 28 | -------------------------------------------------------------------------------- /example/net/serialize_struct.cc: -------------------------------------------------------------------------------- 1 | #include "../../include/coasync/execution_context.hpp" 2 | #include "../../include/coasync/co_spawn.hpp" 3 | #include "../../include/coasync/this_coro.hpp" 4 | #include "../../include/coasync/net/socket.hpp" 5 | #include "../../include/coasync/net/protocol.hpp" 6 | #include "../../include/coasync/net/endpoint.hpp" 7 | #include "../../include/coasync/net/serde_stream.hpp" 8 | #include 9 | using namespace coasync; 10 | 11 | struct example 12 | { 13 | int ivalue; 14 | double fvalue; 15 | std::vector ivector; 16 | }; 17 | struct_meta(example, ivalue, fvalue, ivector); 18 | 19 | awaitable serial() 20 | { 21 | net::tcp::socket socket { co_await this_coro::context, net::tcp::v4() }; 22 | co_await socket.connect(net::tcp::endpoint{net::address_v4::loopback(), 10086}); 23 | net::serde_stream s { std::move(socket) }; 24 | example ex {.ivalue = 888, .fvalue = 3.14, .ivector = {1, 2, 3}}; 25 | co_await s.serialize(ex); 26 | } 27 | 28 | int main() 29 | { 30 | execution_context context {concurrency_arg(2)}; 31 | co_spawn(context, serial(), use_detach); 32 | context.loop(); 33 | } 34 | -------------------------------------------------------------------------------- /example/net/serialize_struct.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/net/serialize_struct.exe -------------------------------------------------------------------------------- /example/pipeline.cc: -------------------------------------------------------------------------------- 1 | #include "../include/coasync/functional.hpp" 2 | #include "../include/coasync/this_coro.hpp" 3 | #include "../include/coasync/co_spawn.hpp" 4 | 5 | using namespace coasync; 6 | using std::chrono::operator""s; 7 | 8 | awaitable void_delay() { 9 | co_await sleep_for(2s); 10 | } 11 | awaitable int_delay() { 12 | co_await sleep_for(2s); 13 | co_return 888; 14 | } 15 | awaitable test() 16 | { 17 | auto a = void_delay() | [] { std::puts("void_functor"); }; 18 | auto b = int_delay() | [](int v) { return v + 999; }; 19 | co_await a; 20 | std::printf("%d\n", co_await b); 21 | } 22 | 23 | 24 | int main() { 25 | execution_context context{concurrency_arg(0)}; 26 | /// Initiate no child threads 27 | co_spawn(context, test(), use_detach); 28 | context.loop(); 29 | } 30 | -------------------------------------------------------------------------------- /example/pipeline.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/pipeline.exe -------------------------------------------------------------------------------- /example/rpc/rpc_client1.cc: -------------------------------------------------------------------------------- 1 | #include "../../include/coasync/execution_context.hpp" 2 | #include "../../include/coasync/this_coro.hpp" 3 | #include "../../include/coasync/co_spawn.hpp" 4 | #include "../../include/coasync/net/socket.hpp" 5 | #include "../../include/coasync/net/protocol.hpp" 6 | #include "../../include/coasync/net/endpoint.hpp" 7 | #include "../../include/coasync/rpc/rpc_client.hpp" 8 | using namespace coasync; 9 | 10 | awaitable test() noexcept 11 | { 12 | net::tcp::socket socket { co_await this_coro::context, net::tcp::v4() }; 13 | co_await socket.connect(net::tcp::endpoint{net::address_v4::loopback(), 10086}); 14 | std::puts("connected"); 15 | net::rpc::rpc_client s { std::move(socket) }; 16 | co_await s.call("sleep", 2); // server sleep 2s 17 | std::printf("[888 + 999 = %d]\n", co_await s.call("add", 888, 999)); 18 | } 19 | 20 | int main() 21 | { 22 | execution_context ctx {concurrency_arg(0)}; 23 | co_spawn(ctx, test(), use_detach); 24 | ctx.loop(); 25 | } 26 | -------------------------------------------------------------------------------- /example/rpc/rpc_client1.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/rpc/rpc_client1.exe -------------------------------------------------------------------------------- /example/rpc/rpc_server1.cc: -------------------------------------------------------------------------------- 1 | #include "../../include/coasync/execution_context.hpp" 2 | #include "../../include/coasync/this_coro.hpp" 3 | #include "../../include/coasync/net/endpoint.hpp" 4 | #include "../../include/coasync/net/protocol.hpp" 5 | #include "../../include/coasync/net/acceptor.hpp" 6 | #include "../../include/coasync/rpc/rpc_server.hpp" 7 | using namespace coasync; 8 | using std::chrono::operator""s; 9 | awaitable test() noexcept { 10 | net::tcp::acceptor acceptor 11 | { 12 | co_await this_coro::context, 13 | net::tcp::endpoint(net::address_v4::loopback(), 10086) 14 | }; 15 | net::rpc::rpc_server server(std::move(acceptor)); 16 | server.bind("sleep", [](int secs) -> void { 17 | std::this_thread::sleep_for(std::chrono::seconds(secs)); 18 | }); 19 | server.bind("add", [](int a, int b) -> int { 20 | return a + b; 21 | }); 22 | co_await server.start(); 23 | std::puts("start return"); 24 | } 25 | int main() { 26 | execution_context ctx {concurrency_arg(0)}; 27 | co_spawn(ctx, test(), use_detach); 28 | ctx.loop(); 29 | } 30 | -------------------------------------------------------------------------------- /example/rpc/rpc_server1.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/rpc/rpc_server1.exe -------------------------------------------------------------------------------- /example/sleep.cc: -------------------------------------------------------------------------------- 1 | #include "../include/coasync/execution_context.hpp" 2 | #include "../include/coasync/functional.hpp" 3 | #include "../include/coasync/this_coro.hpp" 4 | #include "../include/coasync/co_spawn.hpp" 5 | 6 | using namespace coasync; 7 | using std::chrono::operator""s; 8 | 9 | awaitable test(int seconds) 10 | { 11 | co_await sleep_for(std::chrono::seconds(seconds)); 12 | std::puts("sleep awaiken"); 13 | } 14 | 15 | int main() { 16 | execution_context context{concurrency_arg(0)}; 17 | /// Initiate no child threads 18 | co_spawn(context, test(1), use_detach); 19 | co_spawn(context, test(2), use_detach); 20 | co_spawn(context, test(3), use_detach); 21 | context.loop(); 22 | } 23 | -------------------------------------------------------------------------------- /example/timeout.cc: -------------------------------------------------------------------------------- 1 | #include "../include/coasync/functional.hpp" 2 | #include "../include/coasync/this_coro.hpp" 3 | #include "../include/coasync/co_spawn.hpp" 4 | 5 | using namespace coasync; 6 | using std::chrono::operator""s; 7 | 8 | awaitable task_cost_1s() { 9 | co_await sleep_for(1s); 10 | std::puts("task done"); 11 | co_return 999; 12 | } 13 | awaitable task_cost_3s() { 14 | co_await sleep_for(3s); 15 | std::puts("task done"); 16 | co_return 888; 17 | } 18 | awaitable test() 19 | { 20 | std::puts("-------------- task 1 -------------"); 21 | if(auto result = co_await timeout(task_cost_1s(), 10s)) 22 | std::printf("not timeout, value: %d", *result); 23 | else std::puts("timeout"); 24 | 25 | 26 | std::puts("-------------- task 2 -------------"); 27 | if(auto result = co_await timeout(task_cost_3s(), 2s)) 28 | std::printf("not timeout, value: %d", *result); 29 | else std::puts("timeout"); 30 | } 31 | 32 | 33 | int main() { 34 | execution_context context{concurrency_arg(0)}; 35 | /// Initiate no child threads 36 | co_spawn(context, test(), use_detach); 37 | context.loop(); 38 | } 39 | -------------------------------------------------------------------------------- /example/timeout.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/timeout.exe -------------------------------------------------------------------------------- /example/when_any_all.cc: -------------------------------------------------------------------------------- 1 | #include "../include/coasync/when_all.hpp" 2 | #include "../include/coasync/when_any.hpp" 3 | #include "../include/coasync/execution_context.hpp" 4 | #include "../include/coasync/functional.hpp" 5 | #include "../include/coasync/this_coro.hpp" 6 | 7 | using namespace coasync; 8 | using std::chrono::operator""s; 9 | 10 | awaitable delay(int seconds) 11 | { 12 | co_await sleep_for(std::chrono::seconds(seconds)); 13 | std::puts("sleep awaiken"); 14 | co_return co_await this_coro::id; 15 | } 16 | awaitable test() 17 | { 18 | for(unsigned int i {}; i < 10; i ++) 19 | { 20 | auto [a, b, c] = co_await when_all(delay(1), delay(2), delay(3)); 21 | std::printf("when_all results: [%d, %d, %d]\n", a, b, c); 22 | auto result = co_await when_any(delay(1), delay(2), delay(3)); 23 | std::printf("when_any: index: %llu\n", result.index()); 24 | std::visit([](int value) 25 | { 26 | std::printf("when_any: result: %d\n", value); 27 | }, result); 28 | } 29 | co_return; 30 | } 31 | int main() 32 | { 33 | execution_context context{concurrency_arg(3)}; 34 | /// Initiate three child threads 35 | co_spawn(context, test(), use_detach); 36 | context.loop(); 37 | } 38 | -------------------------------------------------------------------------------- /example/when_any_all.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/example/when_any_all.exe -------------------------------------------------------------------------------- /example/yield.cc: -------------------------------------------------------------------------------- 1 | #include "../include/coasync/execution_context.hpp" 2 | #include "../include/coasync/functional.hpp" 3 | #include "../include/coasync/co_spawn.hpp" 4 | using namespace coasync; 5 | using std::chrono::operator""s; 6 | awaitable A() noexcept { 7 | for(unsigned int i {}; i < 5; i ++) { 8 | std::this_thread::sleep_for(1s); 9 | std::puts("-----------A------------"); 10 | co_await yield(); 11 | } 12 | } 13 | awaitable B() noexcept { 14 | for(unsigned int i {}; i < 5; i ++) { 15 | std::this_thread::sleep_for(1s); 16 | std::puts("-----------B------------"); 17 | co_await yield(); 18 | } 19 | } 20 | 21 | int main() { 22 | execution_context context{concurrency_arg(0)}; 23 | co_spawn(context, A(), use_detach); 24 | co_spawn(context, B(), use_detach); 25 | context.loop(); 26 | } 27 | -------------------------------------------------------------------------------- /include/coasync.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_COASYNC_INCLUDED 2 | #define COASYNC_COASYNC_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "coasync/co_spawn.hpp" 9 | #include "coasync/co_bind.hpp" 10 | #include "coasync/co_signal.hpp" 11 | #include "coasync/co_mutex.hpp" 12 | #include "coasync/co_condition_variable.hpp" 13 | #include "coasync/co_latch.hpp" 14 | #include "coasync/co_semaphore.hpp" 15 | 16 | #include "coasync/async_fn.hpp" 17 | #include "coasync/awaitable.hpp" 18 | #include "coasync/generator.hpp" 19 | #include "coasync/channel.hpp" 20 | #include "coasync/execution_context.hpp" 21 | #include "coasync/functional.hpp" 22 | #include "coasync/set_stop_source.hpp" 23 | #include "coasync/this_coro.hpp" 24 | #include "coasync/when_all.hpp" 25 | #include "coasync/when_any.hpp" 26 | #include "coasync/deadline_timer.hpp" 27 | 28 | #include "coasync/net/acceptor.hpp" 29 | #include "coasync/net/address.hpp" 30 | #include "coasync/net/address_v4.hpp" 31 | #include "coasync/net/address_v6.hpp" 32 | #include "coasync/net/address_v4_iterator.hpp" 33 | #include "coasync/net/address_v6_iterator.hpp" 34 | #include "coasync/net/endpoint.hpp" 35 | #include "coasync/net/message_flags.hpp" 36 | #include "coasync/net/option.hpp" 37 | #include "coasync/net/port.hpp" 38 | #include "coasync/net/protocol.hpp" 39 | #include "coasync/net/receive.hpp" 40 | #include "coasync/net/resolver.hpp" 41 | #include "coasync/net/resolver_flags.hpp" 42 | #include "coasync/net/scope_id.hpp" 43 | #include "coasync/net/send.hpp" 44 | #include "coasync/net/serde_stream.hpp" 45 | #include "coasync/net/socket.hpp" 46 | #include "coasync/net/socket_base.hpp" 47 | #include "coasync/net/network_v4.hpp" 48 | #include "coasync/net/network_v6.hpp" 49 | 50 | #include "coasync/rpc/rpc_client.hpp" 51 | #include "coasync/rpc/rpc_server.hpp" 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /include/coasync/awaitable.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_AWAITABLE_INCLUDED 2 | #define COASYNC_AWAITABLE_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "detail/awaitable_frame.hpp" 9 | 10 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 11 | { 12 | /// The return type of a coroutine or asynchronous operation. 13 | /// Support for C++20 Coroutines is provided via the awaitable class template, 14 | /// the use_awaitable completion token, and the co_spawn() function. These facilities 15 | /// allow programs to implement asynchronous logic in a synchronous manner, 16 | /// in conjunction with the co_await keyword 17 | template > 18 | struct awaitable final 19 | { 20 | template 21 | struct rebind_allocator 22 | { 23 | typedef awaitable type; 24 | }; 25 | template 26 | struct rebind_value 27 | { 28 | typedef awaitable type; 29 | }; 30 | typedef Ref value_type; 31 | typedef Alloc allocator_type; 32 | /// awaitable type is used to manage the lifetime of coroutine: 33 | /// providing exception safety with dynamic lifetime, by guaranteeing destroy 34 | /// coroutine-handle on both normal exit and exit through exception. 35 | /// passing ownership of uniquely-owned objects with dynamic lifetime into 36 | /// functions using moving assignment/construction. 37 | /// acquiring ownership of uniquely-owned objects with dynamic lifetime from 38 | /// functions using moving assignment/construction. 39 | /// as the element type in move-aware containers, such as std::vector, which 40 | /// hold coroutine_handle to dynamically/statically-allocated coroutine promise 41 | COASYNC_ATTRIBUTE((always_inline)) awaitable(awaitable&& other) 42 | noexcept: _M_coroutine(std::exchange(other._M_coroutine, nullptr)) 43 | { 44 | } 45 | /// Constructs a awaitable by transferring ownership from other to *this and 46 | /// stores the null coroutine in other. 47 | COASYNC_ATTRIBUTE((always_inline)) awaitable& operator=(awaitable&& other) noexcept 48 | { 49 | if (&other != this) 50 | _M_coroutine = std::exchange(other._M_coroutine, nullptr); 51 | return (*this); 52 | } 53 | COASYNC_ATTRIBUTE((always_inline)) ~ awaitable() 54 | { 55 | if (not _M_coroutine) COASYNC_ATTRIBUTE((unlikely)) 56 | return; 57 | _M_coroutine.destroy(); 58 | } 59 | constexpr awaitable& operator=(awaitable const&) = delete; 60 | COASYNC_ATTRIBUTE((always_inline)) void swap(awaitable& other) noexcept 61 | { 62 | std::swap(_M_coroutine, other._M_coroutine); 63 | } 64 | struct await_result 65 | { 66 | bool await_ready() const noexcept 67 | { 68 | return _M_coroutine.done(); 69 | } 70 | template 71 | std::coroutine_handle<> await_suspend( 72 | std::coroutine_handle> coroutine) noexcept 73 | { 74 | /// There is no way to preserve the awaitable_frame template type itself, 75 | /// and we need to use a generic type to use them, in this case, need to 76 | /// erase the original type of the object 77 | auto previous_frame = std::coroutine_handle::from_address(coroutine.address()); 78 | _M_coroutine.promise().push_frame(previous_frame); 79 | return _M_coroutine; 80 | } 81 | Ref await_resume() const 82 | { 83 | _M_coroutine.promise().rethrow_exception(); 84 | return _M_coroutine.promise().get_value(); 85 | } 86 | std::coroutine_handle> _M_coroutine; 87 | }; 88 | COASYNC_ATTRIBUTE((nodiscard)) await_result operator co_await() const noexcept 89 | { 90 | COASYNC_ASSERT(_M_coroutine != nullptr && !_M_coroutine.done()); 91 | return await_result { _M_coroutine }; 92 | } 93 | COASYNC_ATTRIBUTE((always_inline)) awaitable(std::coroutine_handle> coroutine) noexcept 94 | : _M_coroutine(coroutine) 95 | { 96 | } 97 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 98 | std::coroutine_handle> 99 | COASYNC_API get_coroutine() const noexcept 100 | { 101 | return _M_coroutine; 102 | } 103 | /// Releases the ownership of the managed coroutine promise, if any. 104 | /// get_coroutine() returns nullptr after the call. 105 | /// The caller is responsible for destroy the coroutine 106 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 107 | std::coroutine_handle> 108 | COASYNC_API release_coroutine() noexcept 109 | { 110 | return std::exchange(_M_coroutine, nullptr); 111 | } 112 | private: 113 | std::coroutine_handle> _M_coroutine; 114 | }; 115 | template struct awaitable_traits: std::false_type {}; 116 | template 117 | struct awaitable_traits>: std::true_type 118 | { 119 | typedef Ref value_type; 120 | typedef Alloc allocator_type; 121 | }; 122 | template 123 | struct is_awaitable 124 | : std::bool_constant::value> {}; 125 | template 126 | inline constexpr bool is_awaitable_v 127 | = is_awaitable::value; 128 | } 129 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) std 130 | { 131 | template 132 | struct coroutine_traits, Args...> 133 | { 134 | using promise_type = coasync::detail::awaitable_frame; 135 | }; 136 | } 137 | #endif 138 | -------------------------------------------------------------------------------- /include/coasync/awaitable_group.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_AWAITABLE_GROUP_INCLUDED 2 | #define COASYNC_AWAITABLE_GROUP_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "awaitable.hpp" 9 | #include 10 | 11 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 12 | { 13 | template > 14 | using awaitable_group = std::vector>; 15 | 16 | template 17 | struct is_awaitable_group 18 | : public std::false_type {}; 19 | template 20 | struct is_awaitable_group> 21 | : public std::true_type {}; 22 | 23 | template 24 | inline constexpr bool is_awaitable_group_v 25 | = is_awaitable_group::value; 26 | 27 | } 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /include/coasync/cancellation_error.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_CANCELLATION_ERROR_INCLUDED 2 | #define COASYNC_CANCELLATION_ERROR_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "detail/config.hpp" 9 | #include 10 | 11 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 12 | { 13 | enum class COASYNC_ATTRIBUTE((nodiscard)) cancellation_errc 14 | { 15 | cancellation_requested = 1, 16 | no_frame_registered, 17 | }; 18 | } 19 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) std 20 | { 21 | /// Specialization that allows `cancellation_errc` to convert to `error_code`. 22 | template<> 23 | struct is_error_code_enum : public std::true_type { }; 24 | } 25 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 26 | { 27 | /// Points to a statically-allocated object derived from error_category. 28 | COASYNC_ATTRIBUTE((nodiscard, __gnu__::__const__, always_inline)) 29 | const std::error_category& cancellation_category() noexcept 30 | { 31 | struct COASYNC_ATTRIBUTE((nodiscard)) __category final: public std::error_category 32 | { 33 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 34 | #if defined(__cpp_constexpr) && __cplusplus >=201907L 35 | constexpr 36 | #endif 37 | virtual const char* name() const noexcept 38 | { 39 | return "cancellation"; 40 | } 41 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 42 | #if defined(__cpp_constexpr) && __cplusplus >=201907L 43 | constexpr 44 | #endif 45 | virtual std::string message(int errc) const override 46 | { 47 | switch (errc) 48 | { 49 | case static_cast(cancellation_errc::cancellation_requested): 50 | COASYNC_ATTRIBUTE((likely)) 51 | //TODO 52 | return "cancellation_requested at waiting site"; 53 | case static_cast(cancellation_errc::no_frame_registered): 54 | COASYNC_ATTRIBUTE((unlikely)) 55 | //TODO 56 | return "no_frame_registered at cancelling site"; 57 | default: 58 | COASYNC_ATTRIBUTE((unlikely)) 59 | return "unknown"; 60 | } 61 | } 62 | }; 63 | static struct __category cat; 64 | return cat; 65 | } 66 | 67 | /// Overload of make_error_code for `cancellation_errc`. 68 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 69 | std::error_code make_error_code(cancellation_errc __errc) noexcept 70 | { 71 | return std::error_code(static_cast(__errc), cancellation_category()); 72 | } 73 | 74 | /// Overload of make_error_condition for `cancellation_errc`. 75 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 76 | std::error_condition make_error_condition(cancellation_errc __errc) noexcept 77 | { 78 | return std::error_condition(static_cast(__errc), cancellation_category()); 79 | } 80 | 81 | class COASYNC_ATTRIBUTE((nodiscard)) cancellation_error : public std::logic_error 82 | { 83 | public: 84 | COASYNC_ATTRIBUTE((always_inline)) explicit 85 | cancellation_error(cancellation_errc __errc) 86 | : cancellation_error(std::make_error_code(std::errc(static_cast(__errc)))) 87 | {} 88 | 89 | COASYNC_ATTRIBUTE((always_inline)) 90 | #if defined(__cpp_constexpr) && __cplusplus >=201907L 91 | /*constexpr*/ 92 | #endif 93 | virtual ~cancellation_error() noexcept {} 94 | 95 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 96 | #if defined(__cpp_constexpr) && __cplusplus >=201907L 97 | /*constexpr*/ 98 | #endif 99 | virtual const char* what() const noexcept 100 | { 101 | auto errc = static_cast>(_M_code.value()); 102 | switch (errc) 103 | { 104 | case static_cast(cancellation_errc::cancellation_requested): 105 | COASYNC_ATTRIBUTE((likely)) 106 | //TODO 107 | return "cancellation_requested at waiting site"; 108 | case static_cast(cancellation_errc::no_frame_registered): 109 | COASYNC_ATTRIBUTE((unlikely)) 110 | //TODO 111 | return "no_frame_registered at cancelling site"; 112 | default: 113 | COASYNC_ATTRIBUTE((unlikely)) 114 | return "unknown"; 115 | } 116 | } 117 | 118 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 119 | const std::error_code& code() const noexcept 120 | { 121 | return _M_code; 122 | } 123 | 124 | private: 125 | COASYNC_ATTRIBUTE((always_inline)) explicit 126 | cancellation_error(std::error_code __ec) 127 | : logic_error("std::cancellation_error: " + __ec.message()) 128 | , _M_code(__ec) 129 | { } 130 | 131 | COASYNC_ATTRIBUTE((no_unique_address)) std::error_code _M_code; 132 | }; 133 | } 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /include/coasync/channel.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_CHANNEL_INCLUDED 2 | #define COASYNC_CHANNEL_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "detail/suspendible.hpp" 9 | #include "detail/ring_container.hpp" 10 | #include "detail/service/dequeue_service.hpp" 11 | #include "detail/service/enqueue_service.hpp" 12 | 13 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 14 | { 15 | /// may be used to send messages between different parts of the same application. 16 | /// the set of messages supported by a basic_channel is specified by its template parameters. 17 | /// Messages can be sent and received using asynchronous or non-blocking synchronous operations. 18 | template 19 | struct COASYNC_ATTRIBUTE((nodiscard)) basic_channel 20 | { 21 | /// An async multi-producer multi-consumer basic_channel, where each message can be received 22 | /// by only one of all existing consumers. 23 | /// Bounded basic_channel with limited capacity. 24 | private: 25 | static_assert( 26 | std::is_default_constructible_v 27 | and requires(Mutex& mutex) 28 | { 29 | mutex.lock(); 30 | mutex.unlock(); 31 | }); 32 | public: 33 | typedef Mutex mutex_type; 34 | typedef ring_container container_type; 35 | typedef typename ring_container::value_type value_type; 36 | typedef typename ring_container::reference reference; 37 | typedef typename ring_container::const_reference const_reference; 38 | typedef typename ring_container::size_type size_type; 39 | 40 | COASYNC_ATTRIBUTE((always_inline)) 41 | constexpr explicit basic_channel(execution_context& context) noexcept 42 | : _M_context(context) {} 43 | constexpr basic_channel& operator=(basic_channel const&) = delete; 44 | constexpr basic_channel(basic_channel const&) = delete; 45 | COASYNC_ATTRIBUTE((always_inline)) basic_channel(basic_channel&&) noexcept = default; 46 | COASYNC_ATTRIBUTE((always_inline)) basic_channel& operator=(basic_channel&&) noexcept = default; 47 | COASYNC_ATTRIBUTE((always_inline)) ~ basic_channel() noexcept = default; 48 | 49 | template 50 | requires (not(sizeof...(CtorArgs) == 1 and (std::is_same_v || ...))) 51 | COASYNC_ATTRIBUTE((nodiscard)) awaitable COASYNC_API send(CtorArgs&& ... args) 52 | { 53 | static_assert(std::constructible_from); 54 | Value local_value { std::forward(args) ...}; 55 | co_await detail::related_suspendible(_M_context)(_M_queue, local_value, _M_mutex, Bound); 56 | } 57 | template requires std::is_same_v, Value> 58 | COASYNC_ATTRIBUTE((nodiscard)) awaitable COASYNC_API send(OtherValue&& value) 59 | { 60 | co_await detail::related_suspendible(_M_context)(_M_queue, const_cast(value), _M_mutex, Bound); 61 | } 62 | COASYNC_ATTRIBUTE((nodiscard)) 63 | awaitable COASYNC_API receive() 64 | { 65 | /// see bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103790 66 | /// Bug 103790 - internal compiler error: Segmentation fault when playing with coroutine 67 | /// fixed until GCC 11.1.0 68 | /// declaration of anonymous union in coroutine causes internal compiler error segmentation fault 69 | COASYNC_ATTRIBUTE((gnu::uninitialized)) union Storage 70 | { 71 | Value _M_value; 72 | } storage; 73 | co_await detail::related_suspendible(_M_context)(_M_queue, storage._M_value, _M_mutex); 74 | co_return std::move(storage._M_value); 75 | } 76 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) static constexpr std::size_t capacity() noexcept 77 | { 78 | return Bound; 79 | } 80 | 81 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) execution_context& context() noexcept 82 | { 83 | return _M_context; 84 | } 85 | private: 86 | COASYNC_ATTRIBUTE((no_unique_address)) execution_context& _M_context; 87 | mutable Mutex _M_mutex; 88 | std::queue> _M_queue; 89 | }; 90 | template 91 | using channel 92 | = basic_channel; 93 | } 94 | #endif 95 | -------------------------------------------------------------------------------- /include/coasync/co_bind.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_CO_BIND_INCLUDED 2 | #define COASYNC_CO_BIND_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "co_spawn.hpp" 9 | #include 10 | 11 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 12 | { 13 | /// Encapsulates the synchronous function as asynchronous awaitable type 14 | template 15 | requires std::is_invocable_v 16 | COASYNC_ATTRIBUTE((nodiscard)) 17 | awaitable> 18 | COASYNC_API co_bind(execution_context& context, Fn&& fn, Args&& ... args) 19 | { 20 | using invoke_result_t = std::invoke_result_t; 21 | std::packaged_task packaged_task {std::bind(std::forward(fn), std::forward(args) ...)}; 22 | std::future future = packaged_task.get_future(); 23 | co_spawn(context, [] 24 | #if __cplusplus >= 202207L 25 | COASYNC_ATTRIBUTE((nodiscard)) 26 | #endif 27 | (std::packaged_task packaged_task) -> awaitable 28 | { 29 | co_return (void)packaged_task(); 30 | }(std::move(packaged_task)), use_detach); 31 | return [](std::future future) -> awaitable 32 | { 33 | co_await detail::suspendible()(future); 34 | if constexpr(std::is_void_v) co_return; 35 | else co_return future.get(); 36 | }(std::move(future)); 37 | } 38 | } 39 | #endif 40 | -------------------------------------------------------------------------------- /include/coasync/co_latch.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_CO_LATCH_INCLUDED 2 | #define COASYNC_CO_LATCH_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "co_mutex.hpp" 9 | #include "co_condition_variable.hpp" 10 | 11 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 12 | { 13 | struct execution_context; 14 | /// a downward counter of type std::ptrdiff_t which can be used to synchronize coroutines. 15 | /// The value of the counter is initialized on creation. Threads may block on the latch 16 | /// until the counter is decremented to zero. There is no possibility to increase or reset 17 | /// the counter, which makes the latch a single-use barrier. 18 | template 19 | struct [[nodiscard]] basic_co_latch 20 | { 21 | COASYNC_ATTRIBUTE((always_inline)) 22 | constexpr explicit basic_co_latch(execution_context& context, std::ptrdiff_t count) noexcept 23 | : _M_mutex(context) 24 | , _M_condition(context) 25 | , _M_count(count) {}; 26 | constexpr basic_co_latch& operator=(basic_co_latch const&) = delete; 27 | constexpr basic_co_latch(basic_co_latch const&) = delete; 28 | COASYNC_ATTRIBUTE((always_inline)) basic_co_latch(basic_co_latch&&) noexcept = default; 29 | COASYNC_ATTRIBUTE((always_inline)) basic_co_latch& operator=(basic_co_latch&&) noexcept = default; 30 | COASYNC_ATTRIBUTE((always_inline)) ~ basic_co_latch() noexcept = default; 31 | 32 | COASYNC_ATTRIBUTE((nodiscard)) awaitable COASYNC_API count_down(std::ptrdiff_t update = 1) noexcept 33 | { 34 | auto __l = co_await _M_mutex.scoped(); 35 | COASYNC_ASSERT((_M_count >= update)); 36 | _M_count -= update; 37 | if(_M_count == 0) COASYNC_ATTRIBUTE((unlikely)) 38 | _M_condition.notify_all(); 39 | } 40 | COASYNC_ATTRIBUTE((nodiscard)) awaitable COASYNC_API try_wait() noexcept 41 | { 42 | auto __l = co_await _M_mutex.scoped(); 43 | co_return (_M_count == 0); 44 | } 45 | /// enables multiple tasks to synchronize the beginning of some computation. 46 | COASYNC_ATTRIBUTE((nodiscard)) awaitable COASYNC_API wait() noexcept 47 | { 48 | auto __l = co_await _M_mutex.scoped(); 49 | co_await _M_condition.wait(_M_mutex, [this] 50 | #if __cplusplus >= 202207L 51 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 52 | #endif 53 | () noexcept -> bool 54 | { 55 | return (_M_count == 0); 56 | }); 57 | } 58 | COASYNC_ATTRIBUTE((nodiscard)) awaitable COASYNC_API arrive_and_wait(std::ptrdiff_t update = 1) noexcept 59 | { 60 | co_await count_down(update); 61 | co_await wait(); 62 | } 63 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) execution_context& context() noexcept 64 | { 65 | return _M_mutex.context(); 66 | } 67 | private: 68 | mutable co_mutex _M_mutex; 69 | mutable co_condition_variable _M_condition; 70 | std::ptrdiff_t _M_count; 71 | }; 72 | typedef basic_co_latch co_latch; 73 | } 74 | #endif 75 | -------------------------------------------------------------------------------- /include/coasync/co_mutex.hpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/include/coasync/co_mutex.hpp -------------------------------------------------------------------------------- /include/coasync/co_semaphore.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_CO_SEMAPHORE_INCLUDED 2 | #define COASYNC_CO_SEMAPHORE_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "co_mutex.hpp" 9 | #include "co_condition_variable.hpp" 10 | 11 | #include /// for std::numeric_limits::max() 12 | 13 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 14 | { 15 | struct execution_context; 16 | ///a lightweight synchronization primitive that can control access to a shared resource. 17 | /// Unlike a basic_co_mutex, a counting_semaphore allows more than one concurrent access 18 | /// to the same resource, for at least LeastMaxValue concurrent accessors. 19 | template 20 | struct [[nodiscard]] basic_co_counting_semaphore 21 | { 22 | private: 23 | static_assert(least_max_value <= std::numeric_limits::max()); 24 | public: 25 | COASYNC_ATTRIBUTE((always_inline)) 26 | constexpr explicit basic_co_counting_semaphore(execution_context& context, std::ptrdiff_t desired) noexcept 27 | : _M_mutex(context) 28 | , _M_condition(context) 29 | , _M_count(desired) {}; 30 | constexpr basic_co_counting_semaphore& operator=(basic_co_counting_semaphore const&) = delete; 31 | constexpr basic_co_counting_semaphore(basic_co_counting_semaphore const&) = delete; 32 | COASYNC_ATTRIBUTE((always_inline)) basic_co_counting_semaphore(basic_co_counting_semaphore&&) noexcept = default; 33 | COASYNC_ATTRIBUTE((always_inline)) basic_co_counting_semaphore& operator=(basic_co_counting_semaphore&&) noexcept = default; 34 | COASYNC_ATTRIBUTE((always_inline)) ~ basic_co_counting_semaphore() noexcept = default; 35 | 36 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) static constexpr COASYNC_API max() noexcept 37 | { 38 | return least_max_value; 39 | } 40 | COASYNC_ATTRIBUTE((nodiscard)) awaitable COASYNC_API acquire() noexcept 41 | { 42 | auto __l = co_await _M_mutex.scoped(); 43 | co_await _M_condition.wait(_M_mutex, [this] 44 | #if __cplusplus >= 202207L 45 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 46 | #endif 47 | () noexcept -> bool 48 | { 49 | return _M_count > 0; 50 | }); 51 | -- _M_count; 52 | } 53 | COASYNC_ATTRIBUTE((nodiscard)) awaitable COASYNC_API release(std::ptrdiff_t update = 1) noexcept 54 | { 55 | COASYNC_ASSERT((update <= max() and update != 0)); 56 | auto __l = co_await _M_mutex.scoped(); 57 | COASYNC_ASSERT((_M_count <= max() - update)); 58 | _M_count += update; 59 | if(update > 1) COASYNC_ATTRIBUTE((likely)) 60 | _M_condition.notify_all(); 61 | else COASYNC_ATTRIBUTE((unlikely)) 62 | _M_condition.notify_one(); 63 | co_return; 64 | } 65 | COASYNC_ATTRIBUTE((nodiscard)) awaitable COASYNC_API try_release() noexcept 66 | { 67 | auto __l = co_await _M_mutex.scoped(); 68 | if(_M_count != 0) COASYNC_ATTRIBUTE((unlikely)) 69 | { 70 | (void)(-- _M_count); 71 | co_return (true); 72 | } 73 | co_return (false); 74 | } 75 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) execution_context& context() noexcept 76 | { 77 | return _M_mutex.context(); 78 | } 79 | private: 80 | mutable co_mutex _M_mutex; 81 | mutable co_condition_variable _M_condition; 82 | std::ptrdiff_t _M_count; 83 | }; 84 | template ::max()> 85 | using co_counting_semaphore = basic_co_counting_semaphore; 86 | 87 | typedef basic_co_counting_semaphore<1ull, execution_context> co_binary_semaphore; 88 | 89 | } 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /include/coasync/co_signal.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_CO_SIGNAL_INCLUDED 2 | #define COASYNC_CO_SIGNAL_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "co_spawn.hpp" 9 | #include "detail/signal_condition.hpp" 10 | #include "detail/service/flag_service.hpp" 11 | #include "detail/suspendible.hpp" 12 | #include 13 | 14 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 15 | { 16 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 17 | { 18 | template 19 | requires std::is_invocable_r_v, int> 20 | void COASYNC_API co_signal_impl(execution_context& context, Fn&& fn) 21 | { 22 | return (void) co_spawn(context, [] 23 | #if __cplusplus >= 202207L 24 | COASYNC_ATTRIBUTE((nodiscard)) 25 | #endif 26 | (std::function __fn) 27 | mutable noexcept(std::is_nothrow_invocable_r_v, int>) -> awaitable 28 | { 29 | void(*__fp)(int) = std::signal(static_cast(asynchronous_tag), &detail::signal_condition::_S_set); 30 | std::atomic_flag /* volatile */& __flag = detail::signal_condition::_S_flag; 31 | co_await detail::suspendible()(__flag); 32 | (void) (__fn)(static_cast(asynchronous_tag)); 33 | (void) std::signal(static_cast(asynchronous_tag), __fp); 34 | std::atomic_flag_clear_explicit(&detail::signal_condition::_S_flag, std::memory_order_release); 35 | }(std::forward(fn)), detail::use_detach_t{}); 36 | } 37 | } 38 | template 39 | void COASYNC_API co_signal(execution_context& context, int signum, Fn&& callback) noexcept(false) 40 | { 41 | switch (signum) 42 | { 43 | case SIGABRT: COASYNC_ATTRIBUTE((likely)) 44 | return (void) detail::co_signal_impl(context, std::forward(callback)); 45 | case SIGFPE: COASYNC_ATTRIBUTE((unlikely)) 46 | return (void) detail::co_signal_impl(context, std::forward(callback)); 47 | case SIGINT: COASYNC_ATTRIBUTE((likely)) 48 | return (void) detail::co_signal_impl(context, std::forward(callback)); 49 | case SIGSEGV: COASYNC_ATTRIBUTE((likely)) 50 | return (void) detail::co_signal_impl(context, std::forward(callback)); 51 | case SIGTERM: COASYNC_ATTRIBUTE((likely)) 52 | return (void) detail::co_signal_impl(context, std::forward(callback)); 53 | default: COASYNC_ATTRIBUTE((unlikely)) 54 | throw std::invalid_argument("co_signal: unsurported signal type"); 55 | } 56 | } 57 | } 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /include/coasync/co_spawn.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_CO_SPAWN_INCLUDED 2 | #define COASYNC_CO_SPAWN_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "detail/suspendible.hpp" 9 | #include "detail/service/future_service.hpp" 10 | 11 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 12 | { 13 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 14 | { 15 | /// Spawn a new coroutined-based thread of execution. 16 | /// a high-level wrapper over the Boost.Coroutine library. This function enables programs 17 | /// to implement asynchronous logic in a synchronous manner 18 | struct use_detach_t {}; 19 | struct use_future_t {}; 20 | struct use_awaitable_t {}; 21 | /// The completion token will tag the notification how the thread of execution would 22 | /// be completed. 23 | } 24 | inline constexpr detail::use_detach_t use_detach; 25 | inline constexpr detail::use_future_t use_future; 26 | inline constexpr detail::use_awaitable_t use_awaitable; 27 | template 28 | void COASYNC_API co_spawn( 29 | execution_context& context, 30 | awaitable a, 31 | COASYNC_ATTRIBUTE((maybe_unused)) detail::use_detach_t) 32 | { 33 | std::coroutine_handle> 34 | frame = a.release_coroutine(); 35 | context.push_frame_to_lifetime(frame); 36 | context.push_frame_to_executor(std::coroutine_handle::from_address(frame.address())); 37 | } 38 | template 39 | COASYNC_ATTRIBUTE((nodiscard)) 40 | std::future COASYNC_API co_spawn( 41 | execution_context& context, 42 | awaitable a, 43 | COASYNC_ATTRIBUTE((maybe_unused)) detail::use_future_t) 44 | { 45 | std::promise promise; 46 | std::future future = promise.get_future(); 47 | co_spawn(context, [](awaitable a, std::promise promise) -> awaitable 48 | { 49 | if constexpr(not std::is_void_v) 50 | promise.set_value(static_cast(co_await a)); 51 | else 52 | { 53 | co_await a; 54 | promise.set_value(); 55 | } 56 | }(std::move(a), std::move(promise)), use_detach); 57 | return future; 58 | } 59 | template 60 | COASYNC_ATTRIBUTE((nodiscard)) 61 | awaitable COASYNC_API co_spawn( 62 | execution_context& context, 63 | awaitable a, 64 | COASYNC_ATTRIBUTE((maybe_unused)) detail::use_awaitable_t) 65 | { 66 | return [](std::future future) -> awaitable 67 | { 68 | co_await detail::suspendible()(future); 69 | if constexpr(not std::is_void_v) co_return future.get(); 70 | else 71 | { 72 | future.wait(); 73 | co_return; 74 | } 75 | }(co_spawn(context, std::move(a), use_future)); 76 | } 77 | } 78 | #endif 79 | -------------------------------------------------------------------------------- /include/coasync/deadline_timer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_DEADLINE_TIMER_INCLUDED 2 | #define COASYNC_DEADLINE_TIMER_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "detail/suspendible.hpp" 9 | #include "detail/service/time_service.hpp" 10 | 11 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 12 | { 13 | enum class COASYNC_ATTRIBUTE((nodiscard)) deadline_timer_status: unsigned int 14 | { 15 | timeout = 0, 16 | cancelled 17 | }; 18 | 19 | template 20 | struct COASYNC_ATTRIBUTE((nodiscard)) basic_deadline_timer 21 | { 22 | typedef Rep rep; 23 | typedef Period period; 24 | 25 | template 26 | COASYNC_ATTRIBUTE((always_inline)) 27 | constexpr explicit basic_deadline_timer( 28 | execution_context& ctx, 29 | std::chrono::duration const& dura) noexcept 30 | : _M_context(ctx) 31 | , _M_duration(dura) {} 32 | 33 | constexpr basic_deadline_timer& operator=(basic_deadline_timer const&) = delete; 34 | constexpr basic_deadline_timer(basic_deadline_timer const&) = delete; 35 | COASYNC_ATTRIBUTE((always_inline)) basic_deadline_timer(basic_deadline_timer&&) noexcept = default; 36 | COASYNC_ATTRIBUTE((always_inline)) basic_deadline_timer& operator=(basic_deadline_timer&&) noexcept = default; 37 | COASYNC_ATTRIBUTE((always_inline)) ~ basic_deadline_timer() noexcept = default; 38 | 39 | COASYNC_ATTRIBUTE((nodiscard)) awaitable wait() const 40 | { 41 | try 42 | { 43 | co_await detail::related_suspendible(_M_context)(_M_duration, std::addressof(_M_cancellation)); 44 | } 45 | catch(coasync::cancellation_error& error) 46 | { 47 | if(error.code() == std::make_error_code(static_cast(cancellation_errc::cancellation_requested))) COASYNC_ATTRIBUTE((likely)) 48 | co_return deadline_timer_status::cancelled; 49 | std::rethrow_exception(std::make_exception_ptr(std::move(error))); 50 | } 51 | catch(...) 52 | { 53 | std::rethrow_exception(std::current_exception()); 54 | } 55 | co_return deadline_timer_status::timeout; 56 | } 57 | COASYNC_ATTRIBUTE((always_inline)) void cancel() const noexcept(false) 58 | { 59 | try 60 | { 61 | use_service(_M_context).cancel_frame(_M_cancellation); 62 | } 63 | catch(coasync::cancellation_error& error) 64 | { 65 | if(error.code() 66 | == std::make_error_code(static_cast(cancellation_errc::no_frame_registered))) 67 | COASYNC_ATTRIBUTE((likely)) 68 | return; 69 | std::rethrow_exception(std::make_exception_ptr(std::move(error))); 70 | } 71 | } 72 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) execution_context& context() noexcept 73 | { 74 | return _M_context; 75 | } 76 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 77 | constexpr std::chrono::duration expire_at() const noexcept 78 | { 79 | return _M_duration; 80 | } 81 | private: 82 | COASYNC_ATTRIBUTE((no_unique_address)) execution_context& _M_context; 83 | std::chrono::duration const _M_duration; 84 | mutable long _M_cancellation; 85 | }; 86 | #if defined(__cpp_deduction_guides) 87 | template 88 | basic_deadline_timer(execution_context&, std::chrono::duration const&) 89 | -> basic_deadline_timer; 90 | #endif 91 | template > 92 | using deadline_timer 93 | = basic_deadline_timer; 94 | } 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /include/coasync/detail/awaitable_frame.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_AWAITABLE_FRAME_INCLUDED 2 | #define COASYNC_AWAITABLE_FRAME_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "awaitable_frame_base.hpp" 9 | #include "awaitable_frame_alloc.hpp" 10 | #include "manual_lifetime.hpp" 11 | 12 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 13 | { 14 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 15 | { 16 | template 17 | struct awaitable_frame final 18 | : public awaitable_frame_base 19 | , public awaitable_frame_alloc 20 | { 21 | // nothrow?: awaitable_frame_alloc::operator new 22 | // COASYNC_ATTRIBUTE((noreturn)) 23 | // static std::coroutine_handle get_return_object_on_allocation_failure() 24 | // { 25 | // throw std::bad_alloc(); // or, return Coroutine(nullptr); 26 | // } 27 | std::coroutine_handle get_return_object() noexcept 28 | { 29 | return std::coroutine_handle::from_promise(*this); 30 | } 31 | template 32 | void return_value(U&& value) 33 | noexcept(std::is_nothrow_constructible_v) 34 | requires (not std::is_reference_v) 35 | { 36 | static_assert(std::is_constructible_v); 37 | _M_value.construct(std::forward(value)); 38 | } 39 | void return_value(Ref&& value) 40 | noexcept(std::is_nothrow_move_constructible_v) 41 | { 42 | static_assert(std::is_move_constructible_v); 43 | _M_value.construct(static_cast(value)); 44 | } 45 | /// An interface that can only be called once to get the return value of the coroutine 46 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 47 | Ref COASYNC_API get_value() noexcept 48 | { 49 | Ref value = static_cast(_M_value.get()); 50 | _M_value.destruct(); 51 | return value; 52 | } 53 | using awaitable_frame_alloc::operator new; 54 | using awaitable_frame_alloc::operator delete; 55 | private: 56 | COASYNC_ATTRIBUTE((no_unique_address)) manual_lifetime _M_value; 57 | }; 58 | template 59 | struct awaitable_frame final 60 | : public awaitable_frame_base 61 | , public awaitable_frame_alloc 62 | { 63 | std::coroutine_handle get_return_object() noexcept 64 | { 65 | return std::coroutine_handle::from_promise(*this); 66 | } 67 | void return_void() const noexcept {} 68 | COASYNC_ATTRIBUTE((always_inline)) 69 | void COASYNC_API get_value() const noexcept {} 70 | using awaitable_frame_alloc::operator new; 71 | using awaitable_frame_alloc::operator delete; 72 | }; 73 | 74 | template struct awaitable_frame_traits: std::false_type {}; 75 | template 76 | struct awaitable_frame_traits>: std::true_type 77 | { 78 | typedef Ref value_type; 79 | typedef Alloc allocator_type; 80 | }; 81 | 82 | } 83 | } 84 | #endif 85 | -------------------------------------------------------------------------------- /include/coasync/detail/awaitable_frame_alloc.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_AWAITABLE_FRAME_ALLOC_INCLUDED 2 | #define COASYNC_AWAITABLE_FRAME_ALLOC_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "config.hpp" 9 | #if __cpp_impl_coroutine >= 201902 && __cpp_lib_coroutine >= 201902 10 | # include 11 | #elif defined(__cpp_coroutines) && __has_include() 12 | # include 13 | namespace std { 14 | using std::experimental::coroutine_handle; 15 | using std::experimental::coroutine_traits; 16 | using std::experimental::noop_coroutine; 17 | using std::experimental::suspend_always; 18 | using std::experimental::suspend_never; 19 | } // namespace std 20 | #else 21 | # error This library requires the use of C++20 coroutine support 22 | #endif 23 | #include 24 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 25 | { 26 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 27 | { 28 | /// Encapsulates strategies for access/addressing, allocation/deallocation and 29 | /// construction/destruction of coroutine objects. 30 | static constexpr std::size_t aligned_allocation_size(std::size_t s, std::size_t a) noexcept 31 | { 32 | return (s + a - 1) & ~(a - 1); 33 | } 34 | /// https://en.cppreference.com/w/cpp/language/coroutines 35 | /// Coroutine state is allocated dynamically via non-array operator new. 36 | /// If the Promise type defines a class-level replacement, it will be used, 37 | /// otherwise global operator new will be used. 38 | /// If the Promise type defines a placement form of operator new that takes 39 | /// additional parameters, and they match an argument list where the first argument 40 | /// is the size requested (of type std::size_t) and the rest are the coroutine 41 | /// function arguments, those arguments will be passed to operator new 42 | /// (this makes it possible to use leading-allocator-convention for coroutines). 43 | template 44 | struct awaitable_frame_alloc 45 | { 46 | template 47 | static void* operator new (std::size_t frame_size, std::allocator_arg_t, Alloc alloc, Args& ...) 48 | noexcept( 49 | noexcept(std::declval().allocate((std::size_t) 0)) 50 | and std::is_nothrow_move_constructible_v) 51 | { 52 | void* frame_pointer = alloc.allocate(padded_frame_size(frame_size)); 53 | void* allocator_pointer = std::addressof(get_allocator(frame_pointer, frame_size)); 54 | ::new (allocator_pointer) Alloc(std::move(alloc)); 55 | return frame_pointer; 56 | } 57 | template 58 | static void* operator new (std::size_t frame_size, This&, std::allocator_arg_t, Alloc alloc, Args& ...) 59 | noexcept( 60 | noexcept(std::declval().allocate((std::size_t) 0)) 61 | and std::is_nothrow_move_constructible_v) 62 | { 63 | return awaitable_frame_alloc::operator new (frame_size, std::allocator_arg, std::move(alloc)); 64 | } 65 | static void operator delete (void* ptr, std::size_t frame_size) 66 | noexcept( 67 | noexcept(std::declval().deallocate((std::byte*)0, (std::size_t) 0)) 68 | and std::is_nothrow_destructible_v) 69 | { 70 | Alloc& alloc = get_allocator(ptr, frame_size); 71 | Alloc local_alloc(std::move(alloc)); 72 | alloc.~Alloc(); 73 | local_alloc.deallocate(static_cast(ptr), padded_frame_size(frame_size)); 74 | } 75 | private: 76 | static std::size_t offset_of_allocator(std::size_t frame_size) noexcept 77 | { 78 | return aligned_allocation_size(frame_size, alignof(Alloc)); 79 | } 80 | static std::size_t padded_frame_size(std::size_t frame_size) noexcept 81 | { 82 | return offset_of_allocator(frame_size) + sizeof(Alloc); 83 | } 84 | static Alloc& get_allocator(void* frame_pointer, std::size_t frame_size) noexcept 85 | { 86 | char* allocator_pointer = static_cast(frame_pointer) + offset_of_allocator(frame_size); 87 | #if defined(__cpp_lib_launder) 88 | return *std::launder(reinterpret_cast(allocator_pointer)); 89 | #else 90 | return *reinterpret_cast(allocator_pointer); 91 | #endif 92 | } 93 | }; 94 | template 95 | static bool 96 | constexpr allocator_needs_to_be_stored 97 | = not std::allocator_traits::is_always_equal::value 98 | or not std::is_default_constructible_v; 99 | template 100 | requires (not allocator_needs_to_be_stored) 101 | struct awaitable_frame_alloc 102 | { 103 | static void* operator new (std::size_t frame_size) 104 | noexcept( 105 | noexcept(std::declval().allocate((std::size_t) 0)) 106 | and std::is_nothrow_default_constructible_v) 107 | { 108 | Alloc alloc; 109 | return alloc.allocate(frame_size); 110 | } 111 | static void operator delete (void* frame_pointer, std::size_t frame_size) 112 | noexcept( 113 | noexcept(std::declval().deallocate((std::byte*)0, (std::size_t) 0)) 114 | and std::is_nothrow_destructible_v) 115 | { 116 | Alloc alloc; 117 | alloc.deallocate(static_cast(frame_pointer), frame_size); 118 | } 119 | }; 120 | } 121 | } 122 | #endif 123 | -------------------------------------------------------------------------------- /include/coasync/detail/awaitable_frame_base.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_FRAME_BASE_INCLUDED 2 | #define COASYNC_FRAME_BASE_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "config.hpp" 9 | #if __cpp_impl_coroutine >= 201902 && __cpp_lib_coroutine >= 201902 10 | # include 11 | #elif defined(__cpp_coroutines) && __has_include() 12 | # include 13 | namespace std 14 | { 15 | using std::experimental::coroutine_handle; 16 | using std::experimental::coroutine_traits; 17 | using std::experimental::noop_coroutine; 18 | using std::experimental::suspend_always; 19 | using std::experimental::suspend_never; 20 | } // namespace std 21 | #else 22 | # error This library requires the use of C++20 coroutine support 23 | #endif 24 | #if defined(__cpp_lib_semaphore) 25 | # include 26 | #else 27 | # error This library requires the use of C++20 semaphore support 28 | #endif 29 | #if defined(__cpp_lib_jthread) 30 | # include 31 | #else 32 | # error This library requires the use of C++20 jthread/stop_token support 33 | #endif 34 | #include 35 | 36 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 37 | { 38 | struct execution_context; 39 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 40 | { 41 | struct awaitable_frame_base 42 | { 43 | COASYNC_ATTRIBUTE((always_inline)) 44 | void COASYNC_API set_exception(std::exception_ptr eptr) noexcept 45 | { 46 | _M_exception = eptr; 47 | } 48 | COASYNC_ATTRIBUTE((always_inline)) 49 | void COASYNC_API set_error(std::error_code const& ec) noexcept 50 | { 51 | _M_exception = std::make_exception_ptr(std::system_error(ec)); 52 | } 53 | COASYNC_ATTRIBUTE((always_inline)) 54 | void COASYNC_API rethrow_exception() 55 | { 56 | if(_M_exception) COASYNC_ATTRIBUTE((unlikely)) 57 | std::rethrow_exception(std::move(_M_exception)); 58 | } 59 | COASYNC_ATTRIBUTE((always_inline)) 60 | void COASYNC_API set_context(execution_context* context) noexcept 61 | { 62 | _M_context = context; 63 | } 64 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 65 | execution_context* COASYNC_API get_context() noexcept 66 | { 67 | return _M_context; 68 | } 69 | COASYNC_ATTRIBUTE((always_inline)) 70 | void set_id(std::uint_least32_t uint) noexcept 71 | { 72 | _M_id = uint; 73 | } 74 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 75 | std::uint_least32_t COASYNC_API get_id() const noexcept 76 | { 77 | return _M_id; 78 | } 79 | /// std::binary_semaphore: 80 | /// It is used to maintain a strict serial order in the process 81 | /// of swapping out and swapping in each coroutine group in a multithreaded environment 82 | COASYNC_ATTRIBUTE((always_inline)) 83 | void COASYNC_API set_semaphore(std::binary_semaphore* semaphore) noexcept 84 | { 85 | _M_semaphore = semaphore; 86 | } 87 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 88 | std::binary_semaphore* COASYNC_API get_semaphore() const noexcept 89 | { 90 | return _M_semaphore; 91 | } 92 | /// std::stop_token 93 | /// A user-visible cancel handle. It is checkable if cancellation is requested at a particular time 94 | /// as a cancellation indication, and thus explicitly exits this coroutine 95 | COASYNC_ATTRIBUTE((always_inline)) 96 | void COASYNC_API set_stop_token(std::stop_token stoken) noexcept 97 | { 98 | if(stoken.stop_possible()) COASYNC_ATTRIBUTE((unlikely)) 99 | _M_stop_token = std::move(stoken); 100 | } 101 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 102 | std::stop_token COASYNC_API get_stop_token() const noexcept 103 | { 104 | return _M_stop_token; 105 | } 106 | /// Sets the parent coroutine and passes down the control information 107 | COASYNC_ATTRIBUTE((always_inline)) 108 | void COASYNC_API push_frame(std::coroutine_handle previous_frame) noexcept 109 | { 110 | _M_previous = previous_frame; 111 | set_id( previous_frame.promise().get_id() ); 112 | set_context( previous_frame.promise().get_context() ); 113 | set_semaphore( previous_frame.promise().get_semaphore() ); 114 | set_stop_token( previous_frame.promise().get_stop_token() ); 115 | } 116 | std::suspend_always initial_suspend() const noexcept 117 | { 118 | return std::suspend_always{}; 119 | } 120 | auto final_suspend() const noexcept 121 | { 122 | struct final_result 123 | { 124 | bool await_ready() const noexcept 125 | { 126 | return false; 127 | } 128 | std::coroutine_handle<> await_suspend(COASYNC_ATTRIBUTE((maybe_unused)) std::coroutine_handle<>) const noexcept 129 | { 130 | return _M_previous ? _M_previous : std::noop_coroutine(); 131 | } 132 | void await_resume() const noexcept { } 133 | std::coroutine_handle<> _M_previous; 134 | }; 135 | return final_result {_M_previous}; 136 | } 137 | void unhandled_exception() 138 | { 139 | if (not _M_previous) COASYNC_ATTRIBUTE((unlikely)) 140 | std::rethrow_exception(std::current_exception()); 141 | else COASYNC_ATTRIBUTE((likely)) 142 | set_exception(std::current_exception()); 143 | } 144 | protected: 145 | std::coroutine_handle 146 | _M_previous; 147 | std::exception_ptr _M_exception; 148 | execution_context* _M_context; 149 | std::uint_least32_t _M_id; 150 | std::binary_semaphore* _M_semaphore; 151 | std::stop_token _M_stop_token; 152 | }; 153 | } 154 | } 155 | #endif 156 | -------------------------------------------------------------------------------- /include/coasync/detail/basic_lockable.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __COASYNC_BASIC_LOCKABLE_INCLUDED 2 | #define __COASYNC_BASIC_LOCKABLE_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "config.hpp" 9 | #include "spin_loop_pause.hpp" 10 | #include 11 | 12 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 13 | { 14 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 15 | { 16 | /// Simple spin lock with atomic variables 17 | struct basic_lockable 18 | { 19 | COASYNC_ATTRIBUTE((always_inline)) void lock() noexcept 20 | { 21 | COASYNC_ATTRIBUTE((gnu::uninitialized)) bool desired; 22 | do 23 | { 24 | #if defined(__cpp_lib_atomic_wait) 25 | _M_locked.wait(true, std::memory_order_acquire); 26 | /// Performs atomic waiting operations. Behaves as if it repeatedly performs the following steps: 27 | /// Compare the value representation of this->load(order) with that of old. 28 | /// If those are equal, then blocks until *this is notified by notify_one() or notify_all(), 29 | /// or the thread is unblocked spuriously. Otherwise, returns. 30 | /// These functions are guaranteed to return only if value has changed, even if underlying 31 | /// implementation unblocks spuriously. 32 | #else 33 | detail::__spin_loop_pause(); 34 | #endif 35 | desired = false; 36 | } 37 | while (not _M_locked.compare_exchange_weak(desired, true, 38 | std::memory_order_release, std::memory_order_relaxed)); 39 | } 40 | COASYNC_ATTRIBUTE((always_inline)) void unlock() noexcept 41 | { 42 | if (not _M_locked.load(std::memory_order_acquire)) COASYNC_ATTRIBUTE((unlikely)) 43 | return; 44 | _M_locked.store(false, std::memory_order_release); 45 | #if defined(__cpp_lib_atomic_wait) 46 | /// Performs atomic notifying operations. 47 | /// If there is a thread blocked in atomic waiting operation (i.e. wait()) on *this, 48 | /// then unblocks at least one such thread; otherwise does nothing. 49 | _M_locked.notify_one(); 50 | #endif 51 | } 52 | private: 53 | std::atomic_bool _M_locked { false }; 54 | }; 55 | } 56 | } 57 | #endif 58 | -------------------------------------------------------------------------------- /include/coasync/detail/frame_delegate.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_FRAME_DELEGATE_INCLUDED 2 | #define COASYNC_FRAME_DELEGATE_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "awaitable_frame.hpp" 9 | 10 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 11 | { 12 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 13 | { 14 | /// The address of the coroutine_handle is strongly related to the alignment size of 15 | /// the coroutine_traits<...>::promise_type, so even if there is an inheritance 16 | /// relationship between promises, inconsistent alignment lengths can lead to incorrect 17 | /// addresses after conversion. To put it simply, assuming struct PromiseA : 18 | /// public PromiseB, and you have coroutine_handle b;, it is not allowed 19 | /// to use coroutine_handle::from_address(b.address()) or coroutine_handle::from_promise(b.promise()). 20 | /// This means that due to potential differences in memory alignment between derived 21 | /// and base classes, attempting to reinterpret the address or the promise object 22 | /// from one class to another within the context of coroutines can result in undefined behavior or errors. 23 | struct frame_delegate 24 | { 25 | /// notice that: 26 | /// for the method [static constexpr coroutine_handle from_address( void *addr )] 27 | /// the behavior is undefined if addr is neither a null pointer value 28 | /// nor an underlying address of a coroutine_handle. The behavior is also undefined 29 | /// if the addr is an underlying address of a std::coroutine_handle, where both 30 | /// Promise and P1 are not void, and P1 is different from Promise. 31 | enum Op 32 | { 33 | Op_destroy, Op_done 34 | }; 35 | template 36 | struct delegator 37 | { 38 | COASYNC_ATTRIBUTE((always_inline)) 39 | static void S_delegate( 40 | Op opcode, 41 | std::coroutine_handle frame_base, 42 | /// awaitable_frame_base: type-erasing not supported for std::coroutine_handle

::done/destroy 43 | COASYNC_ATTRIBUTE((maybe_unused)) void* payload) 44 | /// use void* for type-erasing 45 | { 46 | /// The destory and done methods of the coroutine have strong requirements 47 | /// for type alignment in specific implementations; 48 | auto frame = std::coroutine_handle>::from_address(frame_base.address()); 49 | switch(opcode) 50 | { 51 | case Op_done: 52 | COASYNC_ATTRIBUTE((likely)) 53 | * static_cast(payload) = frame.done(); 54 | break; 55 | case Op_destroy: 56 | COASYNC_ATTRIBUTE((unlikely)) 57 | frame.destroy(); 58 | break; 59 | } 60 | /// notice that coroutine resume is OK with uncompatible promise_type 61 | } 62 | }; 63 | typedef void(* delegate_type)(Op, std::coroutine_handle, void*); 64 | }; 65 | } 66 | } 67 | #endif 68 | -------------------------------------------------------------------------------- /include/coasync/detail/frame_lifetime.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_FRAME_LIFETIME_INCLUDED 2 | #define COASYNC_FRAME_LIFETIME_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "frame_delegate.hpp" 9 | #include "basic_lockable.hpp" 10 | #include 11 | #include 12 | 13 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 14 | { 15 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 16 | { 17 | /// Coroutine container, used to check the termination state of the coroutine in polling 18 | /// and periodically clean up the coroutine 19 | struct frame_lifetime 20 | { 21 | private: 22 | struct value_type 23 | { 24 | COASYNC_ATTRIBUTE((always_inline)) 25 | value_type(std::coroutine_handle frame, frame_delegate::delegate_type fp) noexcept 26 | : _M_frame(frame) 27 | , _M_delegate(fp) 28 | /// The semaphore is initialized to the available state 29 | , _M_semaphore(1) 30 | {} 31 | COASYNC_ATTRIBUTE((nodiscard)) static bool destructible(value_type& value) 32 | { 33 | COASYNC_ATTRIBUTE((gnu::uninitialized)) bool frame_complete; 34 | COASYNC_ATTRIBUTE((assume(!!value._M_delegate))); 35 | (* value._M_delegate)(frame_delegate::Op_done, value._M_frame, &frame_complete); 36 | if(frame_complete) COASYNC_ATTRIBUTE((unlikely)) 37 | { 38 | value._M_semaphore.acquire(); 39 | /// The executor will do the release operation after the coroutine completes, 40 | /// so the container needs to do a acquire operation before destroying the semaphore 41 | (* value._M_delegate)(frame_delegate::Op_destroy, value._M_frame, nullptr); 42 | } 43 | return frame_complete; 44 | } 45 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 46 | std::binary_semaphore& get_semaphore() noexcept 47 | { 48 | return _M_semaphore; 49 | } 50 | private: 51 | std::coroutine_handle 52 | _M_frame; 53 | frame_delegate::delegate_type _M_delegate; 54 | std::binary_semaphore _M_semaphore; 55 | }; 56 | public: 57 | explicit frame_lifetime(bool concurrent_hint) noexcept: _M_concurrent_hint(concurrent_hint) {} 58 | /// Pushes a coroutine handle and manages the life cycle 59 | template 60 | COASYNC_ATTRIBUTE((always_inline)) 61 | void COASYNC_API push_frame(std::coroutine_handle> frame) 62 | { 63 | COASYNC_ATTRIBUTE((maybe_unused)) std::unique_lock alternative_lock; 64 | if(_M_concurrent_hint) COASYNC_ATTRIBUTE((likely)) 65 | std::unique_lock(_M_lockable).swap(alternative_lock); 66 | /// check concurrency 67 | frame.promise().set_semaphore( 68 | &_M_frames.emplace_front( 69 | /// Do type erasing and save them in a unified manner 70 | std::coroutine_handle::from_address(frame.address()), 71 | frame_delegate::delegator::S_delegate 72 | ).get_semaphore()); 73 | } 74 | COASYNC_ATTRIBUTE((always_inline)) 75 | void COASYNC_API remove_frame() 76 | { 77 | COASYNC_ATTRIBUTE((maybe_unused)) std::unique_lock alternative_lock; 78 | if(_M_concurrent_hint) COASYNC_ATTRIBUTE((likely)) 79 | std::unique_lock(_M_lockable).swap(alternative_lock); 80 | /// check concurrency 81 | _M_frames.remove_if(&value_type::destructible); 82 | } 83 | /// As a sign to end polling 84 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 85 | bool COASYNC_API empty() const 86 | { 87 | COASYNC_ATTRIBUTE((maybe_unused)) std::unique_lock alternative_lock; 88 | if(_M_concurrent_hint) COASYNC_ATTRIBUTE((likely)) 89 | std::unique_lock(_M_lockable).swap(alternative_lock); 90 | /// check concurrency 91 | return _M_frames.empty(); 92 | } 93 | private: 94 | std::forward_list _M_frames; 95 | mutable basic_lockable _M_lockable; 96 | bool const _M_concurrent_hint; 97 | }; 98 | } 99 | } 100 | #endif 101 | -------------------------------------------------------------------------------- /include/coasync/detail/get_context.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __COASYNC_GET_CONTEXT_INCLUDED 2 | #define __COASYNC_GET_CONTEXT_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "awaitable_frame.hpp" 9 | 10 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 11 | { 12 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 13 | { 14 | /// Gets the scheduler reference associated with the current coroutine 15 | struct get_context 16 | { 17 | bool await_ready() const noexcept 18 | { 19 | return false; 20 | } 21 | template 22 | std::coroutine_handle<> 23 | await_suspend(std::coroutine_handle> coroutine) const noexcept 24 | { 25 | _M_context = coroutine.promise().get_context(); 26 | return coroutine; 27 | } 28 | execution_context& await_resume() const noexcept 29 | { 30 | return * _M_context; 31 | } 32 | private: 33 | mutable execution_context* _M_context = nullptr; 34 | }; 35 | } 36 | } 37 | #endif 38 | -------------------------------------------------------------------------------- /include/coasync/detail/get_frame.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __COASYNC_GET_FRAME_INCLUDED 2 | #define __COASYNC_GET_FRAME_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "awaitable_frame.hpp" 9 | 10 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 11 | { 12 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 13 | { 14 | /// Gets the handle behind the current coroutine[type erased] 15 | struct get_frame 16 | { 17 | bool await_ready() const noexcept 18 | { 19 | return false; 20 | } 21 | template 22 | std::coroutine_handle<> 23 | await_suspend(std::coroutine_handle> coroutine) const noexcept 24 | { 25 | _M_frame = std::coroutine_handle::from_address(coroutine.address()); 26 | return coroutine; 27 | } 28 | std::coroutine_handle await_resume() const noexcept 29 | { 30 | return _M_frame; 31 | } 32 | private: 33 | mutable std::coroutine_handle _M_frame = nullptr; 34 | }; 35 | } 36 | } 37 | #endif 38 | -------------------------------------------------------------------------------- /include/coasync/detail/get_id.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __COASYNC_GET_ID_INCLUDED 2 | #define __COASYNC_GET_ID_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "awaitable_frame.hpp" 9 | 10 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 11 | { 12 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 13 | { 14 | /// Gets the current coroutine id 15 | struct get_id 16 | { 17 | bool await_ready() const noexcept 18 | { 19 | return false; 20 | } 21 | template 22 | std::coroutine_handle<> 23 | await_suspend(std::coroutine_handle> coroutine) const noexcept 24 | { 25 | _M_id = coroutine.promise().get_id(); 26 | return coroutine; 27 | } 28 | std::uint_least32_t await_resume() const noexcept 29 | { 30 | return _M_id; 31 | } 32 | private: 33 | mutable std::uint_least32_t _M_id = 0; 34 | }; 35 | } 36 | } 37 | #endif 38 | 39 | 40 | -------------------------------------------------------------------------------- /include/coasync/detail/get_stop_token.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __COASYNC_GET_STOKEN_INCLUDED 2 | #define __COASYNC_GET_STOKEN_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "awaitable_frame.hpp" 9 | 10 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 11 | { 12 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 13 | { 14 | /// Gets the cancel token associated with the current coroutine 15 | struct get_stop_token 16 | { 17 | bool await_ready() const noexcept 18 | { 19 | return false; 20 | } 21 | template 22 | std::coroutine_handle<> 23 | await_suspend(std::coroutine_handle> coroutine) const noexcept 24 | { 25 | _M_stoken = coroutine.promise().get_stop_token(); 26 | return coroutine; 27 | } 28 | std::stop_token await_resume() const noexcept 29 | { 30 | return std::move(_M_stoken); 31 | } 32 | private: 33 | mutable std::stop_token _M_stoken {}; 34 | }; 35 | } 36 | } 37 | #endif 38 | -------------------------------------------------------------------------------- /include/coasync/detail/manual_lifetime.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNIO_MANUAL_LIFETIME_INCLUDED 2 | #define COASYNIO_MANUAL_LIFETIME_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "config.hpp" 9 | #include 10 | #include 11 | 12 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 13 | { 14 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 15 | { 16 | /// for lazy initialize using union wrapper 17 | /// the uninitialized storage designated by union / pointer 18 | template 19 | struct manual_lifetime 20 | { 21 | COASYNC_ATTRIBUTE((always_inline)) 22 | explicit manual_lifetime() noexcept {} 23 | COASYNC_ATTRIBUTE((always_inline)) 24 | ~manual_lifetime() {} 25 | template 26 | COASYNC_ATTRIBUTE((maybe_unused, always_inline)) 27 | T& construct(Args&& ... args) 28 | noexcept(std::is_nothrow_constructible_v) 29 | { 30 | return *::new (static_cast(std::addressof(_M_value))) T(std::forward(args)...); 31 | } 32 | COASYNC_ATTRIBUTE((always_inline)) 33 | void destruct() noexcept(std::is_nothrow_destructible_v) 34 | { 35 | _M_value.~T(); 36 | } 37 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 38 | T& get()& noexcept 39 | { 40 | return _M_value; 41 | } 42 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 43 | T&& get()&& noexcept 44 | { 45 | return static_cast (_M_value); 46 | } 47 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 48 | const T& get() const& noexcept 49 | { 50 | return _M_value; 51 | } 52 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 53 | const T&& get() const&& noexcept 54 | { 55 | return static_cast (_M_value); 56 | } 57 | private: 58 | /// union: no initialization is performed and the beginning of its lifetime is 59 | /// sequenced after the value computation of the left and right operands and before the assignment 60 | union 61 | { 62 | COASYNC_ATTRIBUTE((no_unique_address)) std::remove_const_t _M_value; 63 | }; 64 | }; 65 | template 66 | struct manual_lifetime 67 | { 68 | COASYNC_ATTRIBUTE((maybe_unused, always_inline)) 69 | T& construct(T& value) noexcept 70 | { 71 | _M_value = std::addressof(value); 72 | return value; 73 | } 74 | COASYNC_ATTRIBUTE((always_inline)) 75 | void destruct() noexcept {} 76 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 77 | T& get() const noexcept 78 | { 79 | return *_M_value; 80 | } 81 | private: 82 | T* _M_value; 83 | }; 84 | template 85 | struct manual_lifetime 86 | { 87 | COASYNC_ATTRIBUTE((maybe_unused, always_inline)) 88 | T&& construct(T&& value) noexcept 89 | { 90 | _M_value = std::addressof(value); 91 | return static_cast (value); 92 | } 93 | COASYNC_ATTRIBUTE((always_inline)) 94 | void destruct() noexcept {} 95 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 96 | T&& get() const noexcept 97 | { 98 | return static_cast (*_M_value); 99 | } 100 | private: 101 | T* _M_value; 102 | }; 103 | template <> struct manual_lifetime { 104 | COASYNC_ATTRIBUTE((always_inline)) 105 | constexpr void construct() const noexcept {} 106 | COASYNC_ATTRIBUTE((always_inline)) 107 | constexpr void destruct() const noexcept {} 108 | COASYNC_ATTRIBUTE((always_inline)) 109 | constexpr void get() const noexcept {} 110 | }; 111 | } 112 | } 113 | #endif 114 | -------------------------------------------------------------------------------- /include/coasync/detail/meta/magic_get.hpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/include/coasync/detail/meta/magic_get.hpp -------------------------------------------------------------------------------- /include/coasync/detail/meta/member_field.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_MERBER_FIELD_INCLUDED 2 | #define COASYNC_MERBER_FIELD_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "../config.hpp" 9 | 10 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 11 | { 12 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 13 | { 14 | /// wraps a member pointer and its name as a type-safe structure. This can be useful 15 | /// for manipulating member pointers and extracting information about them at compile time. 16 | template struct member_field; 17 | template 18 | struct member_field 19 | { 20 | /// A container for storing basic information about class members 21 | typedef Class class_type; 22 | typedef T value_type; 23 | COASYNC_ATTRIBUTE((always_inline)) 24 | consteval member_field(T Class::* ptr, char const* name) noexcept 25 | : _M_ptr(ptr), _M_name(name) { } 26 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 27 | constexpr T Class::* pointer() const noexcept { 28 | return _M_ptr; 29 | } 30 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 31 | constexpr char const* name() const noexcept { 32 | return _M_name; 33 | } 34 | private: 35 | T Class::* _M_ptr; 36 | char const* _M_name; 37 | }; 38 | template 39 | member_field(T Class::*, char const*) -> member_field; 40 | } 41 | } 42 | /// provides us the meta info of the object via meta-object types. 43 | 44 | #define _COASYNC_MEMBER_FIELD(_S, _Pt) coasync::detail::member_field(&_S::_Pt, #_Pt) 45 | #define _COASYNC_ARGS_ENWRAP_0(...) 46 | #define _COASYNC_ARGS_ENWRAP_1(_S, _1) \ 47 | _COASYNC_MEMBER_FIELD(_S, _1) 48 | #define _COASYNC_ARGS_ENWRAP_2(_S, _1, _2) \ 49 | _COASYNC_MEMBER_FIELD(_S, _1), _COASYNC_MEMBER_FIELD(_S, _2) 50 | #define _COASYNC_ARGS_ENWRAP_3(_S, _1, _2, _3) \ 51 | _COASYNC_MEMBER_FIELD(_S, _1), _COASYNC_MEMBER_FIELD(_S, _2), _COASYNC_MEMBER_FIELD(_S, _3) 52 | #define _COASYNC_ARGS_ENWRAP_4(_S, _1, _2, _3, _4) \ 53 | _COASYNC_MEMBER_FIELD(_S, _1), _COASYNC_MEMBER_FIELD(_S, _)2, _COASYNC_MEMBER_FIELD(_S, _3), _COASYNC_MEMBER_FIELD(_S, _4) 54 | #define _COASYNC_ARGS_ENWRAP_5(_S, _1, _2, _3, _4, _5) \ 55 | _COASYNC_MEMBER_FIELD(_S, _1), _COASYNC_MEMBER_FIELD(_S, _2), _COASYNC_MEMBER_FIELD(_S, _3), _COASYNC_MEMBER_FIELD(_S, _4), _COASYNC_MEMBER_FIELD(_S, _5) 56 | #define COASYNC_ARGS_ENWRAP(_S, ...) _COASYNC_ARGS_ENWRAP_INTERNAL(_S, COASYNC_COUNT(__VA_ARGS__), __VA_ARGS__) 57 | #define _COASYNC_ARGS_ENWRAP_INTERNAL(_S, _N, ...) COASYNC_CONCAT(_COASYNC_ARGS_ENWRAP_, _N)(_S, __VA_ARGS__) 58 | 59 | #include 60 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 61 | { 62 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 63 | { 64 | template struct meta: std::false_type {}; 65 | } 66 | } 67 | #define struct_meta(_S, ...) \ 68 | template <> struct coasync::detail::meta<_S>: std::true_type { \ 69 | static constexpr auto fields = std::make_tuple(COASYNC_ARGS_ENWRAP(_S, __VA_ARGS__)); \ 70 | }; 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /include/coasync/detail/meta/signature.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_SIGNATURE_INCLUDED 2 | #define COASYNC_SIGNATURE_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "../config.hpp" 9 | #if defined(__cpp_lib_source_location) 10 | # include 11 | #else 12 | # error This library requires the use of C++20 source_location support 13 | #endif 14 | #include 15 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 16 | { 17 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 18 | { 19 | /// Gets the type name at compile time 20 | template 21 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 22 | consteval std::string_view signature() noexcept 23 | { 24 | /// https://en.cppreference.com/w/cpp/utility/source_location/function_name 25 | /// std::source_location::function_name may help to obtain the names of functions 26 | /// (including the special functions) alongside with their signatures. 27 | std::string_view signature {std::source_location::current().function_name()}; 28 | #if defined(__GNUC__) || defined(__clang__) 29 | std::size_t prefix_sentinel = std::min(signature.find_first_of("=") + 2u, signature.size()); 30 | signature.remove_prefix(prefix_sentinel); 31 | std::size_t suffix_sentinel = signature.size() - signature.find_last_of(";"); 32 | signature.remove_suffix(suffix_sentinel); 33 | #elif defiend(_MSVC) 34 | std::size_t prefix_sentinel = signature.find_last_of("<") + 1; 35 | signature.remove_prefix(prefix_sentinel); 36 | std::size_t suffix_sentinel = signature.size() - signature.find_last_of(">"); 37 | signature.remove_suffix(suffix_sentinel); 38 | #endif 39 | return signature; 40 | } 41 | } 42 | } 43 | #endif 44 | -------------------------------------------------------------------------------- /include/coasync/detail/networking.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __COASYNC_NETWORKING_INCLUDED 2 | #define __COASYNC_NETWORKING_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "config.hpp" 9 | #if defined(__has_include) 10 | # if defined(_WIN32) || defined(_WIN64) 11 | # if __has_include() 12 | # include 13 | # endif 14 | # elif defined(__linux__) 15 | # include 16 | # endif 17 | #endif 18 | /// The error codes returned by Windows Sockets are similar to UNIX socket error code constants, 19 | /// but the constants are all prefixed with WSA. So in Winsock applications the WSAEWOULDBLOCK 20 | /// error code would be returned, while in UNIX applications the EWOULDBLOCK error code 21 | /// would be returned. 22 | #if defined(_WIN32) || defined(_WIN64) 23 | # define get_error_code(error_code) WSA ## error_code 24 | #elif defined(__linux__) 25 | # define get_error_code(error_code) error_code 26 | #endif 27 | #include 28 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 29 | { 30 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 31 | { 32 | #if defined(_WIN32) || defined(_WIN64) 33 | typedef ::SOCKET __native_handle; 34 | #elif defined(__linux__) 35 | typedef int __native_handle; 36 | #endif 37 | #if defined(_WIN32) || defined(_WIN64) 38 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 39 | static int get_errno() noexcept 40 | { 41 | /// In Winsock applications, error codes are retrieved using the WSAGetLastError function, 42 | /// the Windows Sockets substitute for the Windows GetLastError function. 43 | return ::WSAGetLastError(); 44 | } 45 | #elif defined(__linux__) 46 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 47 | static /*consteval*/ int get_errno() noexcept 48 | { 49 | return errno; 50 | } 51 | #endif 52 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 53 | std::error_category const& generic_category() noexcept 54 | { 55 | #if defined(_WIN32) || defined(_WIN64) 56 | struct WSA_system_category final: public std::error_category 57 | { 58 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 59 | virtual const char* name() const noexcept 60 | { 61 | return "wsa_system"; 62 | } 63 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 64 | virtual std::string message(int errc) const override 65 | { 66 | LPVOID buffer; 67 | std::string result; 68 | /// The WSAGetLastError function returns the last error that occurred for the calling thread. 69 | /// When a particular Windows Sockets function indicates an error has occurred, this function 70 | /// should be called immediately to retrieve the extended error code for the failing function 71 | /// call. These error codes and a short text description associated with an error code are 72 | /// defined in the Winerror.h header file. The FormatMessage function can be used to obtain 73 | /// the message string for the returned error. 74 | if (DWORD length 75 | = ::FormatMessage( 76 | FORMAT_MESSAGE_ALLOCATE_BUFFER 77 | | FORMAT_MESSAGE_FROM_SYSTEM | 78 | FORMAT_MESSAGE_IGNORE_INSERTS, 79 | nullptr, 80 | errc, 81 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 82 | reinterpret_cast(std::addressof( 83 | buffer)), 84 | 0, 85 | nullptr)) COASYNC_ATTRIBUTE((likely)) 86 | { 87 | result.assign(reinterpret_cast(buffer), length * sizeof(TCHAR)); 88 | ::LocalFree(buffer); 89 | return result; 90 | } 91 | else COASYNC_ATTRIBUTE((unlikely)) return "No known"; 92 | } 93 | }; 94 | static WSA_system_category __cat {}; 95 | return __cat; 96 | #elif defined(__linux__) 97 | return std::generic_category(); 98 | #endif 99 | } 100 | } 101 | } 102 | #endif 103 | -------------------------------------------------------------------------------- /include/coasync/detail/object_deduce.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_OBJECT_DEDUCE_INCLUDED 2 | #define COASYNC_OBJECT_DEDUCE_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "config.hpp" 9 | #include 10 | 11 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 12 | { 13 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 14 | { 15 | struct void_t {}; 16 | /// Type purification to remove references and void types so that 17 | /// they can be stored in heterogeneous containers(std::tuple/std::variant) 18 | /// heterogeneous value constructor is defined as deleted if the initialization of any element that is a reference 19 | template 20 | using object_deduce_t = std::conditional_t, void_t, std::decay_t>; 21 | } 22 | } 23 | #endif 24 | -------------------------------------------------------------------------------- /include/coasync/detail/remote_queue.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_REMOTE_QUEUE_INCLUDE 2 | #define COASYNC_REMOTE_QUEUE_INCLUDE 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "awaitable_frame_base.hpp" 9 | #include "spin_loop_pause.hpp" 10 | 11 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 12 | { 13 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 14 | { 15 | /// intrusive MPSC queue for remote_queue in task-stealing thread pool 16 | /// Atomic operations are used for thread-safe and efficient handling of cases 17 | /// where multiple producers add elements to the queue and single consumers 18 | /// remove elements from the queue 19 | template struct remote_queue; 20 | template Node::*_M_next> 21 | struct remote_queue<_M_next> 22 | { 23 | private: 24 | std::atomic _M_back{&_M_nil}; 25 | void* _M_front{&_M_nil}; 26 | std::atomic _M_nil = nullptr; 27 | void _M_push_back_nil() 28 | { 29 | _M_nil.store(nullptr, std::memory_order_relaxed); 30 | Node* prev = static_cast(_M_back.exchange(&_M_nil, std::memory_order_acq_rel)); 31 | (prev->*_M_next).store(&_M_nil, std::memory_order_release); 32 | } 33 | public: 34 | COASYNC_ATTRIBUTE((maybe_unused)) bool push_back(Node* new_node) noexcept 35 | { 36 | (new_node->*_M_next).store(nullptr, std::memory_order_relaxed); 37 | void* prev_back = _M_back.exchange(new_node, std::memory_order_acq_rel); 38 | bool is_nil = prev_back == static_cast(&_M_nil); 39 | if (is_nil) COASYNC_ATTRIBUTE((likely)) 40 | _M_nil.store(new_node, std::memory_order_release); 41 | else COASYNC_ATTRIBUTE((unlikely)) 42 | (static_cast(prev_back)->*_M_next).store(new_node, std::memory_order_release); 43 | return is_nil; 44 | } 45 | COASYNC_ATTRIBUTE((nodiscard)) Node* pop_front() noexcept 46 | { 47 | if (_M_front == static_cast(&_M_nil)) COASYNC_ATTRIBUTE((likely)) 48 | { 49 | Node* next = _M_nil.load(std::memory_order_acquire); 50 | if (not next) COASYNC_ATTRIBUTE((likely)) return nullptr; 51 | _M_front = next; 52 | } 53 | Node* front = static_cast(_M_front); 54 | void* next = (front->*_M_next).load(std::memory_order_acquire); 55 | if (next) COASYNC_ATTRIBUTE((unlikely)) 56 | { 57 | _M_front = next; 58 | return front; 59 | } 60 | _M_push_back_nil(); 61 | do 62 | { 63 | __spin_loop_pause(); 64 | next = (front->*_M_next).load(std::memory_order_acquire); 65 | } 66 | while (!next); 67 | _M_front = next; 68 | return front; 69 | } 70 | }; 71 | } 72 | } 73 | #endif 74 | -------------------------------------------------------------------------------- /include/coasync/detail/ring_container.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * std::queue 3 | * Container - The type of the underlying container to use to store the elements. 4 | * The container must satisfy the requirements of SequenceContainer. Additionally, 5 | * it must provide the following functions with the usual semantics: 6 | **/ 7 | 8 | /// A SequenceContainer is a Container that stores objects of the same type in a linear arrangement. 9 | 10 | #ifndef COASYNC_RING_CONTAINER_INCLUDED 11 | #define COASYNC_RING_CONTAINER_INCLUDED 12 | 13 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 14 | # pragma once 15 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 16 | 17 | #include "config.hpp" 18 | 19 | #include 20 | 21 | template requires std::is_default_constructible_v and (N > 0) 22 | struct [[nodiscard]] ring_container: public std::array 23 | { 24 | private: 25 | template 26 | static constexpr bool is_self = sizeof...(Args) == 1 and (std::is_same_v, ring_container> || ...); 27 | public: 28 | typedef std::array::reference reference; 29 | typedef std::array::const_reference const_reference; 30 | typedef std::array::value_type value_type; 31 | typedef std::array::reverse_iterator reverse_iterator; 32 | typedef std::array::const_reverse_iterator const_reverse_iterator; 33 | typedef std::array::iterator iterator; 34 | typedef std::array::const_iterator const_iterator; 35 | typedef std::array::pointer pointer; 36 | typedef std::array::const_pointer const_pointer; 37 | typedef std::array::difference_type difference_type; 38 | typedef std::array::size_type size_type; 39 | using std::array::operator[]; 40 | 41 | // std::array supports assignment from a braced-init-list, but not from an std::initializer_list. 42 | template requires (not is_self) 43 | COASYNC_ATTRIBUTE((always_inline)) 44 | constexpr ring_container(CtorArgs&& ... ctorargs) 45 | noexcept(std::is_nothrow_constructible_v, CtorArgs ...>) 46 | : std::array(std::forward(ctorargs) ...) 47 | , _M_head(), _M_tail() {} 48 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) constexpr reference front() noexcept 49 | { 50 | return (*this)[_M_head]; 51 | } 52 | constexpr ring_container(ring_container const&) noexcept = default; 53 | constexpr ring_container& operator=(ring_container const&) noexcept = default; 54 | constexpr ring_container(ring_container&&) noexcept = default; 55 | constexpr ring_container& operator=(ring_container&&) noexcept = default; 56 | ~ ring_container() noexcept = default; 57 | constexpr void swap(ring_container& other) noexcept 58 | { 59 | std::array::swap(other); 60 | std::swap(_M_head, other._M_head); 61 | std::swap(_M_tail, other._M_tail); 62 | } 63 | 64 | COASYNC_ATTRIBUTE((nodiscard, __gnu__::__const__, always_inline)) constexpr const_reference front() const noexcept 65 | { 66 | return (*this)[_M_head]; 67 | } 68 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) constexpr reference back() noexcept 69 | { 70 | return (*this)[_M_tail]; 71 | } 72 | COASYNC_ATTRIBUTE((nodiscard, __gnu__::__const__, always_inline)) constexpr const_reference back() const noexcept 73 | { 74 | return (*this)[_M_tail]; 75 | } 76 | template 77 | COASYNC_ATTRIBUTE((always_inline)) void push_back(U&& value) noexcept(std::is_nothrow_constructible_v) 78 | { 79 | (void) emplace_back(static_cast(value)); 80 | } 81 | template 82 | COASYNC_ATTRIBUTE((maybe_unused, always_inline)) reference emplace_back(CtorArgs&& ... ctorargs) noexcept(std::is_nothrow_constructible_v) 83 | { 84 | static_assert(std::is_constructible_v); 85 | [[gnu::uninitialized]] T* pointer; 86 | pointer = ::new(std::addressof((*this)[_M_tail])) T{ std::forward(ctorargs) ...}; 87 | _M_tail = (_M_tail + 1) % N; 88 | return * pointer; 89 | } 90 | COASYNC_ATTRIBUTE((always_inline)) void pop_front() noexcept(std::is_nothrow_destructible_v) 91 | { 92 | std::addressof((*this) [_M_head]) -> ~T(); 93 | _M_head = (_M_head + 1) % N; 94 | } 95 | COASYNC_ATTRIBUTE((nodiscard, __gnu__::__const__, always_inline)) constexpr size_type size() const noexcept 96 | { 97 | return (_M_tail + N - _M_head) % N; 98 | } 99 | COASYNC_ATTRIBUTE((nodiscard, __gnu__::__const__, always_inline)) constexpr bool empty() const noexcept 100 | { 101 | return _M_tail == _M_head; 102 | } 103 | private: 104 | std::size_t _M_head {}; 105 | std::size_t _M_tail {}; 106 | }; 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /include/coasync/detail/service/dequeue_service.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_DEQUEUE_SERVICE_INCLUDED 2 | #define COASYNC_DEQUEUE_SERVICE_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "../awaitable_frame_base.hpp" /// for awaitable_frame_base 9 | #include "../basic_lockable.hpp" /// for basic_lockable 10 | #include /// for forward_list 11 | #include /// for unique_lock 12 | #include /// for queue 13 | 14 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 15 | { 16 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 17 | { 18 | template 19 | struct basic_dequeue_service 20 | { 21 | private: 22 | struct composed_context 23 | { 24 | void* _M_queue; 25 | /// weak reference, non-owning 26 | void* _M_value; 27 | void* _M_mutex; 28 | /// use void* for type-erasing 29 | bool(* _M_delegate)(void*, void*, void*); 30 | }; 31 | struct value_type 32 | { 33 | std::coroutine_handle _M_frame; 34 | composed_context _M_context; 35 | }; 36 | template 37 | struct delegator 38 | { 39 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 40 | static bool S_delegate(void* queue, void* value, void* mutex) 41 | { 42 | COASYNC_ATTRIBUTE((gnu::uninitialized)) bool queue_consumable; 43 | std::lock_guard l { * static_cast(mutex) }; 44 | /// Check whether data is available and ejected, and lock to prevent data race 45 | queue_consumable = not static_cast *>(queue) -> empty(); 46 | if(queue_consumable) COASYNC_ATTRIBUTE((unlikely)) 47 | { 48 | ::new(value) Value { std::move(static_cast *>(queue) -> front()) }; 49 | static_cast *>(queue) -> pop(); 50 | } 51 | return queue_consumable; 52 | } 53 | }; 54 | public: 55 | typedef basic_lockable mutex_type; 56 | 57 | COASYNC_ATTRIBUTE((always_inline)) 58 | constexpr explicit basic_dequeue_service(execution_context& context) noexcept 59 | : _M_context(context) {} 60 | constexpr basic_dequeue_service& operator=(basic_dequeue_service const&) = delete; 61 | constexpr basic_dequeue_service(basic_dequeue_service const&) = delete; 62 | COASYNC_ATTRIBUTE((always_inline)) basic_dequeue_service(basic_dequeue_service&&) noexcept = default; 63 | COASYNC_ATTRIBUTE((always_inline)) basic_dequeue_service& operator=(basic_dequeue_service&&) noexcept = default; 64 | COASYNC_ATTRIBUTE((always_inline)) ~ basic_dequeue_service() noexcept = default; 65 | 66 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 67 | static constexpr std::size_t overlap_arity() noexcept 68 | { 69 | return 3u; 70 | } 71 | template 72 | COASYNC_ATTRIBUTE((always_inline)) void post_frame( 73 | std::coroutine_handle frame, 74 | /// post_frame overlaps 75 | std::queue& queue, 76 | Value& value, 77 | Mutex& mutex) 78 | { 79 | COASYNC_ATTRIBUTE((maybe_unused)) std::unique_lock alternative_lock; 80 | if(_M_context.concurrency()) COASYNC_ATTRIBUTE((likely)) 81 | std::unique_lock(_M_lockable).swap(alternative_lock); 82 | /// check concurrent threading 83 | /// Locking is not required for single thread 84 | _M_forward.emplace_front(frame, composed_context 85 | { 86 | &queue, &value, &mutex, &delegator::S_delegate 87 | }); 88 | } 89 | COASYNC_ATTRIBUTE((always_inline)) void commit_frame() 90 | { 91 | std::forward_list> outstanding; 92 | COASYNC_ATTRIBUTE((maybe_unused)) std::unique_lock alternative_lock; 93 | if(_M_context.concurrency()) COASYNC_ATTRIBUTE((likely)) 94 | std::unique_lock(_M_lockable).swap(alternative_lock); 95 | /// test parallelism 96 | /// Locking is not required for single thread 97 | _M_forward.remove_if([this, &outstanding] 98 | #if __cplusplus >= 202207L 99 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 100 | #endif 101 | (value_type& value) mutable -> bool 102 | { 103 | bool const queue_consumable 104 | = (* value._M_context._M_delegate)( 105 | value._M_context._M_queue, 106 | value._M_context._M_value, 107 | value._M_context._M_mutex); 108 | /// test and pop 109 | if(queue_consumable) COASYNC_ATTRIBUTE((unlikely)) 110 | outstanding.emplace_front(value._M_frame); 111 | /// Save the coroutine handle to be resumed, and then execute it 112 | /// after unlocking the alternative_lock to prevent lock nesting 113 | return queue_consumable; 114 | }); 115 | if(alternative_lock.owns_lock()) COASYNC_ATTRIBUTE((likely)) 116 | alternative_lock.unlock(); 117 | /// test parallelism 118 | for(std::coroutine_handle frame: outstanding) 119 | _M_context.push_frame_to_executor(frame); 120 | } 121 | private: 122 | execution_context& _M_context; 123 | mutable basic_lockable _M_lockable; 124 | std::forward_list _M_forward; 125 | }; 126 | typedef basic_dequeue_service dequeue_service; 127 | } 128 | } 129 | #endif 130 | -------------------------------------------------------------------------------- /include/coasync/detail/service/enqueue_service.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_ENQUEUE_SERVICE_INCLUDED 2 | #define COASYNC_ENQUEUE_SERVICE_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "../awaitable_frame_base.hpp" 9 | #include "../basic_lockable.hpp" 10 | #include 11 | #include 12 | #include 13 | 14 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 15 | { 16 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 17 | { 18 | template 19 | struct basic_enqueue_service 20 | { 21 | private: 22 | struct composed_context 23 | { 24 | void* _M_queue; 25 | /// weak reference, non-owning 26 | void* _M_value; 27 | void* _M_mutex; 28 | /// use void* for type-erasing 29 | std::size_t _M_bound; 30 | /// bounded channel, has capacity 31 | bool(* _M_delegate)(void*, void*, void*, std::size_t); 32 | }; 33 | struct value_type 34 | { 35 | std::coroutine_handle _M_frame; 36 | composed_context _M_context; 37 | }; 38 | template 39 | struct delegator 40 | { 41 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 42 | static bool S_delegate(void* queue, void* value, void* mutex, std::size_t bound) 43 | { 44 | COASYNC_ATTRIBUTE((gnu::uninitialized)) bool queue_producable; 45 | std::lock_guard l { * static_cast(mutex) }; 46 | /// Check whether data is out of bound and injected, and lock to prevent data race 47 | queue_producable = static_cast *>(queue) -> size() < bound; 48 | if(queue_producable) COASYNC_ATTRIBUTE((unlikely)) 49 | static_cast *>(queue) -> emplace(std::move(*static_cast(value))); 50 | return queue_producable; 51 | } 52 | }; 53 | public: 54 | typedef basic_lockable mutex_type; 55 | 56 | COASYNC_ATTRIBUTE((always_inline)) 57 | constexpr explicit basic_enqueue_service(execution_context& context) noexcept 58 | : _M_context(context) {} 59 | constexpr basic_enqueue_service& operator=(basic_enqueue_service const&) = delete; 60 | constexpr basic_enqueue_service(basic_enqueue_service const&) = delete; 61 | COASYNC_ATTRIBUTE((always_inline)) basic_enqueue_service(basic_enqueue_service&&) noexcept = default; 62 | COASYNC_ATTRIBUTE((always_inline)) basic_enqueue_service& operator=(basic_enqueue_service&&) noexcept = default; 63 | COASYNC_ATTRIBUTE((always_inline)) ~ basic_enqueue_service() noexcept = default; 64 | 65 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 66 | static constexpr std::size_t overlap_arity() noexcept 67 | { 68 | return 4u; 69 | } 70 | 71 | template 72 | void post_frame( 73 | std::coroutine_handle frame, 74 | /// post_frame overlaps 75 | std::queue& queue, 76 | Value& value, 77 | Mutex& mutex, 78 | std::size_t bound) 79 | { 80 | COASYNC_ATTRIBUTE((maybe_unused)) std::unique_lock alternative_lock; 81 | if(_M_context.concurrency()) COASYNC_ATTRIBUTE((likely)) 82 | std::unique_lock(_M_lockable).swap(alternative_lock); 83 | /// check parrallelism 84 | _M_forward.emplace_front(frame, composed_context 85 | { 86 | &queue, &value, &mutex, bound, &delegator::S_delegate 87 | }); 88 | } 89 | void commit_frame() 90 | { 91 | std::forward_list> outstanding; 92 | COASYNC_ATTRIBUTE((maybe_unused)) std::unique_lock alternative_lock; 93 | if(_M_context.concurrency()) COASYNC_ATTRIBUTE((likely)) 94 | std::unique_lock(_M_lockable).swap(alternative_lock); 95 | /// check parrallelism 96 | _M_forward.remove_if([this, &outstanding] 97 | #if __cplusplus >= 202207L 98 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 99 | #endif 100 | (value_type& value) mutable -> bool 101 | { 102 | bool const queue_producable 103 | = (* value._M_context._M_delegate)( 104 | value._M_context._M_queue, 105 | value._M_context._M_value, 106 | value._M_context._M_mutex, 107 | value._M_context._M_bound); 108 | /// test and push 109 | if(queue_producable) COASYNC_ATTRIBUTE((unlikely)) outstanding.emplace_front(value._M_frame); 110 | /// Save the coroutine handle to be resumed, and then execute it 111 | /// after unlocking the alternative_lock to prevent lock nesting 112 | return queue_producable; 113 | }); 114 | if(alternative_lock.owns_lock()) COASYNC_ATTRIBUTE((likely)) alternative_lock.unlock(); 115 | /// check parrallelism 116 | for(std::coroutine_handle frame: outstanding) 117 | _M_context.push_frame_to_executor(frame); 118 | } 119 | private: 120 | execution_context& _M_context; 121 | mutable basic_lockable _M_lockable; 122 | std::forward_list _M_forward; 123 | }; 124 | typedef basic_enqueue_service enqueue_service; 125 | } 126 | } 127 | #endif 128 | -------------------------------------------------------------------------------- /include/coasync/detail/service/flag_service.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_FLAG_SERVICE_INCLUDED 2 | #define COASYNC_FLAG_SERVICE_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "../awaitable_frame_base.hpp" 9 | #include "../basic_lockable.hpp" 10 | #include 11 | #include 12 | 13 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 14 | { 15 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 16 | { 17 | template 18 | struct basic_flag_service 19 | { 20 | private: 21 | struct value_type 22 | { 23 | std::coroutine_handle _M_frame; 24 | std::atomic_flag* _M_flag; 25 | /// weak reference, non-owning 26 | }; 27 | public: 28 | typedef basic_lockable mutex_type; 29 | 30 | COASYNC_ATTRIBUTE((always_inline)) 31 | constexpr explicit basic_flag_service(execution_context& context) noexcept 32 | : _M_context(context) {} 33 | constexpr basic_flag_service& operator=(basic_flag_service const&) = delete; 34 | constexpr basic_flag_service(basic_flag_service const&) = delete; 35 | COASYNC_ATTRIBUTE((always_inline)) basic_flag_service(basic_flag_service&&) noexcept = default; 36 | COASYNC_ATTRIBUTE((always_inline)) basic_flag_service& operator=(basic_flag_service&&) noexcept = default; 37 | COASYNC_ATTRIBUTE((always_inline)) ~ basic_flag_service() noexcept = default; 38 | 39 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 40 | static constexpr std::size_t overlap_arity() noexcept 41 | { 42 | return 1u; 43 | } 44 | 45 | COASYNC_ATTRIBUTE((always_inline)) void post_frame( 46 | std::coroutine_handle frame, 47 | /// post_frame: overlaps 48 | std::atomic_flag& flag) 49 | { 50 | COASYNC_ATTRIBUTE((maybe_unused)) std::unique_lock alternative_lock; 51 | if(_M_context.concurrency()) COASYNC_ATTRIBUTE((likely)) 52 | std::unique_lock(_M_lockable).swap(alternative_lock); 53 | /// check parrallelism 54 | _M_forward.emplace_front(frame, &flag); 55 | } 56 | COASYNC_ATTRIBUTE((always_inline)) void commit_frame() 57 | { 58 | std::forward_list> outstanding; 59 | COASYNC_ATTRIBUTE((maybe_unused)) std::unique_lock alternative_lock; 60 | if(_M_context.concurrency()) COASYNC_ATTRIBUTE((likely)) 61 | std::unique_lock(_M_lockable).swap(alternative_lock); 62 | /// check parrallelism 63 | _M_forward.remove_if([this, &outstanding] 64 | #if __cplusplus >= 202207L 65 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 66 | #endif 67 | (value_type& value) mutable -> bool 68 | { 69 | bool const flag_set = value._M_flag -> test(std::memory_order_acquire); 70 | /// non-blocking test 71 | /// atomic_flag set: std::memory_order_release order required 72 | if(flag_set) COASYNC_ATTRIBUTE((unlikely)) outstanding.emplace_front(value._M_frame); 73 | /// Save the coroutine handle to be resumed, and then execute it 74 | /// after unlocking the alternative_lock to prevent lock nesting 75 | return flag_set; 76 | }); 77 | if(alternative_lock.owns_lock()) COASYNC_ATTRIBUTE((likely)) alternative_lock.unlock(); 78 | /// check parrallelism 79 | for(std::coroutine_handle frame: outstanding) 80 | _M_context.push_frame_to_executor(frame); 81 | } 82 | private: 83 | execution_context& _M_context; 84 | mutable basic_lockable _M_lockable; 85 | std::forward_list _M_forward; 86 | }; 87 | typedef basic_flag_service flag_service; 88 | } 89 | } 90 | #endif 91 | -------------------------------------------------------------------------------- /include/coasync/detail/service/future_service.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_FUTURE_SERVICE_INCLUDED 2 | #define COASYNC_FUTURE_SERVICE_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "../awaitable_frame_base.hpp" 9 | #include "../basic_lockable.hpp" 10 | #include 11 | #include 12 | 13 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 14 | { 15 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 16 | { 17 | template 18 | struct basic_future_service 19 | { 20 | private: 21 | struct composed_context 22 | { 23 | void* _M_future; 24 | /// use void* for type-erasing 25 | /// weak reference, non-owning 26 | bool(*_M_delegate)(void*); 27 | }; 28 | template 29 | struct delegator 30 | { 31 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 32 | static bool S_delegate(void* future) 33 | { 34 | return static_cast*>(future)->wait_for(std::chrono::nanoseconds(0)) == std::future_status::ready; 35 | /// Non-blocking wait, return immediately 36 | } 37 | }; 38 | struct value_type 39 | { 40 | std::coroutine_handle _M_frame; 41 | composed_context _M_context; 42 | }; 43 | public: 44 | typedef basic_lockable mutex_type; 45 | 46 | COASYNC_ATTRIBUTE((always_inline)) 47 | constexpr explicit basic_future_service(execution_context& context) noexcept 48 | : _M_context(context) {} 49 | constexpr basic_future_service& operator=(basic_future_service const&) = delete; 50 | constexpr basic_future_service(basic_future_service const&) = delete; 51 | COASYNC_ATTRIBUTE((always_inline)) basic_future_service(basic_future_service&&) noexcept = default; 52 | COASYNC_ATTRIBUTE((always_inline)) basic_future_service& operator=(basic_future_service&&) noexcept = default; 53 | COASYNC_ATTRIBUTE((always_inline)) ~ basic_future_service() noexcept = default; 54 | 55 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 56 | static constexpr std::size_t overlap_arity() noexcept 57 | { 58 | return 1u; 59 | } 60 | 61 | template 62 | COASYNC_ATTRIBUTE((always_inline)) void post_frame( 63 | std::coroutine_handle frame, 64 | /// post_frame: overlaps 65 | std::future& future) 66 | { 67 | COASYNC_ATTRIBUTE((maybe_unused)) std::unique_lock alternative_lock; 68 | if(_M_context.concurrency()) COASYNC_ATTRIBUTE((likely)) 69 | std::unique_lock(_M_lockable).swap(alternative_lock); 70 | /// check parrallelism 71 | _M_forward.emplace_front(frame, composed_context { &future, &delegator::S_delegate }); 72 | } 73 | COASYNC_ATTRIBUTE((always_inline)) void commit_frame() 74 | { 75 | std::forward_list> outstanding; 76 | COASYNC_ATTRIBUTE((maybe_unused)) std::unique_lock alternative_lock; 77 | if(_M_context.concurrency()) COASYNC_ATTRIBUTE((likely)) 78 | std::unique_lock(_M_lockable).swap(alternative_lock); 79 | /// check parrallelism 80 | _M_forward.remove_if([this, &outstanding] 81 | #if __cplusplus >= 202207L 82 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 83 | #endif 84 | (value_type& value) mutable -> bool 85 | { 86 | bool const future_gettable = (* value._M_context._M_delegate)(value._M_context._M_future); 87 | /// Once it is available, it is deleted in place, and wait is successfully triggered only once 88 | if(future_gettable) COASYNC_ATTRIBUTE((unlikely)) outstanding.emplace_front(value._M_frame); 89 | /// Save the coroutine handle to be resumed, and then execute it 90 | /// after unlocking the alternative_lock to prevent lock nesting 91 | return future_gettable; 92 | }); 93 | if(alternative_lock.owns_lock()) COASYNC_ATTRIBUTE((likely)) alternative_lock.unlock(); 94 | /// check parrallelism 95 | for(std::coroutine_handle frame: outstanding) 96 | _M_context.push_frame_to_executor(frame); 97 | } 98 | private: 99 | execution_context& _M_context; 100 | mutable basic_lockable _M_lockable; 101 | std::forward_list _M_forward; 102 | }; 103 | typedef basic_future_service future_service; 104 | } 105 | } 106 | #endif 107 | -------------------------------------------------------------------------------- /include/coasync/detail/service/iocp_socket_service.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_IOCP_SOCKET_SERVICE_INCLUDED 2 | #define COASYNC_IOCP_SOCKET_SERVICE_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #if defined(_WIN32) || defined(_WIN64) 9 | 10 | #include "../awaitable_frame_base.hpp" 11 | #include "../basic_lockable.hpp" 12 | #include "../networking.hpp" 13 | #include "../config.hpp" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 21 | { 22 | struct execution_context; // forward declaration 23 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 24 | { 25 | template 26 | struct [[nodiscard]] basic_iocp_socket_service 27 | { 28 | typedef basic_lockable mutex_type; 29 | 30 | COASYNC_ATTRIBUTE((always_inline)) constexpr explicit basic_iocp_socket_service(execution_context& context) noexcept: _M_context(context) 31 | { 32 | _M_iocp = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 0); 33 | if (_M_iocp == nullptr) COASYNC_ATTRIBUTE((unlikely)) 34 | throw std::system_error(detail::get_errno(), detail::generic_category()); 35 | } 36 | constexpr basic_iocp_socket_service& operator=(basic_iocp_socket_service const&) = delete; 37 | constexpr basic_iocp_socket_service(basic_iocp_socket_service const&) = delete; 38 | COASYNC_ATTRIBUTE((always_inline)) basic_iocp_socket_service(basic_iocp_socket_service&&) noexcept = default; 39 | COASYNC_ATTRIBUTE((always_inline)) basic_iocp_socket_service& operator=(basic_iocp_socket_service&&) noexcept = default; 40 | COASYNC_ATTRIBUTE((always_inline)) ~ basic_iocp_socket_service() noexcept = default; 41 | 42 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 43 | static constexpr std::size_t overlap_arity() noexcept 44 | { 45 | return 1u; 46 | } 47 | 48 | COASYNC_ATTRIBUTE((always_inline)) void COASYNC_API post_frame(std::coroutine_handle frame, SOCKET sockfd) 49 | { 50 | COASYNC_ATTRIBUTE((maybe_unused)) std::unique_lock alternative_lock; 51 | if(_M_context.concurrency()) COASYNC_ATTRIBUTE((likely)) 52 | std::unique_lock(_M_lockable).swap(alternative_lock); 53 | ::CreateIoCompletionPort(reinterpret_cast(sockfd), _M_iocp, reinterpret_cast(sockfd), 0); 54 | std::memset(std::addressof(_M_overlapped[sockfd]), 0, sizeof(OVERLAPPED)); 55 | _M_overlapped[sockfd].Internal = reinterpret_cast(frame.address()); 56 | _M_overlapped[sockfd].InternalHigh = reinterpret_cast(frame.address()); 57 | DWORD flags {}; 58 | if constexpr(subcribe_sockin) 59 | { 60 | if(not ::WSARecv(sockfd, nullptr, 0, nullptr, &flags, std::addressof(_M_overlapped[sockfd]), nullptr)) 61 | if(::WSAGetLastError() != ERROR_IO_PENDING) 62 | throw std::system_error(detail::get_errno(), detail::generic_category()); 63 | } 64 | else 65 | { 66 | if(not ::WSASend(sockfd, nullptr, 0, nullptr, flags, std::addressof(_M_overlapped[sockfd]), nullptr)) 67 | if(::WSAGetLastError() != ERROR_IO_PENDING) 68 | throw std::system_error(detail::get_errno(), detail::generic_category()); 69 | } 70 | if(alternative_lock.owns_lock()) COASYNC_ATTRIBUTE((likely)) alternative_lock.unlock(); 71 | } 72 | 73 | COASYNC_ATTRIBUTE((always_inline)) void COASYNC_API commit_frame() 74 | { 75 | COASYNC_ATTRIBUTE((maybe_unused)) std::unique_lock alternative_lock; 76 | if(_M_context.concurrency()) COASYNC_ATTRIBUTE((likely)) 77 | std::unique_lock(_M_lockable).swap(alternative_lock); 78 | if (not ::GetQueuedCompletionStatus(_M_iocp, &_M_bytes_transferred, &_M_completion_key, reinterpret_cast(&_M_overlapped[0]), 0)) 79 | COASYNC_ATTRIBUTE((likely)) return; 80 | auto s = reinterpret_cast(_M_completion_key); 81 | auto f = std::coroutine_handle::from_address(reinterpret_cast(_M_overlapped[s].Internal)); 82 | std::memset(std::addressof(_M_overlapped[s]), 0, sizeof(OVERLAPPED)); 83 | _M_context.push_frame_to_executor(f); 84 | if(alternative_lock.owns_lock()) COASYNC_ATTRIBUTE((likely)) alternative_lock.unlock(); 85 | } 86 | COASYNC_ATTRIBUTE((always_inline)) void COASYNC_API cancel_frame(SOCKET sockfd) 87 | { 88 | COASYNC_ATTRIBUTE((maybe_unused)) std::unique_lock alternative_lock; 89 | if(_M_context.concurrency()) COASYNC_ATTRIBUTE((likely)) 90 | std::unique_lock(_M_lockable).swap(alternative_lock); 91 | if (not ::CancelIoEx(reinterpret_cast(sockfd), std::addressof(_M_overlapped[sockfd]))) COASYNC_ATTRIBUTE((unlikely)) 92 | throw std::system_error(detail::get_errno(), detail::generic_category()); 93 | auto f = std::coroutine_handle::from_address(reinterpret_cast(_M_overlapped[sockfd].Internal)); 94 | _M_overlapped.erase(sockfd); 95 | _M_context.push_frame_to_executor(f); 96 | if(alternative_lock.owns_lock()) COASYNC_ATTRIBUTE((likely)) alternative_lock.unlock(); 97 | } 98 | private: 99 | execution_context& _M_context; 100 | HANDLE _M_iocp; 101 | std::unordered_map _M_overlapped; 102 | mutable basic_lockable _M_lockable; 103 | mutable DWORD _M_bytes_transferred; 104 | mutable ULONG_PTR _M_completion_key; 105 | }; 106 | typedef basic_iocp_socket_service iocp_socketin_service; 107 | typedef basic_iocp_socket_service iocp_socketout_service; 108 | } 109 | } 110 | #endif // #if defined(_WIN32) || defined(_WIN64) 111 | #endif 112 | -------------------------------------------------------------------------------- /include/coasync/detail/service/latch_service.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_LATCH_SERVICE_INCLUDED 2 | #define COASYNC_LATCH_SERVICE_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "../awaitable_frame_base.hpp" 9 | #include "../basic_lockable.hpp" 10 | #include 11 | #include 12 | #include 13 | 14 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 15 | { 16 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 17 | { 18 | template 19 | struct basic_latch_service 20 | { 21 | private: 22 | struct value_type 23 | { 24 | std::coroutine_handle _M_frame; 25 | std::latch* _M_latch; 26 | /// weak reference, non-owning 27 | }; 28 | public: 29 | typedef basic_lockable mutex_type; 30 | 31 | COASYNC_ATTRIBUTE((always_inline)) 32 | constexpr explicit basic_latch_service(execution_context& context) noexcept 33 | : _M_context(context) {} 34 | constexpr basic_latch_service& operator=(basic_latch_service const&) = delete; 35 | constexpr basic_latch_service(basic_latch_service const&) = delete; 36 | COASYNC_ATTRIBUTE((always_inline)) basic_latch_service(basic_latch_service&&) noexcept = default; 37 | COASYNC_ATTRIBUTE((always_inline)) basic_latch_service& operator=(basic_latch_service&&) noexcept = default; 38 | COASYNC_ATTRIBUTE((always_inline)) ~ basic_latch_service() noexcept = default; 39 | 40 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 41 | static constexpr std::size_t overlap_arity() noexcept 42 | { 43 | return 1u; 44 | } 45 | 46 | void post_frame(std::coroutine_handle frame, std::latch& latch) 47 | { 48 | COASYNC_ATTRIBUTE((maybe_unused)) std::unique_lock alternative_lock; 49 | if(_M_context.concurrency()) COASYNC_ATTRIBUTE((likely)) 50 | std::unique_lock(_M_lockable).swap(alternative_lock); 51 | /// check parrallelism 52 | _M_forward.emplace_front(frame, &latch); 53 | } 54 | void commit_frame() 55 | { 56 | std::forward_list> outstanding; 57 | COASYNC_ATTRIBUTE((maybe_unused)) std::unique_lock alternative_lock; 58 | if(_M_context.concurrency()) COASYNC_ATTRIBUTE((likely)) 59 | std::unique_lock(_M_lockable).swap(alternative_lock); 60 | /// check parrallelism 61 | _M_forward.remove_if([this, &outstanding] 62 | #if __cplusplus >= 202207L 63 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 64 | #endif 65 | (value_type& value) mutable -> bool 66 | { 67 | bool const flag_set = value._M_latch -> try_wait(); 68 | /// non-blocking test 69 | if(flag_set) COASYNC_ATTRIBUTE((unlikely)) outstanding.emplace_front(value._M_frame); 70 | /// Save the coroutine handle to be resumed, and then execute it 71 | /// after unlocking the alternative_lock to prevent lock nesting 72 | return flag_set; 73 | }); 74 | if(alternative_lock.owns_lock()) COASYNC_ATTRIBUTE((likely)) alternative_lock.unlock(); 75 | /// check parrallelism 76 | for(std::coroutine_handle frame: outstanding) 77 | _M_context.push_frame_to_executor(frame); 78 | } 79 | private: 80 | execution_context& _M_context; 81 | mutable basic_lockable _M_lockable; 82 | std::forward_list _M_forward; 83 | }; 84 | typedef basic_latch_service latch_service; 85 | } 86 | } 87 | #endif 88 | -------------------------------------------------------------------------------- /include/coasync/detail/service/service_traits.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_SERVICE_TRAITS_INCLUDED 2 | #define COASYNC_SERVICE_TRAITS_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "../config.hpp" 9 | #include 10 | 11 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 12 | { 13 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 14 | { 15 | template 16 | struct has_post_frame_member 17 | : std::false_type {}; 18 | template 19 | struct has_post_frame_member> 20 | : std::true_type {}; 21 | template 22 | inline constexpr bool has_post_frame_member_v 23 | = has_post_frame_member::value; 24 | 25 | template 26 | struct has_commit_frame_member 27 | : std::false_type {}; 28 | template 29 | struct has_commit_frame_member> 30 | : std::true_type {}; 31 | template 32 | inline constexpr bool has_commit_frame_member_v 33 | = has_commit_frame_member::value; 34 | 35 | template 36 | struct has_cancel_frame_member 37 | : std::false_type {}; 38 | template 39 | struct has_cancel_frame_member> 40 | : std::true_type {}; 41 | template 42 | inline constexpr bool has_cancel_frame_member_v 43 | = has_cancel_frame_member::value; 44 | 45 | template 46 | concept basic_service 47 | = has_post_frame_member_v and has_commit_frame_member_v; 48 | template 49 | concept cancellable_service 50 | = (basic_service) and has_cancel_frame_member_v; 51 | 52 | template 53 | struct service_traits 54 | { 55 | private: 56 | static_assert(basic_service<__service_type>); 57 | public: 58 | typedef __service_type service_type; 59 | typedef typename __service_type::mutex_type mutex_type; 60 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) static constexpr overlap_arity() noexcept 61 | { 62 | return __service_type::overlap_arity(); 63 | } 64 | }; 65 | } 66 | } 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /include/coasync/detail/service/socket_service.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_SOCKET_SERVICE_INCLUDED 2 | #define COASYNC_SOCKET_SERVICE_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #if defined(_WIN32) || defined(_WIN64) 9 | #include "select_socket_service.hpp" 10 | //#include "iocp_socket_service.hpp" 11 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 12 | { 13 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 14 | { 15 | typedef select_socketin_service socketin_service; 16 | typedef select_socketout_service socketout_service; 17 | //typedef iocp_socketin_service socketin_service; 18 | //typedef iocp_socketout_service socketout_service; 19 | 20 | } 21 | } 22 | 23 | #elif defined(__linux__) && __has_include() 24 | #include "epoll_socket_service.hpp" 25 | 26 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 27 | { 28 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 29 | { 30 | typedef epoll_socketin_service sockin_service; 31 | typedef epoll_socketout_service sockout_service; 32 | } 33 | } 34 | #endif 35 | 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /include/coasync/detail/service/yield_service.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_YIELD_SERVICE_INCLUDED 2 | #define COASYNC_YIELD_SERVICE_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "../awaitable_frame_base.hpp" 9 | #include "../basic_lockable.hpp" 10 | #include 11 | #include 12 | 13 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 14 | { 15 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 16 | { 17 | /// do yield and releases the current coroutine's use of the thread's computing resources, 18 | /// gives scheduling rights to the central scheduler, and resumes execution again 19 | /// at some future time 20 | template 21 | struct basic_yield_service 22 | { 23 | COASYNC_ATTRIBUTE((always_inline)) 24 | constexpr explicit basic_yield_service(execution_context& context) noexcept 25 | : _M_context(context) {} 26 | constexpr basic_yield_service& operator=(basic_yield_service const&) = delete; 27 | constexpr basic_yield_service(basic_yield_service const&) = delete; 28 | COASYNC_ATTRIBUTE((always_inline)) basic_yield_service(basic_yield_service&&) noexcept = default; 29 | COASYNC_ATTRIBUTE((always_inline)) basic_yield_service& operator=(basic_yield_service&&) noexcept = default; 30 | COASYNC_ATTRIBUTE((always_inline)) ~ basic_yield_service() noexcept = default; 31 | 32 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 33 | static constexpr std::size_t overlap_arity() noexcept 34 | { 35 | return 0u; 36 | } 37 | COASYNC_ATTRIBUTE((always_inline)) void post_frame(std::coroutine_handle frame) 38 | { 39 | COASYNC_ATTRIBUTE((maybe_unused)) std::unique_lock alternative_lock; 40 | if(_M_context.concurrency()) COASYNC_ATTRIBUTE((likely)) 41 | std::unique_lock(_M_lockable).swap(alternative_lock); 42 | /// check concurrent threading 43 | /// Locking is not required for single thread 44 | _M_forward.emplace_front(frame); 45 | } 46 | COASYNC_ATTRIBUTE((always_inline)) void commit_frame() 47 | { 48 | std::forward_list> outstanding; 49 | COASYNC_ATTRIBUTE((maybe_unused)) std::unique_lock alternative_lock; 50 | if(_M_context.concurrency()) COASYNC_ATTRIBUTE((likely)) 51 | std::unique_lock(_M_lockable).swap(alternative_lock); 52 | /// check concurrent threading 53 | /// Locking is not required for single thread 54 | _M_forward.swap(outstanding); 55 | /// swap out the coroutines to be resumed, present lock nesting[push_frame_to_executor does locking] 56 | if(alternative_lock.owns_lock()) COASYNC_ATTRIBUTE((likely)) alternative_lock.unlock(); 57 | /// check concurrent threading 58 | for(std::coroutine_handle frame: outstanding) 59 | _M_context.push_frame_to_executor(frame); 60 | } 61 | private: 62 | execution_context& _M_context; 63 | mutable basic_lockable _M_lockable; 64 | std::forward_list> 65 | _M_forward; 66 | }; 67 | typedef basic_yield_service yield_service; 68 | } 69 | } 70 | #endif 71 | -------------------------------------------------------------------------------- /include/coasync/detail/service_registry.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_SERVICE_REGISTRY_INCLUDED 2 | #define COASYNC_SERVICE_REGISTRY_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "config.hpp" 9 | #include 10 | #if defined(__cpp_lib_memory_resource) 11 | # include 12 | #else 13 | # error This library requires the use of C++17 pmr support 14 | #endif 15 | 16 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 17 | { 18 | struct execution_context; /// forward declaration 19 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 20 | { 21 | struct null_service_t 22 | { 23 | explicit null_service_t(auto&) {} 24 | void post_frame(auto&& ...) {} 25 | void commit_frame(auto&& ...) {} 26 | }; 27 | /// empty tag type 28 | /// The null type acts as the virtual head node of the intrusive linked list 29 | template 30 | struct basic_service_registry 31 | { 32 | private: 33 | inline static std::atomic_long S_type_count = 0; 34 | template 35 | struct type_id 36 | { 37 | inline static long const S_value = S_type_count.fetch_add(1, std::memory_order_relaxed); 38 | }; 39 | enum Op 40 | { 41 | Op_new, 42 | Op_delete, 43 | Op_commit, 44 | Op_wait, 45 | }; 46 | template 47 | struct manager 48 | { 49 | static void S_manage(Op opcode, std::atomic* service, execution_context* context) 50 | { 51 | if constexpr(std::is_same_v) return; 52 | switch(opcode) 53 | { 54 | case Op_new: 55 | service -> store(std::pmr::polymorphic_allocator { context -> memory_resource() }.new_object(*context), std::memory_order_release); 56 | service -> notify_all(); 57 | break; 58 | case Op_delete: 59 | if(not service -> load(std::memory_order_acquire)) service -> wait(nullptr); 60 | std::pmr::polymorphic_allocator { context -> memory_resource() } 61 | .delete_object(static_cast(service -> load(std::memory_order_acquire))); 62 | break; 63 | case Op_commit: 64 | if(not service -> load(std::memory_order_acquire)) service -> wait(nullptr); 65 | static_cast(service -> load(std::memory_order_acquire)) -> commit_frame(); 66 | break; 67 | case Op_wait: 68 | if(not service -> load(std::memory_order_acquire)) service -> wait(nullptr); 69 | break; 70 | } 71 | } 72 | }; 73 | public: 74 | template 75 | basic_service_registry(COASYNC_ATTRIBUTE((maybe_unused)) std::in_place_type_t) noexcept 76 | : _M_service(nullptr) 77 | , _M_type_id(type_id::S_value) 78 | , _M_next(nullptr) 79 | , _M_manage(&manager::S_manage) 80 | { 81 | } 82 | 83 | basic_service_registry& operator=(basic_service_registry const&) = delete; 84 | 85 | friend void erase_after(basic_service_registry* position, execution_context& context) 86 | { 87 | position = position -> _M_next.load(std::memory_order_acquire); 88 | while (position != nullptr) 89 | { 90 | basic_service_registry* tmp = (position -> _M_next).load(std::memory_order_acquire); 91 | (* position -> _M_manage )(Op_delete, &position -> _M_service, &context); 92 | delete position; 93 | position = tmp; 94 | } 95 | } 96 | friend void commit_after(basic_service_registry* position, execution_context& context) 97 | { 98 | position = position -> _M_next.load(std::memory_order_acquire); 99 | while(position != nullptr) 100 | { 101 | (* position -> _M_manage )(Op_commit, &position -> _M_service, &context); 102 | position = position -> _M_next.load(std::memory_order_acquire); 103 | } 104 | } 105 | template 106 | COASYNC_ATTRIBUTE((nodiscard)) friend service_type& find_from(basic_service_registry* position, execution_context& context) 107 | { 108 | std::unique_ptr prepare = 109 | std::make_unique(std::in_place_type); 110 | long service_id = type_id::S_value; 111 | while (true) 112 | { 113 | if (position -> _M_type_id == service_id) COASYNC_ATTRIBUTE((unlikely)) 114 | { 115 | (* position -> _M_manage )(Op_wait, &position -> _M_service, &context); 116 | return * static_cast(position -> _M_service.load(std::memory_order_acquire)); 117 | } 118 | if (position -> _M_next.load(std::memory_order_acquire) == nullptr) 119 | { 120 | basic_service_registry* old_nullptr = nullptr; 121 | if (position -> _M_next.compare_exchange_weak(old_nullptr, prepare.get(), 122 | std::memory_order_release, std::memory_order_relaxed)) COASYNC_ATTRIBUTE((likely)) 123 | { 124 | prepare.release(); 125 | basic_service_registry* target = position -> _M_next.load(std::memory_order_acquire); 126 | (* target -> _M_manage )(Op_new, &target -> _M_service, &context); 127 | return * static_cast(target -> _M_service.load(std::memory_order_acquire)); 128 | } 129 | } 130 | position = position -> _M_next.load(std::memory_order_acquire); 131 | } 132 | } 133 | private: 134 | std::atomic _M_service; 135 | long const _M_type_id; 136 | std::atomic _M_next; 137 | void(*_M_manage)(Op, std::atomic*, execution_context*); 138 | }; 139 | typedef basic_service_registry service_registry; 140 | } 141 | } 142 | #endif 143 | -------------------------------------------------------------------------------- /include/coasync/detail/signal_condition.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_SIGNAL_CONDITION_INCLUDED 2 | #define COASYNC_SIGNAL_CONDITION_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "config.hpp" 9 | #include 10 | 11 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 12 | { 13 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 14 | { 15 | 16 | template 17 | struct [[nodiscard]] signal_condition 18 | { 19 | alignas(64) static inline /* volatile */ std::atomic_flag _S_flag {}; 20 | static COASYNC_ATTRIBUTE((always_inline)) void _S_set([[maybe_unused]] int signum) noexcept 21 | { 22 | COASYNC_ASSERT(signum == asynchronous_tag); 23 | std::atomic_thread_fence(std::memory_order_relaxed); 24 | std::atomic_flag_test_and_set_explicit(&_S_flag, std::memory_order_release); 25 | } 26 | }; 27 | 28 | } 29 | } 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/coasync/detail/spin_loop_pause.hpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/include/coasync/detail/spin_loop_pause.hpp -------------------------------------------------------------------------------- /include/coasync/detail/suspendible.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_SUSPENDIBLE_INCULDED 2 | #define COASYNC_SUSPENDIBLE_INCULDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "../awaitable.hpp" 9 | #include "get_frame.hpp" 10 | #include "get_context.hpp" 11 | #include "spin_loop_pause.hpp" 12 | 13 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 14 | { 15 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 16 | { 17 | /// cpo object: Different asynchronous operations are performed using different service types 18 | /// The name cpo denotes a customization point object, which is a const function 19 | /// object of a literal semiregular class type. 20 | template 21 | struct [[nodiscard]] suspendible 22 | { 23 | template 24 | COASYNC_ATTRIBUTE((nodiscard)) 25 | awaitable operator()(Overlaps&& ... overlaps) const 26 | { 27 | std::coroutine_handle frame = co_await get_frame(); 28 | service_type& service = use_service(co_await get_context()); 29 | service.post_frame(frame, std::forward(overlaps) ...); 30 | co_await std::suspend_always(); 31 | detail::__spin_loop_pause(); 32 | } 33 | }; 34 | 35 | template 36 | struct [[nodiscard]] related_suspendible 37 | { 38 | COASYNC_ATTRIBUTE((always_inline)) 39 | explicit constexpr related_suspendible(execution_context& context) noexcept 40 | : _M_context(context) {} 41 | constexpr related_suspendible& operator=(related_suspendible const&) = delete; 42 | constexpr related_suspendible& operator=(related_suspendible&&) = delete; 43 | COASYNC_ATTRIBUTE((always_inline)) ~ related_suspendible() = default; 44 | template 45 | COASYNC_ATTRIBUTE((nodiscard)) 46 | awaitable operator()(Overlaps&& ... overlaps) const&& 47 | { 48 | std::coroutine_handle frame = co_await get_frame(); 49 | use_service(_M_context).post_frame(frame, std::forward(overlaps) ...); 50 | co_await std::suspend_always(); 51 | detail::__spin_loop_pause(); 52 | } 53 | private: 54 | execution_context& _M_context; 55 | }; 56 | template 57 | struct [[nodiscard]] transformed_awaitable 58 | { 59 | COASYNC_ATTRIBUTE((always_inline)) 60 | explicit constexpr transformed_awaitable( 61 | COASYNC_ATTRIBUTE((maybe_unused)) std::in_place_type_t, 62 | Overlaps&& ... overlaps) noexcept 63 | : _M_overlaps(std::forward_as_tuple(static_cast(overlaps) ...)) 64 | {} 65 | constexpr transformed_awaitable& operator=(transformed_awaitable const&) = delete; 66 | constexpr transformed_awaitable& operator=(transformed_awaitable&&) = delete; 67 | COASYNC_ATTRIBUTE((always_inline)) ~ transformed_awaitable() = default; 68 | bool await_ready() const noexcept 69 | { 70 | return false; 71 | } 72 | template 73 | std::coroutine_handle<> 74 | await_suspend(std::coroutine_handle> coroutine) const noexcept 75 | { 76 | std::apply([coroutine](Overlaps&&... overlaps) 77 | { 78 | execution_context& context = *coroutine.promise().get_context(); 79 | std::coroutine_handle frame = std::coroutine_handle::from_address(coroutine.address()); 80 | use_service(context).post_frame(frame, std::forward(overlaps) ...); 81 | }, _M_overlaps); 82 | return std::noop_coroutine(); 83 | } 84 | void await_resume() const noexcept 85 | { 86 | } 87 | private: 88 | COASYNC_ATTRIBUTE((no_unique_address)) std::tuple _M_overlaps; 89 | }; 90 | #if defined(__cpp_deduction_guides) 91 | template 92 | transformed_awaitable(std::in_place_type_t,Overlaps&& ...) 93 | -> transformed_awaitable; 94 | #endif 95 | } 96 | } 97 | #endif 98 | -------------------------------------------------------------------------------- /include/coasync/detail/varint.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_MAGIC_GET_INCLUDED 2 | #define COASYNC_MAGIC_GET_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "config.hpp" 9 | #include 10 | 11 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 12 | { 13 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 14 | { 15 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 16 | std::uint8_t* varint_encode(std::uint8_t* ptr, std::uint64_t val) 17 | COASYNC_ATTRIBUTE((gnu::nonnull)) 18 | { 19 | while (val >= 0x80) 20 | { 21 | *ptr ++ = static_cast(val) | 0x80; 22 | val >>= 7; 23 | } 24 | *ptr ++ = static_cast(val); 25 | return ptr; 26 | } 27 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 28 | std::uint8_t* varint_decode(std::uint8_t* ptr, std::uint64_t* pVal) 29 | COASYNC_ATTRIBUTE((gnu::nonnull)) 30 | { 31 | std::uint8_t offset = 0; 32 | std::uint64_t result = 0; 33 | do 34 | { 35 | result |= static_cast((*ptr) & ~0x80) << offset; 36 | offset += 7; 37 | } 38 | while (((*ptr ++) & 0x80) != 0ull); 39 | *pVal = result; 40 | return ptr; 41 | } 42 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 43 | std::uint8_t* zigzag_encode(std::uint8_t* ptr, std::int64_t val) 44 | COASYNC_ATTRIBUTE((gnu::nonnull)) 45 | { 46 | return varint_encode(ptr, static_cast((val << 1) ^ (val >> 63))); 47 | } 48 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 49 | std::uint8_t* zigzag_decode(std::uint8_t* ptr, std::int64_t* pVal) 50 | COASYNC_ATTRIBUTE((gnu::nonnull)) 51 | { 52 | COASYNC_ATTRIBUTE((gnu::uninitialized)) uint64_t u64val; 53 | ptr = varint_decode(ptr, &u64val); 54 | *pVal = static_cast((u64val >> 1) ^ - (u64val & 1)); 55 | return ptr; 56 | } 57 | } 58 | } 59 | #endif 60 | -------------------------------------------------------------------------------- /include/coasync/detail/windows_initiate.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __COASYNC_WINDOWS_INITIATE_INCLUDED 2 | #define __COASYNC_WINDOWS_INITIATE_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | /// The WSAStartup function must be the first Windows Sockets function called by an application 9 | /// or DLL. It allows an application or DLL to specify the version of Windows Sockets required 10 | /// and retrieve details of the specific Windows Sockets implementation. The application or DLL 11 | /// can only issue further Windows Sockets functions after successfully calling WSAStartup. 12 | #include "config.hpp" 13 | #if defined(__has_include) 14 | # if defined(_WIN32) && __has_include() 15 | # include 16 | # if defined(MSVC) && defined (_MSC_VER) && _MSC_VER > 1000 17 | # pragma comment(lib, "ws2_32.lib") 18 | # endif 19 | # endif 20 | #endif 21 | #include 22 | #include 23 | 24 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 25 | { 26 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 27 | { 28 | /// RAII type for Windows socket library linkage and cleanup 29 | struct windows_initiate final 30 | { 31 | constexpr windows_initiate& operator=(windows_initiate const&) = delete; 32 | constexpr windows_initiate& operator=(windows_initiate &&) = delete; 33 | COASYNC_ATTRIBUTE((always_inline)) explicit windows_initiate() noexcept 34 | { 35 | #if defined(_WIN32) || defined(_WIN64) 36 | COASYNC_ATTRIBUTE((gnu::uninitialized)) WSADATA wsa_data; 37 | int errc = ::WSAStartup(MAKEWORD(2, 2), std::addressof(wsa_data)); 38 | if (errc == 0) COASYNC_ATTRIBUTE((likely)) return; 39 | switch (errc) 40 | { 41 | case WSASYSNOTREADY: 42 | std::fprintf( 43 | stderr, "The underlying network subsystem is not ready for network communication. "); 44 | COASYNC_TERMINATE(); 45 | case WSAVERNOTSUPPORTED: 46 | std::fprintf( 47 | stderr, "The version of Windows Sockets support requested is not provided " 48 | "by this particular Windows Sockets implementation. "); 49 | COASYNC_TERMINATE(); 50 | case WSAEINPROGRESS: 51 | std::fprintf( 52 | stderr, "A blocking Windows Sockets 1.1 operation is in progress."); 53 | COASYNC_TERMINATE(); 54 | case WSAEPROCLIM: 55 | std::fprintf( 56 | stderr, "A limit on the number of tasks supported by the Windows Sockets " 57 | " implementation has been reached. "); 58 | COASYNC_TERMINATE(); 59 | default: 60 | std::fprintf( 61 | stderr, "The lpWSAData parameter is not a valid pointer. "); 62 | COASYNC_TERMINATE(); 63 | } 64 | if (LOBYTE(wsa_data.wVersion) != 2 or HIBYTE(wsa_data.wVersion) != 2) [[unlikely]] 65 | { 66 | std::fprintf(stderr, "no useable winsock dll"); 67 | ::WSACleanup(); 68 | COASYNC_TERMINATE(); 69 | } 70 | #endif 71 | } 72 | ///An application or DLL is required to perform a successful WSAStartup call before it can 73 | /// use Windows Sockets services. When it has completed the use of Windows Sockets, the application 74 | /// or DLL must call WSACleanup to deregister itself from a Windows Sockets implementation and allow 75 | /// the implementation to free any resources allocated on behalf of the application or DLL. 76 | COASYNC_ATTRIBUTE((always_inline)) ~ windows_initiate() noexcept 77 | { 78 | #if defined(_WIN32) || defined(_WIN64) 79 | ::WSACleanup(); 80 | #endif 81 | } 82 | }; 83 | } 84 | } 85 | #endif 86 | -------------------------------------------------------------------------------- /include/coasync/execution_context.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_EXECUTION_CONTEXT_INCLUDED 2 | #define COASYNC_EXECUTION_CONTEXT_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "detail/frame_executor.hpp" 9 | #include "detail/frame_lifetime.hpp" 10 | #include "detail/service_registry.hpp" 11 | #include "detail/windows_initiate.hpp" 12 | #include "detail/spin_loop_pause.hpp" 13 | 14 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 15 | { 16 | enum class COASYNC_ATTRIBUTE((nodiscard)) concurrency_arg 17 | : unsigned int {}; 18 | struct execution_context 19 | { 20 | COASYNC_ATTRIBUTE((always_inline)) 21 | explicit execution_context(concurrency_arg conc = concurrency_arg(std::thread::hardware_concurrency())) noexcept 22 | :_M_executor((unsigned int)(conc)), _M_lifetime(static_cast((unsigned int)(conc) == 0)) 23 | { 24 | if(concurrency() == 0) COASYNC_ATTRIBUTE((likely)) 25 | std::construct_at(&_UnM_unsync_pool_resource); 26 | else COASYNC_ATTRIBUTE((unlikely)) 27 | std::construct_at(&_UnM_sync_pool_resource); 28 | } 29 | COASYNC_ATTRIBUTE((always_inline))~ execution_context() noexcept 30 | { 31 | erase_after(&_M_registry, *this); 32 | if(concurrency() == 0) COASYNC_ATTRIBUTE((likely)) 33 | std::destroy_at(&_UnM_unsync_pool_resource); 34 | else COASYNC_ATTRIBUTE((unlikely)) 35 | std::destroy_at(&_UnM_sync_pool_resource); 36 | } 37 | /// Starts blocking polling and drives the event loop 38 | COASYNC_ATTRIBUTE((always_inline)) 39 | void COASYNC_API loop() 40 | { 41 | /// start the thread pool(if concurreny > 0) 42 | _M_executor.request_launch(); 43 | while(not _M_lifetime.empty()) 44 | { 45 | /// Distribute tasks as sole producer 46 | _M_executor.dispatch_frame(); 47 | std::this_thread::yield(); 48 | detail::__spin_loop_pause(); 49 | /// Query whether a schedulable coroutine has expired 50 | commit_after(&_M_registry, *this); 51 | detail::__spin_loop_pause(); 52 | /// Destroy invalid coroutine groups 53 | _M_lifetime.remove_frame(); 54 | detail::__spin_loop_pause(); 55 | } 56 | _M_executor.request_stop(); 57 | } 58 | COASYNC_ATTRIBUTE((always_inline)) 59 | void COASYNC_API push_frame_to_executor(std::coroutine_handle frame) 60 | { 61 | /// post a task to executor/thread_pool 62 | _M_executor.push_frame(frame); 63 | } 64 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 65 | unsigned int COASYNC_API concurrency() const noexcept 66 | { 67 | return _M_executor.concurrency(); 68 | } 69 | template 70 | COASYNC_ATTRIBUTE((always_inline)) 71 | void COASYNC_API push_frame_to_lifetime(std::coroutine_handle> frame) 72 | { 73 | /// Associate the coroutine object with this context 74 | frame.promise().set_context(this); 75 | frame.promise().set_id(_M_counter.fetch_add(1, std::memory_order_relaxed)); 76 | /// Push it into the coroutine container, and monitor the life cycle 77 | _M_lifetime.push_frame(frame); 78 | } 79 | template 80 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 81 | friend service_type& COASYNC_API use_service(execution_context& context) 82 | { 83 | /// Linear query from beginning to end, if not created in place 84 | return find_from(&context._M_registry, context); 85 | } 86 | private: 87 | friend detail::service_registry; 88 | /// Provides memory resources for service registry to allocate schedulers 89 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 90 | std::pmr::memory_resource* memory_resource() noexcept 91 | { 92 | return concurrency() == 0 ? 93 | static_cast(std::addressof(_UnM_unsync_pool_resource)) : 94 | static_cast(std::addressof(_UnM_sync_pool_resource)); 95 | } 96 | detail::frame_executor _M_executor; 97 | detail::frame_lifetime _M_lifetime; 98 | detail::service_registry _M_registry 99 | { 100 | std::in_place_type 101 | }; 102 | COASYNC_ATTRIBUTE((no_unique_address)) detail::windows_initiate 103 | _M_windows; 104 | std::atomic_long _M_counter; 105 | union 106 | { 107 | std::pmr::unsynchronized_pool_resource _UnM_unsync_pool_resource; 108 | std::pmr::synchronized_pool_resource _UnM_sync_pool_resource; 109 | }; 110 | }; 111 | } 112 | #endif 113 | -------------------------------------------------------------------------------- /include/coasync/net/acceptor.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_ACCEPTOR_INCLUDED 2 | #define COASYNC_ACCEPTOR_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "socket.hpp" 9 | 10 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 11 | { 12 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) net 13 | { 14 | /// Provides the ability to accept new connections. 15 | template 16 | struct __basic_acceptor: public __basic_socket 17 | { 18 | typedef socket_base::context_type context_type; 19 | typedef socket_base::native_handle_type native_handle_type; 20 | using socket_base::is_open; 21 | using socket_base::native_handle; 22 | using socket_base::context; 23 | using socket_base::cancel; 24 | using socket_base::close; 25 | using socket_base::non_blocking; 26 | using socket_base::io_control; 27 | using socket_base::available; 28 | using socket_base::listen; 29 | using socket_base::release; 30 | using __basic_socket::set_option; 31 | using __basic_socket::get_option; 32 | using __basic_socket::open; 33 | using __basic_socket::assign; 34 | using __basic_socket::bind; 35 | using __basic_socket::local_endpoint; 36 | using __basic_socket::remote_endpoint; 37 | using __basic_socket::protocol; 38 | using __basic_socket::send_to; 39 | using __basic_socket::receive_from; 40 | using __basic_socket::receive; 41 | using __basic_socket::send; 42 | using __basic_socket::connect; 43 | typedef Protocol protocol_type; 44 | typedef Protocol::endpoint endpoint_type; 45 | typedef Protocol::endpoint::address_type address_type; 46 | typedef Protocol::endpoint::address_v4_type address_v4_type; 47 | typedef Protocol::endpoint::address_v6_type address_v6_type; 48 | typedef Protocol::endpoint::port_type port_type; 49 | using __basic_socket::__basic_socket; 50 | using __basic_socket::operator=; 51 | /// extracts the first connection on the queue of pending connections on current socket. 52 | /// It then creates and returns a handle to the new socket. The newly created socket 53 | /// is the socket that will handle the actual connection; 54 | COASYNC_ATTRIBUTE((nodiscard)) 55 | awaitable<__basic_socket> COASYNC_API accept() 56 | { 57 | // assert(&context() == &(co_await detail::get_context())); 58 | co_await detail::related_suspendible(context())(native_handle()); 59 | socket_option::error err; 60 | get_option(err); 61 | if (int ec = err.get()) COASYNC_ATTRIBUTE((unlikely)) 62 | throw std::system_error(ec, detail::generic_category()); 63 | native_handle_type sockfd = ::accept(native_handle(), nullptr, 0); 64 | if (sockfd == native_handle_type(-1)) COASYNC_ATTRIBUTE((unlikely)) 65 | throw std::system_error(detail::get_errno(), detail::generic_category()); 66 | co_return __basic_socket(context(), protocol(), sockfd); 67 | } 68 | COASYNC_ATTRIBUTE((always_inline)) 69 | __basic_acceptor(context_type& context, endpoint_type const& ep) 70 | noexcept 71 | : __basic_socket(context, ep.protocol()) 72 | , M_endpoint(ep) 73 | { 74 | open(ep.protocol()); 75 | set_option(socket_option::reuse_address(true)); 76 | bind(ep); 77 | listen(max_listen_connections); 78 | } 79 | COASYNC_ATTRIBUTE((always_inline)) 80 | constexpr __basic_acceptor(__basic_acceptor&& other) noexcept 81 | : __basic_socket(std::move(other)) 82 | , M_endpoint(std::move(other.M_endpoint)) 83 | { 84 | } 85 | COASYNC_ATTRIBUTE((always_inline)) 86 | constexpr __basic_acceptor& operator=(__basic_acceptor&& other) noexcept 87 | { 88 | if (std::addressof(other) == this) COASYNC_ATTRIBUTE((unlikely)) 89 | return (*this); 90 | static_cast<__basic_socket&>(*this) = std::move(other); 91 | M_endpoint = std::move(other.M_endpoint); 92 | return (*this); 93 | } 94 | COASYNC_ATTRIBUTE((always_inline)) ~ __basic_acceptor() noexcept = default; 95 | private: 96 | endpoint_type M_endpoint; 97 | }; 98 | template 99 | using basic_acceptor = __basic_acceptor; 100 | } 101 | } 102 | #endif 103 | -------------------------------------------------------------------------------- /include/coasync/net/message_flags.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __COASYNC_MESSAGE_FLAGS_INCLUDED 2 | #define __COASYNC_MESSAGE_FLAGS_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "../detail/config.hpp" 9 | #if defined(__has_include) 10 | # if defined(_WIN32) || defined(_WIN64) 11 | # if __has_include() 12 | # include 13 | # endif 14 | # elif defined(linux) 15 | # if __has_include 16 | # include 17 | # endif 18 | # endif 19 | #endif 20 | 21 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 22 | { 23 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) net 24 | { 25 | /// Bitmask type for flags that can be passed to send and receive operations. 26 | /// The flags argument is the bitwise OR of zero or more of the following flags. 27 | enum class message_flags : int { }; 28 | constexpr message_flags message_peek = static_cast(MSG_PEEK); 29 | constexpr message_flags message_out_of_band = static_cast(MSG_OOB); 30 | constexpr message_flags message_do_not_route = static_cast(MSG_DONTROUTE); 31 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 32 | constexpr message_flags operator&(message_flags f1, message_flags f2) noexcept 33 | { 34 | return static_cast( static_cast(f1) & static_cast(f2) ); 35 | } 36 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 37 | constexpr message_flags operator|(message_flags f1, message_flags f2) noexcept 38 | { 39 | return static_cast( static_cast(f1) | static_cast (f2) ); 40 | } 41 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 42 | constexpr message_flags operator^(message_flags f1, message_flags f2) noexcept 43 | { 44 | return static_cast( static_cast(f1) ^ static_cast(f2) ); 45 | } 46 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 47 | constexpr message_flags operator~(message_flags f) noexcept 48 | { 49 | return static_cast( ~static_cast(f) ); 50 | } 51 | COASYNC_ATTRIBUTE((maybe_unused, always_inline)) 52 | constexpr message_flags& operator&=(message_flags& f1, message_flags f2) noexcept 53 | { 54 | return f1 = (f1 & f2); 55 | } 56 | COASYNC_ATTRIBUTE((maybe_unused, always_inline)) 57 | constexpr message_flags& operator|=(message_flags& f1, message_flags f2) noexcept 58 | { 59 | return f1 = (f1 | f2); 60 | } 61 | COASYNC_ATTRIBUTE((maybe_unused, always_inline)) 62 | constexpr message_flags& operator^=(message_flags& f1, message_flags f2) noexcept 63 | { 64 | return f1 = (f1 ^ f2); 65 | } 66 | } 67 | } 68 | #endif 69 | -------------------------------------------------------------------------------- /include/coasync/net/network_v6.hpp: -------------------------------------------------------------------------------- 1 | /// network_v6/network_v6 2 | 3 | #ifndef COASYNC_NETWORK_V6_INCLUDED 4 | #define COASYNC_NETWORK_V6_INCLUDED 5 | 6 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | # pragma once 8 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 9 | 10 | #include "address_v6.hpp" 11 | #include "address_v6_iterator.hpp" 12 | 13 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 14 | { 15 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) net 16 | { 17 | struct [[nodiscard]] network_v6 18 | { 19 | typedef int prefix_length_type; 20 | typedef address_v6 address_type; 21 | 22 | COASYNC_ATTRIBUTE((always_inline)) 23 | constexpr explicit network_v6() noexcept 24 | : _M_addr() 25 | , _M_prefix_len(0) {} 26 | COASYNC_ATTRIBUTE((always_inline)) 27 | constexpr network_v6(address_v6 const& addr, int prefix_len) noexcept(false) 28 | : _M_addr(addr) 29 | , _M_prefix_len(prefix_len) 30 | { 31 | if (_M_prefix_len < 0 || _M_prefix_len > 128) COASYNC_ATTRIBUTE((unlikely)) 32 | throw std::out_of_range("network_v6: invalid prefix length"); 33 | } 34 | 35 | COASYNC_ATTRIBUTE((always_inline)) 36 | ~ network_v6() noexcept 37 | { 38 | } 39 | COASYNC_ATTRIBUTE((always_inline)) 40 | constexpr network_v6(network_v6 const&) noexcept = default; 41 | COASYNC_ATTRIBUTE((always_inline)) 42 | constexpr network_v6& operator=(network_v6 const&) noexcept = default; 43 | COASYNC_ATTRIBUTE((always_inline)) 44 | constexpr network_v6(network_v6&&) noexcept = default; 45 | COASYNC_ATTRIBUTE((always_inline)) 46 | constexpr network_v6& operator=(network_v6&&) noexcept = default; 47 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) /*constexpr*/ address_v6 address() const noexcept 48 | { 49 | return _M_addr; 50 | } 51 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) constexpr int prefix_length() const noexcept 52 | { 53 | return _M_prefix_len; 54 | } 55 | COASYNC_ATTRIBUTE((noreturn, always_inline)) /*constexpr*/ address_v6 network() const noexcept 56 | { 57 | COASYNC_TERMINATE(); 58 | } 59 | 60 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 61 | address_v6_range hosts() const noexcept 62 | { 63 | if (is_host()) COASYNC_ATTRIBUTE((likely)) 64 | return address_v6_range{ address(), *++address_v6_iterator(address()) }; 65 | COASYNC_TERMINATE(); // { network(), XXX broadcast() XXX }; // TODO 66 | } 67 | 68 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 69 | /*constexpr*/ network_v6 canonical() const noexcept 70 | { 71 | return network_v6{network(), prefix_length()}; 72 | } 73 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 74 | constexpr bool is_host() const noexcept 75 | { 76 | return _M_prefix_len == 128; 77 | } 78 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 79 | /*constexpr*/ bool is_subnet_of(const network_v6& __other) const noexcept 80 | { 81 | if (__other.prefix_length() < prefix_length()) COASYNC_ATTRIBUTE((likely)) 82 | { 83 | network_v6 __net(address(), __other.prefix_length()); 84 | return __net.canonical() == __other.canonical(); 85 | } 86 | return false; 87 | } 88 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 89 | std::string to_string() const noexcept 90 | { 91 | return address().to_string() + '/' + std::to_string(prefix_length()); 92 | } 93 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 94 | friend /*constexpr*/ bool operator==(const network_v6& __a, const network_v6& __b) noexcept 95 | { 96 | return __a.address() == __b.address() 97 | && __a.prefix_length() == __b.prefix_length(); 98 | } 99 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 100 | friend /*constexpr*/ bool operator!=(const network_v6& __a, const network_v6& __b) noexcept 101 | { 102 | return !(__a == __b); 103 | } 104 | private: 105 | COASYNC_ATTRIBUTE((no_unique_address)) address_v6 _M_addr; 106 | int _M_prefix_len; 107 | }; 108 | 109 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) network_v6 110 | make_network_v6(const address_v6& __a, int __prefix_len) 111 | { 112 | return network_v6{__a, __prefix_len}; 113 | } 114 | 115 | template 116 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) std::basic_ostream<_CharT, _Traits>& 117 | operator<<(std::basic_ostream<_CharT, _Traits>& __os, const network_v6& __net) 118 | { 119 | return __os << __net.to_string(); 120 | } 121 | 122 | } 123 | } 124 | 125 | #endif 126 | -------------------------------------------------------------------------------- /include/coasync/net/port.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __COASYNC_PORT_INCLUDED 2 | #define __COASYNC_PORT_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "../detail/config.hpp" 9 | #include 10 | 11 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 12 | { 13 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) net 14 | { 15 | typedef std::uint_least16_t port_type; 16 | } 17 | } 18 | #endif 19 | -------------------------------------------------------------------------------- /include/coasync/net/resolver_flags.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_RESOLVER_FLAGS_INCLUDED 2 | #define COASYNC_RESOLVER_FLAGS_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "../detail/config.hpp" 9 | #if defined(__has_include) 10 | # if defined(_WIN32) || defined(_WIN64) 11 | # if __has_include() 12 | # include 13 | # endif 14 | # elif defined(linux) 15 | # if __has_include 16 | # include 17 | # endif 18 | # endif 19 | #endif 20 | 21 | enum class resolver_flags : int { }; 22 | /// Flags that indicate options used in the getaddrinfo function. 23 | /// Supported values for the ai_flags member can be a combination of the following options. 24 | inline static constexpr resolver_flags passive = static_cast(AI_PASSIVE); 25 | inline static constexpr resolver_flags canonical_name = static_cast(AI_CANONNAME); 26 | inline static constexpr resolver_flags numeric_host = static_cast(AI_NUMERICHOST); 27 | inline static constexpr resolver_flags numeric_service = static_cast(AI_NUMERICSERV); 28 | inline static constexpr resolver_flags v4_mapped = static_cast(AI_V4MAPPED); 29 | inline static constexpr resolver_flags all_matching = static_cast(AI_ALL); 30 | inline static constexpr resolver_flags address_configured = static_cast(AI_ADDRCONFIG); 31 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 32 | constexpr resolver_flags operator&(resolver_flags f1, resolver_flags f2) noexcept 33 | { 34 | return static_cast( static_cast(f1) & int(f2) ); 35 | } 36 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 37 | constexpr resolver_flags operator|(resolver_flags f1, resolver_flags f2) noexcept 38 | { 39 | return static_cast( static_cast(f1) | int(f2) ); 40 | } 41 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 42 | constexpr resolver_flags operator^(resolver_flags f1, resolver_flags f2) noexcept 43 | { 44 | return static_cast( static_cast(f1) ^ static_cast(f2) ); 45 | } 46 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 47 | constexpr resolver_flags operator~(resolver_flags f) noexcept 48 | { 49 | return static_cast( ~static_cast(f) ); 50 | } 51 | COASYNC_ATTRIBUTE((maybe_unused, always_inline)) 52 | constexpr resolver_flags& operator&=(resolver_flags& f1, resolver_flags f2) noexcept 53 | { 54 | return f1 = (f1 & f2); 55 | } 56 | COASYNC_ATTRIBUTE((maybe_unused, always_inline)) 57 | constexpr resolver_flags& operator|=(resolver_flags& f1, resolver_flags f2) noexcept 58 | { 59 | return f1 = (f1 | f2); 60 | } 61 | COASYNC_ATTRIBUTE((maybe_unused, always_inline)) 62 | constexpr resolver_flags& operator^=(resolver_flags& f1, resolver_flags f2) noexcept 63 | { 64 | return f1 = (f1 ^ f2); 65 | } 66 | #endif 67 | -------------------------------------------------------------------------------- /include/coasync/net/scope_id.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_SCOPE_ID_INCLUDED 2 | #define COASYNC_SCOPE_ID_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "../detail/config.hpp" 9 | #include 10 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 11 | { 12 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) net 13 | { 14 | typedef std::uint_least32_t scope_id_type; 15 | } 16 | } 17 | #endif 18 | -------------------------------------------------------------------------------- /include/coasync/net/send.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_SEND_INCLUDED 2 | #define COASYNC_SEND_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "../awaitable.hpp" 9 | #include "option.hpp" 10 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 11 | { 12 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) net 13 | { 14 | template struct __basic_socket; 15 | struct tcp; 16 | /// a composed asynchronous operation that writes a exact amount of data to a stream 17 | /// before completion. 18 | template 19 | requires std::ranges::contiguous_range 20 | and std::ranges::sized_range 21 | and std::same_as> 22 | COASYNC_ATTRIBUTE((nodiscard)) 23 | awaitable send_exactly(__basic_socket& socket, Rng const& buffer) 24 | { 25 | std::size_t sentinel {}; 26 | std::size_t transfer_bytes = std::ranges::size(buffer); 27 | while(sentinel < transfer_bytes) 28 | { 29 | int outcoming_bytes = co_await socket.send(std::ranges::subrange { buffer.begin() + sentinel, buffer.end() }); 30 | if(outcoming_bytes <= 0) COASYNC_ATTRIBUTE((unlikely)) 31 | throw std::system_error(static_cast(std::errc::connection_aborted), std::generic_category()); 32 | sentinel += outcoming_bytes; 33 | } 34 | } 35 | } 36 | } 37 | #endif 38 | -------------------------------------------------------------------------------- /include/coasync/net/serde_stream.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_SERDE_STREAM_INCLUDED 2 | #define COASYNC_SERDE_STREAM_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "../detail/meta/serde_stream_base.hpp" 9 | #include "socket.hpp" 10 | #include "send.hpp" 11 | #include "receive.hpp" 12 | #include 13 | 14 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 15 | { 16 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) net 17 | { 18 | template 19 | struct basic_serde_stream: detail::serde_stream_base> 20 | { 21 | using char_type = std::stringstream::char_type; 22 | using int_type = std::stringstream::int_type; 23 | using pos_type = std::stringstream::pos_type; 24 | using off_type = std::stringstream::off_type; 25 | using allocator_type = std::stringstream::allocator_type; 26 | using detail::serde_stream_base::serialize; 27 | using detail::serde_stream_base::deserialize; 28 | 29 | COASYNC_ATTRIBUTE((always_inline)) 30 | constexpr explicit basic_serde_stream(__basic_socket socket) noexcept 31 | : _M_socket(std::move(socket)) 32 | { 33 | COASYNC_ASSERT((_M_socket.is_open())); 34 | } 35 | constexpr basic_serde_stream& operator=(basic_serde_stream const&) = delete; 36 | constexpr basic_serde_stream(basic_serde_stream const&) = delete; 37 | COASYNC_ATTRIBUTE((always_inline)) basic_serde_stream(basic_serde_stream&&) noexcept = default; 38 | COASYNC_ATTRIBUTE((always_inline)) basic_serde_stream& operator=(basic_serde_stream&&) noexcept = default; 39 | COASYNC_ATTRIBUTE((always_inline)) ~ basic_serde_stream() noexcept { 40 | }; 41 | 42 | COASYNC_ATTRIBUTE((nodiscard)) 43 | awaitable read(char_type* s, std::streamsize count) COASYNC_ATTRIBUTE((gnu::nonnull)) 44 | { 45 | /// If there are not enough bytes of data in the current buffer, more data 46 | /// needs to be read from the socket. 47 | if(_M_available < count) COASYNC_ATTRIBUTE((unlikely)) 48 | { 49 | std::string buffer = co_await receive_at_least(_M_socket, count); 50 | _M_buffer.write(buffer.data(), buffer.size()); 51 | _M_available += buffer.size(); 52 | } 53 | _M_buffer.read(s, count); 54 | _M_available -= count; 55 | } 56 | COASYNC_ATTRIBUTE((nodiscard)) 57 | awaitable write(char_type const* s, std::streamsize count) 58 | { 59 | co_await send_exactly(_M_socket, std::span { s, static_cast(count) }); 60 | } 61 | private: 62 | std::streamsize _M_available {0}; 63 | std::stringstream _M_buffer; 64 | /// socket must be opened 65 | /// Using this socket for data transfers other than serialization is an unexpected behavior 66 | __basic_socket _M_socket; 67 | }; 68 | typedef basic_serde_stream serde_stream; 69 | } 70 | } 71 | #endif 72 | -------------------------------------------------------------------------------- /include/coasync/rpc/rpc_client.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_RPC_CLIENT_INCLUDED 2 | #define COASYNC_RPC_CLIENT_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "../net/serde_stream.hpp" 9 | #include "../detail/object_deduce.hpp" 10 | 11 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 12 | { 13 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) net 14 | { 15 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) rpc 16 | { 17 | template struct basic_rpc_client 18 | { 19 | private: 20 | typedef basic_serde_stream serde_stream; 21 | typedef __basic_socket socket; 22 | public: 23 | COASYNC_ATTRIBUTE((always_inline)) 24 | constexpr explicit basic_rpc_client(socket s) noexcept 25 | : _M_stream(std::move(s)) {} 26 | constexpr basic_rpc_client& operator=(basic_rpc_client const&) = delete; 27 | constexpr basic_rpc_client(basic_rpc_client const&) = delete; 28 | COASYNC_ATTRIBUTE((always_inline)) basic_rpc_client(basic_rpc_client&&) noexcept = default; 29 | COASYNC_ATTRIBUTE((always_inline)) basic_rpc_client& operator=(basic_rpc_client&&) noexcept = default; 30 | COASYNC_ATTRIBUTE((always_inline)) ~ basic_rpc_client() noexcept 31 | { 32 | }; 33 | 34 | template 35 | COASYNC_ATTRIBUTE((nodiscard)) 36 | awaitable call(std::string const& __fnname, Args&& ... __args) 37 | { 38 | static_assert( 39 | std::is_void_v or 40 | (std::is_object_v and not std::is_pointer_v)); 41 | 42 | co_await _M_stream.serialize(__fnname); 43 | std::tuple arguments { std::forward(__args)... }; 44 | co_await _M_stream.serialize(arguments); 45 | 46 | COASYNC_ATTRIBUTE((gnu::uninitialized)) union _Storage 47 | { 48 | detail::object_deduce_t _M_value; 49 | } __storage; 50 | co_await _M_stream.deserialize(static_cast&>(__storage._M_value)); 51 | 52 | if constexpr(std::is_void_v) 53 | co_return; 54 | else 55 | co_return std::move(__storage._M_value); 56 | } 57 | private: 58 | serde_stream _M_stream; 59 | }; 60 | typedef basic_rpc_client rpc_client; 61 | } 62 | } 63 | } 64 | #endif 65 | -------------------------------------------------------------------------------- /include/coasync/rpc/rpc_control_variable.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_RPC_CONTROL_VARIABLE_INCLUDED 2 | #define COASYNC_RPC_CONTROL_VARIABLE_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "../detail/config.hpp" 9 | 10 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 11 | { 12 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) net 13 | { 14 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) rpc 15 | { 16 | struct COASYNC_ATTRIBUTE((nodiscard)) rpc_control_variable 17 | { 18 | COASYNC_ATTRIBUTE((always_inline)) 19 | constexpr void disable_response() noexcept 20 | { 21 | _M_disable_response = true; 22 | } 23 | COASYNC_ATTRIBUTE((always_inline)) 24 | constexpr void close_session() noexcept 25 | { 26 | COASYNC_ATTRIBUTE((assume((_M_close_session != nullptr)))); 27 | * _M_close_session = true; 28 | } 29 | COASYNC_ATTRIBUTE((always_inline)) 30 | constexpr void shutdown_server() noexcept 31 | { 32 | COASYNC_ATTRIBUTE((assume((_M_close_session != nullptr)))); 33 | * _M_shutdown_server = true; 34 | } 35 | private: 36 | template 37 | friend struct basic_rpc_server; 38 | COASYNC_ATTRIBUTE((always_inline)) 39 | explicit constexpr rpc_control_variable() noexcept 40 | : _M_disable_response(false) 41 | {} 42 | alignas(64) bool _M_disable_response; 43 | alignas(64) bool* _M_close_session; 44 | alignas(64) bool* _M_shutdown_server; 45 | }; 46 | } 47 | } 48 | } 49 | #endif 50 | -------------------------------------------------------------------------------- /include/coasync/set_stop_source.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_SET_STOP_SOURCE_INCLUDED 2 | #define COASYNC_SET_STOP_SOURCE_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "awaitable.hpp" 9 | 10 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 11 | { 12 | /// intended to associate a std::stop_source with an awaitable object using pipeline operator|. 13 | /// It enables the coroutine to listen for stop requests and propagate them through the 14 | /// awaitable chain. 15 | struct set_stop_source 16 | { 17 | COASYNC_ATTRIBUTE((always_inline)) 18 | explicit set_stop_source(std::stop_source& source) noexcept 19 | : _M_stop_source(static_cast(source)) 20 | {} 21 | constexpr set_stop_source& operator=(set_stop_source const&) = delete; 22 | constexpr set_stop_source& operator=(set_stop_source&&) = delete; 23 | template 24 | COASYNC_ATTRIBUTE((nodiscard, always_inline)) 25 | friend awaitable COASYNC_API operator|(awaitable a, set_stop_source&& source) 26 | { 27 | a.get_coroutine().promise().set_stop_token(source._M_stop_source.get_token()); 28 | return a; 29 | } 30 | private: 31 | std::stop_source&& _M_stop_source; 32 | }; 33 | } 34 | #endif 35 | -------------------------------------------------------------------------------- /include/coasync/this_coro.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_THIS_CORO_INCLUDED 2 | #define COASYNC_THIS_CORO_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "detail/get_context.hpp" 9 | #include "detail/get_id.hpp" 10 | #include "detail/get_stop_token.hpp" 11 | #include "detail/get_frame.hpp" 12 | 13 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 14 | { 15 | namespace this_coro { 16 | /// Awaitable object that returns the execution_context reference of the current coroutine. 17 | inline constexpr detail::get_context context {}; 18 | /// Awaitable object that returns the coroutine-id of the current coroutine. 19 | inline constexpr detail::get_id id {}; 20 | /// Awaitable object that returns the coroutine-handle of the current coroutine. 21 | inline constexpr detail::get_frame frame {}; 22 | /// Awaitable object that returns the stop_token of the current coroutine. 23 | inline detail::get_stop_token stop_token {}; 24 | }; 25 | } 26 | #endif 27 | -------------------------------------------------------------------------------- /include/coasync/when_all.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_WHEN_ALL_INCLUDED 2 | #define COASYNC_WHEN_ALL_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "detail/service/latch_service.hpp" 9 | #include "detail/suspendible.hpp" 10 | #include "detail/object_deduce.hpp" 11 | #include "co_spawn.hpp" 12 | #include "awaitable_group.hpp" 13 | #include 14 | 15 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 16 | { 17 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 18 | { 19 | template 20 | COASYNC_ATTRIBUTE((nodiscard)) 21 | awaitable when_all_wrapper( 22 | awaitable a, 23 | std::latch& local_latch, 24 | manual_lifetime>& desired_value, 25 | std::vector& eptr_collection, 26 | basic_lockable& eptr_lockable) 27 | { 28 | try 29 | { 30 | if constexpr(std::is_void_v) co_await a; 31 | else desired_value.construct(static_cast(co_await a)); 32 | } 33 | catch(...) 34 | { 35 | std::lock_guard l { eptr_lockable }; 36 | eptr_collection.emplace_back(std::current_exception()); 37 | } 38 | local_latch.count_down(); 39 | } 40 | template 41 | COASYNC_ATTRIBUTE((nodiscard)) 42 | awaitable ...>> 43 | when_all_impl(COASYNC_ATTRIBUTE((maybe_unused)) std::index_sequence, awaitable... aargs) 44 | { 45 | std::latch local_latch { sizeof...(Indices) }; 46 | std::vector eptr_collection; 47 | basic_lockable eptr_lockable; 48 | execution_context& context = co_await get_context(); 49 | std::tuple> ...> values; 50 | eptr_collection.reserve(sizeof...(Indices)); 51 | (co_spawn( 52 | context, 53 | when_all_wrapper(std::move(aargs), local_latch, std::get(values), eptr_collection, eptr_lockable), use_detach_t()), ...); 54 | co_await suspendible()(local_latch); 55 | if(not eptr_collection.empty()) COASYNC_ATTRIBUTE((unlikely)) 56 | std::rethrow_exception(std::make_exception_ptr(std::move(eptr_collection))); 57 | co_return std::make_tuple(std::move(std::get(values).get()) ...); 58 | } 59 | template 60 | COASYNC_ATTRIBUTE((nodiscard)) 61 | awaitable>> COASYNC_API when_all_impl(awaitable_group group) 62 | { 63 | std::latch local_latch { static_cast(group.size()) }; 64 | std::vector>> value_collection; 65 | std::vector> values; 66 | std::vector eptr_collection; 67 | basic_lockable eptr_lockable; 68 | execution_context& context = co_await get_context(); 69 | value_collection.resize(group.size()); 70 | values.reserve(group.size()); 71 | eptr_collection.reserve(group.size()); 72 | for(std::size_t index {}; index < group.size(); index ++) 73 | co_spawn( 74 | context, 75 | when_all_wrapper(std::move(group[index]), local_latch, value_collection[index], eptr_collection, eptr_lockable), 76 | detail::use_detach_t()); 77 | co_await suspendible()(local_latch); 78 | if(not eptr_collection.empty()) COASYNC_ATTRIBUTE((unlikely)) 79 | std::rethrow_exception(std::make_exception_ptr(std::move(eptr_collection))); 80 | 81 | for(manual_lifetime>& manual: value_collection) 82 | values.emplace_back(std::move(manual.get())); 83 | co_return std::move(values); 84 | } 85 | } 86 | /// Create a awaitable object that becomes ready when all of the input awaitables become ready. 87 | /// The behavior is undefined if any input awaitable is invalid. 88 | template 89 | COASYNC_ATTRIBUTE((nodiscard)) 90 | awaitable ...>> 91 | COASYNC_API when_all(awaitable ...aargs) 92 | { 93 | return detail::when_all_impl(std::make_index_sequence(), std::move(aargs) ...); 94 | } 95 | template 96 | COASYNC_ATTRIBUTE((nodiscard)) 97 | awaitable>> COASYNC_API when_all(awaitable_group group) 98 | { 99 | return detail::when_all_impl(std::move(group)); 100 | } 101 | } 102 | #endif 103 | -------------------------------------------------------------------------------- /include/coasync/when_any.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COASYNC_WHEN_ANY_INCLUDED 2 | #define COASYNC_WHEN_ANY_INCLUDED 3 | 4 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 5 | # pragma once 6 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 7 | 8 | #include "detail/service/flag_service.hpp" 9 | #include "detail/suspendible.hpp" 10 | #include "detail/object_deduce.hpp" 11 | #include "set_stop_source.hpp" 12 | #include "co_spawn.hpp" 13 | #include "awaitable_group.hpp" 14 | #include 15 | 16 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) coasync 17 | { 18 | namespace COASYNC_ATTRIBUTE((gnu::visibility("default"))) detail 19 | { 20 | template 21 | COASYNC_ATTRIBUTE((nodiscard)) 22 | awaitable when_any_wrapper( 23 | awaitable a, 24 | std::shared_ptr flag_arrive, 25 | std::size_t const this_place, 26 | manual_lifetime>& first_value, 27 | std::atomic_size_t& first_arrive, 28 | std::exception_ptr& first_eptr) 29 | { 30 | manual_lifetime prepare_value; 31 | std::exception_ptr prepare_eptr {}; 32 | try 33 | { 34 | if constexpr(std::is_void_v) co_await a; 35 | else prepare_value.construct(static_cast(co_await a)); 36 | } 37 | catch(...) 38 | { 39 | prepare_eptr = std::current_exception(); 40 | } 41 | if(not flag_arrive -> test_and_set(std::memory_order_release)) COASYNC_ATTRIBUTE((unlikely)) 42 | { 43 | if constexpr(not std::is_void_v) 44 | first_value.construct(static_cast(prepare_value.get())); 45 | if(prepare_eptr) COASYNC_ATTRIBUTE((unlikely)) first_eptr = std::move(prepare_eptr); 46 | first_arrive.store(this_place, std::memory_order_release); 47 | } 48 | } 49 | template 50 | COASYNC_ATTRIBUTE((nodiscard)) 51 | awaitable ...>> 52 | when_any_impl( 53 | COASYNC_ATTRIBUTE((maybe_unused)) std::index_sequence, 54 | awaitable... aargs) 55 | { 56 | std::shared_ptr flag_arrive = std::make_shared(); 57 | std::stop_source ssource; 58 | std::exception_ptr first_eptr {}; 59 | std::atomic_size_t first_arrive {std::size_t(-1)}; 60 | execution_context& context = co_await get_context(); 61 | manual_lifetime ...>> 62 | desired_value; 63 | std::tuple< manual_lifetime> ...> 64 | candidates; 65 | flag_arrive -> clear(); 66 | (co_spawn( 67 | context, 68 | when_any_wrapper(std::move(aargs), flag_arrive, Indices, std::get(candidates), first_arrive, first_eptr) 69 | | set_stop_source(ssource), use_detach_t()), ...); 70 | co_await suspendible()(* flag_arrive); 71 | ssource.request_stop(); 72 | while(first_arrive.load(std::memory_order_acquire) == std::size_t(-1)) 73 | std::this_thread::yield(); 74 | if(first_eptr) COASYNC_ATTRIBUTE((unlikely)) std::rethrow_exception(std::move(first_eptr)); 75 | ((first_arrive == Indices 76 | and (desired_value.construct(std::in_place_index, std::move(std::get(candidates).get())), false)), ...); 77 | co_return std::move(desired_value.get()); 78 | } 79 | template 80 | COASYNC_ATTRIBUTE((nodiscard)) 81 | awaitable> 82 | COASYNC_API when_any_impl(awaitable_group agroup) 83 | { 84 | std::shared_ptr flag_arrive = std::make_shared(); 85 | std::stop_source ssource; 86 | std::exception_ptr first_eptr {}; 87 | std::atomic_size_t first_arrive {std::size_t(-1)}; 88 | execution_context& context = co_await detail::get_context(); 89 | detail::manual_lifetime> desired_value; 90 | flag_arrive -> clear(); 91 | for(awaitable& a: agroup) 92 | co_spawn( 93 | context, 94 | when_any_wrapper(std::move(a), flag_arrive, 0, desired_value, first_arrive, first_eptr), 95 | detail::use_detach_t()); 96 | co_await detail::suspendible()(* flag_arrive); 97 | ssource.request_stop(); 98 | while(first_arrive.load(std::memory_order_acquire) == std::size_t(-1)) 99 | std::this_thread::yield(); 100 | if(first_eptr) COASYNC_ATTRIBUTE((unlikely)) 101 | std::rethrow_exception(std::move(first_eptr)); 102 | co_return std::move(desired_value.get()); 103 | } 104 | } 105 | /// Create a awaitable object that becomes ready when at least one of the input awaitables become ready. 106 | /// The behavior is undefined if any awaitable future is invalid. 107 | template 108 | COASYNC_ATTRIBUTE((nodiscard)) 109 | awaitable ...>> 110 | COASYNC_API when_any(awaitable... aargs) 111 | { 112 | return detail::when_any_impl(std::make_index_sequence(), std::move(aargs) ...); 113 | } 114 | template 115 | COASYNC_ATTRIBUTE((nodiscard)) 116 | awaitable> COASYNC_API when_any(awaitable_group agroup) 117 | { 118 | return detail::when_any_impl(std::move(agroup)); 119 | } 120 | } 121 | #endif 122 | -------------------------------------------------------------------------------- /perf/coasync/dummy.cc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/perf/coasync/dummy.cc -------------------------------------------------------------------------------- /test/coasync/dummy.cc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/test/coasync/dummy.cc -------------------------------------------------------------------------------- /tool-scripts/coasync/dummy.cc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RFoe/coasync/c725019def0380a586c047d7ab586ba2eb68fc29/tool-scripts/coasync/dummy.cc -------------------------------------------------------------------------------- /xmake.lua: -------------------------------------------------------------------------------- 1 | project("coasync") 2 | version("0.0.1") 3 | 4 | add_requires("c++20") 5 | 6 | includes("include") 7 | 8 | add_cxxflags("-fcoroutines -fconcepts -freport-bug -Wno-interference-size") 9 | 10 | local is_master_project = os.getcwd() == os.projectdir() 11 | 12 | if is_master_project then 13 | if os.is("windows") then 14 | add_cxxflags("/W4") 15 | else 16 | add_cxxflags("-Wall -Wextra -Wdeprecated -Wdocumentation") 17 | end 18 | end 19 | 20 | if is_master_project and _OPTIONS["tests"] == "on" then 21 | add_subdirs("tests") 22 | end 23 | 24 | package("find") 25 | set_name("coasync") 26 | set_version(project.version()) 27 | set_desc("A header-only async library") 28 | set_includedirs("include", {public = true}) 29 | set_links("coasync") 30 | 31 | rule("install") 32 | on("postbuild", function (target) 33 | os.cp("include/*", os.getenv("prefix") .. "/include") 34 | os.cp("cmake/coasync-config.cmake", os.getenv("prefix") .. "/lib/cmake/coasync") 35 | os.cp("coasync-config-version.cmake", os.getenv("prefix") .. "/lib/cmake/coasync") 36 | end) 37 | 38 | add_rules("install") 39 | --------------------------------------------------------------------------------