diff --git a/.clang-format b/.clang-format index d2edd510..83519c67 100644 --- a/.clang-format +++ b/.clang-format @@ -37,7 +37,7 @@ BraceWrapping: { BeforeCatch: true, BeforeElse: true, IndentBraces: false, - #SplitEmptyFunctionBody: false + SplitEmptyFunction: false } BreakBeforeInheritanceComma: true BreakBeforeTernaryOperators: true diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..14ed46a6 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,243 @@ +cmake_minimum_required(VERSION 3.11) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) + +include(CheckIncludeFileCXX) +macro(check_include_file_cxx _header _var) + _check_include_file_cxx(${_header} ${_var}) + if(NOT ${_var}) + set(${_var} 0) + endif() +endmacro() + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +project(cppcoro) + +set(${PROJECT_NAME}_PUBLIC_HEADERS + include/cppcoro/async_auto_reset_event.hpp + include/cppcoro/async_generator.hpp + include/cppcoro/async_latch.hpp + include/cppcoro/async_manual_reset_event.hpp + include/cppcoro/async_mutex.hpp + include/cppcoro/async_scope.hpp + include/cppcoro/awaitable_traits.hpp + include/cppcoro/broken_promise.hpp + include/cppcoro/cancellation_registration.hpp + include/cppcoro/cancellation_source.hpp + include/cppcoro/cancellation_token.hpp + include/cppcoro/config.hpp + + include/cppcoro/detail/any.hpp + include/cppcoro/detail/get_awaiter.hpp + include/cppcoro/detail/is_awaiter.hpp + include/cppcoro/detail/lightweight_manual_reset_event.hpp + include/cppcoro/detail/manual_lifetime.hpp + include/cppcoro/detail/remove_rvalue_reference.hpp + include/cppcoro/detail/sync_wait_task.hpp + include/cppcoro/detail/unwrap_reference.hpp + include/cppcoro/detail/void_value.hpp + include/cppcoro/detail/when_all_counter.hpp + include/cppcoro/detail/when_all_ready_awaitable.hpp + include/cppcoro/detail/when_all_task.hpp + + include/cppcoro/file.hpp + include/cppcoro/file_buffering_mode.hpp + include/cppcoro/file_open_mode.hpp + include/cppcoro/file_read_operation.hpp + include/cppcoro/file_share_mode.hpp + include/cppcoro/file_write_operation.hpp + include/cppcoro/fmap.hpp + include/cppcoro/generator.hpp + include/cppcoro/inline_scheduler.hpp + include/cppcoro/is_awaitable.hpp + include/cppcoro/multi_producer_sequencer.hpp + include/cppcoro/on_scope_exit.hpp + include/cppcoro/operation_cancelled.hpp + include/cppcoro/readable_file.hpp + include/cppcoro/read_only_file.hpp + include/cppcoro/read_write_file.hpp + include/cppcoro/recursive_generator.hpp + include/cppcoro/resume_on.hpp + include/cppcoro/round_robin_scheduler.hpp + include/cppcoro/schedule_on.hpp + include/cppcoro/sequence_barrier.hpp + include/cppcoro/sequence_range.hpp + include/cppcoro/sequence_traits.hpp + include/cppcoro/shared_task.hpp + include/cppcoro/single_consumer_async_auto_reset_event.hpp + include/cppcoro/single_consumer_event.hpp + include/cppcoro/single_producer_sequencer.hpp + include/cppcoro/static_thread_pool.hpp + include/cppcoro/sync_wait.hpp + include/cppcoro/task.hpp + include/cppcoro/when_all.hpp + include/cppcoro/when_all_ready.hpp + include/cppcoro/writable_file.hpp + include/cppcoro/write_only_file.hpp + + include/cppcoro/net/ipv4_address.hpp + include/cppcoro/net/ipv4_endpoint.hpp + include/cppcoro/net/ipv6_address.hpp + include/cppcoro/net/ipv6_endpoint.hpp + include/cppcoro/net/ip_address.hpp + include/cppcoro/net/ip_endpoint.hpp + include/cppcoro/io_service.hpp + + include/cppcoro/file.hpp + include/cppcoro/read_only_file.hpp + include/cppcoro/file_read_operation.hpp + include/cppcoro/readable_file.hpp + + include/cppcoro/net/socket.hpp + include/cppcoro/net/socket_accept_operation.hpp + include/cppcoro/net/socket_connect_operation.hpp + include/cppcoro/net/socket_disconnect_operation.hpp + include/cppcoro/net/socket_recv_from_operation.hpp + include/cppcoro/net/socket_recv_operation.hpp + include/cppcoro/net/socket_send_operation.hpp + include/cppcoro/net/socket_send_to_operation.hpp + ) + +set(${PROJECT_NAME}_SOURCES + lib/async_auto_reset_event.cpp + lib/async_manual_reset_event.cpp + lib/async_mutex.cpp + lib/auto_reset_event.cpp + lib/cancellation_registration.cpp + lib/cancellation_source.cpp + lib/cancellation_state.cpp + lib/cancellation_token.cpp + lib/lightweight_manual_reset_event.cpp + lib/spin_mutex.cpp + lib/spin_wait.cpp + lib/static_thread_pool.cpp + lib/ipv4_address.cpp + lib/ipv4_endpoint.cpp + lib/ipv6_address.cpp + lib/ipv6_endpoint.cpp + lib/ip_address.cpp + lib/ip_endpoint.cpp + lib/io_service.cpp + + lib/file.cpp + lib/read_only_file.cpp + lib/file_read_operation.cpp + lib/readable_file.cpp + lib/file_write_operation.cpp + lib/writable_file.cpp + lib/write_only_file.cpp + lib/read_write_file.cpp + + lib/socket.cpp + lib/socket_accept_operation.cpp + lib/socket_connect_operation.cpp + lib/socket_disconnect_operation.cpp + lib/socket_helpers.cpp + lib/socket_recv_from_operation.cpp + lib/socket_recv_operation.cpp + lib/socket_send_operation.cpp + lib/socket_send_to_operation.cpp + ) + + +if(WIN32) + list(APPEND ${PROJECT_NAME}_SOURCES + lib/win32.cpp + ) + + list(APPEND ${PROJECT_NAME}_PUBLIC_HEADERS + include/cppcoro/detail/win32.hpp + include/cppcoro/detail/win32_overlapped_operation.hpp + ) +else() + list(APPEND ${PROJECT_NAME}_SOURCES + lib/linux.cpp + ) + list(APPEND ${PROJECT_NAME}_PUBLIC_HEADERS + include/cppcoro/detail/linux.hpp + include/cppcoro/detail/linux_uring_operation.hpp + ) +endif() + +add_library(${PROJECT_NAME} ${${PROJECT_NAME}_SOURCES} ${${PROJECT_NAME}_PUBLIC_HEADERS}) +target_include_directories(${PROJECT_NAME} PUBLIC $) + +if(WIN32) + set(CMAKE_REQUIRED_FLAGS /std:c++latest /await) + target_link_libraries(${PROJECT_NAME} PUBLIC ws2_32 Synchronization) +elseif(CMAKE_CXX_COMPILER_ID MATCHES GNU) + set(CMAKE_REQUIRED_FLAGS -fcoroutines) +elseif(CMAKE_CXX_COMPILER_ID MATCHES Clang) + set(CMAKE_REQUIRED_FLAGS -stdlib=libc++ -fcoroutines-ts) +endif() +target_compile_options(${PROJECT_NAME} PUBLIC ${CMAKE_REQUIRED_FLAGS}) + +if(UNIX) + find_package(Threads REQUIRED) + include(geturing) + target_link_libraries(${PROJECT_NAME} PUBLIC Threads::Threads liburing::liburing) +endif() + +check_include_file_cxx(coroutine HAS_STD_COROUTINE_HEADER) +check_include_file_cxx(experimental/coroutine HAS_STD_EXPERIMENTAL_COROUTINES_HEADER) + +check_include_file_cxx(filesystem HAS_STD_FILESYSTEM_HEADER) +check_include_file_cxx(experimental/filesystem HAS_STD_EXPERIMENTAL_FILESYSTEM_HEADER) + +message(STATUS "HAS_STD_COROUTINE_HEADER: ${HAS_STD_COROUTINE_HEADER}") +message(STATUS "HAS_STD_EXPERIMENTAL_COROUTINES_HEADER: ${HAS_STD_EXPERIMENTAL_COROUTINES_HEADER}") +message(STATUS "HAS_STD_FILESYSTEM_HEADER: ${HAS_STD_FILESYSTEM_HEADER}") +message(STATUS "HAS_STD_EXPERIMENTAL_FILESYSTEM_HEADER: ${HAS_STD_EXPERIMENTAL_FILESYSTEM_HEADER}") + +target_compile_options(${PROJECT_NAME} PUBLIC + -DCPPCORO_HAS_STD_COROUTINE_HEADER=${HAS_STD_COROUTINE_HEADER} + ) +target_compile_options(${PROJECT_NAME} PUBLIC + -DCPPCORO_HAS_STD_EXPERIMENTAL_COROUTINES_HEADER=${HAS_STD_EXPERIMENTAL_COROUTINES_HEADER} + ) + +target_compile_options(${PROJECT_NAME} PUBLIC + -DCPPCORO_HAS_STD_FILESYSTEM_HEADER=${HAS_STD_FILESYSTEM_HEADER} + ) + +target_compile_options(${PROJECT_NAME} PUBLIC + -DCPPCORO_HAS_STD_EXPERIMENTAL_FILESYSTEM_HEADER=${HAS_STD_EXPERIMENTAL_FILESYSTEM_HEADER} + ) + +add_library(cppcoro::cppcoro ALIAS ${PROJECT_NAME}) + +enable_testing() +if(ENABLE_TESTING) + add_subdirectory(test) +endif() + +install(TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}-targets + ARCHIVE DESTINATION lib + ) +install(DIRECTORY include/ DESTINATION include) + +configure_file(cmake/cppcoroConfig.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/cppcoroConfig.cmake" + @ONLY + ) + +export(EXPORT ${PROJECT_NAME}-targets + FILE "${CMAKE_CURRENT_BINARY_DIR}/cppcoroTargets.cmake" + NAMESPACE cppcoro:: + ) + +set(CONFIG_PACKAGE_LOCATION lib/cmake/${PROJECT_NAME}) +install(EXPORT ${PROJECT_NAME}-targets + FILE cppcoroTargets.cmake + NAMESPACE cppcoro:: + DESTINATION ${CONFIG_PACKAGE_LOCATION} + ) +install( + FILES + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Findliburing.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/cppcoroConfig.cmake" + DESTINATION ${CONFIG_PACKAGE_LOCATION} +) diff --git a/README.md b/README.md index 70628521..a20448d2 100644 --- a/README.md +++ b/README.md @@ -714,7 +714,7 @@ namespace cppcoro { public: bool await_ready() const noexcept; - bool await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept; + bool await_suspend(stdcoro::coroutine_handle<> awaiter) noexcept; void await_resume() const noexcept; }; @@ -722,7 +722,7 @@ namespace cppcoro { public: bool await_ready() const noexcept; - bool await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept; + bool await_suspend(stdcoro::coroutine_handle<> awaiter) noexcept; [[nodiscard]] async_mutex_lock await_resume() const noexcept; }; @@ -836,7 +836,7 @@ namespace cppcoro async_manual_reset_event_operation(async_manual_reset_event& event) noexcept; bool await_ready() const noexcept; - bool await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept; + bool await_suspend(stdcoro::coroutine_handle<> awaiter) noexcept; void await_resume() const noexcept; }; } @@ -904,7 +904,7 @@ namespace cppcoro async_auto_reset_event_operation(const async_auto_reset_event_operation& other) noexcept; bool await_ready() const noexcept; - bool await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept; + bool await_suspend(stdcoro::coroutine_handle<> awaiter) noexcept; void await_resume() const noexcept; }; @@ -1389,7 +1389,7 @@ namespace cppcoro schedule_operation(static_thread_pool* tp) noexcept; bool await_ready() noexcept; - bool await_suspend(std::experimental::coroutine_handle<> h) noexcept; + bool await_suspend(stdcoro::coroutine_handle<> h) noexcept; bool await_resume() noexcept; private: @@ -1546,7 +1546,7 @@ namespace cppcoro schedule_operation& operator=(const schedule_operation&) noexcept; bool await_ready() const noexcept; - void await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept; + void await_suspend(stdcoro::coroutine_handle<> awaiter) noexcept; void await_resume() noexcept; }; @@ -1560,7 +1560,7 @@ namespace cppcoro timed_schedule_operation& operator=(timed_schedule_operation&&) = delete; bool await_ready() const noexcept; - void await_suspend(std::experimental::coroutine_handle<> awaiter); + void await_suspend(stdcoro::coroutine_handle<> awaiter); void await_resume(); }; @@ -1596,7 +1596,7 @@ Example: #include #include -namespace fs = std::experimental::filesystem; +namespace fs = stdcoro::filesystem; cppcoro::task count_lines(cppcoro::io_service& ioService, fs::path path) { @@ -1740,7 +1740,7 @@ namespace cppcoro file_read_operation(file_read_operation&& other) noexcept; bool await_ready() const noexcept; - bool await_suspend(std::experimental::coroutine_handle<> awaiter); + bool await_suspend(stdcoro::coroutine_handle<> awaiter); std::size_t await_resume(); }; @@ -1752,7 +1752,7 @@ namespace cppcoro file_write_operation(file_write_operation&& other) noexcept; bool await_ready() const noexcept; - bool await_suspend(std::experimental::coroutine_handle<> awaiter); + bool await_suspend(stdcoro::coroutine_handle<> awaiter); std::size_t await_resume(); }; @@ -1774,7 +1774,7 @@ namespace cppcoro [[nodiscard]] static read_only_file open( io_service& ioService, - const std::experimental::filesystem::path& path, + const stdcoro::filesystem::path& path, file_share_mode shareMode = file_share_mode::read, file_buffering_mode bufferingMode = file_buffering_mode::default_); @@ -1787,7 +1787,7 @@ namespace cppcoro [[nodiscard]] static write_only_file open( io_service& ioService, - const std::experimental::filesystem::path& path, + const stdcoro::filesystem::path& path, file_open_mode openMode = file_open_mode::create_or_open, file_share_mode shareMode = file_share_mode::none, file_buffering_mode bufferingMode = file_buffering_mode::default_); @@ -1801,7 +1801,7 @@ namespace cppcoro [[nodiscard]] static read_write_file open( io_service& ioService, - const std::experimental::filesystem::path& path, + const stdcoro::filesystem::path& path, file_open_mode openMode = file_open_mode::create_or_open, file_share_mode shareMode = file_share_mode::none, file_buffering_mode bufferingMode = file_buffering_mode::default_); @@ -2794,7 +2794,7 @@ coroutine. A type that satisfies `Awaiter` must have, for an instance of the type, `awaiter`: - `awaiter.await_ready()` -> `bool` -- `awaiter.await_suspend(std::experimental::coroutine_handle{})` -> `void` or `bool` or `std::experimental::coroutine_handle

` for some `P`. +- `awaiter.await_suspend(stdcoro::coroutine_handle{})` -> `void` or `bool` or `stdcoro::coroutine_handle

` for some `P`. - `awaiter.await_resume()` -> `T` Any type that implements the `Awaiter` concept also implements the `Awaitable` concept. @@ -3125,7 +3125,7 @@ ninja install-clang \ ### Building libc++ -The cppcoro project requires libc++ as it contains the `` +The cppcoro project requires libc++ as it contains the `` header required to use C++ coroutines under Clang. Checkout `libc++` + `llvm`: diff --git a/cmake/Findliburing.cmake b/cmake/Findliburing.cmake new file mode 100644 index 00000000..6f6c465b --- /dev/null +++ b/cmake/Findliburing.cmake @@ -0,0 +1,15 @@ +find_path(LIBURING_INCLUDE_DIR liburing.h + PATH_SUFFIXES liburing) + +find_library(LIBURING_LIBRARY NAMES uring) + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(liburing DEFAULT_MSG + LIBURING_LIBRARY LIBURING_INCLUDE_DIR) + +mark_as_advanced(LIBURING_LIBRARY LIBURING_INCLUDE_DIR) + +add_library(liburing::liburing INTERFACE IMPORTED GLOBAL) +target_link_libraries(liburing::liburing INTERFACE ${LIBURING_LIBRARY}) +target_include_directories(liburing::liburing INTERFACE ${LIBURING_INCLUDE_DIR}) diff --git a/cmake/cppcoroConfig.cmake.in b/cmake/cppcoroConfig.cmake.in new file mode 100644 index 00000000..645f7ddf --- /dev/null +++ b/cmake/cppcoroConfig.cmake.in @@ -0,0 +1,15 @@ +find_package(Threads REQUIRED) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") +get_filename_component(CPPCORO_INSTALL_PATH "${CMAKE_CURRENT_LIST_DIR}/../../.." ABSOLUTE) + +find_package(liburing) +if(NOT liburing_FOUND) + add_library(liburing INTERFACE IMPORTED GLOBAL) + target_link_libraries(liburing INTERFACE ${CPPCORO_INSTALL_PATH}/lib/liburing.a) + target_include_directories(liburing INTERFACE ${CPPCORO_INSTALL_PATH}/include) + add_library(liburing::liburing ALIAS liburing) +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/cppcoroTargets.cmake") +target_include_directories(cppcoro::cppcoro INTERFACE ${CPPCORO_INSTALL_PATH}/include) diff --git a/cmake/geturing.cmake b/cmake/geturing.cmake new file mode 100644 index 00000000..51679475 --- /dev/null +++ b/cmake/geturing.cmake @@ -0,0 +1,23 @@ +find_package(liburing) +if(NOT liburing_FOUND) + include(ExternalProject) + + set(LIBURING_TMP_INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/liburing) + externalproject_add(_fetch_uring + GIT_REPOSITORY https://github.com/axboe/liburing.git + GIT_TAG master + CONFIGURE_COMMAND ./configure --prefix=${LIBURING_TMP_INSTALL_DIR} + BUILD_COMMAND $(MAKE) + INSTALL_COMMAND $(MAKE) install + BUILD_IN_SOURCE ON + ) + execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${LIBURING_TMP_INSTALL_DIR}/include) + add_library(_liburing INTERFACE IMPORTED GLOBAL) + target_link_libraries(_liburing INTERFACE ${LIBURING_TMP_INSTALL_DIR}/lib/liburing.a) + target_include_directories(_liburing INTERFACE ${LIBURING_TMP_INSTALL_DIR}/include) + add_dependencies(_liburing _fetch_uring) + add_library(liburing::liburing ALIAS _liburing) + + install(FILES ${LIBURING_TMP_INSTALL_DIR}/lib/liburing.a DESTINATION lib) + install(DIRECTORY ${LIBURING_TMP_INSTALL_DIR}/include/ DESTINATION include) +endif() \ No newline at end of file diff --git a/include/cppcoro/async_auto_reset_event.hpp b/include/cppcoro/async_auto_reset_event.hpp index ce1f2d80..ca0f57a4 100644 --- a/include/cppcoro/async_auto_reset_event.hpp +++ b/include/cppcoro/async_auto_reset_event.hpp @@ -5,7 +5,8 @@ #ifndef CPPCORO_ASYNC_AUTO_RESET_EVENT_HPP_INCLUDED #define CPPCORO_ASYNC_AUTO_RESET_EVENT_HPP_INCLUDED -#include +#include + #include #include @@ -80,7 +81,7 @@ namespace cppcoro async_auto_reset_event_operation(const async_auto_reset_event_operation& other) noexcept; bool await_ready() const noexcept { return m_event == nullptr; } - bool await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept; + bool await_suspend(stdcoro::coroutine_handle<> awaiter) noexcept; void await_resume() const noexcept {} private: @@ -89,7 +90,7 @@ namespace cppcoro const async_auto_reset_event* m_event; async_auto_reset_event_operation* m_next; - std::experimental::coroutine_handle<> m_awaiter; + stdcoro::coroutine_handle<> m_awaiter; std::atomic m_refCount; }; diff --git a/include/cppcoro/async_generator.hpp b/include/cppcoro/async_generator.hpp index 4c2140da..2bd8caef 100644 --- a/include/cppcoro/async_generator.hpp +++ b/include/cppcoro/async_generator.hpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include @@ -45,7 +45,7 @@ namespace cppcoro async_generator_promise_base(const async_generator_promise_base& other) = delete; async_generator_promise_base& operator=(const async_generator_promise_base& other) = delete; - std::experimental::suspend_always initial_suspend() const noexcept + stdcoro::suspend_always initial_suspend() const noexcept { return {}; } @@ -89,7 +89,7 @@ namespace cppcoro std::exception_ptr m_exception; - std::experimental::coroutine_handle<> m_consumerCoroutine; + stdcoro::coroutine_handle<> m_consumerCoroutine; protected: @@ -100,7 +100,7 @@ namespace cppcoro { public: - async_generator_yield_operation(std::experimental::coroutine_handle<> consumer) noexcept + async_generator_yield_operation(stdcoro::coroutine_handle<> consumer) noexcept : m_consumer(consumer) {} @@ -109,8 +109,8 @@ namespace cppcoro return false; } - std::experimental::coroutine_handle<> - await_suspend([[maybe_unused]] std::experimental::coroutine_handle<> producer) noexcept + stdcoro::coroutine_handle<> + await_suspend([[maybe_unused]] stdcoro::coroutine_handle<> producer) noexcept { return m_consumer; } @@ -119,7 +119,7 @@ namespace cppcoro private: - std::experimental::coroutine_handle<> m_consumer; + stdcoro::coroutine_handle<> m_consumer; }; @@ -145,7 +145,7 @@ namespace cppcoro async_generator_advance_operation( async_generator_promise_base& promise, - std::experimental::coroutine_handle<> producerCoroutine) noexcept + stdcoro::coroutine_handle<> producerCoroutine) noexcept : m_promise(std::addressof(promise)) , m_producerCoroutine(producerCoroutine) { @@ -155,8 +155,8 @@ namespace cppcoro bool await_ready() const noexcept { return false; } - std::experimental::coroutine_handle<> - await_suspend(std::experimental::coroutine_handle<> consumerCoroutine) noexcept + stdcoro::coroutine_handle<> + await_suspend(stdcoro::coroutine_handle<> consumerCoroutine) noexcept { m_promise->m_consumerCoroutine = consumerCoroutine; return m_producerCoroutine; @@ -165,7 +165,7 @@ namespace cppcoro protected: async_generator_promise_base* m_promise; - std::experimental::coroutine_handle<> m_producerCoroutine; + stdcoro::coroutine_handle<> m_producerCoroutine; }; @@ -242,7 +242,7 @@ namespace cppcoro class async_generator_iterator final { using promise_type = async_generator_promise; - using handle_type = std::experimental::coroutine_handle; + using handle_type = stdcoro::coroutine_handle; public: @@ -307,7 +307,7 @@ namespace cppcoro class async_generator_begin_operation final : public async_generator_advance_operation { using promise_type = async_generator_promise; - using handle_type = std::experimental::coroutine_handle; + using handle_type = stdcoro::coroutine_handle; public: @@ -358,7 +358,7 @@ namespace cppcoro {} explicit async_generator(promise_type& promise) noexcept - : m_coroutine(std::experimental::coroutine_handle::from_promise(promise)) + : m_coroutine(stdcoro::coroutine_handle::from_promise(promise)) {} async_generator(async_generator&& other) noexcept @@ -408,7 +408,7 @@ namespace cppcoro private: - std::experimental::coroutine_handle m_coroutine; + stdcoro::coroutine_handle m_coroutine; }; @@ -451,7 +451,7 @@ namespace cppcoro async_generator_promise_base(const async_generator_promise_base& other) = delete; async_generator_promise_base& operator=(const async_generator_promise_base& other) = delete; - std::experimental::suspend_always initial_suspend() const noexcept + stdcoro::suspend_always initial_suspend() const noexcept { return {}; } @@ -556,7 +556,7 @@ namespace cppcoro std::exception_ptr m_exception; - std::experimental::coroutine_handle<> m_consumerCoroutine; + stdcoro::coroutine_handle<> m_consumerCoroutine; protected: @@ -579,7 +579,7 @@ namespace cppcoro return m_initialState == state::value_not_ready_consumer_suspended; } - bool await_suspend(std::experimental::coroutine_handle<> producer) noexcept; + bool await_suspend(stdcoro::coroutine_handle<> producer) noexcept; void await_resume() noexcept {} @@ -625,7 +625,7 @@ namespace cppcoro } inline bool async_generator_yield_operation::await_suspend( - std::experimental::coroutine_handle<> producer) noexcept + stdcoro::coroutine_handle<> producer) noexcept { state currentState = m_initialState; if (currentState == state::value_not_ready_consumer_active) @@ -711,7 +711,7 @@ namespace cppcoro async_generator_advance_operation( async_generator_promise_base& promise, - std::experimental::coroutine_handle<> producerCoroutine) noexcept + stdcoro::coroutine_handle<> producerCoroutine) noexcept : m_promise(std::addressof(promise)) , m_producerCoroutine(producerCoroutine) { @@ -740,7 +740,7 @@ namespace cppcoro return m_initialState == state::value_ready_producer_suspended; } - bool await_suspend(std::experimental::coroutine_handle<> consumerCoroutine) noexcept + bool await_suspend(stdcoro::coroutine_handle<> consumerCoroutine) noexcept { m_promise->m_consumerCoroutine = consumerCoroutine; @@ -791,7 +791,7 @@ namespace cppcoro protected: async_generator_promise_base* m_promise; - std::experimental::coroutine_handle<> m_producerCoroutine; + stdcoro::coroutine_handle<> m_producerCoroutine; private: @@ -872,7 +872,7 @@ namespace cppcoro class async_generator_iterator final { using promise_type = async_generator_promise; - using handle_type = std::experimental::coroutine_handle; + using handle_type = stdcoro::coroutine_handle; public: @@ -937,7 +937,7 @@ namespace cppcoro class async_generator_begin_operation final : public async_generator_advance_operation { using promise_type = async_generator_promise; - using handle_type = std::experimental::coroutine_handle; + using handle_type = stdcoro::coroutine_handle; public: @@ -988,7 +988,7 @@ namespace cppcoro {} explicit async_generator(promise_type& promise) noexcept - : m_coroutine(std::experimental::coroutine_handle::from_promise(promise)) + : m_coroutine(stdcoro::coroutine_handle::from_promise(promise)) {} async_generator(async_generator&& other) noexcept @@ -1041,7 +1041,7 @@ namespace cppcoro private: - std::experimental::coroutine_handle m_coroutine; + stdcoro::coroutine_handle m_coroutine; }; diff --git a/include/cppcoro/async_manual_reset_event.hpp b/include/cppcoro/async_manual_reset_event.hpp index 5f20688e..aaca0648 100644 --- a/include/cppcoro/async_manual_reset_event.hpp +++ b/include/cppcoro/async_manual_reset_event.hpp @@ -5,7 +5,7 @@ #ifndef CPPCORO_ASYNC_MANUAL_RESET_EVENT_HPP_INCLUDED #define CPPCORO_ASYNC_MANUAL_RESET_EVENT_HPP_INCLUDED -#include +#include #include #include @@ -87,7 +87,7 @@ namespace cppcoro explicit async_manual_reset_event_operation(const async_manual_reset_event& event) noexcept; bool await_ready() const noexcept; - bool await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept; + bool await_suspend(stdcoro::coroutine_handle<> awaiter) noexcept; void await_resume() const noexcept {} private: @@ -96,7 +96,7 @@ namespace cppcoro const async_manual_reset_event& m_event; async_manual_reset_event_operation* m_next; - std::experimental::coroutine_handle<> m_awaiter; + stdcoro::coroutine_handle<> m_awaiter; }; } diff --git a/include/cppcoro/async_mutex.hpp b/include/cppcoro/async_mutex.hpp index cc193112..e92a10e4 100644 --- a/include/cppcoro/async_mutex.hpp +++ b/include/cppcoro/async_mutex.hpp @@ -5,7 +5,7 @@ #ifndef CPPCORO_ASYNC_MUTEX_HPP_INCLUDED #define CPPCORO_ASYNC_MUTEX_HPP_INCLUDED -#include +#include #include #include #include // for std::adopt_lock_t @@ -166,7 +166,7 @@ namespace cppcoro {} bool await_ready() const noexcept { return false; } - bool await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept; + bool await_suspend(stdcoro::coroutine_handle<> awaiter) noexcept; void await_resume() const noexcept {} protected: @@ -178,7 +178,7 @@ namespace cppcoro private: async_mutex_lock_operation* m_next; - std::experimental::coroutine_handle<> m_awaiter; + stdcoro::coroutine_handle<> m_awaiter; }; diff --git a/include/cppcoro/async_scope.hpp b/include/cppcoro/async_scope.hpp index c46b8c6e..46f85548 100644 --- a/include/cppcoro/async_scope.hpp +++ b/include/cppcoro/async_scope.hpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include @@ -52,7 +52,7 @@ namespace cppcoro return m_scope->m_count.load(std::memory_order_acquire) == 0; } - bool await_suspend(std::experimental::coroutine_handle<> continuation) noexcept + bool await_suspend(stdcoro::coroutine_handle<> continuation) noexcept { m_scope->m_continuation = continuation; return m_scope->m_count.fetch_sub(1u, std::memory_order_acq_rel) > 1u; @@ -85,8 +85,8 @@ namespace cppcoro { struct promise_type { - std::experimental::suspend_never initial_suspend() { return {}; } - std::experimental::suspend_never final_suspend() { return {}; } + stdcoro::suspend_never initial_suspend() { return {}; } + stdcoro::suspend_never final_suspend() { return {}; } void unhandled_exception() { std::terminate(); } oneway_task get_return_object() { return {}; } void return_void() {} @@ -94,7 +94,7 @@ namespace cppcoro }; std::atomic m_count; - std::experimental::coroutine_handle<> m_continuation; + stdcoro::coroutine_handle<> m_continuation; }; } diff --git a/include/cppcoro/cancellation_registration.hpp b/include/cppcoro/cancellation_registration.hpp index 79eab909..64d267b5 100644 --- a/include/cppcoro/cancellation_registration.hpp +++ b/include/cppcoro/cancellation_registration.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace cppcoro { diff --git a/include/cppcoro/detail/is_awaiter.hpp b/include/cppcoro/detail/is_awaiter.hpp index 1538c8f3..3f140cc5 100644 --- a/include/cppcoro/detail/is_awaiter.hpp +++ b/include/cppcoro/detail/is_awaiter.hpp @@ -6,7 +6,7 @@ #define CPPCORO_DETAIL_IS_AWAITER_HPP_INCLUDED #include -#include +#include namespace cppcoro { @@ -18,7 +18,7 @@ namespace cppcoro {}; template - struct is_coroutine_handle> + struct is_coroutine_handle> : std::true_type {}; @@ -42,12 +42,12 @@ namespace cppcoro template struct is_awaiter().await_ready()), - decltype(std::declval().await_suspend(std::declval>())), + decltype(std::declval().await_suspend(std::declval>())), decltype(std::declval().await_resume())>> : std::conjunction< std::is_constructible().await_ready())>, detail::is_valid_await_suspend_return_value< - decltype(std::declval().await_suspend(std::declval>()))>> + decltype(std::declval().await_suspend(std::declval>()))>> {}; } } diff --git a/include/cppcoro/detail/linux.hpp b/include/cppcoro/detail/linux.hpp new file mode 100644 index 00000000..811a53cf --- /dev/null +++ b/include/cppcoro/detail/linux.hpp @@ -0,0 +1,137 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Lewis Baker +// Licenced under MIT license. See LICENSE.txt for details. +/////////////////////////////////////////////////////////////////////////////// +#ifndef CPPCORO_DETAIL_LINUX_HPP_INCLUDED +#define CPPCORO_DETAIL_LINUX_HPP_INCLUDED + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace cppcoro +{ + namespace detail + { + namespace lnx + { + using fd_t = int; + + enum message_type + { + CALLBACK_TYPE, + RESUME_TYPE + }; + + void check_required_kernel(int major, int minor, std::string_view message); + + class safe_fd + { + public: + safe_fd() + : m_fd(-1) + { + } + + explicit safe_fd(fd_t fd) + : m_fd(fd) + { + } + + safe_fd(const safe_fd& other) = delete; + + safe_fd(safe_fd&& other) noexcept + : m_fd(other.m_fd) + { + other.m_fd = -1; + } + + ~safe_fd() { close(); } + + safe_fd& operator=(safe_fd fd) noexcept + { + swap(fd); + return *this; + } + + constexpr fd_t fd() const { return m_fd; } + constexpr fd_t handle() const { return m_fd; } + + /// Calls close() and sets the fd to -1. + void close() noexcept; + + void swap(safe_fd& other) noexcept { std::swap(m_fd, other.m_fd); } + + bool operator==(const safe_fd& other) const { return m_fd == other.m_fd; } + + bool operator!=(const safe_fd& other) const { return m_fd != other.m_fd; } + + bool operator==(fd_t fd) const { return m_fd == fd; } + + bool operator!=(fd_t fd) const { return m_fd != fd; } + + private: + fd_t m_fd; + }; + + struct message + { + enum message_type m_type; + void* m_ptr; + int m_result; + }; + + struct io_state : lnx::message + { + using callback_type = void(io_state* state); + callback_type* m_callback; + }; + + class uring_queue { + public: + explicit uring_queue(size_t queue_length = 32, uint32_t flags = 0); + ~uring_queue() noexcept; + uring_queue(uring_queue&&) = delete; + uring_queue& operator=(uring_queue&&) = delete; + uring_queue(uring_queue const&) = delete; + uring_queue& operator=(uring_queue const&) = delete; + bool dequeue(void*& message, message_type& type, bool wait); + struct io_uring *handle() { return &ring_; } + int submit() noexcept; + io_uring_sqe *get_sqe() noexcept; + + private: + std::mutex m_inMux; + std::mutex m_outMux; + io_uring ring_{}; + }; + } // namespace linux + + using safe_handle = lnx::safe_fd; + using dword_t = int; + struct sock_buf { + sock_buf(void *buf, size_t sz) : buffer(buf), size(sz) {} + void * buffer; + size_t size; + }; + using handle_t = lnx::fd_t; + } // namespace detail +} // namespace cppcoro + +#endif diff --git a/include/cppcoro/detail/linux_uring_operation.hpp b/include/cppcoro/detail/linux_uring_operation.hpp new file mode 100644 index 00000000..03680540 --- /dev/null +++ b/include/cppcoro/detail/linux_uring_operation.hpp @@ -0,0 +1,377 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Lewis Baker +// Licenced under MIT license. See LICENSE.txt for details. +/////////////////////////////////////////////////////////////////////////////// +#ifndef CPPCORO_DETAIL_LINUX_URING_OPERATION_HPP_INCLUDED +#define CPPCORO_DETAIL_LINUX_URING_OPERATION_HPP_INCLUDED + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace cppcoro +{ + namespace detail + { + class uring_operation_base + { + void submitt(io_uring_sqe* sqe) + { + m_message.m_ptr = m_awaitingCoroutine.address(); + io_uring_sqe_set_data(sqe, &m_message); + m_ioService.submit(); + } + + public: + uring_operation_base(io_service& ioService, size_t offset = 0) noexcept + : m_ioService(ioService) + , m_offset(offset) + , m_message{ detail::lnx::message_type::RESUME_TYPE, nullptr, -1 } + { + } + + bool try_start_read(int fd, void* buffer, size_t size) noexcept + { + m_vec.iov_base = buffer; + m_vec.iov_len = size; + auto sqe = m_ioService.get_sqe(); + io_uring_prep_readv(sqe, fd, &m_vec, 1, m_offset); + submitt(sqe); + return true; + } + + bool try_start_write(int fd, const void* buffer, size_t size) noexcept + { + m_vec.iov_base = const_cast(buffer); + m_vec.iov_len = size; + auto sqe = m_ioService.get_sqe(); + io_uring_prep_writev(sqe, fd, &m_vec, 1, m_offset); + submitt(sqe); + return true; + } + + bool try_start_send(int fd, const void* buffer, size_t size) noexcept + { + auto sqe = m_ioService.get_sqe(); + io_uring_prep_send(sqe, fd, buffer, size, 0); + submitt(sqe); + return true; + } + + bool try_start_sendto( + int fd, const void* to, size_t to_size, void* buffer, size_t size) noexcept + { + m_vec.iov_base = buffer; + m_vec.iov_len = size; + std::memset(&m_msghdr, 0, sizeof(m_msghdr)); + m_msghdr.msg_name = const_cast(to); + m_msghdr.msg_namelen = to_size; + m_msghdr.msg_iov = &m_vec; + m_msghdr.msg_iovlen = 1; + auto sqe = m_ioService.get_sqe(); + io_uring_prep_sendmsg(sqe, fd, &m_msghdr, 0); + submitt(sqe); + return true; + } + + bool try_start_recv(int fd, void* buffer, size_t size, int flags) noexcept + { + auto sqe = m_ioService.get_sqe(); + io_uring_prep_recv(sqe, fd, buffer, size, flags); + submitt(sqe); + return true; + } + + bool try_start_recvfrom( + int fd, void* from, size_t from_size, void* buffer, size_t size, int flags) noexcept + { + m_vec.iov_base = buffer; + m_vec.iov_len = size; + std::memset(&m_msghdr, 0, sizeof(m_msghdr)); + m_msghdr.msg_name = from; + m_msghdr.msg_namelen = from_size; + m_msghdr.msg_iov = &m_vec; + m_msghdr.msg_iovlen = 1; + auto sqe = m_ioService.get_sqe(); + io_uring_prep_recvmsg(sqe, fd, &m_msghdr, flags); + submitt(sqe); + return true; + } + + bool try_start_connect(int fd, const void* to, size_t to_size) noexcept + { + auto sqe = m_ioService.get_sqe(); + io_uring_prep_connect( + sqe, fd, reinterpret_cast(const_cast(to)), to_size); + submitt(sqe); + return true; + } + + bool try_start_disconnect(int fd) noexcept + { + auto sqe = m_ioService.get_sqe(); + io_uring_prep_close(sqe, fd); + submitt(sqe); + return true; + } + + bool try_start_accept(int fd, const void* to, socklen_t* to_size) noexcept + { + auto sqe = m_ioService.get_sqe(); + io_uring_prep_accept( + sqe, fd, reinterpret_cast(const_cast(to)), to_size, 0); + submitt(sqe); + return true; + } + + bool cancel_io() + { + auto sqe = m_ioService.get_sqe(); + io_uring_prep_cancel(sqe, &m_message, 0); + m_ioService.submit(); + return true; + } + + std::size_t get_result() + { + if (m_message.m_result < 0) + { + throw std::system_error{ -m_message.m_result, std::system_category() }; + } + + return m_message.m_result; + } + + size_t m_offset; + stdcoro::coroutine_handle<> m_awaitingCoroutine; + iovec m_vec; + msghdr m_msghdr; + detail::lnx::message m_message; + io_service& m_ioService; + }; + + template + class uring_operation : protected uring_operation_base + { + protected: + uring_operation(io_service& ioService, size_t offset = 0) noexcept + : uring_operation_base(ioService, offset) + { + } + + public: + bool await_ready() const noexcept { return false; } + + CPPCORO_NOINLINE + bool await_suspend(stdcoro::coroutine_handle<> awaitingCoroutine) + { + static_assert(std::is_base_of_v); + + m_awaitingCoroutine = awaitingCoroutine; + return static_cast(this)->try_start(); + } + + decltype(auto) await_resume() { return static_cast(this)->get_result(); } + }; + + template + class uring_operation_cancellable : protected uring_operation_base + { + // ERROR_OPERATION_ABORTED value from + static constexpr int error_operation_aborted = -ECANCELED; + + protected: + uring_operation_cancellable(io_service& ioService, cancellation_token&& ct) noexcept + : uring_operation_base(ioService, 0) + , m_state(ct.is_cancellation_requested() ? state::completed : state::not_started) + , m_cancellationToken(std::move(ct)) + { + m_message.m_result = error_operation_aborted; + } + + uring_operation_cancellable( + io_service& ioService, size_t offset, cancellation_token&& ct) noexcept + : uring_operation_base(ioService, offset) + , m_state(ct.is_cancellation_requested() ? state::completed : state::not_started) + , m_cancellationToken(std::move(ct)) + { + m_message.m_result = error_operation_aborted; + } + + public: + bool await_ready() const noexcept + { + return m_state.load(std::memory_order_relaxed) == state::completed; + } + + CPPCORO_NOINLINE + bool await_suspend(stdcoro::coroutine_handle<> awaitingCoroutine) + { + static_assert(std::is_base_of_v); + + m_awaitingCoroutine = awaitingCoroutine; + + // TRICKY: Register cancellation callback before starting the operation + // in case the callback registration throws due to insufficient + // memory. We need to make sure that the logic that occurs after + // starting the operation is noexcept, otherwise we run into the + // problem of not being able to cancel the started operation and + // the dilemma of what to do with the exception. + // + // However, doing this means that the cancellation callback may run + // prior to returning below so in the case that cancellation may + // occur we defer setting the state to 'started' until after + // the operation has finished starting. The cancellation callback + // will only attempt to request cancellation of the operation with + // CancelIoEx() once the state has been set to 'started'. + if (m_cancellationToken.is_cancellation_requested()) + { + return false; + } + + const bool canBeCancelled = m_cancellationToken.can_be_cancelled(); + m_state.store(state::started, std::memory_order_relaxed); + + // Now start the operation. + const bool willCompleteAsynchronously = static_cast(this)->try_start(); + if (!willCompleteAsynchronously) + { + // Operation completed synchronously, resume awaiting coroutine immediately. + return false; + } + + if (canBeCancelled) + { + // Need to flag that the operation has finished starting now. + + // However, the operation may have completed concurrently on + // another thread, transitioning directly from not_started -> complete. + // Or it may have had the cancellation callback execute and transition + // from not_started -> cancellation_requested. We use a compare-exchange + // to determine a winner between these potential racing cases. + state oldState = state::not_started; + if (!m_state.compare_exchange_strong( + oldState, + state::started, + std::memory_order_release, + std::memory_order_acquire)) + { + if (oldState == state::cancellation_requested) + { + // Request the operation be cancelled. + // Note that it may have already completed on a background + // thread by now so this request for cancellation may end up + // being ignored. + static_cast(this)->cancel(); + + if (!m_state.compare_exchange_strong( + oldState, + state::started, + std::memory_order_release, + std::memory_order_acquire)) + { + assert(oldState == state::completed); + return false; + } + } + else + { + m_cancellationRegistration.emplace( + std::move(m_cancellationToken), [this] { + m_state.store( + state::cancellation_requested, std::memory_order_acquire); + static_cast(this)->cancel(); + }); + assert(oldState == state::started); + return true; + } + } + } + + return true; + } + + decltype(auto) await_resume() + { + if (m_message.m_result == error_operation_aborted) + { + throw operation_cancelled{}; + } + else if (m_message.m_result < 0) + { + if (m_message.m_result == -EINTR && + m_state.load(std::memory_order_acquire) == state::cancellation_requested) + { + throw operation_cancelled{}; + } + throw std::system_error{ -m_message.m_result, std::system_category() }; + } + + return static_cast(this)->get_result(); + } + + private: + enum class state + { + not_started, + started, + cancellation_requested, + completed + }; + + void on_cancellation_requested() noexcept + { + auto oldState = m_state.load(std::memory_order_acquire); + if (oldState == state::not_started) + { + // This callback is running concurrently with await_suspend(). + // The call to start the operation may not have returned yet so + // we can't safely request cancellation of it. Instead we try to + // notify the await_suspend() thread by transitioning the state + // to state::cancellation_requested so that the await_suspend() + // thread can request cancellation after it has finished starting + // the operation. + const bool transferredCancelResponsibility = m_state.compare_exchange_strong( + oldState, + state::cancellation_requested, + std::memory_order_release, + std::memory_order_acquire); + if (transferredCancelResponsibility) + { + return; + } + } + + // No point requesting cancellation if the operation has already completed. + if (oldState != state::completed) + { + static_cast(this)->cancel(); + } + } + + std::atomic m_state; + cppcoro::cancellation_token m_cancellationToken; + std::optional m_cancellationRegistration; + }; + + using io_operation_base = uring_operation_base; + + template + using io_operation = uring_operation; + + template + using io_operation_cancellable = uring_operation_cancellable; + } // namespace detail +} // namespace cppcoro + +#endif // CPPCORO_DETAIL_LINUX_URING_OPERATION_HPP_INCLUDED diff --git a/include/cppcoro/detail/stdcoro.hpp b/include/cppcoro/detail/stdcoro.hpp new file mode 100644 index 00000000..e0f20de8 --- /dev/null +++ b/include/cppcoro/detail/stdcoro.hpp @@ -0,0 +1,24 @@ +#ifndef CPPCORO_STDCORO_HPP_INCLUDED +#define CPPCORO_STDCORO_HPP_INCLUDED + +#if CPPCORO_HAS_STD_COROUTINE_HEADER +#include +namespace stdcoro = std; +#elif CPPCORO_HAS_STD_EXPERIMENTAL_COROUTINES_HEADER +#include +namespace stdcoro = std::experimental; +#else +#error "Coroutines are not supported" +#endif + +#if CPPCORO_HAS_STD_FILESYSTEM_HEADER +#include +namespace stdfs = std::filesystem; +#elif CPPCORO_HAS_STD_EXPERIMENTAL_FILESYSTEM_HEADER +#include +namespace stdfs = std::experimental::filesystem; +#else +#error "Filesystem is not supported" +#endif + +#endif // CPPCORO_STDCORO_HPP_INCLUDED diff --git a/include/cppcoro/detail/sync_wait_task.hpp b/include/cppcoro/detail/sync_wait_task.hpp index f36b6f8f..32cdf1aa 100644 --- a/include/cppcoro/detail/sync_wait_task.hpp +++ b/include/cppcoro/detail/sync_wait_task.hpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include @@ -23,7 +23,7 @@ namespace cppcoro template class sync_wait_task_promise final { - using coroutine_handle_t = std::experimental::coroutine_handle>; + using coroutine_handle_t = stdcoro::coroutine_handle>; public: @@ -43,7 +43,7 @@ namespace cppcoro return coroutine_handle_t::from_promise(*this); } - std::experimental::suspend_always initial_suspend() noexcept + stdcoro::suspend_always initial_suspend() noexcept { return{}; } @@ -88,7 +88,7 @@ namespace cppcoro bool await_ready() noexcept { return true; } - void await_suspend(std::experimental::coroutine_handle<>) noexcept {} + void await_suspend(stdcoro::coroutine_handle<>) noexcept {} sync_wait_task_promise& await_resume() noexcept { return *m_promise; @@ -139,7 +139,7 @@ namespace cppcoro template<> class sync_wait_task_promise { - using coroutine_handle_t = std::experimental::coroutine_handle>; + using coroutine_handle_t = stdcoro::coroutine_handle>; public: @@ -157,7 +157,7 @@ namespace cppcoro return coroutine_handle_t::from_promise(*this); } - std::experimental::suspend_always initial_suspend() noexcept + stdcoro::suspend_always initial_suspend() noexcept { return{}; } @@ -210,7 +210,7 @@ namespace cppcoro using promise_type = sync_wait_task_promise; - using coroutine_handle_t = std::experimental::coroutine_handle; + using coroutine_handle_t = stdcoro::coroutine_handle; sync_wait_task(coroutine_handle_t coroutine) noexcept : m_coroutine(coroutine) diff --git a/include/cppcoro/detail/when_all_counter.hpp b/include/cppcoro/detail/when_all_counter.hpp index 5ed8d888..37bd145a 100644 --- a/include/cppcoro/detail/when_all_counter.hpp +++ b/include/cppcoro/detail/when_all_counter.hpp @@ -5,7 +5,7 @@ #ifndef CPPCORO_DETAIL_WHEN_ALL_COUNTER_HPP_INCLUDED #define CPPCORO_DETAIL_WHEN_ALL_COUNTER_HPP_INCLUDED -#include +#include #include #include @@ -29,7 +29,7 @@ namespace cppcoro return static_cast(m_awaitingCoroutine); } - bool try_await(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept + bool try_await(stdcoro::coroutine_handle<> awaitingCoroutine) noexcept { m_awaitingCoroutine = awaitingCoroutine; return m_count.fetch_sub(1, std::memory_order_acq_rel) > 1; @@ -46,7 +46,7 @@ namespace cppcoro protected: std::atomic m_count; - std::experimental::coroutine_handle<> m_awaitingCoroutine; + stdcoro::coroutine_handle<> m_awaitingCoroutine; }; } diff --git a/include/cppcoro/detail/when_all_ready_awaitable.hpp b/include/cppcoro/detail/when_all_ready_awaitable.hpp index 2fe80c68..344761fa 100644 --- a/include/cppcoro/detail/when_all_ready_awaitable.hpp +++ b/include/cppcoro/detail/when_all_ready_awaitable.hpp @@ -7,7 +7,7 @@ #include -#include +#include #include namespace cppcoro @@ -26,7 +26,7 @@ namespace cppcoro explicit constexpr when_all_ready_awaitable(std::tuple<>) noexcept {} constexpr bool await_ready() const noexcept { return true; } - void await_suspend(std::experimental::coroutine_handle<>) noexcept {} + void await_suspend(stdcoro::coroutine_handle<>) noexcept {} std::tuple<> await_resume() const noexcept { return {}; } }; @@ -66,7 +66,7 @@ namespace cppcoro return m_awaitable.is_ready(); } - bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept + bool await_suspend(stdcoro::coroutine_handle<> awaitingCoroutine) noexcept { return m_awaitable.try_await(awaitingCoroutine); } @@ -98,7 +98,7 @@ namespace cppcoro return m_awaitable.is_ready(); } - bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept + bool await_suspend(stdcoro::coroutine_handle<> awaitingCoroutine) noexcept { return m_awaitable.try_await(awaitingCoroutine); } @@ -124,7 +124,7 @@ namespace cppcoro return m_counter.is_ready(); } - bool try_await(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept + bool try_await(stdcoro::coroutine_handle<> awaitingCoroutine) noexcept { start_tasks(std::make_integer_sequence{}); return m_counter.try_await(awaitingCoroutine); @@ -177,7 +177,7 @@ namespace cppcoro return m_awaitable.is_ready(); } - bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept + bool await_suspend(stdcoro::coroutine_handle<> awaitingCoroutine) noexcept { return m_awaitable.try_await(awaitingCoroutine); } @@ -212,7 +212,7 @@ namespace cppcoro return m_awaitable.is_ready(); } - bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept + bool await_suspend(stdcoro::coroutine_handle<> awaitingCoroutine) noexcept { return m_awaitable.try_await(awaitingCoroutine); } @@ -238,7 +238,7 @@ namespace cppcoro return m_counter.is_ready(); } - bool try_await(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept + bool try_await(stdcoro::coroutine_handle<> awaitingCoroutine) noexcept { for (auto&& task : m_tasks) { diff --git a/include/cppcoro/detail/when_all_task.hpp b/include/cppcoro/detail/when_all_task.hpp index abaff33b..3803f2d2 100644 --- a/include/cppcoro/detail/when_all_task.hpp +++ b/include/cppcoro/detail/when_all_task.hpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include namespace cppcoro @@ -28,7 +28,7 @@ namespace cppcoro { public: - using coroutine_handle_t = std::experimental::coroutine_handle>; + using coroutine_handle_t = stdcoro::coroutine_handle>; when_all_task_promise() noexcept {} @@ -38,7 +38,7 @@ namespace cppcoro return coroutine_handle_t::from_promise(*this); } - std::experimental::suspend_always initial_suspend() noexcept + stdcoro::suspend_always initial_suspend() noexcept { return{}; } @@ -97,7 +97,7 @@ namespace cppcoro bool await_ready() noexcept { return true; } - void await_suspend(std::experimental::coroutine_handle<>) noexcept {} + void await_suspend(stdcoro::coroutine_handle<>) noexcept {} when_all_task_promise& await_resume() noexcept { return *m_promise; @@ -155,7 +155,7 @@ namespace cppcoro { public: - using coroutine_handle_t = std::experimental::coroutine_handle>; + using coroutine_handle_t = stdcoro::coroutine_handle>; when_all_task_promise() noexcept {} @@ -165,7 +165,7 @@ namespace cppcoro return coroutine_handle_t::from_promise(*this); } - std::experimental::suspend_always initial_suspend() noexcept + stdcoro::suspend_always initial_suspend() noexcept { return{}; } diff --git a/include/cppcoro/detail/win32.hpp b/include/cppcoro/detail/win32.hpp index a1e68e35..0d7ab850 100644 --- a/include/cppcoro/detail/win32.hpp +++ b/include/cppcoro/detail/win32.hpp @@ -164,16 +164,18 @@ namespace cppcoro bool operator!=(handle_t handle) const { - return m_handle != handle; - } + return m_handle != handle; } private: - handle_t m_handle; - }; - } - } + } // namespace win32 + + using dword_t = win32::dword_t; + using handle_t = win32::handle_t; + using sock_buf = win32::wsabuf; + using safe_handle = win32::safe_handle; + } // namespace detail } #endif diff --git a/include/cppcoro/detail/win32_overlapped_operation.hpp b/include/cppcoro/detail/win32_overlapped_operation.hpp index b921aa15..0bd77218 100644 --- a/include/cppcoro/detail/win32_overlapped_operation.hpp +++ b/include/cppcoro/detail/win32_overlapped_operation.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include namespace cppcoro @@ -100,7 +100,7 @@ namespace cppcoro bool await_ready() const noexcept { return false; } CPPCORO_NOINLINE - bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) + bool await_suspend(stdcoro::coroutine_handle<> awaitingCoroutine) { static_assert(std::is_base_of_v); @@ -127,7 +127,7 @@ namespace cppcoro operation->m_awaitingCoroutine.resume(); } - std::experimental::coroutine_handle<> m_awaitingCoroutine; + stdcoro::coroutine_handle<> m_awaitingCoroutine; }; @@ -186,7 +186,7 @@ namespace cppcoro } CPPCORO_NOINLINE - bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) + bool await_suspend(stdcoro::coroutine_handle<> awaitingCoroutine) { static_assert(std::is_base_of_v); @@ -367,10 +367,17 @@ namespace cppcoro std::atomic m_state; cppcoro::cancellation_token m_cancellationToken; std::optional m_cancellationCallback; - std::experimental::coroutine_handle<> m_awaitingCoroutine; - + stdcoro::coroutine_handle<> m_awaitingCoroutine; }; - } + + using io_operation_base = win32_overlapped_operation_base; + + template + using io_operation = win32_overlapped_operation; + + template + using io_operation_cancellable = win32_overlapped_operation_cancellable; + } // namespace detail } #endif diff --git a/include/cppcoro/file.hpp b/include/cppcoro/file.hpp index aa507537..3a447bbd 100644 --- a/include/cppcoro/file.hpp +++ b/include/cppcoro/file.hpp @@ -7,16 +7,17 @@ #include +#include #include #include #include #if CPPCORO_OS_WINNT # include +#else +# include #endif -#include - namespace cppcoro { class io_service; @@ -30,24 +31,24 @@ namespace cppcoro virtual ~file(); /// Get the size of the file in bytes. - std::uint64_t size() const; + std::size_t size() const; protected: -#if CPPCORO_OS_WINNT - file(detail::win32::safe_handle&& fileHandle) noexcept; + explicit file(detail::safe_handle&& fileHandle) noexcept; - static detail::win32::safe_handle open( - detail::win32::dword_t fileAccess, + static detail::safe_handle open( + detail::dword_t fileAccess, io_service& ioService, - const std::experimental::filesystem::path& path, + const stdfs::path& path, file_open_mode openMode, file_share_mode shareMode, file_buffering_mode bufferingMode); - detail::win32::safe_handle m_fileHandle; + detail::safe_handle m_fileHandle; +#ifdef CPPCORO_OS_LINUX + io_service *m_ioService; #endif - }; } diff --git a/include/cppcoro/file_read_operation.hpp b/include/cppcoro/file_read_operation.hpp index 509d4547..1c822b21 100644 --- a/include/cppcoro/file_read_operation.hpp +++ b/include/cppcoro/file_read_operation.hpp @@ -9,92 +9,99 @@ #include #include +#include #include #include -#include #if CPPCORO_OS_WINNT -# include -# include +#include +#include +#elif CPPCORO_OS_LINUX + +#include + +#endif namespace cppcoro { class file_read_operation_impl { public: - file_read_operation_impl( - detail::win32::handle_t fileHandle, - void* buffer, - std::size_t byteCount) noexcept + detail::handle_t fileHandle, void* buffer, std::size_t byteCount) noexcept : m_fileHandle(fileHandle) , m_buffer(buffer) , m_byteCount(byteCount) {} - bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; - void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; + bool try_start(cppcoro::detail::io_operation_base& operation) noexcept; + void cancel(cppcoro::detail::io_operation_base& operation) noexcept; private: - - detail::win32::handle_t m_fileHandle; + detail::handle_t m_fileHandle; void* m_buffer; std::size_t m_byteCount; - }; - class file_read_operation - : public cppcoro::detail::win32_overlapped_operation + class file_read_operation : public cppcoro::detail::io_operation { public: - file_read_operation( - detail::win32::handle_t fileHandle, +#if CPPCORO_OS_LINUX + io_service& ioService, +#endif + detail::handle_t fileHandle, std::uint64_t fileOffset, void* buffer, std::size_t byteCount) noexcept - : cppcoro::detail::win32_overlapped_operation(fileOffset) + : cppcoro::detail::io_operation( +#if CPPCORO_OS_LINUX + ioService, +#endif + fileOffset) , m_impl(fileHandle, buffer, byteCount) {} private: - - friend class cppcoro::detail::win32_overlapped_operation; + friend cppcoro::detail::io_operation; bool try_start() noexcept { return m_impl.try_start(*this); } file_read_operation_impl m_impl; - }; class file_read_operation_cancellable - : public cppcoro::detail::win32_overlapped_operation_cancellable + : public cppcoro::detail::io_operation_cancellable { public: - file_read_operation_cancellable( - detail::win32::handle_t fileHandle, +#if CPPCORO_OS_LINUX + io_service& ioService, +#endif + detail::handle_t fileHandle, std::uint64_t fileOffset, void* buffer, std::size_t byteCount, cancellation_token&& cancellationToken) noexcept - : cppcoro::detail::win32_overlapped_operation_cancellable( - fileOffset, std::move(cancellationToken)) + : cppcoro::detail::io_operation_cancellable( +#if CPPCORO_OS_LINUX + ioService, +#endif + fileOffset, + std::move(cancellationToken)) , m_impl(fileHandle, buffer, byteCount) {} private: - - friend class cppcoro::detail::win32_overlapped_operation_cancellable; + friend cppcoro::detail::io_operation_cancellable; bool try_start() noexcept { return m_impl.try_start(*this); } + void cancel() noexcept { m_impl.cancel(*this); } file_read_operation_impl m_impl; - }; -#endif -} +} // namespace cppcoro #endif diff --git a/include/cppcoro/file_write_operation.hpp b/include/cppcoro/file_write_operation.hpp index afc30d55..93797374 100644 --- a/include/cppcoro/file_write_operation.hpp +++ b/include/cppcoro/file_write_operation.hpp @@ -9,91 +9,104 @@ #include #include +#include #include #include -#include #if CPPCORO_OS_WINNT -# include -# include + +#include +#include + +#elif CPPCORO_OS_LINUX + +#include + +#endif namespace cppcoro { class file_write_operation_impl { public: - file_write_operation_impl( - detail::win32::handle_t fileHandle, - const void* buffer, - std::size_t byteCount) noexcept + detail::handle_t fileHandle, const void* buffer, std::size_t byteCount) noexcept : m_fileHandle(fileHandle) , m_buffer(buffer) , m_byteCount(byteCount) - {} + { + } - bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; - void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; + bool try_start(cppcoro::detail::io_operation_base& operation) noexcept; - private: + void cancel(cppcoro::detail::io_operation_base& operation) noexcept; - detail::win32::handle_t m_fileHandle; + private: + detail::handle_t m_fileHandle; const void* m_buffer; std::size_t m_byteCount; - }; - class file_write_operation - : public cppcoro::detail::win32_overlapped_operation + class file_write_operation : public cppcoro::detail::io_operation { public: - file_write_operation( - detail::win32::handle_t fileHandle, +#if CPPCORO_OS_LINUX + io_service& ioService, +#endif + detail::handle_t fileHandle, std::uint64_t fileOffset, const void* buffer, std::size_t byteCount) noexcept - : cppcoro::detail::win32_overlapped_operation(fileOffset) + : cppcoro::detail::io_operation( +#if CPPCORO_OS_LINUX + ioService, +#endif + fileOffset) , m_impl(fileHandle, buffer, byteCount) - {} + { + } private: - - friend class cppcoro::detail::win32_overlapped_operation; + friend cppcoro::detail::io_operation; bool try_start() noexcept { return m_impl.try_start(*this); } file_write_operation_impl m_impl; - }; class file_write_operation_cancellable - : public cppcoro::detail::win32_overlapped_operation_cancellable + : public cppcoro::detail::io_operation_cancellable { public: - file_write_operation_cancellable( - detail::win32::handle_t fileHandle, +#if CPPCORO_OS_LINUX + io_service& ioService, +#endif + detail::handle_t fileHandle, std::uint64_t fileOffset, const void* buffer, std::size_t byteCount, cancellation_token&& ct) noexcept - : cppcoro::detail::win32_overlapped_operation_cancellable(fileOffset, std::move(ct)) + : cppcoro::detail::io_operation_cancellable( +#if CPPCORO_OS_LINUX + ioService, +#endif + fileOffset, + std::move(ct)) , m_impl(fileHandle, buffer, byteCount) - {} + { + } private: - - friend class cppcoro::detail::win32_overlapped_operation_cancellable; + friend cppcoro::detail::io_operation_cancellable; bool try_start() noexcept { return m_impl.try_start(*this); } + void cancel() noexcept { m_impl.cancel(*this); } file_write_operation_impl m_impl; - }; -} - -#endif // CPPCORO_OS_WINNT +} // namespace cppcoro #endif diff --git a/include/cppcoro/fmap.hpp b/include/cppcoro/fmap.hpp index a339493c..aaf5017c 100644 --- a/include/cppcoro/fmap.hpp +++ b/include/cppcoro/fmap.hpp @@ -38,8 +38,8 @@ namespace cppcoro } template - decltype(auto) await_suspend(std::experimental::coroutine_handle coro) - noexcept(noexcept(static_cast(m_awaiter).await_suspend(std::move(coro)))) + decltype(auto) await_suspend(stdcoro::coroutine_handle coro) + //noexcept(noexcept(static_cast(m_awaiter).await_suspend(std::move(coro)))) { return static_cast(m_awaiter).await_suspend(std::move(coro)); } @@ -48,7 +48,7 @@ namespace cppcoro typename AWAIT_RESULT = decltype(std::declval().await_resume()), std::enable_if_t, int> = 0> decltype(auto) await_resume() - noexcept(noexcept(std::invoke(static_cast(m_func)))) + //noexcept(noexcept(std::invoke(static_cast(m_func)))) { static_cast(m_awaiter).await_resume(); return std::invoke(static_cast(m_func)); @@ -58,7 +58,7 @@ namespace cppcoro typename AWAIT_RESULT = decltype(std::declval().await_resume()), std::enable_if_t, int> = 0> decltype(auto) await_resume() - noexcept(noexcept(std::invoke(static_cast(m_func), static_cast(m_awaiter).await_resume()))) + //noexcept(noexcept(std::invoke(static_cast(m_func), static_cast(m_awaiter).await_resume()))) { return std::invoke( static_cast(m_func), diff --git a/include/cppcoro/generator.hpp b/include/cppcoro/generator.hpp index d39d7e7c..7b036340 100644 --- a/include/cppcoro/generator.hpp +++ b/include/cppcoro/generator.hpp @@ -5,7 +5,7 @@ #ifndef CPPCORO_GENERATOR_HPP_INCLUDED #define CPPCORO_GENERATOR_HPP_INCLUDED -#include +#include #include #include #include @@ -32,19 +32,19 @@ namespace cppcoro generator get_return_object() noexcept; - constexpr std::experimental::suspend_always initial_suspend() const { return {}; } - constexpr std::experimental::suspend_always final_suspend() const { return {}; } + constexpr stdcoro::suspend_always initial_suspend() const { return {}; } + constexpr stdcoro::suspend_always final_suspend() const { return {}; } template< typename U = T, std::enable_if_t::value, int> = 0> - std::experimental::suspend_always yield_value(std::remove_reference_t& value) noexcept + stdcoro::suspend_always yield_value(std::remove_reference_t& value) noexcept { m_value = std::addressof(value); return {}; } - std::experimental::suspend_always yield_value(std::remove_reference_t&& value) noexcept + stdcoro::suspend_always yield_value(std::remove_reference_t&& value) noexcept { m_value = std::addressof(value); return {}; @@ -66,7 +66,7 @@ namespace cppcoro // Don't allow any use of 'co_await' inside the generator coroutine. template - std::experimental::suspend_never await_transform(U&& value) = delete; + stdcoro::suspend_never await_transform(U&& value) = delete; void rethrow_if_exception() { @@ -88,7 +88,7 @@ namespace cppcoro template class generator_iterator { - using coroutine_handle = std::experimental::coroutine_handle>; + using coroutine_handle = stdcoro::coroutine_handle>; public: @@ -223,11 +223,11 @@ namespace cppcoro friend class detail::generator_promise; - explicit generator(std::experimental::coroutine_handle coroutine) noexcept + explicit generator(stdcoro::coroutine_handle coroutine) noexcept : m_coroutine(coroutine) {} - std::experimental::coroutine_handle m_coroutine; + stdcoro::coroutine_handle m_coroutine; }; @@ -242,7 +242,7 @@ namespace cppcoro template generator generator_promise::get_return_object() noexcept { - using coroutine_handle = std::experimental::coroutine_handle>; + using coroutine_handle = stdcoro::coroutine_handle>; return generator{ coroutine_handle::from_promise(*this) }; } } diff --git a/include/cppcoro/inline_scheduler.hpp b/include/cppcoro/inline_scheduler.hpp index bd439f37..0cf1ed1d 100644 --- a/include/cppcoro/inline_scheduler.hpp +++ b/include/cppcoro/inline_scheduler.hpp @@ -5,7 +5,7 @@ #ifndef CPPCORO_INLINE_SCHEDULER_HPP_INCLUDED #define CPPCORO_INLINE_SCHEDULER_HPP_INCLUDED -#include +#include namespace cppcoro { @@ -15,7 +15,7 @@ namespace cppcoro inline_scheduler() noexcept = default; - std::experimental::suspend_never schedule() const noexcept + stdcoro::suspend_never schedule() const noexcept { return {}; } diff --git a/include/cppcoro/io_service.hpp b/include/cppcoro/io_service.hpp index 6d2a3464..81756ece 100644 --- a/include/cppcoro/io_service.hpp +++ b/include/cppcoro/io_service.hpp @@ -12,6 +12,9 @@ #if CPPCORO_OS_WINNT # include #endif +#if CPPCORO_OS_LINUX +#include +#endif #include #include @@ -19,7 +22,7 @@ #include #include #include -#include +#include namespace cppcoro { @@ -43,7 +46,11 @@ namespace cppcoro /// actively processing events. /// Note that the number of active threads may temporarily go /// above this number. - io_service(std::uint32_t concurrencyHint); +#if CPPCORO_OS_WINNT + io_service(std::uint32_t concurrencyHint); +#elif CPPCORO_OS_LINUX + io_service(size_t queue_length); +#endif ~io_service(); @@ -135,11 +142,17 @@ namespace cppcoro #if CPPCORO_OS_WINNT detail::win32::handle_t native_iocp_handle() noexcept; void ensure_winsock_initialised(); +#elif CPPCORO_OS_LINUX + int submit() noexcept; + io_uring_sqe *get_sqe() noexcept; #endif private: +#if CPPCORO_OS_WINNT class timer_thread_state; +#endif + class timer_queue; friend class schedule_operation; @@ -156,7 +169,9 @@ namespace cppcoro void post_wake_up_event() noexcept; +#if CPPCORO_OS_WINNT timer_thread_state* ensure_timer_thread_started(); +#endif static constexpr std::uint32_t stop_requested_flag = 1; static constexpr std::uint32_t active_thread_count_increment = 2; @@ -174,12 +189,19 @@ namespace cppcoro std::mutex m_winsockInitialisationMutex; #endif +#if CPPCORO_OS_LINUX + detail::lnx::uring_queue m_uq; + detail::lnx::message m_nopMessage{ detail::lnx::message_type::RESUME_TYPE, nullptr }; +#endif + // Head of a linked-list of schedule operations that are // ready to run but that failed to be queued to the I/O // completion port (eg. due to low memory). std::atomic m_scheduleOperations; +#if CPPCORO_OS_WINNT std::atomic m_timerState; +#endif }; @@ -192,7 +214,7 @@ namespace cppcoro {} bool await_ready() const noexcept { return false; } - void await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept; + void await_suspend(stdcoro::coroutine_handle<> awaiter) noexcept; void await_resume() const noexcept {} private: @@ -201,9 +223,12 @@ namespace cppcoro friend class io_service::timed_schedule_operation; io_service& m_service; - std::experimental::coroutine_handle<> m_awaiter; + stdcoro::coroutine_handle<> m_awaiter; schedule_operation* m_next; +#if CPPCORO_OS_LINUX + detail::lnx::message m_message{detail::lnx::message_type::RESUME_TYPE}; +#endif }; class io_service::timed_schedule_operation @@ -224,13 +249,16 @@ namespace cppcoro timed_schedule_operation& operator=(const timed_schedule_operation& other) = delete; bool await_ready() const noexcept; - void await_suspend(std::experimental::coroutine_handle<> awaiter); + void await_suspend(stdcoro::coroutine_handle<> awaiter); void await_resume(); private: friend class io_service::timer_queue; + +#if CPPCORO_OS_WINNT friend class io_service::timer_thread_state; +#endif io_service::schedule_operation m_scheduleOperation; std::chrono::high_resolution_clock::time_point m_resumeTime; @@ -242,6 +270,9 @@ namespace cppcoro std::atomic m_refCount; +#if CPPCORO_OS_LINUX + detail::lnx::message m_message{detail::lnx::message_type::RESUME_TYPE}; +#endif }; class io_work_scope diff --git a/include/cppcoro/multi_producer_sequencer.hpp b/include/cppcoro/multi_producer_sequencer.hpp index 72589c3d..2b6c6593 100644 --- a/include/cppcoro/multi_producer_sequencer.hpp +++ b/include/cppcoro/multi_producer_sequencer.hpp @@ -194,7 +194,7 @@ namespace cppcoro return m_barrierWait.await_ready(); } - auto await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept + auto await_suspend(stdcoro::coroutine_handle<> awaitingCoroutine) noexcept { return m_barrierWait.await_suspend(awaitingCoroutine); } @@ -273,7 +273,7 @@ namespace cppcoro return m_waitOp.await_ready(); } - auto await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept + auto await_suspend(stdcoro::coroutine_handle<> awaitingCoroutine) noexcept { return m_waitOp.await_suspend(awaitingCoroutine); } @@ -347,7 +347,7 @@ namespace cppcoro return !TRAITS::precedes(m_lastKnownPublished, m_targetSequence); } - bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept + bool await_suspend(stdcoro::coroutine_handle<> awaitingCoroutine) noexcept { m_awaitingCoroutine = awaitingCoroutine; @@ -384,7 +384,7 @@ namespace cppcoro SEQUENCE m_targetSequence; SEQUENCE m_lastKnownPublished; multi_producer_sequencer_wait_operation_base* m_next; - std::experimental::coroutine_handle<> m_awaitingCoroutine; + stdcoro::coroutine_handle<> m_awaitingCoroutine; std::atomic m_readyToResume; }; diff --git a/include/cppcoro/net/socket.hpp b/include/cppcoro/net/socket.hpp index a61eac61..e5005d30 100644 --- a/include/cppcoro/net/socket.hpp +++ b/include/cppcoro/net/socket.hpp @@ -92,10 +92,9 @@ namespace cppcoro socket& operator=(socket&& other) noexcept; -#if CPPCORO_OS_WINNT - /// Get the Win32 socket handle assocaited with this socket. - cppcoro::detail::win32::socket_t native_handle() noexcept { return m_handle; } + auto native_handle() noexcept { return m_handle; } +#if CPPCORO_OS_WINNT /// Query whether I/O operations that complete synchronously will skip posting /// an I/O completion event to the I/O completion port. /// @@ -243,24 +242,29 @@ namespace cppcoro void close_recv(); private: - friend class socket_accept_operation_impl; friend class socket_connect_operation_impl; + friend class socket_recv_operation_impl; + friend class socket_recv_from_operation_impl; #if CPPCORO_OS_WINNT explicit socket( - cppcoro::detail::win32::socket_t handle, - bool skipCompletionOnSuccess) noexcept; + cppcoro::detail::win32::socket_t handle, bool skipCompletionOnSuccess) noexcept; +#elif CPPCORO_OS_LINUX + explicit socket(io_service& ioService, cppcoro::detail::lnx::fd_t fd) noexcept; #endif #if CPPCORO_OS_WINNT cppcoro::detail::win32::socket_t m_handle; bool m_skipCompletionOnSuccess; +#elif CPPCORO_OS_LINUX + cppcoro::detail::lnx::fd_t m_handle = -1; + io_service& m_ioService; + int m_recvFlags = 0; #endif ip_endpoint m_localEndPoint; ip_endpoint m_remoteEndPoint; - }; } } diff --git a/include/cppcoro/net/socket_accept_operation.hpp b/include/cppcoro/net/socket_accept_operation.hpp index 567091d7..9c376fa8 100644 --- a/include/cppcoro/net/socket_accept_operation.hpp +++ b/include/cppcoro/net/socket_accept_operation.hpp @@ -6,16 +6,19 @@ #define CPPCORO_NET_SOCKET_ACCEPT_OPERATION_HPP_INCLUDED #include -#include #include +#include #if CPPCORO_OS_WINNT -# include -# include +#include +#include +#elif CPPCORO_OS_LINUX +#include +#endif -# include -# include -# include +#include +#include +#include namespace cppcoro { @@ -26,84 +29,97 @@ namespace cppcoro class socket_accept_operation_impl { public: - - socket_accept_operation_impl( - socket& listeningSocket, - socket& acceptingSocket) noexcept + socket_accept_operation_impl(socket& listeningSocket, socket& acceptingSocket) noexcept : m_listeningSocket(listeningSocket) , m_acceptingSocket(acceptingSocket) - {} + { +#if CPPCORO_OS_LINUX + detail::lnx::check_required_kernel(5, 5, "socket accept operation"); +#endif + } - bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; - void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; - void get_result(cppcoro::detail::win32_overlapped_operation_base& operation); + bool try_start(cppcoro::detail::io_operation_base& operation) noexcept; + void cancel(cppcoro::detail::io_operation_base& operation) noexcept; + void get_result(cppcoro::detail::io_operation_base& operation); private: - #if CPPCORO_COMPILER_MSVC -# pragma warning(push) -# pragma warning(disable : 4324) // Structure padded due to alignment +#pragma warning(push) +#pragma warning(disable : 4324) // Structure padded due to alignment #endif socket& m_listeningSocket; socket& m_acceptingSocket; alignas(8) std::uint8_t m_addressBuffer[88]; -#if CPPCORO_COMPILER_MSVC -# pragma warning(pop) +#if CPPCORO_OS_LINUX + socklen_t m_addressBufferLength = sizeof(m_addressBuffer); #endif +#if CPPCORO_COMPILER_MSVC +#pragma warning(pop) +#endif }; class socket_accept_operation - : public cppcoro::detail::win32_overlapped_operation + : public cppcoro::detail::io_operation { public: - socket_accept_operation( +#if CPPCORO_OS_LINUX + io_service& ioService, +#endif socket& listeningSocket, socket& acceptingSocket) noexcept - : m_impl(listeningSocket, acceptingSocket) - {} + : cppcoro::detail::io_operation( +#if CPPCORO_OS_LINUX + ioService +#endif + ) + , m_impl(listeningSocket, acceptingSocket) + { + } private: - - friend class cppcoro::detail::win32_overlapped_operation; + friend cppcoro::detail::io_operation; bool try_start() noexcept { return m_impl.try_start(*this); } void get_result() { m_impl.get_result(*this); } socket_accept_operation_impl m_impl; - }; class socket_accept_operation_cancellable - : public cppcoro::detail::win32_overlapped_operation_cancellable + : public cppcoro::detail::io_operation_cancellable { public: - socket_accept_operation_cancellable( +#if CPPCORO_OS_LINUX + io_service& ioService, +#endif socket& listeningSocket, socket& acceptingSocket, cancellation_token&& ct) noexcept - : cppcoro::detail::win32_overlapped_operation_cancellable(std::move(ct)) + : cppcoro::detail::io_operation_cancellable( +#if CPPCORO_OS_LINUX + ioService, +#endif + std::move(ct)) , m_impl(listeningSocket, acceptingSocket) - {} + { + } private: - - friend class cppcoro::detail::win32_overlapped_operation_cancellable; + friend cppcoro::detail::io_operation_cancellable< + socket_accept_operation_cancellable>; bool try_start() noexcept { return m_impl.try_start(*this); } void cancel() noexcept { m_impl.cancel(*this); } void get_result() { m_impl.get_result(*this); } socket_accept_operation_impl m_impl; - }; - } -} - -#endif // CPPCORO_OS_WINNT + } // namespace net +} // namespace cppcoro #endif diff --git a/include/cppcoro/net/socket_connect_operation.hpp b/include/cppcoro/net/socket_connect_operation.hpp index b7eedd3c..aec1d9d4 100644 --- a/include/cppcoro/net/socket_connect_operation.hpp +++ b/include/cppcoro/net/socket_connect_operation.hpp @@ -12,6 +12,9 @@ #if CPPCORO_OS_WINNT # include # include +#elif CPPCORO_OS_LINUX +# include +#endif namespace cppcoro { @@ -28,33 +31,44 @@ namespace cppcoro const ip_endpoint& remoteEndPoint) noexcept : m_socket(socket) , m_remoteEndPoint(remoteEndPoint) - {} + { +#if CPPCORO_OS_LINUX + detail::lnx::check_required_kernel(5, 5, "socket connect operation"); +#endif + } - bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; - void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; - void get_result(cppcoro::detail::win32_overlapped_operation_base& operation); + bool try_start(cppcoro::detail::io_operation_base& operation) noexcept; + void cancel(cppcoro::detail::io_operation_base& operation) noexcept; + void get_result(cppcoro::detail::io_operation_base& operation); private: socket& m_socket; ip_endpoint m_remoteEndPoint; - }; class socket_connect_operation - : public cppcoro::detail::win32_overlapped_operation + : public cppcoro::detail::io_operation { public: socket_connect_operation( +#if CPPCORO_OS_LINUX + io_service &ioService, +#endif socket& socket, const ip_endpoint& remoteEndPoint) noexcept - : m_impl(socket, remoteEndPoint) + : cppcoro::detail::io_operation( +#if CPPCORO_OS_LINUX + ioService +#endif + ) + , m_impl(socket, remoteEndPoint) {} private: - friend class cppcoro::detail::win32_overlapped_operation; + friend cppcoro::detail::io_operation; bool try_start() noexcept { return m_impl.try_start(*this); } decltype(auto) get_result() { return m_impl.get_result(*this); } @@ -64,21 +78,28 @@ namespace cppcoro }; class socket_connect_operation_cancellable - : public cppcoro::detail::win32_overlapped_operation_cancellable + : public cppcoro::detail::io_operation_cancellable { public: socket_connect_operation_cancellable( +#if CPPCORO_OS_LINUX + io_service &ioService, +#endif socket& socket, const ip_endpoint& remoteEndPoint, cancellation_token&& ct) noexcept - : cppcoro::detail::win32_overlapped_operation_cancellable(std::move(ct)) + : cppcoro::detail::io_operation_cancellable( +#if CPPCORO_OS_LINUX + ioService, +#endif + std::move(ct)) , m_impl(socket, remoteEndPoint) {} private: - friend class cppcoro::detail::win32_overlapped_operation_cancellable; + friend cppcoro::detail::io_operation_cancellable; bool try_start() noexcept { return m_impl.try_start(*this); } void cancel() noexcept { m_impl.cancel(*this); } @@ -90,6 +111,4 @@ namespace cppcoro } } -#endif // CPPCORO_OS_WINNT - #endif diff --git a/include/cppcoro/net/socket_disconnect_operation.hpp b/include/cppcoro/net/socket_disconnect_operation.hpp index 7bdcc03f..1b3cc9bd 100644 --- a/include/cppcoro/net/socket_disconnect_operation.hpp +++ b/include/cppcoro/net/socket_disconnect_operation.hpp @@ -9,8 +9,11 @@ #include #if CPPCORO_OS_WINNT -# include -# include +#include +#include +#elif CPPCORO_OS_LINUX +#include +#endif namespace cppcoro { @@ -21,65 +24,78 @@ namespace cppcoro class socket_disconnect_operation_impl { public: - socket_disconnect_operation_impl(socket& socket) noexcept : m_socket(socket) - {} + { + } - bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; - void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; - void get_result(cppcoro::detail::win32_overlapped_operation_base& operation); + bool try_start(cppcoro::detail::io_operation_base& operation) noexcept; + void cancel(cppcoro::detail::io_operation_base& operation) noexcept; + void get_result(cppcoro::detail::io_operation_base& operation); private: - socket& m_socket; - }; class socket_disconnect_operation - : public cppcoro::detail::win32_overlapped_operation + : public cppcoro::detail::io_operation { public: - - socket_disconnect_operation(socket& socket) noexcept - : m_impl(socket) - {} + socket_disconnect_operation( +#if CPPCORO_OS_LINUX + io_service& ioService, +#endif + socket& socket) noexcept + : cppcoro::detail::io_operation( +#if CPPCORO_OS_LINUX + ioService +#endif + ) + , m_impl(socket) + { + } private: - - friend class cppcoro::detail::win32_overlapped_operation; + friend cppcoro::detail::io_operation; bool try_start() noexcept { return m_impl.try_start(*this); } void get_result() { m_impl.get_result(*this); } socket_disconnect_operation_impl m_impl; - }; class socket_disconnect_operation_cancellable - : public cppcoro::detail::win32_overlapped_operation_cancellable + : public cppcoro::detail::io_operation_cancellable< + socket_disconnect_operation_cancellable> { public: - - socket_disconnect_operation_cancellable(socket& socket, cancellation_token&& ct) noexcept - : cppcoro::detail::win32_overlapped_operation_cancellable(std::move(ct)) + socket_disconnect_operation_cancellable( +#if CPPCORO_OS_LINUX + io_service& ioService, +#endif + socket& socket, + cancellation_token&& ct) noexcept + : cppcoro::detail::io_operation_cancellable< + socket_disconnect_operation_cancellable>( +#if CPPCORO_OS_LINUX + ioService, +#endif + std::move(ct)) , m_impl(socket) - {} + { + } private: - - friend class cppcoro::detail::win32_overlapped_operation_cancellable; + friend cppcoro::detail::io_operation_cancellable< + socket_disconnect_operation_cancellable>; bool try_start() noexcept { return m_impl.try_start(*this); } void cancel() noexcept { m_impl.cancel(*this); } void get_result() { m_impl.get_result(*this); } socket_disconnect_operation_impl m_impl; - }; - } -} - -#endif // CPPCORO_OS_WINNT + } // namespace net +} // namespace cppcoro #endif diff --git a/include/cppcoro/net/socket_recv_from_operation.hpp b/include/cppcoro/net/socket_recv_from_operation.hpp index 37f2d01a..09595a4f 100644 --- a/include/cppcoro/net/socket_recv_from_operation.hpp +++ b/include/cppcoro/net/socket_recv_from_operation.hpp @@ -13,8 +13,11 @@ #include #if CPPCORO_OS_WINNT -# include -# include +#include +#include +#elif CPPCORO_OS_LINUX +#include +#endif namespace cppcoro::net { @@ -23,84 +26,89 @@ namespace cppcoro::net class socket_recv_from_operation_impl { public: - socket_recv_from_operation_impl( - socket& socket, - void* buffer, - std::size_t byteCount) noexcept + socket& socket, void* buffer, std::size_t byteCount) noexcept : m_socket(socket) , m_buffer(buffer, byteCount) - {} + { + } - bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; - void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; - std::tuple get_result( - cppcoro::detail::win32_overlapped_operation_base& operation); + bool try_start(cppcoro::detail::io_operation_base& operation) noexcept; + void cancel(cppcoro::detail::io_operation_base& operation) noexcept; + std::tuple + get_result(cppcoro::detail::io_operation_base& operation); private: - socket& m_socket; - cppcoro::detail::win32::wsabuf m_buffer; + cppcoro::detail::sock_buf m_buffer; static constexpr std::size_t sockaddrStorageAlignment = 4; // Storage suitable for either SOCKADDR_IN or SOCKADDR_IN6 alignas(sockaddrStorageAlignment) std::uint8_t m_sourceSockaddrStorage[28]; int m_sourceSockaddrLength; - }; class socket_recv_from_operation - : public cppcoro::detail::win32_overlapped_operation + : public cppcoro::detail::io_operation { public: - socket_recv_from_operation( +#if CPPCORO_OS_LINUX + io_service& ioService, +#endif socket& socket, void* buffer, std::size_t byteCount) noexcept - : m_impl(socket, buffer, byteCount) - {} + : cppcoro::detail::io_operation( +#if CPPCORO_OS_LINUX + ioService +#endif + ) + , m_impl(socket, buffer, byteCount) + { + } private: - - friend class cppcoro::detail::win32_overlapped_operation; + friend cppcoro::detail::io_operation; bool try_start() noexcept { return m_impl.try_start(*this); } decltype(auto) get_result() { return m_impl.get_result(*this); } socket_recv_from_operation_impl m_impl; - }; class socket_recv_from_operation_cancellable - : public cppcoro::detail::win32_overlapped_operation_cancellable + : public cppcoro::detail::io_operation_cancellable { public: - socket_recv_from_operation_cancellable( +#if CPPCORO_OS_LINUX + io_service& ioService, +#endif socket& socket, void* buffer, std::size_t byteCount, cancellation_token&& ct) noexcept - : cppcoro::detail::win32_overlapped_operation_cancellable(std::move(ct)) + : cppcoro::detail::io_operation_cancellable( +#if CPPCORO_OS_LINUX + ioService, +#endif + std::move(ct)) , m_impl(socket, buffer, byteCount) - {} + { + } private: - - friend class cppcoro::detail::win32_overlapped_operation_cancellable; + friend cppcoro::detail::io_operation_cancellable; bool try_start() noexcept { return m_impl.try_start(*this); } void cancel() noexcept { m_impl.cancel(*this); } decltype(auto) get_result() { return m_impl.get_result(*this); } socket_recv_from_operation_impl m_impl; - }; -} - -#endif // CPPCORO_OS_WINNT +} // namespace cppcoro::net #endif diff --git a/include/cppcoro/net/socket_recv_operation.hpp b/include/cppcoro/net/socket_recv_operation.hpp index c9dca8b2..0d9cdcfc 100644 --- a/include/cppcoro/net/socket_recv_operation.hpp +++ b/include/cppcoro/net/socket_recv_operation.hpp @@ -11,8 +11,11 @@ #include #if CPPCORO_OS_WINNT -# include -# include +#include +#include +#elif CPPCORO_OS_LINUX +#include +#endif namespace cppcoro::net { @@ -21,74 +24,84 @@ namespace cppcoro::net class socket_recv_operation_impl { public: - - socket_recv_operation_impl( - socket& s, - void* buffer, - std::size_t byteCount) noexcept + socket_recv_operation_impl(socket& s, void* buffer, std::size_t byteCount) noexcept : m_socket(s) , m_buffer(buffer, byteCount) - {} + { + } - bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; - void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; + bool try_start(cppcoro::detail::io_operation_base& operation) noexcept; + void cancel(cppcoro::detail::io_operation_base& operation) noexcept; - private: +#ifdef CPPCORO_OS_LINUX + std::size_t get_result(cppcoro::detail::io_operation_base& operation); +#endif + private: socket& m_socket; - cppcoro::detail::win32::wsabuf m_buffer; - + cppcoro::detail::sock_buf m_buffer; }; - class socket_recv_operation - : public cppcoro::detail::win32_overlapped_operation + class socket_recv_operation : public cppcoro::detail::io_operation { public: - socket_recv_operation( +#if CPPCORO_OS_LINUX + io_service& ioService, +#endif socket& s, void* buffer, std::size_t byteCount) noexcept - : m_impl(s, buffer, byteCount) - {} + : cppcoro::detail::io_operation( +#if CPPCORO_OS_LINUX + ioService +#endif + ) + , m_impl(s, buffer, byteCount) + { + } private: - - friend class cppcoro::detail::win32_overlapped_operation; + friend cppcoro::detail::io_operation; bool try_start() noexcept { return m_impl.try_start(*this); } +#ifdef CPPCORO_OS_LINUX + std::size_t get_result() { return m_impl.get_result(*this); } +#endif socket_recv_operation_impl m_impl; - }; class socket_recv_operation_cancellable - : public cppcoro::detail::win32_overlapped_operation_cancellable + : public cppcoro::detail::io_operation_cancellable { public: - socket_recv_operation_cancellable( +#if CPPCORO_OS_LINUX + io_service& ioService, +#endif socket& s, void* buffer, std::size_t byteCount, cancellation_token&& ct) noexcept - : cppcoro::detail::win32_overlapped_operation_cancellable(std::move(ct)) + : cppcoro::detail::io_operation_cancellable( +#if CPPCORO_OS_LINUX + ioService, +#endif + std::move(ct)) , m_impl(s, buffer, byteCount) - {} + { + } private: - - friend class cppcoro::detail::win32_overlapped_operation_cancellable; + friend cppcoro::detail::io_operation_cancellable; bool try_start() noexcept { return m_impl.try_start(*this); } void cancel() noexcept { m_impl.cancel(*this); } socket_recv_operation_impl m_impl; - }; -} - -#endif // CPPCORO_OS_WINNT +} // namespace cppcoro::net #endif diff --git a/include/cppcoro/net/socket_send_operation.hpp b/include/cppcoro/net/socket_send_operation.hpp index 702d2abd..58b6b3b0 100644 --- a/include/cppcoro/net/socket_send_operation.hpp +++ b/include/cppcoro/net/socket_send_operation.hpp @@ -11,8 +11,11 @@ #include #if CPPCORO_OS_WINNT -# include -# include +#include +#include +#elif CPPCORO_OS_LINUX +#include +#endif namespace cppcoro::net { @@ -21,74 +24,77 @@ namespace cppcoro::net class socket_send_operation_impl { public: - - socket_send_operation_impl( - socket& s, - const void* buffer, - std::size_t byteCount) noexcept + socket_send_operation_impl(socket& s, const void* buffer, std::size_t byteCount) noexcept : m_socket(s) , m_buffer(const_cast(buffer), byteCount) - {} + { + } - bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; - void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; + bool try_start(cppcoro::detail::io_operation_base& operation) noexcept; + void cancel(cppcoro::detail::io_operation_base& operation) noexcept; private: - socket& m_socket; - cppcoro::detail::win32::wsabuf m_buffer; - + cppcoro::detail::sock_buf m_buffer; }; - class socket_send_operation - : public cppcoro::detail::win32_overlapped_operation + class socket_send_operation : public cppcoro::detail::io_operation { public: - socket_send_operation( +#if CPPCORO_OS_LINUX + io_service& ioService, +#endif socket& s, const void* buffer, std::size_t byteCount) noexcept - : m_impl(s, buffer, byteCount) - {} + : cppcoro::detail::io_operation( +#if CPPCORO_OS_LINUX + ioService +#endif + ) + , m_impl(s, buffer, byteCount) + { + } private: - - friend class cppcoro::detail::win32_overlapped_operation; + friend cppcoro::detail::io_operation; bool try_start() noexcept { return m_impl.try_start(*this); } socket_send_operation_impl m_impl; - }; class socket_send_operation_cancellable - : public cppcoro::detail::win32_overlapped_operation_cancellable + : public cppcoro::detail::io_operation_cancellable { public: - socket_send_operation_cancellable( +#if CPPCORO_OS_LINUX + io_service& ioService, +#endif socket& s, const void* buffer, std::size_t byteCount, cancellation_token&& ct) noexcept - : cppcoro::detail::win32_overlapped_operation_cancellable(std::move(ct)) + : cppcoro::detail::io_operation_cancellable( +#if CPPCORO_OS_LINUX + ioService, +#endif + std::move(ct)) , m_impl(s, buffer, byteCount) - {} + { + } private: - - friend class cppcoro::detail::win32_overlapped_operation_cancellable; + friend cppcoro::detail::io_operation_cancellable; bool try_start() noexcept { return m_impl.try_start(*this); } void cancel() noexcept { return m_impl.cancel(*this); } socket_send_operation_impl m_impl; - }; -} - -#endif // CPPCORO_OS_WINNT +} // namespace cppcoro::net #endif diff --git a/include/cppcoro/net/socket_send_to_operation.hpp b/include/cppcoro/net/socket_send_to_operation.hpp index 60d51b24..2c6a1812 100644 --- a/include/cppcoro/net/socket_send_to_operation.hpp +++ b/include/cppcoro/net/socket_send_to_operation.hpp @@ -12,8 +12,12 @@ #include #if CPPCORO_OS_WINNT -# include -# include +#include +#include +#elif CPPCORO_OS_LINUX +#include +#include +#endif namespace cppcoro::net { @@ -22,7 +26,6 @@ namespace cppcoro::net class socket_send_to_operation_impl { public: - socket_send_to_operation_impl( socket& s, const ip_endpoint& destination, @@ -31,70 +34,80 @@ namespace cppcoro::net : m_socket(s) , m_destination(destination) , m_buffer(const_cast(buffer), byteCount) - {} + { + } - bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; - void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept; + bool try_start(cppcoro::detail::io_operation_base& operation) noexcept; + void cancel(cppcoro::detail::io_operation_base& operation) noexcept; private: - socket& m_socket; ip_endpoint m_destination; - cppcoro::detail::win32::wsabuf m_buffer; - + cppcoro::detail::sock_buf m_buffer; +#if CPPCORO_OS_LINUX + sockaddr_storage m_destinationStorage; +#endif }; - class socket_send_to_operation - : public cppcoro::detail::win32_overlapped_operation + class socket_send_to_operation : public cppcoro::detail::io_operation { public: - socket_send_to_operation( +#if CPPCORO_OS_LINUX + io_service& ioService, +#endif socket& s, const ip_endpoint& destination, const void* buffer, std::size_t byteCount) noexcept - : m_impl(s, destination, buffer, byteCount) - {} + : cppcoro::detail::io_operation( +#if CPPCORO_OS_LINUX + ioService +#endif + ) + , m_impl(s, destination, buffer, byteCount) + { + } private: - - friend class cppcoro::detail::win32_overlapped_operation; + friend cppcoro::detail::io_operation; bool try_start() noexcept { return m_impl.try_start(*this); } socket_send_to_operation_impl m_impl; - }; class socket_send_to_operation_cancellable - : public cppcoro::detail::win32_overlapped_operation_cancellable + : public cppcoro::detail::io_operation_cancellable { public: - socket_send_to_operation_cancellable( +#if CPPCORO_OS_LINUX + io_service& ioService, +#endif socket& s, const ip_endpoint& destination, const void* buffer, std::size_t byteCount, cancellation_token&& ct) noexcept - : cppcoro::detail::win32_overlapped_operation_cancellable(std::move(ct)) + : cppcoro::detail::io_operation_cancellable( +#if CPPCORO_OS_LINUX + ioService, +#endif + std::move(ct)) , m_impl(s, destination, buffer, byteCount) - {} + { + } private: - - friend class cppcoro::detail::win32_overlapped_operation_cancellable; + friend cppcoro::detail::io_operation_cancellable; bool try_start() noexcept { return m_impl.try_start(*this); } void cancel() noexcept { return m_impl.cancel(*this); } socket_send_to_operation_impl m_impl; - }; -} - -#endif // CPPCORO_OS_WINNT +} // namespace cppcoro::net #endif diff --git a/include/cppcoro/read_only_file.hpp b/include/cppcoro/read_only_file.hpp index a836fbc3..774169ce 100644 --- a/include/cppcoro/read_only_file.hpp +++ b/include/cppcoro/read_only_file.hpp @@ -9,8 +9,6 @@ #include #include -#include - namespace cppcoro { class read_only_file : public readable_file @@ -43,16 +41,13 @@ namespace cppcoro [[nodiscard]] static read_only_file open( io_service& ioService, - const std::experimental::filesystem::path& path, + const stdfs::path& path, file_share_mode shareMode = file_share_mode::read, file_buffering_mode bufferingMode = file_buffering_mode::default_); protected: -#if CPPCORO_OS_WINNT - read_only_file(detail::win32::safe_handle&& fileHandle) noexcept; -#endif - + read_only_file(detail::safe_handle&& fileHandle) noexcept; }; } diff --git a/include/cppcoro/read_write_file.hpp b/include/cppcoro/read_write_file.hpp index f58bea5c..1d41724e 100644 --- a/include/cppcoro/read_write_file.hpp +++ b/include/cppcoro/read_write_file.hpp @@ -11,8 +11,6 @@ #include #include -#include - namespace cppcoro { class read_write_file : public readable_file, public writable_file @@ -49,17 +47,14 @@ namespace cppcoro [[nodiscard]] static read_write_file open( io_service& ioService, - const std::experimental::filesystem::path& path, + const stdfs::path& path, file_open_mode openMode = file_open_mode::create_or_open, file_share_mode shareMode = file_share_mode::none, file_buffering_mode bufferingMode = file_buffering_mode::default_); protected: -#if CPPCORO_OS_WINNT - read_write_file(detail::win32::safe_handle&& fileHandle) noexcept; -#endif - + read_write_file(detail::safe_handle&& fileHandle) noexcept; }; } diff --git a/include/cppcoro/recursive_generator.hpp b/include/cppcoro/recursive_generator.hpp index 07e4f726..f5f3afa6 100644 --- a/include/cppcoro/recursive_generator.hpp +++ b/include/cppcoro/recursive_generator.hpp @@ -7,7 +7,7 @@ #include -#include +#include #include #include #include @@ -39,12 +39,12 @@ namespace cppcoro return recursive_generator{ *this }; } - std::experimental::suspend_always initial_suspend() noexcept + stdcoro::suspend_always initial_suspend() noexcept { return {}; } - std::experimental::suspend_always final_suspend() noexcept + stdcoro::suspend_always final_suspend() noexcept { return {}; } @@ -56,13 +56,13 @@ namespace cppcoro void return_void() noexcept {} - std::experimental::suspend_always yield_value(T& value) noexcept + stdcoro::suspend_always yield_value(T& value) noexcept { m_value = std::addressof(value); return {}; } - std::experimental::suspend_always yield_value(T&& value) noexcept + stdcoro::suspend_always yield_value(T&& value) noexcept { m_value = std::addressof(value); return {}; @@ -87,7 +87,7 @@ namespace cppcoro return this->m_childPromise == nullptr; } - void await_suspend(std::experimental::coroutine_handle) noexcept + void await_suspend(stdcoro::coroutine_handle) noexcept {} void await_resume() @@ -122,11 +122,11 @@ namespace cppcoro // Don't allow any use of 'co_await' inside the recursive_generator coroutine. template - std::experimental::suspend_never await_transform(U&& value) = delete; + stdcoro::suspend_never await_transform(U&& value) = delete; void destroy() noexcept { - std::experimental::coroutine_handle::from_promise(*this).destroy(); + stdcoro::coroutine_handle::from_promise(*this).destroy(); } void throw_if_exception() @@ -139,7 +139,7 @@ namespace cppcoro bool is_complete() noexcept { - return std::experimental::coroutine_handle::from_promise(*this).done(); + return stdcoro::coroutine_handle::from_promise(*this).done(); } T& value() noexcept @@ -167,7 +167,7 @@ namespace cppcoro void resume() noexcept { - std::experimental::coroutine_handle::from_promise(*this).resume(); + stdcoro::coroutine_handle::from_promise(*this).resume(); } std::add_pointer_t m_value; diff --git a/include/cppcoro/resume_on.hpp b/include/cppcoro/resume_on.hpp index 4bb1d7c5..aacd94fa 100644 --- a/include/cppcoro/resume_on.hpp +++ b/include/cppcoro/resume_on.hpp @@ -117,10 +117,11 @@ namespace cppcoro template async_generator resume_on(SCHEDULER& scheduler, async_generator source) { - for co_await(auto& value : source) + // for co_await(auto& value : source) // removed from C++20 + for (auto value_it = co_await source.begin(); value_it != source.end(); value_it = co_await ++value_it) { co_await scheduler.schedule(); - co_yield value; + co_yield *value_it; } } } diff --git a/include/cppcoro/round_robin_scheduler.hpp b/include/cppcoro/round_robin_scheduler.hpp index 6749c61d..5b453d13 100644 --- a/include/cppcoro/round_robin_scheduler.hpp +++ b/include/cppcoro/round_robin_scheduler.hpp @@ -7,7 +7,7 @@ #include -#include +#include #include #include #include @@ -44,8 +44,8 @@ namespace cppcoro return false; } - std::experimental::coroutine_handle<> await_suspend( - std::experimental::coroutine_handle<> awaitingCoroutine) noexcept + stdcoro::coroutine_handle<> await_suspend( + stdcoro::coroutine_handle<> awaitingCoroutine) noexcept { return m_scheduler.exchange_next(awaitingCoroutine); } @@ -61,7 +61,7 @@ namespace cppcoro public: round_robin_scheduler() noexcept : m_index(0) - , m_noop(std::experimental::noop_coroutine()) + , m_noop(stdcoro::noop_coroutine()) { for (size_t i = 0; i < N - 1; ++i) { @@ -104,8 +104,8 @@ namespace cppcoro private: - std::experimental::coroutine_handle exchange_next( - std::experimental::coroutine_handle<> coroutine) noexcept + stdcoro::coroutine_handle exchange_next( + stdcoro::coroutine_handle<> coroutine) noexcept { auto coroutineToResume = std::exchange( m_scheduler.m_coroutines[m_scheduler.m_index], @@ -115,8 +115,8 @@ namespace cppcoro } size_t m_index; - const std::experimental::coroutine_handle<> m_noop; - std::array, N - 1> m_coroutines; + const stdcoro::coroutine_handle<> m_noop; + std::array, N - 1> m_coroutines; }; #endif } diff --git a/include/cppcoro/sequence_barrier.hpp b/include/cppcoro/sequence_barrier.hpp index d23a5c09..26e49f57 100644 --- a/include/cppcoro/sequence_barrier.hpp +++ b/include/cppcoro/sequence_barrier.hpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include namespace cppcoro { @@ -161,7 +161,7 @@ namespace cppcoro return !TRAITS::precedes(m_lastKnownPublished, m_targetSequence); } - bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept + bool await_suspend(stdcoro::coroutine_handle<> awaitingCoroutine) noexcept { m_awaitingCoroutine = awaitingCoroutine; m_barrier.add_awaiter(this); @@ -192,7 +192,7 @@ namespace cppcoro const SEQUENCE m_targetSequence; SEQUENCE m_lastKnownPublished; sequence_barrier_wait_operation_base* m_next; - std::experimental::coroutine_handle<> m_awaitingCoroutine; + stdcoro::coroutine_handle<> m_awaitingCoroutine; std::atomic m_readyToResume; }; diff --git a/include/cppcoro/shared_task.hpp b/include/cppcoro/shared_task.hpp index 797ba30a..c915614d 100644 --- a/include/cppcoro/shared_task.hpp +++ b/include/cppcoro/shared_task.hpp @@ -17,7 +17,7 @@ #include #include -#include +#include namespace cppcoro { @@ -28,7 +28,7 @@ namespace cppcoro { struct shared_task_waiter { - std::experimental::coroutine_handle<> m_continuation; + stdcoro::coroutine_handle<> m_continuation; shared_task_waiter* m_next; }; @@ -41,7 +41,7 @@ namespace cppcoro bool await_ready() const noexcept { return false; } template - void await_suspend(std::experimental::coroutine_handle h) noexcept + void await_suspend(stdcoro::coroutine_handle h) noexcept { shared_task_promise_base& promise = h.promise(); @@ -79,7 +79,7 @@ namespace cppcoro , m_exception(nullptr) {} - std::experimental::suspend_always initial_suspend() noexcept { return {}; } + stdcoro::suspend_always initial_suspend() noexcept { return {}; } final_awaiter final_suspend() noexcept { return {}; } void unhandled_exception() noexcept @@ -124,7 +124,7 @@ namespace cppcoro /// waiter->m_coroutine will be resumed when the task completes. /// false if the coroutine was already completed and the awaiting /// coroutine can continue without suspending. - bool try_await(shared_task_waiter* waiter, std::experimental::coroutine_handle<> coroutine) + bool try_await(shared_task_waiter* waiter, stdcoro::coroutine_handle<> coroutine) { void* const valueReadyValue = this; void* const notStartedValue = &this->m_waiters; @@ -304,10 +304,10 @@ namespace cppcoro struct awaitable_base { - std::experimental::coroutine_handle m_coroutine; + stdcoro::coroutine_handle m_coroutine; detail::shared_task_waiter m_waiter; - awaitable_base(std::experimental::coroutine_handle coroutine) noexcept + awaitable_base(stdcoro::coroutine_handle coroutine) noexcept : m_coroutine(coroutine) {} @@ -316,7 +316,7 @@ namespace cppcoro return !m_coroutine || m_coroutine.promise().is_ready(); } - bool await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept + bool await_suspend(stdcoro::coroutine_handle<> awaiter) noexcept { m_waiter.m_continuation = awaiter; return m_coroutine.promise().try_await(&m_waiter, m_coroutine); @@ -329,7 +329,7 @@ namespace cppcoro : m_coroutine(nullptr) {} - explicit shared_task(std::experimental::coroutine_handle coroutine) + explicit shared_task(stdcoro::coroutine_handle coroutine) : m_coroutine(coroutine) { // Don't increment the ref-count here since it has already been @@ -452,7 +452,7 @@ namespace cppcoro } } - std::experimental::coroutine_handle m_coroutine; + stdcoro::coroutine_handle m_coroutine; }; @@ -480,7 +480,7 @@ namespace cppcoro shared_task shared_task_promise::get_return_object() noexcept { return shared_task{ - std::experimental::coroutine_handle::from_promise(*this) + stdcoro::coroutine_handle::from_promise(*this) }; } @@ -488,14 +488,14 @@ namespace cppcoro shared_task shared_task_promise::get_return_object() noexcept { return shared_task{ - std::experimental::coroutine_handle::from_promise(*this) + stdcoro::coroutine_handle::from_promise(*this) }; } inline shared_task shared_task_promise::get_return_object() noexcept { return shared_task{ - std::experimental::coroutine_handle::from_promise(*this) + stdcoro::coroutine_handle::from_promise(*this) }; } } diff --git a/include/cppcoro/single_consumer_async_auto_reset_event.hpp b/include/cppcoro/single_consumer_async_auto_reset_event.hpp index 821dbe73..3c844715 100644 --- a/include/cppcoro/single_consumer_async_auto_reset_event.hpp +++ b/include/cppcoro/single_consumer_async_auto_reset_event.hpp @@ -5,7 +5,7 @@ #ifndef CPPCORO_SINGLE_CONSUMER_ASYNC_AUTO_RESET_EVENT_HPP_INCLUDED #define CPPCORO_SINGLE_CONSUMER_ASYNC_AUTO_RESET_EVENT_HPP_INCLUDED -#include +#include #include #include #include @@ -26,7 +26,7 @@ namespace cppcoro if (oldValue != nullptr && oldValue != this) { // There was a waiting coroutine that we now need to resume. - auto handle = *static_cast*>(oldValue); + auto handle = *static_cast*>(oldValue); // We also need to transition the state back to 'not set' before // resuming the coroutine. This operation needs to be 'acquire' @@ -54,7 +54,7 @@ namespace cppcoro bool await_ready() const noexcept { return false; } - bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept + bool await_suspend(stdcoro::coroutine_handle<> awaitingCoroutine) noexcept { m_awaitingCoroutine = awaitingCoroutine; @@ -82,7 +82,7 @@ namespace cppcoro private: const single_consumer_async_auto_reset_event& m_event; - std::experimental::coroutine_handle<> m_awaitingCoroutine; + stdcoro::coroutine_handle<> m_awaitingCoroutine; }; return awaiter{ *this }; diff --git a/include/cppcoro/single_consumer_event.hpp b/include/cppcoro/single_consumer_event.hpp index 167311c4..340704dd 100644 --- a/include/cppcoro/single_consumer_event.hpp +++ b/include/cppcoro/single_consumer_event.hpp @@ -6,7 +6,7 @@ #define CPPCORO_SINGLE_CONSUMER_EVENT_HPP_INCLUDED #include -#include +#include namespace cppcoro { @@ -84,7 +84,7 @@ namespace cppcoro return m_event.is_set(); } - bool await_suspend(std::experimental::coroutine_handle<> awaiter) + bool await_suspend(stdcoro::coroutine_handle<> awaiter) { m_event.m_awaiter = awaiter; @@ -120,7 +120,7 @@ namespace cppcoro // by encoding 'not_set' as 0 (nullptr), 'set' as 1 and // 'not_set_consumer_waiting' as a coroutine handle pointer. std::atomic m_state; - std::experimental::coroutine_handle<> m_awaiter; + stdcoro::coroutine_handle<> m_awaiter; }; } diff --git a/include/cppcoro/single_producer_sequencer.hpp b/include/cppcoro/single_producer_sequencer.hpp index d0333111..cb4a7f10 100644 --- a/include/cppcoro/single_producer_sequencer.hpp +++ b/include/cppcoro/single_producer_sequencer.hpp @@ -160,7 +160,7 @@ namespace cppcoro return m_consumerWaitOperation.await_ready(); } - auto await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept + auto await_suspend(stdcoro::coroutine_handle<> awaitingCoroutine) noexcept { return m_consumerWaitOperation.await_suspend(awaitingCoroutine); } @@ -199,7 +199,7 @@ namespace cppcoro return m_consumerWaitOperation.await_ready(); } - auto await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept + auto await_suspend(stdcoro::coroutine_handle<> awaitingCoroutine) noexcept { return m_consumerWaitOperation.await_suspend(awaitingCoroutine); } diff --git a/include/cppcoro/static_thread_pool.hpp b/include/cppcoro/static_thread_pool.hpp index 95ef3c50..028aaa5d 100644 --- a/include/cppcoro/static_thread_pool.hpp +++ b/include/cppcoro/static_thread_pool.hpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include namespace cppcoro { @@ -38,7 +38,7 @@ namespace cppcoro schedule_operation(static_thread_pool* tp) noexcept : m_threadPool(tp) {} bool await_ready() noexcept { return false; } - void await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept; + void await_suspend(stdcoro::coroutine_handle<> awaitingCoroutine) noexcept; void await_resume() noexcept {} private: @@ -46,7 +46,7 @@ namespace cppcoro friend class static_thread_pool; static_thread_pool* m_threadPool; - std::experimental::coroutine_handle<> m_awaitingCoroutine; + stdcoro::coroutine_handle<> m_awaitingCoroutine; schedule_operation* m_next; }; diff --git a/include/cppcoro/task.hpp b/include/cppcoro/task.hpp index 3bff404f..c8ea9f23 100644 --- a/include/cppcoro/task.hpp +++ b/include/cppcoro/task.hpp @@ -18,7 +18,7 @@ #include #include -#include +#include namespace cppcoro { @@ -36,8 +36,8 @@ namespace cppcoro #if CPPCORO_COMPILER_SUPPORTS_SYMMETRIC_TRANSFER template - std::experimental::coroutine_handle<> await_suspend( - std::experimental::coroutine_handle coro) noexcept + stdcoro::coroutine_handle<> await_suspend( + stdcoro::coroutine_handle coro) noexcept { return coro.promise().m_continuation; } @@ -49,7 +49,7 @@ namespace cppcoro // were crashing under x86 optimised builds. template CPPCORO_NOINLINE - void await_suspend(std::experimental::coroutine_handle coroutine) + void await_suspend(stdcoro::coroutine_handle coroutine) { task_promise_base& promise = coroutine.promise(); @@ -79,7 +79,7 @@ namespace cppcoro auto initial_suspend() noexcept { - return std::experimental::suspend_always{}; + return stdcoro::suspend_always{}; } auto final_suspend() noexcept @@ -88,12 +88,12 @@ namespace cppcoro } #if CPPCORO_COMPILER_SUPPORTS_SYMMETRIC_TRANSFER - void set_continuation(std::experimental::coroutine_handle<> continuation) noexcept + void set_continuation(stdcoro::coroutine_handle<> continuation) noexcept { m_continuation = continuation; } #else - bool try_set_continuation(std::experimental::coroutine_handle<> continuation) + bool try_set_continuation(stdcoro::coroutine_handle<> continuation) { m_continuation = continuation; return !m_state.exchange(true, std::memory_order_acq_rel); @@ -102,7 +102,7 @@ namespace cppcoro private: - std::experimental::coroutine_handle<> m_continuation; + stdcoro::coroutine_handle<> m_continuation; #if !CPPCORO_COMPILER_SUPPORTS_SYMMETRIC_TRANSFER // Initially false. Set to true when either a continuation is registered @@ -292,9 +292,9 @@ namespace cppcoro struct awaitable_base { - std::experimental::coroutine_handle m_coroutine; + stdcoro::coroutine_handle m_coroutine; - awaitable_base(std::experimental::coroutine_handle coroutine) noexcept + awaitable_base(stdcoro::coroutine_handle coroutine) noexcept : m_coroutine(coroutine) {} @@ -304,14 +304,14 @@ namespace cppcoro } #if CPPCORO_COMPILER_SUPPORTS_SYMMETRIC_TRANSFER - std::experimental::coroutine_handle<> await_suspend( - std::experimental::coroutine_handle<> awaitingCoroutine) noexcept + stdcoro::coroutine_handle<> await_suspend( + stdcoro::coroutine_handle<> awaitingCoroutine) noexcept { m_coroutine.promise().set_continuation(awaitingCoroutine); return m_coroutine; } #else - bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept + bool await_suspend(stdcoro::coroutine_handle<> awaitingCoroutine) noexcept { // NOTE: We are using the bool-returning version of await_suspend() here // to work around a potential stack-overflow issue if a coroutine @@ -341,7 +341,7 @@ namespace cppcoro : m_coroutine(nullptr) {} - explicit task(std::experimental::coroutine_handle coroutine) + explicit task(stdcoro::coroutine_handle coroutine) : m_coroutine(coroutine) {} @@ -446,7 +446,7 @@ namespace cppcoro private: - std::experimental::coroutine_handle m_coroutine; + stdcoro::coroutine_handle m_coroutine; }; @@ -455,18 +455,18 @@ namespace cppcoro template task task_promise::get_return_object() noexcept { - return task{ std::experimental::coroutine_handle::from_promise(*this) }; + return task{ stdcoro::coroutine_handle::from_promise(*this) }; } inline task task_promise::get_return_object() noexcept { - return task{ std::experimental::coroutine_handle::from_promise(*this) }; + return task{ stdcoro::coroutine_handle::from_promise(*this) }; } template task task_promise::get_return_object() noexcept { - return task{ std::experimental::coroutine_handle::from_promise(*this) }; + return task{ stdcoro::coroutine_handle::from_promise(*this) }; } } diff --git a/include/cppcoro/write_only_file.hpp b/include/cppcoro/write_only_file.hpp index d3bf627a..e64902f9 100644 --- a/include/cppcoro/write_only_file.hpp +++ b/include/cppcoro/write_only_file.hpp @@ -10,8 +10,6 @@ #include #include -#include - namespace cppcoro { class write_only_file : public writable_file @@ -48,17 +46,14 @@ namespace cppcoro [[nodiscard]] static write_only_file open( io_service& ioService, - const std::experimental::filesystem::path& path, + const stdfs::path& path, file_open_mode openMode = file_open_mode::create_or_open, file_share_mode shareMode = file_share_mode::none, file_buffering_mode bufferingMode = file_buffering_mode::default_); protected: -#if CPPCORO_OS_WINNT - write_only_file(detail::win32::safe_handle&& fileHandle) noexcept; -#endif - + write_only_file(detail::safe_handle&& fileHandle) noexcept; }; } diff --git a/lib/async_auto_reset_event.cpp b/lib/async_auto_reset_event.cpp index 96a2ac98..24b95bcf 100644 --- a/lib/async_auto_reset_event.cpp +++ b/lib/async_auto_reset_event.cpp @@ -241,7 +241,7 @@ cppcoro::async_auto_reset_event_operation::async_auto_reset_event_operation( {} bool cppcoro::async_auto_reset_event_operation::await_suspend( - std::experimental::coroutine_handle<> awaiter) noexcept + stdcoro::coroutine_handle<> awaiter) noexcept { m_awaiter = awaiter; diff --git a/lib/async_manual_reset_event.cpp b/lib/async_manual_reset_event.cpp index 62eebdb3..a8a09341 100644 --- a/lib/async_manual_reset_event.cpp +++ b/lib/async_manual_reset_event.cpp @@ -72,7 +72,7 @@ bool cppcoro::async_manual_reset_event_operation::await_ready() const noexcept } bool cppcoro::async_manual_reset_event_operation::await_suspend( - std::experimental::coroutine_handle<> awaiter) noexcept + stdcoro::coroutine_handle<> awaiter) noexcept { m_awaiter = awaiter; diff --git a/lib/async_mutex.cpp b/lib/async_mutex.cpp index d426d600..48fce48c 100644 --- a/lib/async_mutex.cpp +++ b/lib/async_mutex.cpp @@ -85,7 +85,7 @@ void cppcoro::async_mutex::unlock() waitersHead->m_awaiter.resume(); } -bool cppcoro::async_mutex_lock_operation::await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept +bool cppcoro::async_mutex_lock_operation::await_suspend(stdcoro::coroutine_handle<> awaiter) noexcept { m_awaiter = awaiter; diff --git a/lib/file.cpp b/lib/file.cpp index 3583217f..807a81ca 100644 --- a/lib/file.cpp +++ b/lib/file.cpp @@ -14,12 +14,17 @@ # define WIN32_LEAN_AND_MEAN # endif # include +#elif CPPCORO_OS_LINUX +#include +#include +#include +#include #endif cppcoro::file::~file() {} -std::uint64_t cppcoro::file::size() const +std::size_t cppcoro::file::size() const { #if CPPCORO_OS_WINNT LARGE_INTEGER size; @@ -36,22 +41,62 @@ std::uint64_t cppcoro::file::size() const } return size.QuadPart; +#else + struct stat st; + + if (fstat(m_fileHandle.fd(), &st) < 0) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "error getting file size: fstat" + }; + } + if (S_ISREG(st.st_mode)) + { + return st.st_size; + } + else if (S_ISBLK(st.st_mode)) + { + unsigned long long bytes; + + if (ioctl(m_fileHandle.fd(), BLKGETSIZE64, &bytes) != 0) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "error getting file size: ioctl" + }; + } + + return bytes; + } + + throw std::system_error + { + static_cast(errno), + std::system_category(), + "error getting file size" + }; #endif } -cppcoro::file::file(detail::win32::safe_handle&& fileHandle) noexcept +cppcoro::file::file(detail::safe_handle&& fileHandle) noexcept : m_fileHandle(std::move(fileHandle)) { } -cppcoro::detail::win32::safe_handle cppcoro::file::open( - detail::win32::dword_t fileAccess, +cppcoro::detail::safe_handle cppcoro::file::open( + detail::dword_t fileAccess, io_service& ioService, - const std::experimental::filesystem::path& path, + const stdfs::path& path, file_open_mode openMode, file_share_mode shareMode, file_buffering_mode bufferingMode) { +#if CPPCORO_OS_WINNT DWORD flags = FILE_FLAG_OVERLAPPED; if ((bufferingMode & file_buffering_mode::random_access) == file_buffering_mode::random_access) { @@ -163,6 +208,52 @@ cppcoro::detail::win32::safe_handle cppcoro::file::open( "error opening file: SetFileCompletionNotificationModes" }; } +#elif CPPCORO_OS_LINUX + (void) bufferingMode; // unused yet + + int flags = 0; + + switch (openMode) { + case file_open_mode::create_or_open: + flags = O_CREAT; + break; + case file_open_mode::create_always: + flags = O_CREAT | O_TRUNC; + break; + case file_open_mode::create_new: + flags = O_CREAT | O_EXCL; + break; + case file_open_mode::open_existing: + break; + case file_open_mode::truncate_existing: + flags = O_TRUNC; + break; + } + + switch (shareMode) { + case file_share_mode::read: + flags |= O_RDONLY; + break; + case file_share_mode::write: + flags |= O_WRONLY; + break; + case file_share_mode::read_write: + case file_share_mode::none: + flags |= O_RDWR; + break; + default: + throw std::system_error {0, std::system_category(), "file::open unsupported share_mode"}; + } + + detail::safe_handle fileHandle(::open(path.c_str(), flags, fileAccess)); + if (fileHandle.fd() < 0) + { + throw std::system_error { + errno, + std::generic_category() + }; + } +#endif return std::move(fileHandle); } diff --git a/lib/file_read_operation.cpp b/lib/file_read_operation.cpp index ca274898..403c4158 100644 --- a/lib/file_read_operation.cpp +++ b/lib/file_read_operation.cpp @@ -12,7 +12,7 @@ # include bool cppcoro::file_read_operation_impl::try_start( - cppcoro::detail::win32_overlapped_operation_base& operation) noexcept + cppcoro::detail::io_operation_base& operation) noexcept { const DWORD numberOfBytesToRead = m_byteCount <= 0xFFFFFFFF ? @@ -45,9 +45,28 @@ bool cppcoro::file_read_operation_impl::try_start( } void cppcoro::file_read_operation_impl::cancel( - cppcoro::detail::win32_overlapped_operation_base& operation) noexcept + cppcoro::detail::io_operation_base& operation) noexcept { (void)::CancelIoEx(m_fileHandle, operation.get_overlapped()); } +#elif CPPCORO_OS_LINUX + +bool cppcoro::file_read_operation_impl::try_start( + cppcoro::detail::uring_operation_base& operation) noexcept +{ + const size_t numberOfBytesToRead = + m_byteCount <= std::numeric_limits::max() ? + m_byteCount : std::numeric_limits::max(); + return operation.try_start_read(m_fileHandle, m_buffer, numberOfBytesToRead); +} + +void cppcoro::file_read_operation_impl::cancel( + cppcoro::detail::uring_operation_base& operation) noexcept +{ + if (operation.m_awaitingCoroutine.address() != nullptr) { + operation.cancel_io(); + } +} + #endif // CPPCORO_OS_WINNT diff --git a/lib/file_write_operation.cpp b/lib/file_write_operation.cpp index 68a3ac41..4c18bebf 100644 --- a/lib/file_write_operation.cpp +++ b/lib/file_write_operation.cpp @@ -6,17 +6,16 @@ #include #if CPPCORO_OS_WINNT -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif -# include +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include bool cppcoro::file_write_operation_impl::try_start( - cppcoro::detail::win32_overlapped_operation_base& operation) noexcept + cppcoro::detail::io_operation_base& operation) noexcept { const DWORD numberOfBytesToWrite = - m_byteCount <= 0xFFFFFFFF ? - static_cast(m_byteCount) : DWORD(0xFFFFFFFF); + m_byteCount <= 0xFFFFFFFF ? static_cast(m_byteCount) : DWORD(0xFFFFFFFF); DWORD numberOfBytesWritten = 0; BOOL ok = ::WriteFile( @@ -45,9 +44,28 @@ bool cppcoro::file_write_operation_impl::try_start( } void cppcoro::file_write_operation_impl::cancel( - cppcoro::detail::win32_overlapped_operation_base& operation) noexcept + cppcoro::detail::io_operation_base& operation) noexcept { (void)::CancelIoEx(m_fileHandle, operation.get_overlapped()); } -#endif // CPPCORO_OS_WINNT +#elif CPPCORO_OS_LINUX + +bool cppcoro::file_write_operation_impl::try_start( + cppcoro::detail::uring_operation_base& operation) noexcept +{ + const size_t numberOfBytesToWrite = m_byteCount <= std::numeric_limits::max() + ? m_byteCount + : std::numeric_limits::max(); + return operation.try_start_write(m_fileHandle, m_buffer, numberOfBytesToWrite); +} + +void cppcoro::file_write_operation_impl::cancel( + cppcoro::detail::uring_operation_base& operation) noexcept +{ + if (operation.m_awaitingCoroutine.address() != nullptr) { + operation.cancel_io(); + } +} + +#endif // CPPCORO_OS_WINNT diff --git a/lib/io_service.cpp b/lib/io_service.cpp index 393c3ad9..a7ace60d 100644 --- a/lib/io_service.cpp +++ b/lib/io_service.cpp @@ -6,22 +6,30 @@ #include #include -#include -#include #include +#include +#include #include #if CPPCORO_OS_WINNT -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif -# ifndef NOMINMAX -# define NOMINMAX -# endif -# include -# include -# include -# include +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#include +#include +#endif + + +#if CPPCORO_OS_LINUX +#include +typedef int DWORD; +#define INFINITE (DWORD)-1 //needed for timeout values in io_service::timer_thread_state::run() +typedef long long int LONGLONG; #endif namespace @@ -78,7 +86,7 @@ namespace return cppcoro::detail::win32::safe_handle{ handle }; } #endif -} +} // namespace /// \brief /// A queue of pending timers that supports efficiently determining @@ -115,7 +123,6 @@ class cppcoro::io_service::timer_queue cppcoro::io_service::timed_schedule_operation*& timerList) noexcept; private: - struct timer_entry { timer_entry(cppcoro::io_service::timed_schedule_operation* timer) @@ -293,6 +300,7 @@ void cppcoro::io_service::timer_queue::remove_cancelled_timers( } } +#if CPPCORO_OS_WINNT class cppcoro::io_service::timer_thread_state { public: @@ -320,24 +328,37 @@ class cppcoro::io_service::timer_thread_state std::thread m_thread; }; - - +#endif cppcoro::io_service::io_service() +#if CPPCORO_OS_WINNT : io_service(0) +#elif CPPCORO_OS_LINUX + : io_service(10) +#endif { } -cppcoro::io_service::io_service(std::uint32_t concurrencyHint) +cppcoro::io_service::io_service( +#if CPPCORO_OS_WINNT + std::uint32_t concurrencyHint +#elif CPPCORO_OS_LINUX + size_t queue_length +#endif + ) : m_threadState(0) , m_workCount(0) #if CPPCORO_OS_WINNT , m_iocpHandle(create_io_completion_port(concurrencyHint)) , m_winsockInitialised(false) , m_winsockInitialisationMutex() +#elif CPPCORO_OS_LINUX + , m_uq(queue_length) #endif , m_scheduleOperations(nullptr) +#if CPPCORO_OS_WINNT , m_timerState(nullptr) +#endif { } @@ -346,7 +367,9 @@ cppcoro::io_service::~io_service() assert(m_scheduleOperations.load(std::memory_order_relaxed) == nullptr); assert(m_threadState.load(std::memory_order_relaxed) < active_thread_count_increment); +#if CPPCORO_OS_WINNT delete m_timerState.load(std::memory_order_relaxed); +#endif #if CPPCORO_OS_WINNT if (m_winsockInitialised.load(std::memory_order_relaxed)) @@ -437,8 +460,8 @@ void cppcoro::io_service::stop() noexcept if ((oldState & stop_requested_flag) == 0) { for (auto activeThreadCount = oldState / active_thread_count_increment; - activeThreadCount > 0; - --activeThreadCount) + activeThreadCount > 0; + --activeThreadCount) { post_wake_up_event(); } @@ -471,13 +494,12 @@ void cppcoro::io_service::notify_work_finished() noexcept } } +#if CPPCORO_OS_WINNT cppcoro::detail::win32::handle_t cppcoro::io_service::native_iocp_handle() noexcept { return m_iocpHandle.handle(); } -#if CPPCORO_OS_WINNT - void cppcoro::io_service::ensure_winsock_initialised() { if (!m_winsockInitialised.load(std::memory_order_acquire)) @@ -501,8 +523,14 @@ void cppcoro::io_service::ensure_winsock_initialised() } } } - -#endif // CPPCORO_OS_WINNT +#elif CPPCORO_OS_LINUX +int cppcoro::io_service::submit() noexcept { + return m_uq.submit(); +} +io_uring_sqe *cppcoro::io_service::get_sqe() noexcept { + return m_uq.get_sqe(); +} +#endif // CPPCORO_OS_WINNT void cppcoro::io_service::schedule_impl(schedule_operation* operation) noexcept { @@ -512,6 +540,14 @@ void cppcoro::io_service::schedule_impl(schedule_operation* operation) noexcept 0, reinterpret_cast(operation->m_awaiter.address()), nullptr); +#elif CPPCORO_OS_LINUX + auto sqe = get_sqe(); + io_uring_prep_nop(sqe); + operation->m_message.m_ptr = operation->m_awaiter.address(); + io_uring_sqe_set_data(sqe, &operation->m_message); + int res = submit(); + bool ok = res == 1; +#endif if (!ok) { // Failed to post to the I/O completion port. @@ -526,26 +562,30 @@ void cppcoro::io_service::schedule_impl(schedule_operation* operation) noexcept { operation->m_next = head; } while (!m_scheduleOperations.compare_exchange_weak( - head, - operation, - std::memory_order_release, - std::memory_order_acquire)); + head, operation, std::memory_order_release, std::memory_order_acquire)); } -#endif } void cppcoro::io_service::try_reschedule_overflow_operations() noexcept { + auto* operation = m_scheduleOperations.exchange(nullptr, std::memory_order_acquire); + while (operation != nullptr) + { + auto* next = operation->m_next; #if CPPCORO_OS_WINNT - auto* operation = m_scheduleOperations.exchange(nullptr, std::memory_order_acquire); - while (operation != nullptr) - { - auto* next = operation->m_next; BOOL ok = ::PostQueuedCompletionStatus( m_iocpHandle.handle(), 0, reinterpret_cast(operation->m_awaiter.address()), nullptr); +#else + auto sqe = get_sqe(); + io_uring_prep_nop(sqe); + operation->m_message.m_ptr = operation->m_awaiter.address(); + io_uring_sqe_set_data(sqe, &operation->m_message); + int res = submit(); + bool ok = res == 1; +#endif if (!ok) { // Still unable to queue these operations. @@ -571,7 +611,6 @@ void cppcoro::io_service::try_reschedule_overflow_operations() noexcept operation = next; } -#endif } bool cppcoro::io_service::try_enter_event_loop() noexcept @@ -598,12 +637,12 @@ void cppcoro::io_service::exit_event_loop() noexcept bool cppcoro::io_service::try_process_one_event(bool waitForEvent) { -#if CPPCORO_OS_WINNT if (is_stop_requested()) { return false; } +#if CPPCORO_OS_WINNT const DWORD timeout = waitForEvent ? INFINITE : 0; while (true) @@ -642,8 +681,8 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) { // This was a coroutine scheduled via a call to // io_service::schedule(). - std::experimental::coroutine_handle<>::from_address( - reinterpret_cast(completionKey)).resume(); + stdcoro::coroutine_handle<>::from_address(reinterpret_cast(completionKey)) + .resume(); return true; } @@ -673,6 +712,63 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) }; } } +#else + while (true) + { + try_reschedule_overflow_operations(); + void* message = nullptr; + detail::lnx::message_type type = detail::lnx::RESUME_TYPE; + + bool status = false; + + try + { + // std::lock_guard guard{ m_uq_mux }; + status = m_uq.dequeue(message, type, waitForEvent); + } + catch (std::system_error& err) + { + if (err.code() == std::errc::interrupted && + (m_threadState.load(std::memory_order_relaxed) & stop_requested_flag) == 0) + { + return false; + } + else + { + throw err; + } + } + + if (!status) + { + return false; + } + + if (type == detail::lnx::CALLBACK_TYPE) + { + auto* state = static_cast( + reinterpret_cast(message)); + + state->m_callback(state); + + return true; + } + else + { + if ((unsigned long long)message != 0) + { + auto coro = stdcoro::coroutine_handle<>::from_address(reinterpret_cast(message)); + if (!coro.done()) + coro.resume(); + } + + if (is_stop_requested()) + { + return false; + } + return true; + } + } #endif } @@ -685,9 +781,15 @@ void cppcoro::io_service::post_wake_up_event() noexcept // and the system is out of memory. In this case threads should find other events // in the queue next time they check anyway and thus wake-up. (void)::PostQueuedCompletionStatus(m_iocpHandle.handle(), 0, 0, nullptr); +#else + auto sqe = io_uring_get_sqe(m_uq.handle()); + io_uring_prep_nop(sqe); + io_uring_sqe_set_data(sqe, &m_nopMessage); + io_uring_submit(m_uq.handle()); #endif } +#if CPPCORO_OS_WINNT cppcoro::io_service::timer_thread_state* cppcoro::io_service::ensure_timer_thread_started() { @@ -712,10 +814,8 @@ cppcoro::io_service::ensure_timer_thread_started() } cppcoro::io_service::timer_thread_state::timer_thread_state() -#if CPPCORO_OS_WINNT : m_wakeUpEvent(create_auto_reset_event()) , m_waitableTimerEvent(create_waitable_timer_event()) -#endif , m_newlyQueuedTimers(nullptr) , m_timerCancellationRequested(false) , m_shutDownRequested(false) @@ -742,18 +842,16 @@ void cppcoro::io_service::timer_thread_state::request_timer_cancellation() noexc void cppcoro::io_service::timer_thread_state::run() noexcept { -#if CPPCORO_OS_WINNT using clock = std::chrono::high_resolution_clock; using time_point = clock::time_point; timer_queue timerQueue; +#if CPPCORO_OS_WINNT const DWORD waitHandleCount = 2; - const HANDLE waitHandles[waitHandleCount] = - { - m_wakeUpEvent.handle(), - m_waitableTimerEvent.handle() - }; + const HANDLE waitHandles[waitHandleCount] = { m_wakeUpEvent.handle(), + m_waitableTimerEvent.handle() }; +#endif time_point lastSetWaitEventTime = time_point::max(); @@ -762,13 +860,25 @@ void cppcoro::io_service::timer_thread_state::run() noexcept DWORD timeout = INFINITE; while (!m_shutDownRequested.load(std::memory_order_relaxed)) { + bool waitEvent = false; + bool timerEvent = false; +#if CPPCORO_OS_WINNT const DWORD waitResult = ::WaitForMultipleObjectsEx( waitHandleCount, waitHandles, - FALSE, // waitAll + FALSE, // waitAll timeout, - FALSE); // alertable + FALSE); // alertable if (waitResult == WAIT_OBJECT_0 || waitResult == WAIT_FAILED) + { + waitEvent = true; + } + else if (waitResult == WAIT_OBJECT_0 + 1) + { + timerEvent = true; + } +#endif + if (waitEvent) { // Wake-up event (WAIT_OBJECT_0) // @@ -805,7 +915,7 @@ void cppcoro::io_service::timer_thread_state::run() noexcept } } } - else if (waitResult == (WAIT_OBJECT_0 + 1)) + else if (timerEvent) { lastSetWaitEventTime = time_point::max(); } @@ -831,8 +941,10 @@ void cppcoro::io_service::timer_thread_state::run() noexcept auto timeUntilNextDueTime = earliestDueTime - currentTime; // Negative value indicates relative time. +#if CPPCORO_OS_WINNT LARGE_INTEGER dueTime; - dueTime.QuadPart = -std::chrono::duration_cast(timeUntilNextDueTime).count(); + dueTime.QuadPart = + -std::chrono::duration_cast(timeUntilNextDueTime).count(); // Period of 0 indicates no repeat on the timer. const LONG period = 0; @@ -848,6 +960,7 @@ void cppcoro::io_service::timer_thread_state::run() noexcept nullptr, nullptr, resumeFromSuspend); +#endif if (ok) { lastSetWaitEventTime = earliestDueTime; @@ -870,7 +983,8 @@ void cppcoro::io_service::timer_thread_state::run() noexcept { timeout = static_cast( std::chrono::duration_cast( - timeUntilNextDueTime).count()); + timeUntilNextDueTime) + .count()); } else { @@ -900,18 +1014,21 @@ void cppcoro::io_service::timer_thread_state::run() noexcept timersReadyToResume = nextTimer; } } -#endif } void cppcoro::io_service::timer_thread_state::wake_up_timer_thread() noexcept { #if CPPCORO_OS_WINNT (void)::SetEvent(m_wakeUpEvent.handle()); +#elif CPPCORO_OS_LINUX + uint64_t count = 1; + (void)write(m_wakeupfd.fd(), &count, sizeof(uint64_t)); #endif } +#endif void cppcoro::io_service::schedule_operation::await_suspend( - std::experimental::coroutine_handle<> awaiter) noexcept + stdcoro::coroutine_handle<> awaiter) noexcept { m_awaiter = awaiter; m_service.schedule_impl(this); @@ -926,6 +1043,13 @@ cppcoro::io_service::timed_schedule_operation::timed_schedule_operation( , m_cancellationToken(std::move(cancellationToken)) , m_refCount(2) { +#if CPPCORO_OS_LINUX + m_cancellationRegistration.emplace(std::move(m_cancellationToken), [&service, this] { + auto sqe = service.get_sqe(); + io_uring_prep_cancel(sqe, &m_message, 0); + service.submit(); + }); +#endif } cppcoro::io_service::timed_schedule_operation::timed_schedule_operation( @@ -943,16 +1067,42 @@ cppcoro::io_service::timed_schedule_operation::~timed_schedule_operation() bool cppcoro::io_service::timed_schedule_operation::await_ready() const noexcept { + std::chrono::nanoseconds ns; return m_cancellationToken.is_cancellation_requested(); } +#if CPPCORO_OS_LINUX +template +constexpr __kernel_timespec duration_to_timespec(std::chrono::duration<_Rep, _Period> dur) +{ + auto ns = std::chrono::duration_cast(dur); + auto secs = std::chrono::duration_cast(dur); + ns -= secs; + + return {secs.count(), ns.count()}; +} + +#include + +template +constexpr __kernel_timespec timepoint_to_timespec(std::chrono::time_point<_Clock, _Dur> tp) +{ + auto secs = std::chrono::time_point_cast(tp); + auto ns = std::chrono::time_point_cast(tp) - + std::chrono::time_point_cast(secs); + + return {secs.time_since_epoch().count(), ns.count()}; +} +#endif + void cppcoro::io_service::timed_schedule_operation::await_suspend( - std::experimental::coroutine_handle<> awaiter) + stdcoro::coroutine_handle<> awaiter) { m_scheduleOperation.m_awaiter = awaiter; auto& service = m_scheduleOperation.m_service; +#if CPPCORO_OS_WINNT // Ensure the timer state is initialised and the timer thread started. auto* timerState = service.ensure_timer_thread_started(); @@ -978,7 +1128,6 @@ void cppcoro::io_service::timed_schedule_operation::await_suspend( // rescheduled. Whichever thread decrements the ref-count to 0 // is responsible for scheduling the awaiter for resumption. - // Not sure if we need 'acquire' semantics on this load and // on the failure-case of the compare_exchange below. // @@ -1000,6 +1149,15 @@ void cppcoro::io_service::timed_schedule_operation::await_suspend( { timerState->wake_up_timer_thread(); } +#elif CPPCORO_OS_LINUX + auto sqe = service.get_sqe(); + auto timout = m_resumeTime - std::chrono::high_resolution_clock::now();; + auto ts = duration_to_timespec(timout); + io_uring_prep_timeout(sqe, &ts, 1, 0); + m_message.m_ptr = m_scheduleOperation.m_awaiter.address(); + io_uring_sqe_set_data(sqe, &m_message); + service.submit(); +#endif // Use 'acquire' semantics here to synchronise with the 'release' // operation performed on the timer thread to ensure that we have @@ -1009,7 +1167,7 @@ void cppcoro::io_service::timed_schedule_operation::await_suspend( // thread or whatever I/O thread resumes the coroutine. if (m_refCount.fetch_sub(1, std::memory_order_acquire) == 1) { - service.schedule_impl(&m_scheduleOperation); + service.schedule_impl(&m_scheduleOperation); } } @@ -1017,4 +1175,15 @@ void cppcoro::io_service::timed_schedule_operation::await_resume() { m_cancellationRegistration.reset(); m_cancellationToken.throw_if_cancellation_requested(); +#if CPPCORO_OS_LINUX + if (m_message.m_result == -ETIME) { + } else if (m_message.m_result == -ECANCELED) { + throw operation_cancelled{}; + } else if (m_message.m_result < 0) { + throw std::system_error { + -m_message.m_result, + std::generic_category() + }; + } +#endif } diff --git a/lib/linux.cpp b/lib/linux.cpp new file mode 100644 index 00000000..297115f0 --- /dev/null +++ b/lib/linux.cpp @@ -0,0 +1,112 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Microsoft +// Licenced under MIT license. See LICENSE.txt for details. +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace cppcoro { + namespace detail { + namespace lnx { + uring_queue::uring_queue(size_t queue_length, uint32_t flags) { + check_required_kernel(5, 4, "initializing uring library"); + auto err = io_uring_queue_init(queue_length, &ring_, flags); + if (err < 0) { + throw std::system_error + { + static_cast(-err), + std::system_category(), + "Error initializing uring" + }; + } + } + + uring_queue::~uring_queue() noexcept { + io_uring_queue_exit(&ring_); + } + + io_uring_sqe *uring_queue::get_sqe() noexcept { + m_inMux.lock(); + return io_uring_get_sqe(&ring_); + } + + int uring_queue::submit() noexcept { +// std::lock_guard guard(m_mux); + int res = io_uring_submit(&ring_); + m_inMux.unlock(); + return res; + + } + + bool uring_queue::dequeue(void *&msg, message_type &type, bool wait) { + std::lock_guard guard(m_outMux); + io_uring_cqe *cqe; + int ret; + if (wait) + ret = io_uring_wait_cqe(&ring_, &cqe); + else ret = io_uring_peek_cqe(&ring_, &cqe); + if (ret == -EAGAIN) { + return false; + } else if (ret < 0) { + throw std::system_error{-ret, + std::system_category(), + std::string{"io_uring_peek_cqe failed"}}; + } else + { + auto res = cqe->res; + auto msg_ptr = reinterpret_cast(io_uring_cqe_get_data(cqe)); + if (msg_ptr != nullptr) + { + msg = msg_ptr->m_ptr; + type = msg_ptr->m_type; + msg_ptr->m_result = res; + } + io_uring_cqe_seen(&ring_, cqe); + return true; // completed + } + } + + void safe_fd::close() noexcept { + if (m_fd != -1) { + ::close(m_fd); + m_fd = -1; + } + } + + static auto get_kernel_version() { + utsname buffer; + if (uname(&buffer) != 0) { + throw std::system_error( + errno, + std::generic_category() + ); + } + int major, minor; + const char *release = &buffer.release[0]; + const char *release_end = release + _UTSNAME_RELEASE_LENGTH; + release = std::from_chars(release, release_end, major).ptr; + ++release; + release = std::from_chars(release, release_end, minor).ptr; + return std::tuple{major, minor}; + } + + void check_required_kernel(int major, int minor, std::string_view message) { + static auto[detected_major, detected_minor] = get_kernel_version(); + if (detected_major < major + or (detected_major <= major + and detected_minor < minor)) { + throw std::runtime_error(std::string(message.begin(), message.end()) + + ": invalid kernel detected, " + std::to_string(major) + '.' + std::to_string(minor) + + " required, " + std::to_string(detected_major) + '.' + std::to_string(detected_minor) + " detected."); + } + } + } + } +} diff --git a/lib/read_only_file.cpp b/lib/read_only_file.cpp index 614aa48a..525e4030 100644 --- a/lib/read_only_file.cpp +++ b/lib/read_only_file.cpp @@ -3,34 +3,40 @@ // Licenced under MIT license. See LICENSE.txt for details. /////////////////////////////////////////////////////////////////////////////// -#include +#include #if CPPCORO_OS_WINNT # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif # include +#elif CPPCORO_OS_LINUX +#define GENERIC_READ (S_IRUSR | S_IRGRP | S_IROTH) +#endif cppcoro::read_only_file cppcoro::read_only_file::open( io_service& ioService, - const std::experimental::filesystem::path& path, + const stdfs::path& path, file_share_mode shareMode, file_buffering_mode bufferingMode) { - return read_only_file(file::open( + read_only_file file(file::open( GENERIC_READ, ioService, path, file_open_mode::open_existing, shareMode, bufferingMode)); +#ifdef CPPCORO_OS_LINUX + file.m_ioService = &ioService; +#endif + return std::move(file); } cppcoro::read_only_file::read_only_file( - detail::win32::safe_handle&& fileHandle) noexcept + detail::safe_handle&& fileHandle) noexcept : file(std::move(fileHandle)) - , readable_file(detail::win32::safe_handle{}) + , readable_file(detail::safe_handle{}) { } -#endif diff --git a/lib/read_write_file.cpp b/lib/read_write_file.cpp index 231e6790..f6d31dd8 100644 --- a/lib/read_write_file.cpp +++ b/lib/read_write_file.cpp @@ -3,36 +3,42 @@ // Licenced under MIT license. See LICENSE.txt for details. /////////////////////////////////////////////////////////////////////////////// -#include +#include #if CPPCORO_OS_WINNT # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif # include +#else +#define GENERIC_READ (S_IRUSR | S_IRGRP | S_IROTH) +#define GENERIC_WRITE (S_IWUSR | S_IWGRP /* | S_IWOTH */) +#endif cppcoro::read_write_file cppcoro::read_write_file::open( io_service& ioService, - const std::experimental::filesystem::path& path, + const stdfs::path& path, file_open_mode openMode, file_share_mode shareMode, file_buffering_mode bufferingMode) { - return read_write_file(file::open( + auto file = read_write_file(file::open( GENERIC_READ | GENERIC_WRITE, ioService, path, openMode, shareMode, bufferingMode)); +#ifdef CPPCORO_OS_LINUX + file.m_ioService = &ioService; +#endif + return std::move(file); } cppcoro::read_write_file::read_write_file( - detail::win32::safe_handle&& fileHandle) noexcept + detail::safe_handle&& fileHandle) noexcept : file(std::move(fileHandle)) - , readable_file(detail::win32::safe_handle{}) - , writable_file(detail::win32::safe_handle{}) + , readable_file(detail::safe_handle{}) + , writable_file(detail::safe_handle{}) { } - -#endif diff --git a/lib/readable_file.cpp b/lib/readable_file.cpp index 12b90f2b..96a946ba 100644 --- a/lib/readable_file.cpp +++ b/lib/readable_file.cpp @@ -5,14 +5,15 @@ #include -#if CPPCORO_OS_WINNT - cppcoro::file_read_operation cppcoro::readable_file::read( std::uint64_t offset, void* buffer, std::size_t byteCount) const noexcept { return file_read_operation( +#if CPPCORO_OS_LINUX + *m_ioService, +#endif m_fileHandle.handle(), offset, buffer, @@ -26,11 +27,12 @@ cppcoro::file_read_operation_cancellable cppcoro::readable_file::read( cancellation_token ct) const noexcept { return file_read_operation_cancellable( +#if CPPCORO_OS_LINUX + *m_ioService, +#endif m_fileHandle.handle(), offset, buffer, byteCount, std::move(ct)); } - -#endif diff --git a/lib/socket.cpp b/lib/socket.cpp index fd438a19..0ff90947 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -17,20 +17,25 @@ #include "socket_helpers.hpp" #if CPPCORO_OS_WINNT -# include -# include -# include -# include +#include +#include +#define last_error WSAGetLastError() +#elif CPPCORO_OS_LINUX +#include +#include +#include +#include + +#define last_error errno +#endif namespace { namespace local { - std::tuple create_socket( - int addressFamily, - int socketType, - int protocol, - HANDLE ioCompletionPort) +#if CPPCORO_OS_WINNT + std::tuple + create_socket(int addressFamily, int socketType, int protocol, HANDLE ioCompletionPort) { // Enumerate available protocol providers for the specified socket type. @@ -78,7 +83,8 @@ namespace for (int i = 0; i < protocolCount; ++i) { auto& info = infos[i]; - if (info.iAddressFamily == addressFamily && info.iProtocol == protocol && info.iSocketType == socketType) + if (info.iAddressFamily == addressFamily && info.iProtocol == protocol && + info.iSocketType == socketType) { selectedProtocolInfo = &info; break; @@ -97,21 +103,17 @@ namespace const DWORD flags = WSA_FLAG_OVERLAPPED | flagNoInherit; - const SOCKET socketHandle = ::WSASocketW( - addressFamily, socketType, protocol, selectedProtocolInfo, 0, flags); + const SOCKET socketHandle = + ::WSASocketW(addressFamily, socketType, protocol, selectedProtocolInfo, 0, flags); if (socketHandle == INVALID_SOCKET) { const int errorCode = ::WSAGetLastError(); throw std::system_error( - errorCode, - std::system_category(), - "Error creating socket: WSASocketW"); + errorCode, std::system_category(), "Error creating socket: WSASocketW"); } - auto closeSocketOnFailure = cppcoro::on_scope_failure([&] - { - ::closesocket(socketHandle); - }); + auto closeSocketOnFailure = + cppcoro::on_scope_failure([&] { ::closesocket(socketHandle); }); // This is needed on operating systems earlier than Windows 7 to prevent // socket handles from being inherited. On Windows 7 or later this is @@ -129,10 +131,7 @@ namespace // Associate the socket with the I/O completion port. { const HANDLE result = ::CreateIoCompletionPort( - (HANDLE)socketHandle, - ioCompletionPort, - ULONG_PTR(0), - DWORD(0)); + (HANDLE)socketHandle, ioCompletionPort, ULONG_PTR(0), DWORD(0)); if (result == nullptr) { const DWORD errorCode = ::GetLastError(); @@ -153,9 +152,8 @@ namespace completionModeFlags |= FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; } - const BOOL ok = ::SetFileCompletionNotificationModes( - (HANDLE)socketHandle, - completionModeFlags); + const BOOL ok = + ::SetFileCompletionNotificationModes((HANDLE)socketHandle, completionModeFlags); if (!ok) { const DWORD errorCode = ::GetLastError(); @@ -175,7 +173,8 @@ namespace // We expect clients to call Disconnect() or use CloseSend() to cleanly // shut-down connections instead. BOOL value = TRUE; - const int result = ::setsockopt(socketHandle, + const int result = ::setsockopt( + socketHandle, SOL_SOCKET, SO_DONTLINGER, reinterpret_cast(&value), @@ -192,17 +191,34 @@ namespace return std::make_tuple(socketHandle, skipCompletionPortOnSuccess); } - } -} +#else + int create_socket(int domain, int type, int protocol) { + int sock = socket(domain, type, protocol); + if (sock < 0) + { + throw std::system_error( + last_error, std::system_category(), "Error creating socket: socket"); + } + return sock; + } +#endif + } // namespace local +} // namespace cppcoro::net::socket cppcoro::net::socket::create_tcpv4(io_service& ioSvc) { +#if CPPCORO_OS_WINNT ioSvc.ensure_winsock_initialised(); - - auto[socketHandle, skipCompletionPortOnSuccess] = local::create_socket( - AF_INET, SOCK_STREAM, IPPROTO_TCP, ioSvc.native_iocp_handle()); + auto [socketHandle, skipCompletionPortOnSuccess] = + local::create_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, ioSvc.native_iocp_handle()); socket result(socketHandle, skipCompletionPortOnSuccess); + +#elif CPPCORO_OS_LINUX + auto socketHandle = local::create_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + socket result(ioSvc, socketHandle); +#endif + result.m_localEndPoint = ipv4_endpoint(); result.m_remoteEndPoint = ipv4_endpoint(); return result; @@ -210,12 +226,18 @@ cppcoro::net::socket cppcoro::net::socket::create_tcpv4(io_service& ioSvc) cppcoro::net::socket cppcoro::net::socket::create_tcpv6(io_service& ioSvc) { +#if CPPCORO_OS_WINNT ioSvc.ensure_winsock_initialised(); - - auto[socketHandle, skipCompletionPortOnSuccess] = local::create_socket( - AF_INET6, SOCK_STREAM, IPPROTO_TCP, ioSvc.native_iocp_handle()); + auto [socketHandle, skipCompletionPortOnSuccess] = + local::create_socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, ioSvc.native_iocp_handle()); socket result(socketHandle, skipCompletionPortOnSuccess); + +#elif CPPCORO_OS_LINUX + auto socketHandle = local::create_socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + socket result(ioSvc, socketHandle); +#endif + result.m_localEndPoint = ipv6_endpoint(); result.m_remoteEndPoint = ipv6_endpoint(); return result; @@ -223,12 +245,21 @@ cppcoro::net::socket cppcoro::net::socket::create_tcpv6(io_service& ioSvc) cppcoro::net::socket cppcoro::net::socket::create_udpv4(io_service& ioSvc) { +#if CPPCORO_OS_WINNT ioSvc.ensure_winsock_initialised(); - - auto[socketHandle, skipCompletionPortOnSuccess] = local::create_socket( - AF_INET, SOCK_DGRAM, IPPROTO_UDP, ioSvc.native_iocp_handle()); + auto [socketHandle, skipCompletionPortOnSuccess] = + local::create_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, ioSvc.native_iocp_handle()); socket result(socketHandle, skipCompletionPortOnSuccess); + +#elif CPPCORO_OS_LINUX + auto socketHandle = local::create_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + socket result(ioSvc, socketHandle); + // we use MSG_TRUNC flag to detect whatever the incoming datagram + // fits within size, linux does not a MORE_DATA like error code. + result.m_recvFlags = MSG_TRUNC; +#endif + result.m_localEndPoint = ipv4_endpoint(); result.m_remoteEndPoint = ipv4_endpoint(); return result; @@ -236,12 +267,21 @@ cppcoro::net::socket cppcoro::net::socket::create_udpv4(io_service& ioSvc) cppcoro::net::socket cppcoro::net::socket::create_udpv6(io_service& ioSvc) { +#if CPPCORO_OS_WINNT ioSvc.ensure_winsock_initialised(); - - auto[socketHandle, skipCompletionPortOnSuccess] = local::create_socket( - AF_INET6, SOCK_DGRAM, IPPROTO_UDP, ioSvc.native_iocp_handle()); + auto [socketHandle, skipCompletionPortOnSuccess] = + local::create_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, ioSvc.native_iocp_handle()); socket result(socketHandle, skipCompletionPortOnSuccess); + +#elif CPPCORO_OS_LINUX + auto socketHandle = local::create_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + socket result(ioSvc, socketHandle); + // we use MSG_TRUNC flag to detect whatever the incoming datagram + // fits within size, linux does not a MORE_DATA like error code. + result.m_recvFlags = MSG_TRUNC; +#endif + result.m_localEndPoint = ipv6_endpoint(); result.m_remoteEndPoint = ipv6_endpoint(); return result; @@ -249,10 +289,16 @@ cppcoro::net::socket cppcoro::net::socket::create_udpv6(io_service& ioSvc) cppcoro::net::socket::socket(socket&& other) noexcept : m_handle(std::exchange(other.m_handle, INVALID_SOCKET)) +#if CPPCORO_OS_WINNT , m_skipCompletionOnSuccess(other.m_skipCompletionOnSuccess) +#elif CPPCORO_OS_LINUX + , m_ioService(other.m_ioService) + , m_recvFlags(other.m_recvFlags) +#endif , m_localEndPoint(std::move(other.m_localEndPoint)) , m_remoteEndPoint(std::move(other.m_remoteEndPoint)) -{} +{ +} cppcoro::net::socket::~socket() { @@ -262,8 +308,7 @@ cppcoro::net::socket::~socket() } } -cppcoro::net::socket& -cppcoro::net::socket::operator=(socket&& other) noexcept +cppcoro::net::socket& cppcoro::net::socket::operator=(socket&& other) noexcept { auto handle = std::exchange(other.m_handle, INVALID_SOCKET); if (m_handle != INVALID_SOCKET) @@ -272,7 +317,11 @@ cppcoro::net::socket::operator=(socket&& other) noexcept } m_handle = handle; +#if CPPCORO_OS_WINNT m_skipCompletionOnSuccess = other.m_skipCompletionOnSuccess; +#elif CPPCORO_OS_LINUX + m_recvFlags = other.m_recvFlags; +#endif m_localEndPoint = other.m_localEndPoint; m_remoteEndPoint = other.m_remoteEndPoint; @@ -288,14 +337,14 @@ void cppcoro::net::socket::bind(const ip_endpoint& localEndPoint) SOCKADDR_IN& ipv4Sockaddr = *reinterpret_cast(sockaddr); ipv4Sockaddr.sin_family = AF_INET; std::memcpy(&ipv4Sockaddr.sin_addr, localEndPoint.to_ipv4().address().bytes(), 4); - ipv4Sockaddr.sin_port = localEndPoint.to_ipv4().port(); + ipv4Sockaddr.sin_port = htons(localEndPoint.to_ipv4().port()); } else { SOCKADDR_IN6& ipv6Sockaddr = *reinterpret_cast(sockaddr); ipv6Sockaddr.sin6_family = AF_INET6; std::memcpy(&ipv6Sockaddr.sin6_addr, localEndPoint.to_ipv6().address().bytes(), 16); - ipv6Sockaddr.sin6_port = localEndPoint.to_ipv6().port(); + ipv6Sockaddr.sin6_port = htons(localEndPoint.to_ipv6().port()); } int result = ::bind(m_handle, sockaddr, sizeof(sockaddrStorage)); @@ -311,14 +360,10 @@ void cppcoro::net::socket::bind(const ip_endpoint& localEndPoint) // WSAEINVAL: socket already bound // WSAENOBUFS: system failed to allocate memory // WSAENOTSOCK: socket was not a valid socket. - int errorCode = ::WSAGetLastError(); - throw std::system_error( - errorCode, - std::system_category(), - "Error binding to endpoint: bind()"); + throw std::system_error(last_error, std::system_category(), "Error binding to endpoint: bind()"); } - int sockaddrLen = sizeof(sockaddrStorage); + socklen_t sockaddrLen = sizeof(sockaddrStorage); result = ::getsockname(m_handle, sockaddr, &sockaddrLen); if (result == 0) { @@ -335,11 +380,8 @@ void cppcoro::net::socket::listen() int result = ::listen(m_handle, SOMAXCONN); if (result != 0) { - int errorCode = ::WSAGetLastError(); throw std::system_error( - errorCode, - std::system_category(), - "Failed to start listening on bound endpoint: listen"); + last_error, std::system_category(), "Failed to start listening on bound endpoint: listen"); } } @@ -364,96 +406,176 @@ void cppcoro::net::socket::listen(std::uint32_t backlog) // WSAENOTSOCK: socket was not a valid socket. // WSAEOPNOTSUPP: The socket does not support listening - int errorCode = ::WSAGetLastError(); throw std::system_error( - errorCode, - std::system_category(), - "Failed to start listening on bound endpoint: listen"); + last_error, std::system_category(), "Failed to start listening on bound endpoint: listen"); } } -cppcoro::net::socket_accept_operation -cppcoro::net::socket::accept(socket& acceptingSocket) noexcept +cppcoro::net::socket_accept_operation cppcoro::net::socket::accept(socket& acceptingSocket) noexcept { - return socket_accept_operation{ *this, acceptingSocket }; + return socket_accept_operation + { +#if CPPCORO_OS_LINUX + m_ioService, +#endif + *this, acceptingSocket + }; } cppcoro::net::socket_accept_operation_cancellable cppcoro::net::socket::accept(socket& acceptingSocket, cancellation_token ct) noexcept { - return socket_accept_operation_cancellable{ *this, acceptingSocket, std::move(ct) }; + return socket_accept_operation_cancellable + { +#if CPPCORO_OS_LINUX + m_ioService, +#endif + *this, acceptingSocket, std::move(ct) + }; } cppcoro::net::socket_connect_operation cppcoro::net::socket::connect(const ip_endpoint& remoteEndPoint) noexcept { - return socket_connect_operation{ *this, remoteEndPoint }; + return socket_connect_operation + { +#if CPPCORO_OS_LINUX + m_ioService, +#endif + *this, remoteEndPoint + }; } cppcoro::net::socket_connect_operation_cancellable cppcoro::net::socket::connect(const ip_endpoint& remoteEndPoint, cancellation_token ct) noexcept { - return socket_connect_operation_cancellable{ *this, remoteEndPoint, std::move(ct) }; + return socket_connect_operation_cancellable + { +#if CPPCORO_OS_LINUX + m_ioService, +#endif + *this, remoteEndPoint, std::move(ct) + }; } -cppcoro::net::socket_disconnect_operation -cppcoro::net::socket::disconnect() noexcept +cppcoro::net::socket_disconnect_operation cppcoro::net::socket::disconnect() noexcept { - return socket_disconnect_operation(*this); + return socket_disconnect_operation( +#if CPPCORO_OS_LINUX + m_ioService, +#endif + *this); } cppcoro::net::socket_disconnect_operation_cancellable cppcoro::net::socket::disconnect(cancellation_token ct) noexcept { - return socket_disconnect_operation_cancellable{ *this, std::move(ct) }; + return socket_disconnect_operation_cancellable + { +#if CPPCORO_OS_LINUX + m_ioService, +#endif + *this, std::move(ct) + }; } cppcoro::net::socket_send_operation cppcoro::net::socket::send(const void* buffer, std::size_t byteCount) noexcept { - return socket_send_operation{ *this, buffer, byteCount }; + return socket_send_operation + { +#if CPPCORO_OS_LINUX + m_ioService, +#endif + *this, buffer, byteCount + }; } -cppcoro::net::socket_send_operation_cancellable -cppcoro::net::socket::send(const void* buffer, std::size_t byteCount, cancellation_token ct) noexcept +cppcoro::net::socket_send_operation_cancellable cppcoro::net::socket::send( + const void* buffer, std::size_t byteCount, cancellation_token ct) noexcept { - return socket_send_operation_cancellable{ *this, buffer, byteCount, std::move(ct) }; + return socket_send_operation_cancellable + { +#if CPPCORO_OS_LINUX + m_ioService, +#endif + *this, buffer, byteCount, std::move(ct) + }; } cppcoro::net::socket_recv_operation cppcoro::net::socket::recv(void* buffer, std::size_t byteCount) noexcept { - return socket_recv_operation{ *this, buffer, byteCount }; + return socket_recv_operation + { +#if CPPCORO_OS_LINUX + m_ioService, +#endif + *this, buffer, byteCount + }; } cppcoro::net::socket_recv_operation_cancellable cppcoro::net::socket::recv(void* buffer, std::size_t byteCount, cancellation_token ct) noexcept { - return socket_recv_operation_cancellable{ *this, buffer, byteCount, std::move(ct) }; + return socket_recv_operation_cancellable + { +#if CPPCORO_OS_LINUX + m_ioService, +#endif + *this, buffer, byteCount, std::move(ct) + }; } cppcoro::net::socket_recv_from_operation cppcoro::net::socket::recv_from(void* buffer, std::size_t byteCount) noexcept { - return socket_recv_from_operation{ *this, buffer, byteCount }; + return socket_recv_from_operation + { +#if CPPCORO_OS_LINUX + m_ioService, +#endif + *this, buffer, byteCount + }; } cppcoro::net::socket_recv_from_operation_cancellable cppcoro::net::socket::recv_from(void* buffer, std::size_t byteCount, cancellation_token ct) noexcept { - return socket_recv_from_operation_cancellable{ *this, buffer, byteCount, std::move(ct) }; + return socket_recv_from_operation_cancellable + { +#if CPPCORO_OS_LINUX + m_ioService, +#endif + *this, buffer, byteCount, std::move(ct) + }; } -cppcoro::net::socket_send_to_operation -cppcoro::net::socket::send_to(const ip_endpoint& destination, const void* buffer, std::size_t byteCount) noexcept +cppcoro::net::socket_send_to_operation cppcoro::net::socket::send_to( + const ip_endpoint& destination, const void* buffer, std::size_t byteCount) noexcept { - return socket_send_to_operation{ *this, destination, buffer, byteCount }; + return socket_send_to_operation + { +#if CPPCORO_OS_LINUX + m_ioService, +#endif + *this, destination, buffer, byteCount + }; } -cppcoro::net::socket_send_to_operation_cancellable -cppcoro::net::socket::send_to(const ip_endpoint& destination, const void* buffer, std::size_t byteCount, cancellation_token ct) noexcept +cppcoro::net::socket_send_to_operation_cancellable cppcoro::net::socket::send_to( + const ip_endpoint& destination, + const void* buffer, + std::size_t byteCount, + cancellation_token ct) noexcept { - return socket_send_to_operation_cancellable{ *this, destination, buffer, byteCount, std::move(ct) }; + return socket_send_to_operation_cancellable + { +#if CPPCORO_OS_LINUX + m_ioService, +#endif + *this, destination, buffer, byteCount, std::move(ct) + }; } void cppcoro::net::socket::close_send() @@ -461,11 +583,8 @@ void cppcoro::net::socket::close_send() int result = ::shutdown(m_handle, SD_SEND); if (result == SOCKET_ERROR) { - int errorCode = ::WSAGetLastError(); throw std::system_error( - errorCode, - std::system_category(), - "failed to close socket send stream: shutdown(SD_SEND)"); + last_error, std::system_category(), "failed to close socket send stream: shutdown(SD_SEND)"); } } @@ -474,20 +593,23 @@ void cppcoro::net::socket::close_recv() int result = ::shutdown(m_handle, SD_RECEIVE); if (result == SOCKET_ERROR) { - int errorCode = ::WSAGetLastError(); throw std::system_error( - errorCode, + last_error, std::system_category(), "failed to close socket receive stream: shutdown(SD_RECEIVE)"); } } +#if CPPCORO_OS_WINNT cppcoro::net::socket::socket( - cppcoro::detail::win32::socket_t handle, - bool skipCompletionOnSuccess) noexcept + cppcoro::detail::win32::socket_t handle, bool skipCompletionOnSuccess) noexcept : m_handle(handle) , m_skipCompletionOnSuccess(skipCompletionOnSuccess) +#elif CPPCORO_OS_LINUX +cppcoro::net::socket::socket( + cppcoro::io_service& ioService, cppcoro::detail::lnx::fd_t handle) noexcept + : m_handle(handle) + , m_ioService(ioService) +#endif { } - -#endif diff --git a/lib/socket_accept_operation.cpp b/lib/socket_accept_operation.cpp index b65ab142..4712dc49 100644 --- a/lib/socket_accept_operation.cpp +++ b/lib/socket_accept_operation.cpp @@ -20,110 +20,146 @@ // and socket_accept_operation_cancellable. bool cppcoro::net::socket_accept_operation_impl::try_start( - cppcoro::detail::win32_overlapped_operation_base& operation) noexcept + cppcoro::detail::win32_overlapped_operation_base& operation) noexcept { - static_assert( - (sizeof(m_addressBuffer) / 2) >= (16 + sizeof(SOCKADDR_IN)) && - (sizeof(m_addressBuffer) / 2) >= (16 + sizeof(SOCKADDR_IN6)), - "AcceptEx requires address buffer to be at least 16 bytes more than largest address."); - - // Need to read this flag before starting the operation, otherwise - // it may be possible that the operation will complete immediately - // on another thread and then destroy the socket before we get a - // chance to read it. - const bool skipCompletionOnSuccess = m_listeningSocket.skip_completion_on_success(); - - DWORD bytesReceived = 0; - BOOL ok = ::AcceptEx( - m_listeningSocket.native_handle(), - m_acceptingSocket.native_handle(), - m_addressBuffer, - 0, - sizeof(m_addressBuffer) / 2, - sizeof(m_addressBuffer) / 2, - &bytesReceived, - operation.get_overlapped()); - if (!ok) - { - int errorCode = ::WSAGetLastError(); - if (errorCode != ERROR_IO_PENDING) - { - operation.m_errorCode = static_cast(errorCode); - return false; - } - } - else if (skipCompletionOnSuccess) - { - operation.m_errorCode = ERROR_SUCCESS; - return false; - } - - return true; + static_assert( + (sizeof(m_addressBuffer) / 2) >= (16 + sizeof(SOCKADDR_IN)) && + (sizeof(m_addressBuffer) / 2) >= (16 + sizeof(SOCKADDR_IN6)), + "AcceptEx requires address buffer to be at least 16 bytes more than largest address."); + + // Need to read this flag before starting the operation, otherwise + // it may be possible that the operation will complete immediately + // on another thread and then destroy the socket before we get a + // chance to read it. + const bool skipCompletionOnSuccess = m_listeningSocket.skip_completion_on_success(); + + DWORD bytesReceived = 0; + BOOL ok = ::AcceptEx( + m_listeningSocket.native_handle(), + m_acceptingSocket.native_handle(), + m_addressBuffer, + 0, + sizeof(m_addressBuffer) / 2, + sizeof(m_addressBuffer) / 2, + &bytesReceived, + operation.get_overlapped()); + if (!ok) + { + int errorCode = ::WSAGetLastError(); + if (errorCode != ERROR_IO_PENDING) + { + operation.m_errorCode = static_cast(errorCode); + return false; + } + } + else if (skipCompletionOnSuccess) + { + operation.m_errorCode = ERROR_SUCCESS; + return false; + } + + return true; } void cppcoro::net::socket_accept_operation_impl::cancel( - cppcoro::detail::win32_overlapped_operation_base& operation) noexcept + cppcoro::detail::win32_overlapped_operation_base& operation) noexcept { - (void)::CancelIoEx( - reinterpret_cast(m_listeningSocket.native_handle()), - operation.get_overlapped()); + (void)::CancelIoEx( + reinterpret_cast(m_listeningSocket.native_handle()), + operation.get_overlapped()); } void cppcoro::net::socket_accept_operation_impl::get_result( - cppcoro::detail::win32_overlapped_operation_base& operation) + cppcoro::detail::win32_overlapped_operation_base& operation) { - if (operation.m_errorCode != ERROR_SUCCESS) - { - throw std::system_error{ - static_cast(operation.m_errorCode), - std::system_category(), - "Accepting a connection failed: AcceptEx" - }; - } - - sockaddr* localSockaddr = nullptr; - sockaddr* remoteSockaddr = nullptr; - - INT localSockaddrLength; - INT remoteSockaddrLength; - - ::GetAcceptExSockaddrs( - m_addressBuffer, - 0, - sizeof(m_addressBuffer) / 2, - sizeof(m_addressBuffer) / 2, - &localSockaddr, - &localSockaddrLength, - &remoteSockaddr, - &remoteSockaddrLength); - - m_acceptingSocket.m_localEndPoint = - detail::sockaddr_to_ip_endpoint(*localSockaddr); - - m_acceptingSocket.m_remoteEndPoint = - detail::sockaddr_to_ip_endpoint(*remoteSockaddr); - - { - // Need to set SO_UPDATE_ACCEPT_CONTEXT after the accept completes - // to ensure that ::shutdown() and ::setsockopt() calls work on the - // accepted socket. - SOCKET listenSocket = m_listeningSocket.native_handle(); - const int result = ::setsockopt( - m_acceptingSocket.native_handle(), - SOL_SOCKET, - SO_UPDATE_ACCEPT_CONTEXT, - (const char*)&listenSocket, - sizeof(SOCKET)); - if (result == SOCKET_ERROR) - { - const int errorCode = ::WSAGetLastError(); - throw std::system_error{ - errorCode, - std::system_category(), - "Socket accept operation failed: setsockopt(SO_UPDATE_ACCEPT_CONTEXT)" - }; - } - } + if (operation.m_errorCode != ERROR_SUCCESS) + { + throw std::system_error{ + static_cast(operation.m_errorCode), + std::system_category(), + "Accepting a connection failed: AcceptEx" + }; + } + + sockaddr* localSockaddr = nullptr; + sockaddr* remoteSockaddr = nullptr; + + INT localSockaddrLength; + INT remoteSockaddrLength; + + ::GetAcceptExSockaddrs( + m_addressBuffer, + 0, + sizeof(m_addressBuffer) / 2, + sizeof(m_addressBuffer) / 2, + &localSockaddr, + &localSockaddrLength, + &remoteSockaddr, + &remoteSockaddrLength); + + m_acceptingSocket.m_localEndPoint = + detail::sockaddr_to_ip_endpoint(*localSockaddr); + + m_acceptingSocket.m_remoteEndPoint = + detail::sockaddr_to_ip_endpoint(*remoteSockaddr); + + { + // Need to set SO_UPDATE_ACCEPT_CONTEXT after the accept completes + // to ensure that ::shutdown() and ::setsockopt() calls work on the + // accepted socket. + SOCKET listenSocket = m_listeningSocket.native_handle(); + const int result = ::setsockopt( + m_acceptingSocket.native_handle(), + SOL_SOCKET, + SO_UPDATE_ACCEPT_CONTEXT, + (const char*)&listenSocket, + sizeof(SOCKET)); + if (result == SOCKET_ERROR) + { + const int errorCode = ::WSAGetLastError(); + throw std::system_error{ + errorCode, + std::system_category(), + "Socket accept operation failed: setsockopt(SO_UPDATE_ACCEPT_CONTEXT)" + }; + } + } +} + +#else + +bool cppcoro::net::socket_accept_operation_impl::try_start( + cppcoro::detail::io_operation_base &operation) noexcept { + return operation.try_start_accept(m_listeningSocket.native_handle(), &m_addressBuffer[0], &m_addressBufferLength); +} + +void cppcoro::net::socket_accept_operation_impl::cancel( + cppcoro::detail::io_operation_base &operation) noexcept { + operation.cancel_io(); +} + +void cppcoro::net::socket_accept_operation_impl::get_result( + cppcoro::detail::io_operation_base &operation) { + auto fd = operation.get_result(); + m_acceptingSocket = socket(operation.m_ioService, fd); + m_addressBufferLength = sizeof(m_addressBuffer); + if (getpeername(fd, reinterpret_cast(&m_addressBuffer[0]), &m_addressBufferLength) < 0) { + throw std::system_error{ + errno, + std::generic_category() + }; + } + m_acceptingSocket.m_remoteEndPoint = detail::sockaddr_to_ip_endpoint( + std::ref(*reinterpret_cast(&m_addressBuffer[0]))); + m_addressBufferLength = sizeof(m_addressBuffer); + if (getsockname(fd, reinterpret_cast(&m_addressBuffer[0]), &m_addressBufferLength) < 0) { + throw std::system_error{ + errno, + std::generic_category() + }; + } + m_acceptingSocket.m_localEndPoint = detail::sockaddr_to_ip_endpoint( + std::ref(*reinterpret_cast(&m_addressBuffer[0]))); } #endif diff --git a/lib/socket_connect_operation.cpp b/lib/socket_connect_operation.cpp index 3e6036f2..8bee1c23 100644 --- a/lib/socket_connect_operation.cpp +++ b/lib/socket_connect_operation.cpp @@ -20,7 +20,7 @@ # include bool cppcoro::net::socket_connect_operation_impl::try_start( - cppcoro::detail::win32_overlapped_operation_base& operation) noexcept + cppcoro::detail::io_operation_base& operation) noexcept { // Lookup the address of the ConnectEx function pointer for this socket. LPFN_CONNECTEX connectExPtr; @@ -86,7 +86,7 @@ bool cppcoro::net::socket_connect_operation_impl::try_start( } void cppcoro::net::socket_connect_operation_impl::cancel( - cppcoro::detail::win32_overlapped_operation_base& operation) noexcept + cppcoro::detail::io_operation_base& operation) noexcept { (void)::CancelIoEx( reinterpret_cast(m_socket.native_handle()), @@ -94,7 +94,7 @@ void cppcoro::net::socket_connect_operation_impl::cancel( } void cppcoro::net::socket_connect_operation_impl::get_result( - cppcoro::detail::win32_overlapped_operation_base& operation) + cppcoro::detail::io_operation_base& operation) { if (operation.m_errorCode != ERROR_SUCCESS) { @@ -175,4 +175,34 @@ void cppcoro::net::socket_connect_operation_impl::get_result( } } +#else + +bool cppcoro::net::socket_connect_operation_impl::try_start( + cppcoro::detail::io_operation_base& operation) noexcept +{ + SOCKADDR_STORAGE remoteSockaddrStorage; + detail::ip_endpoint_to_sockaddr(m_remoteEndPoint, std::ref(remoteSockaddrStorage)); + return operation.try_start_connect(m_socket.native_handle(), &remoteSockaddrStorage, sizeof(remoteSockaddrStorage)); +} + +void cppcoro::net::socket_connect_operation_impl::cancel( + cppcoro::detail::io_operation_base& operation) noexcept +{ + operation.cancel_io(); +} + +void cppcoro::net::socket_connect_operation_impl::get_result( + cppcoro::detail::io_operation_base& operation) +{ + SOCKADDR_STORAGE remoteSockaddrStorage; + socklen_t remoteSockaddrStorageLength = sizeof(remoteSockaddrStorage); + if(getpeername(m_socket.native_handle(), reinterpret_cast(&remoteSockaddrStorage), &remoteSockaddrStorageLength) < 0) { + throw std::system_error{ + errno, + std::generic_category() + }; + } + m_socket.m_remoteEndPoint = detail::sockaddr_to_ip_endpoint(std::ref(*reinterpret_cast(&remoteSockaddrStorage))); +} + #endif diff --git a/lib/socket_disconnect_operation.cpp b/lib/socket_disconnect_operation.cpp index fc874811..7dc9a678 100644 --- a/lib/socket_disconnect_operation.cpp +++ b/lib/socket_disconnect_operation.cpp @@ -17,7 +17,7 @@ # include bool cppcoro::net::socket_disconnect_operation_impl::try_start( - cppcoro::detail::win32_overlapped_operation_base& operation) noexcept + cppcoro::detail::io_operation_base& operation) noexcept { // Lookup the address of the DisconnectEx function pointer for this socket. LPFN_DISCONNECTEX disconnectExPtr; @@ -79,7 +79,7 @@ bool cppcoro::net::socket_disconnect_operation_impl::try_start( } void cppcoro::net::socket_disconnect_operation_impl::cancel( - cppcoro::detail::win32_overlapped_operation_base& operation) noexcept + cppcoro::detail::io_operation_base& operation) noexcept { (void)::CancelIoEx( reinterpret_cast(m_socket.native_handle()), @@ -87,7 +87,7 @@ void cppcoro::net::socket_disconnect_operation_impl::cancel( } void cppcoro::net::socket_disconnect_operation_impl::get_result( - cppcoro::detail::win32_overlapped_operation_base& operation) + cppcoro::detail::io_operation_base& operation) { if (operation.m_errorCode != ERROR_SUCCESS) { @@ -104,4 +104,23 @@ void cppcoro::net::socket_disconnect_operation_impl::get_result( } } +#elif CPPCORO_OS_LINUX + +bool cppcoro::net::socket_disconnect_operation_impl::try_start( + cppcoro::detail::io_operation_base& operation) noexcept +{ + return operation.try_start_disconnect(m_socket.native_handle()); +} + +void cppcoro::net::socket_disconnect_operation_impl::cancel( + cppcoro::detail::io_operation_base& operation) noexcept +{ + operation.cancel_io(); +} + +void cppcoro::net::socket_disconnect_operation_impl::get_result( + cppcoro::detail::io_operation_base& operation) +{ + operation.get_result(); +} #endif diff --git a/lib/socket_helpers.cpp b/lib/socket_helpers.cpp index f00bc09f..50dd4b54 100644 --- a/lib/socket_helpers.cpp +++ b/lib/socket_helpers.cpp @@ -7,14 +7,15 @@ #include -#if CPPCORO_OS_WINNT #include #include +#if CPPCORO_OS_WINNT #include #include #include #include +#endif cppcoro::net::ip_endpoint @@ -41,7 +42,7 @@ cppcoro::net::detail::sockaddr_to_ip_endpoint(const sockaddr& address) noexcept std::memcpy(&ipv6Address, &address, sizeof(ipv6Address)); return ipv6_endpoint{ - ipv6_address{ ipv6Address.sin6_addr.u.Byte }, + ipv6_address{ ipv6Address.sin6_addr.s6_addr }, ntohs(ipv6Address.sin6_port) }; } @@ -74,12 +75,14 @@ int cppcoro::net::detail::ip_endpoint_to_sockaddr( std::memcpy(&ipv6Address.sin6_addr, ipv6EndPoint.address().bytes(), 16); ipv6Address.sin6_port = htons(ipv6EndPoint.port()); ipv6Address.sin6_flowinfo = 0; - ipv6Address.sin6_scope_struct = SCOPEID_UNSPECIFIED_INIT; +#if CPPCORO_OS_WINNT + ipv6Address.sin6_scope_struct = SCOPEID_UNSPECIFIED_INIT; +#else + ipv6Address.sin6_scope_id = 0; +#endif std::memcpy(&address.get(), &ipv6Address, sizeof(ipv6Address)); return sizeof(SOCKADDR_IN6); } } - -#endif // CPPCORO_OS_WINNT diff --git a/lib/socket_helpers.hpp b/lib/socket_helpers.hpp index 2083f3a0..cc0d89ee 100644 --- a/lib/socket_helpers.hpp +++ b/lib/socket_helpers.hpp @@ -11,6 +11,18 @@ # include struct sockaddr; struct sockaddr_storage; +#else +# include +# define SD_RECEIVE SHUT_RD +# define SD_SEND SHUT_WR +# define INVALID_SOCKET -1 +# define SOCKET_ERROR -1 +# define SOCKADDR_STORAGE struct sockaddr_storage +# define SOCKADDR struct sockaddr +# define SOCKADDR_IN struct sockaddr_in +# define SOCKADDR_IN6 struct sockaddr_in6 +# define closesocket(__handle) close((__handle)) +# include #endif namespace cppcoro @@ -21,7 +33,6 @@ namespace cppcoro namespace detail { -#if CPPCORO_OS_WINNT /// Convert a sockaddr to an IP endpoint. ip_endpoint sockaddr_to_ip_endpoint(const sockaddr& address) noexcept; @@ -38,8 +49,6 @@ namespace cppcoro int ip_endpoint_to_sockaddr( const ip_endpoint& endPoint, std::reference_wrapper address) noexcept; - -#endif } } } diff --git a/lib/socket_recv_from_operation.cpp b/lib/socket_recv_from_operation.cpp index 8e994ec6..b2769872 100644 --- a/lib/socket_recv_from_operation.cpp +++ b/lib/socket_recv_from_operation.cpp @@ -3,94 +3,125 @@ // Licenced under MIT license. See LICENSE.txt for details. /////////////////////////////////////////////////////////////////////////////// -#include #include +#include + +#include "socket_helpers.hpp" #if CPPCORO_OS_WINNT -# include "socket_helpers.hpp" -# include -# include -# include -# include +#include +#include bool cppcoro::net::socket_recv_from_operation_impl::try_start( - cppcoro::detail::win32_overlapped_operation_base& operation) noexcept + cppcoro::detail::io_operation_base& operation) noexcept { - static_assert( - sizeof(m_sourceSockaddrStorage) >= sizeof(SOCKADDR_IN) && - sizeof(m_sourceSockaddrStorage) >= sizeof(SOCKADDR_IN6)); - static_assert( - sockaddrStorageAlignment >= alignof(SOCKADDR_IN) && - sockaddrStorageAlignment >= alignof(SOCKADDR_IN6)); - - // Need to read this flag before starting the operation, otherwise - // it may be possible that the operation will complete immediately - // on another thread, resume the coroutine and then destroy the - // socket before we get a chance to read it. - const bool skipCompletionOnSuccess = m_socket.skip_completion_on_success(); - - m_sourceSockaddrLength = sizeof(m_sourceSockaddrStorage); - - DWORD numberOfBytesReceived = 0; - DWORD flags = 0; - int result = ::WSARecvFrom( - m_socket.native_handle(), - reinterpret_cast(&m_buffer), - 1, // buffer count - &numberOfBytesReceived, - &flags, - reinterpret_cast(&m_sourceSockaddrStorage), - &m_sourceSockaddrLength, - operation.get_overlapped(), - nullptr); - if (result == SOCKET_ERROR) - { - int errorCode = ::WSAGetLastError(); - if (errorCode != WSA_IO_PENDING) - { - // Failed synchronously. - operation.m_errorCode = static_cast(errorCode); - operation.m_numberOfBytesTransferred = numberOfBytesReceived; - return false; - } - } - else if (skipCompletionOnSuccess) - { - // Completed synchronously, no completion event will be posted to the IOCP. - operation.m_errorCode = ERROR_SUCCESS; - operation.m_numberOfBytesTransferred = numberOfBytesReceived; - return false; - } + static_assert( + sizeof(m_sourceSockaddrStorage) >= sizeof(SOCKADDR_IN) && + sizeof(m_sourceSockaddrStorage) >= sizeof(SOCKADDR_IN6)); + static_assert( + sockaddrStorageAlignment >= alignof(SOCKADDR_IN) && + sockaddrStorageAlignment >= alignof(SOCKADDR_IN6)); + + // Need to read this flag before starting the operation, otherwise + // it may be possible that the operation will complete immediately + // on another thread, resume the coroutine and then destroy the + // socket before we get a chance to read it. + const bool skipCompletionOnSuccess = m_socket.skip_completion_on_success(); + + m_sourceSockaddrLength = sizeof(m_sourceSockaddrStorage); - // Operation will complete asynchronously. - return true; + DWORD numberOfBytesReceived = 0; + DWORD flags = 0; + int result = ::WSARecvFrom( + m_socket.native_handle(), + reinterpret_cast(&m_buffer), + 1, // buffer count + &numberOfBytesReceived, + &flags, + reinterpret_cast(&m_sourceSockaddrStorage), + &m_sourceSockaddrLength, + operation.get_overlapped(), + nullptr); + if (result == SOCKET_ERROR) + { + int errorCode = ::WSAGetLastError(); + if (errorCode != WSA_IO_PENDING) + { + // Failed synchronously. + operation.m_errorCode = static_cast(errorCode); + operation.m_numberOfBytesTransferred = numberOfBytesReceived; + return false; + } + } + else if (skipCompletionOnSuccess) + { + // Completed synchronously, no completion event will be posted to the IOCP. + operation.m_errorCode = ERROR_SUCCESS; + operation.m_numberOfBytesTransferred = numberOfBytesReceived; + return false; + } + + // Operation will complete asynchronously. + return true; } void cppcoro::net::socket_recv_from_operation_impl::cancel( - cppcoro::detail::win32_overlapped_operation_base& operation) noexcept + cppcoro::detail::io_operation_base& operation) noexcept +{ + (void)::CancelIoEx( + reinterpret_cast(m_socket.native_handle()), operation.get_overlapped()); +} + +std::tuple +cppcoro::net::socket_recv_from_operation_impl::get_result( + cppcoro::detail::io_operation_base& operation) { - (void)::CancelIoEx( - reinterpret_cast(m_socket.native_handle()), - operation.get_overlapped()); + if (operation.m_errorCode != ERROR_SUCCESS) + { + throw std::system_error( + static_cast(operation.m_errorCode), + std::system_category(), + "Error receiving message on socket: WSARecvFrom"); + } + + return std::make_tuple( + static_cast(operation.m_numberOfBytesTransferred), + detail::sockaddr_to_ip_endpoint(*reinterpret_cast(&m_sourceSockaddrStorage))); +} + +#elif CPPCORO_OS_LINUX + +bool cppcoro::net::socket_recv_from_operation_impl::try_start( + cppcoro::detail::io_operation_base &operation) noexcept { + return operation.try_start_recvfrom( + m_socket.native_handle(), + &m_sourceSockaddrStorage, + sizeof(m_sourceSockaddrStorage), + m_buffer.buffer, + m_buffer.size, + m_socket.m_recvFlags); +} + +void cppcoro::net::socket_recv_from_operation_impl::cancel( + cppcoro::detail::uring_operation_base &operation) noexcept { + operation.cancel_io(); } std::tuple cppcoro::net::socket_recv_from_operation_impl::get_result( - cppcoro::detail::win32_overlapped_operation_base& operation) + cppcoro::detail::io_operation_base &operation) { - if (operation.m_errorCode != ERROR_SUCCESS) + auto size = operation.get_result(); // may throw errors + if (size > m_buffer.size) { - throw std::system_error( - static_cast(operation.m_errorCode), - std::system_category(), - "Error receiving message on socket: WSARecvFrom"); + throw std::system_error{ + EAGAIN, std::generic_category() // TODO is EAGAIN the good choice here ? + }; } - return std::make_tuple( - static_cast(operation.m_numberOfBytesTransferred), - detail::sockaddr_to_ip_endpoint( - *reinterpret_cast(&m_sourceSockaddrStorage))); + size, + detail::sockaddr_to_ip_endpoint(*reinterpret_cast(&m_sourceSockaddrStorage))); } #endif diff --git a/lib/socket_recv_operation.cpp b/lib/socket_recv_operation.cpp index 9930e9ba..805d4d5f 100644 --- a/lib/socket_recv_operation.cpp +++ b/lib/socket_recv_operation.cpp @@ -7,13 +7,11 @@ #include #if CPPCORO_OS_WINNT -# include -# include -# include -# include +#include +#include bool cppcoro::net::socket_recv_operation_impl::try_start( - cppcoro::detail::win32_overlapped_operation_base& operation) noexcept + cppcoro::detail::io_operation_base& operation) noexcept { // Need to read this flag before starting the operation, otherwise // it may be possible that the operation will complete immediately @@ -26,7 +24,7 @@ bool cppcoro::net::socket_recv_operation_impl::try_start( int result = ::WSARecv( m_socket.native_handle(), reinterpret_cast(&m_buffer), - 1, // buffer count + 1, // buffer count &numberOfBytesReceived, &flags, operation.get_overlapped(), @@ -54,13 +52,39 @@ bool cppcoro::net::socket_recv_operation_impl::try_start( return true; } - void cppcoro::net::socket_recv_operation_impl::cancel( - cppcoro::detail::win32_overlapped_operation_base& operation) noexcept + cppcoro::detail::io_operation_base& operation) noexcept { (void)::CancelIoEx( - reinterpret_cast(m_socket.native_handle()), - operation.get_overlapped()); + reinterpret_cast(m_socket.native_handle()), operation.get_overlapped()); +} + +#else + +bool cppcoro::net::socket_recv_operation_impl::try_start( + cppcoro::detail::io_operation_base& operation) noexcept +{ + return operation.try_start_recv( + m_socket.native_handle(), m_buffer.buffer, m_buffer.size, m_socket.m_recvFlags); +} + +void cppcoro::net::socket_recv_operation_impl::cancel( + cppcoro::detail::io_operation_base& operation) noexcept +{ + operation.cancel_io(); +} + +std::size_t +cppcoro::net::socket_recv_operation_impl::get_result(cppcoro::detail::io_operation_base& operation) +{ + auto size = operation.get_result(); + if (size > m_buffer.size) + { + throw std::system_error{ + EAGAIN, std::generic_category() // TODO is EAGAIN the good choice here ? + }; + } + return size; } #endif diff --git a/lib/socket_send_operation.cpp b/lib/socket_send_operation.cpp index e5217e1f..471e73a2 100644 --- a/lib/socket_send_operation.cpp +++ b/lib/socket_send_operation.cpp @@ -7,13 +7,11 @@ #include #if CPPCORO_OS_WINNT -# include -# include -# include -# include +#include +#include bool cppcoro::net::socket_send_operation_impl::try_start( - cppcoro::detail::win32_overlapped_operation_base& operation) noexcept + cppcoro::detail::io_operation_base& operation) noexcept { // Need to read this flag before starting the operation, otherwise // it may be possible that the operation will complete immediately @@ -54,11 +52,25 @@ bool cppcoro::net::socket_send_operation_impl::try_start( } void cppcoro::net::socket_send_operation_impl::cancel( - cppcoro::detail::win32_overlapped_operation_base& operation) noexcept + cppcoro::detail::io_operation_base& operation) noexcept { (void)::CancelIoEx( reinterpret_cast(m_socket.native_handle()), operation.get_overlapped()); } +#else + +bool cppcoro::net::socket_send_operation_impl::try_start( + cppcoro::detail::io_operation_base& operation) noexcept +{ + return operation.try_start_send(m_socket.native_handle(), m_buffer.buffer, m_buffer.size); +} + +void cppcoro::net::socket_send_operation_impl::cancel( + cppcoro::detail::io_operation_base& operation) noexcept +{ + operation.cancel_io(); +} + #endif diff --git a/lib/socket_send_to_operation.cpp b/lib/socket_send_to_operation.cpp index 80db2481..707c5d37 100644 --- a/lib/socket_send_to_operation.cpp +++ b/lib/socket_send_to_operation.cpp @@ -3,19 +3,17 @@ // Licenced under MIT license. See LICENSE.txt for details. /////////////////////////////////////////////////////////////////////////////// -#include #include +#include -#if CPPCORO_OS_WINNT -# include "socket_helpers.hpp" +#include "socket_helpers.hpp" -# include -# include -# include -# include +#if CPPCORO_OS_WINNT +#include +#include bool cppcoro::net::socket_send_to_operation_impl::try_start( - cppcoro::detail::win32_overlapped_operation_base& operation) noexcept + cppcoro::detail::io_operation_base& operation) noexcept { // Need to read this flag before starting the operation, otherwise // it may be possible that the operation will complete immediately @@ -24,16 +22,16 @@ bool cppcoro::net::socket_send_to_operation_impl::try_start( const bool skipCompletionOnSuccess = m_socket.skip_completion_on_success(); SOCKADDR_STORAGE destinationAddress; - const int destinationLength = detail::ip_endpoint_to_sockaddr( - m_destination, std::ref(destinationAddress)); + const int destinationLength = + detail::ip_endpoint_to_sockaddr(m_destination, std::ref(destinationAddress)); DWORD numberOfBytesSent = 0; int result = ::WSASendTo( m_socket.native_handle(), reinterpret_cast(&m_buffer), - 1, // buffer count + 1, // buffer count &numberOfBytesSent, - 0, // flags + 0, // flags reinterpret_cast(&destinationAddress), destinationLength, operation.get_overlapped(), @@ -62,11 +60,29 @@ bool cppcoro::net::socket_send_to_operation_impl::try_start( } void cppcoro::net::socket_send_to_operation_impl::cancel( - cppcoro::detail::win32_overlapped_operation_base& operation) noexcept + cppcoro::detail::io_operation_base& operation) noexcept { (void)::CancelIoEx( - reinterpret_cast(m_socket.native_handle()), - operation.get_overlapped()); + reinterpret_cast(m_socket.native_handle()), operation.get_overlapped()); +} +#elif CPPCORO_OS_LINUX +bool cppcoro::net::socket_send_to_operation_impl::try_start( + cppcoro::detail::io_operation_base& operation) noexcept +{ + const int destinationLength = + detail::ip_endpoint_to_sockaddr(m_destination, std::ref(m_destinationStorage)); + return operation.try_start_sendto( + m_socket.native_handle(), + &m_destinationStorage, + destinationLength, + m_buffer.buffer, + m_buffer.size); +} + +void cppcoro::net::socket_send_to_operation_impl::cancel( + cppcoro::detail::io_operation_base& operation) noexcept +{ + operation.cancel_io(); } #endif diff --git a/lib/static_thread_pool.cpp b/lib/static_thread_pool.cpp index 36c39ce9..ddd0b62c 100644 --- a/lib/static_thread_pool.cpp +++ b/lib/static_thread_pool.cpp @@ -32,7 +32,7 @@ namespace cppcoro { public: - explicit thread_state() + thread_state() : m_localQueue( std::make_unique[]>( local::initial_local_queue_size)) @@ -319,7 +319,7 @@ namespace cppcoro }; void static_thread_pool::schedule_operation::await_suspend( - std::experimental::coroutine_handle<> awaitingCoroutine) noexcept + stdcoro::coroutine_handle<> awaitingCoroutine) noexcept { m_awaitingCoroutine = awaitingCoroutine; m_threadPool->schedule_impl(this); diff --git a/lib/writable_file.cpp b/lib/writable_file.cpp index 3ba63e7c..8f675c52 100644 --- a/lib/writable_file.cpp +++ b/lib/writable_file.cpp @@ -43,6 +43,22 @@ void cppcoro::writable_file::set_size( }; } } +#else + +void cppcoro::writable_file::set_size( + std::uint64_t fileSize) +{ + if (ftruncate(m_fileHandle.fd(), fileSize) < 0) { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "error setting file size: ftruncate" + }; + } +} + +#endif cppcoro::file_write_operation cppcoro::writable_file::write( std::uint64_t offset, @@ -50,6 +66,9 @@ cppcoro::file_write_operation cppcoro::writable_file::write( std::size_t byteCount) noexcept { return file_write_operation{ +#if CPPCORO_OS_LINUX + *m_ioService, +#endif m_fileHandle.handle(), offset, buffer, @@ -64,6 +83,9 @@ cppcoro::file_write_operation_cancellable cppcoro::writable_file::write( cancellation_token ct) noexcept { return file_write_operation_cancellable{ +#if CPPCORO_OS_LINUX + *m_ioService, +#endif m_fileHandle.handle(), offset, buffer, @@ -71,5 +93,3 @@ cppcoro::file_write_operation_cancellable cppcoro::writable_file::write( std::move(ct) }; } - -#endif diff --git a/lib/write_only_file.cpp b/lib/write_only_file.cpp index 4c91bddf..1d6990cb 100644 --- a/lib/write_only_file.cpp +++ b/lib/write_only_file.cpp @@ -3,35 +3,41 @@ // Licenced under MIT license. See LICENSE.txt for details. /////////////////////////////////////////////////////////////////////////////// -#include +#include #if CPPCORO_OS_WINNT # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif # include +#elif CPPCORO_OS_LINUX +#define GENERIC_WRITE (S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH) +#endif cppcoro::write_only_file cppcoro::write_only_file::open( io_service& ioService, - const std::experimental::filesystem::path& path, + const stdfs::path& path, file_open_mode openMode, file_share_mode shareMode, file_buffering_mode bufferingMode) { - return write_only_file(file::open( + auto file = write_only_file(file::open( GENERIC_WRITE, ioService, path, openMode, shareMode, bufferingMode)); +#if CPPCORO_OS_LINUX + file.m_ioService = &ioService; +#endif + return std::move(file); } cppcoro::write_only_file::write_only_file( - detail::win32::safe_handle&& fileHandle) noexcept + detail::safe_handle&& fileHandle) noexcept : file(std::move(fileHandle)) - , writable_file(detail::win32::safe_handle{}) + , writable_file(detail::safe_handle{}) { } -#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 00000000..40e82c91 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 3.14) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +include(FetchContent) +set(DOCTEST_NO_INSTALL OFF CACHE BOOL "" FORCE) +fetchcontent_declare(_fetch_doctest + PREFIX ${CMAKE_BINARY_DIR}/doctest + GIT_REPOSITORY https://github.com/onqtam/doctest.git + LOG_DOWNLOAD ON + ) +fetchcontent_makeavailable(_fetch_doctest) +set_target_properties(doctest_with_main PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON) +include(${doctest_SOURCE_DIR}/scripts/cmake/doctest.cmake) +add_library(test_main main.cpp counted.cpp) + +# note: doctest_with_main looks broken +target_link_libraries(test_main PUBLIC doctest::doctest) +link_libraries(${PROJECT_NAME} test_main) + +macro(make_test _target) + add_executable(${_target} ${ARGV}) + doctest_discover_tests(${_target}) +endmacro() + +make_test(async_generator_tests async_generator_tests.cpp) +make_test(async_latch_tests async_latch_tests.cpp) +make_test(async_manual_reset_event_tests async_manual_reset_event_tests.cpp) +make_test(async_mutex_tests async_mutex_tests.cpp) +make_test(cancellation_token_tests cancellation_token_tests.cpp) +make_test(file_tests file_tests.cpp) +make_test(generator_tests generator_tests.cpp) +make_test(io_service_tests io_service_tests.cpp) +make_test(ip_address_tests ip_address_tests.cpp) +make_test(ip_endpoint_tests ip_endpoint_tests.cpp) +make_test(ipv4_address_tests ipv4_address_tests.cpp) +make_test(ipv4_endpoint_tests ipv4_endpoint_tests.cpp) +make_test(ipv6_address_tests ipv6_address_tests.cpp) +make_test(ipv6_endpoint_tests ipv6_endpoint_tests.cpp) +make_test(multi_producer_sequencer_tests multi_producer_sequencer_tests.cpp) +make_test(recursive_generator_tests recursive_generator_tests.cpp) +make_test(scheduling_operator_tests scheduling_operator_tests.cpp) +make_test(sequence_barrier_tests sequence_barrier_tests.cpp) +make_test(shared_task_tests shared_task_tests.cpp) +make_test(single_consumer_async_auto_reset_event_tests single_consumer_async_auto_reset_event_tests.cpp) +make_test(single_producer_sequencer_tests single_producer_sequencer_tests.cpp) +make_test(socket_tests socket_tests.cpp) +make_test(static_thread_pool_tests static_thread_pool_tests.cpp) +make_test(sync_wait_tests sync_wait_tests.cpp) +make_test(task_tests task_tests.cpp) +make_test(when_all_ready_tests when_all_ready_tests.cpp) +make_test(when_all_tests when_all_tests.cpp) diff --git a/test/async_generator_tests.cpp b/test/async_generator_tests.cpp index 3f0c4af7..1766abfc 100644 --- a/test/async_generator_tests.cpp +++ b/test/async_generator_tests.cpp @@ -271,9 +271,10 @@ TEST_CASE("large number of synchronous completions doesn't result in stack-overf auto consumer = [](cppcoro::async_generator sequence) -> cppcoro::task<> { std::uint32_t expected = 0; - for co_await(std::uint32_t i : sequence) + // for co_await(std::uint32_t i : sequence) // sadly removed from C++20 and unhandled by GCC + for (auto i_it = co_await sequence.begin(); i_it != sequence.end(); i_it = co_await ++i_it) { - CHECK(i == expected++); + CHECK(*i_it == expected++); } CHECK(expected == 1'000'000u); diff --git a/test/file_tests.cpp b/test/file_tests.cpp index 89742b8f..5d665a63 100644 --- a/test/file_tests.cpp +++ b/test/file_tests.cpp @@ -24,7 +24,7 @@ TEST_SUITE_BEGIN("file"); -namespace fs = std::experimental::filesystem; +namespace fs = stdfs; namespace { @@ -60,14 +60,14 @@ namespace fs::remove_all(m_path); } - const std::experimental::filesystem::path& temp_dir() + const stdfs::path& temp_dir() { return m_path; } private: - std::experimental::filesystem::path m_path; + stdfs::path m_path; }; diff --git a/test/io_service_fixture.hpp b/test/io_service_fixture.hpp index 9552ebeb..2cdb1dc6 100644 --- a/test/io_service_fixture.hpp +++ b/test/io_service_fixture.hpp @@ -19,8 +19,8 @@ struct io_service_fixture { public: - io_service_fixture(std::uint32_t threadCount = 1) - : m_ioService() + io_service_fixture(std::uint32_t threadCount = 1, std::uint32_t maxEvents = 32) + : m_ioService(maxEvents) { m_ioThreads.reserve(threadCount); try @@ -60,11 +60,11 @@ struct io_service_fixture }; -template +template struct io_service_fixture_with_threads : io_service_fixture { io_service_fixture_with_threads() - : io_service_fixture(thread_count) + : io_service_fixture(thread_count, max_events) {} }; diff --git a/test/io_service_tests.cpp b/test/io_service_tests.cpp index 7ea21289..159663c2 100644 --- a/test/io_service_tests.cpp +++ b/test/io_service_tests.cpp @@ -69,19 +69,23 @@ TEST_CASE("schedule coroutine") }())); } -TEST_CASE_FIXTURE(io_service_fixture_with_threads<2>, "multiple I/O threads servicing events") +static constexpr int mio_task_count = 500; +using mio_service_fixture_with_threads = io_service_fixture_with_threads<100, mio_task_count>; + +TEST_CASE_FIXTURE(mio_service_fixture_with_threads, "multiple I/O threads servicing events") { std::atomic completedCount = 0; auto runOnIoThread = [&]() -> cppcoro::task<> { co_await io_service().schedule(); + co_await io_service().schedule(); ++completedCount; }; std::vector> tasks; { - for (int i = 0; i < 1000; ++i) + for (int i = 0; i < mio_task_count; ++i) { tasks.emplace_back(runOnIoThread()); } @@ -89,7 +93,7 @@ TEST_CASE_FIXTURE(io_service_fixture_with_threads<2>, "multiple I/O threads serv cppcoro::sync_wait(cppcoro::when_all(std::move(tasks))); - CHECK(completedCount == 1000); + CHECK(completedCount == mio_task_count); } TEST_CASE("Multiple concurrent timers") diff --git a/test/scheduling_operator_tests.cpp b/test/scheduling_operator_tests.cpp index 238eb75c..c4deaab6 100644 --- a/test/scheduling_operator_tests.cpp +++ b/test/scheduling_operator_tests.cpp @@ -87,9 +87,10 @@ TEST_CASE_FIXTURE(io_service_fixture, "schedule_on async_generator<> function") auto seq = schedule_on(io_service(), makeSequence()); int expected = 1; - for co_await(int value : seq) + // for co_await(int value : seq) // sadly removed from C++20 and unhandled by GCC + for (auto value_it = co_await seq.begin(); value_it != seq.end(); value_it = co_await ++value_it) { - CHECK(value == expected++); + CHECK(*value_it == expected++); // Transfer exection back to main thread before // awaiting next item in the loop to chck that @@ -177,16 +178,17 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on async_generator<> function" auto seq = resume_on(otherIoService, makeSequence()); int expected = 1; - for co_await(int value : seq) + // for co_await(int value : seq) // sadly removed from C++20 and unhandled by GCC + for (auto value_it = co_await seq.begin(); value_it != seq.end(); value_it = co_await ++value_it) { // Every time we receive a value it should be on our requested // scheduler (ie. main thread) CHECK(std::this_thread::get_id() == mainThreadId); - CHECK(value == expected++); + CHECK(*value_it == expected++); // Occasionally transfer execution to a different thread before // awaiting next element. - if (value == 2) + if (*value_it == 2) { co_await io_service().schedule(); } diff --git a/test/socket_tests.cpp b/test/socket_tests.cpp index e51162d1..84da8289 100644 --- a/test/socket_tests.cpp +++ b/test/socket_tests.cpp @@ -3,45 +3,49 @@ // Licenced under MIT license. See LICENSE.txt for details. /////////////////////////////////////////////////////////////////////////////// +#include +#include +#include #include #include +#include +#include #include #include -#include -#include -#include -#include -#include + +#include #include "doctest/doctest.h" using namespace cppcoro; using namespace cppcoro::net; +using cosocket = cppcoro::net::socket; + TEST_SUITE_BEGIN("socket"); TEST_CASE("create TCP/IPv4") { io_service ioSvc; - auto socket = socket::create_tcpv4(ioSvc); + auto socket = cosocket::create_tcpv4(ioSvc); } TEST_CASE("create TCP/IPv6") { io_service ioSvc; - auto socket = socket::create_tcpv6(ioSvc); + auto socket = cosocket::create_tcpv6(ioSvc); } TEST_CASE("create UDP/IPv4") { io_service ioSvc; - auto socket = socket::create_udpv4(ioSvc); + auto socket = cosocket::create_udpv4(ioSvc); } TEST_CASE("create UDP/IPv6") { io_service ioSvc; - auto socket = socket::create_udpv6(ioSvc); + auto socket = cosocket::create_udpv6(ioSvc); } TEST_CASE("TCP/IPv4 connect/disconnect") @@ -52,16 +56,16 @@ TEST_CASE("TCP/IPv4 connect/disconnect") task serverTask; - auto server = [&](socket listeningSocket) -> task + auto server = [&](cosocket listeningSocket) -> task { - auto s = socket::create_tcpv4(ioSvc); + auto s = cosocket::create_tcpv4(ioSvc); co_await listeningSocket.accept(s); co_await s.disconnect(); co_return 0; }; { - auto serverSocket = socket::create_tcpv4(ioSvc); + auto serverSocket = cosocket::create_tcpv4(ioSvc); serverSocket.bind(ipv4_endpoint{ ipv4_address::loopback(), 0 }); serverSocket.listen(3); serverAddress = serverSocket.local_endpoint(); @@ -70,7 +74,7 @@ TEST_CASE("TCP/IPv4 connect/disconnect") auto client = [&]() -> task { - auto s = socket::create_tcpv4(ioSvc); + auto s = cosocket::create_tcpv4(ioSvc); s.bind(ipv4_endpoint{ ipv4_address::loopback(), 0 }); co_await s.connect(serverAddress); co_await s.disconnect(); @@ -97,14 +101,14 @@ TEST_CASE("send/recv TCP/IPv4") { io_service ioSvc; - auto listeningSocket = socket::create_tcpv4(ioSvc); + auto listeningSocket = cosocket::create_tcpv4(ioSvc); listeningSocket.bind(ipv4_endpoint{ ipv4_address::loopback(), 0 }); listeningSocket.listen(3); auto echoServer = [&]() -> task { - auto acceptingSocket = socket::create_tcpv4(ioSvc); + auto acceptingSocket = cosocket::create_tcpv4(ioSvc); co_await listeningSocket.accept(acceptingSocket); @@ -134,7 +138,7 @@ TEST_CASE("send/recv TCP/IPv4") auto echoClient = [&]() -> task { - auto connectingSocket = socket::create_tcpv4(ioSvc); + auto connectingSocket = cosocket::create_tcpv4(ioSvc); connectingSocket.bind(ipv4_endpoint{}); @@ -214,14 +218,14 @@ TEST_CASE("send/recv TCP/IPv4 many connections") { io_service ioSvc; - auto listeningSocket = socket::create_tcpv4(ioSvc); + auto listeningSocket = cosocket::create_tcpv4(ioSvc); listeningSocket.bind(ipv4_endpoint{ ipv4_address::loopback(), 0 }); listeningSocket.listen(20); cancellation_source canceller; - auto handleConnection = [](socket s) -> task + auto handleConnection = [](cosocket s) -> task { std::uint8_t buffer[64]; std::size_t bytesReceived; @@ -253,7 +257,7 @@ TEST_CASE("send/recv TCP/IPv4 many connections") try { while (true) { - auto acceptingSocket = socket::create_tcpv4(ioSvc); + auto acceptingSocket = cosocket::create_tcpv4(ioSvc); co_await listeningSocket.accept(acceptingSocket, ct); connectionScope.spawn( handleConnection(std::move(acceptingSocket))); @@ -277,7 +281,7 @@ TEST_CASE("send/recv TCP/IPv4 many connections") auto echoClient = [&]() -> task<> { - auto connectingSocket = socket::create_tcpv4(ioSvc); + auto connectingSocket = cosocket::create_tcpv4(ioSvc); connectingSocket.bind(ipv4_endpoint{}); @@ -368,7 +372,7 @@ TEST_CASE("udp send_to/recv_from") { io_service ioSvc; - auto server = [&](socket serverSocket) -> task + auto server = [&](cosocket serverSocket) -> task { std::uint8_t buffer[100]; @@ -387,15 +391,20 @@ TEST_CASE("udp send_to/recv_from") std::tie(bytesReceived, remoteEndPoint) = co_await serverSocket.recv_from(buffer, 100); FAIL("Should have thrown"); } - catch (const std::system_error&) + catch (const std::system_error& ex) { +#if CPPCORO_OS_LINUX + CHECK(ex.code() == std::errc::resource_unavailable_try_again); +#elif CPPCORO_OS_WINNT // TODO: Map this situation to some kind of error_condition value. - // The win32 ERROR_MORE_DATA error code doesn't seem to map to any of the standard std::errc values. + // The win32 ERROR_MORE_DATA error code doesn't seem to map to any of the standard + // std::errc values. // // CHECK(ex.code() == ???); // // Possibly also need to switch to returning a std::error_code directly rather than // throwing a std::system_error for this case. +#endif } // Send an NACK response. @@ -412,7 +421,7 @@ TEST_CASE("udp send_to/recv_from") task serverTask; { - auto serverSocket = socket::create_udpv4(ioSvc); + auto serverSocket = cosocket::create_udpv4(ioSvc); serverSocket.bind(ipv4_endpoint{ ipv4_address::loopback(), 0 }); serverAddress = serverSocket.local_endpoint(); serverTask = server(std::move(serverSocket)); @@ -420,7 +429,7 @@ TEST_CASE("udp send_to/recv_from") auto client = [&]() -> task { - auto socket = socket::create_udpv4(ioSvc); + auto socket = cosocket::create_udpv4(ioSvc); // don't need to bind(), should be implicitly bound on first send_to(). diff --git a/test/static_thread_pool_tests.cpp b/test/static_thread_pool_tests.cpp index ce93299d..62a4d72b 100644 --- a/test/static_thread_pool_tests.cpp +++ b/test/static_thread_pool_tests.cpp @@ -132,7 +132,7 @@ TEST_CASE("launch sub-task with many sub-tasks") struct fork_join_operation { std::atomic m_count; - std::experimental::coroutine_handle<> m_coro; + stdcoro::coroutine_handle<> m_coro; fork_join_operation() : m_count(1) {} @@ -151,7 +151,7 @@ struct fork_join_operation bool await_ready() noexcept { return m_count.load(std::memory_order_acquire) == 1; } - bool await_suspend(std::experimental::coroutine_handle<> coro) noexcept + bool await_suspend(stdcoro::coroutine_handle<> coro) noexcept { m_coro = coro; return m_count.fetch_sub(1, std::memory_order_acq_rel) != 1; @@ -187,7 +187,7 @@ cppcoro::task for_each_async(SCHEDULER& scheduler, RANGE& range, FUNC func bool await_ready() noexcept { return false; } CPPCORO_NOINLINE - void await_suspend(std::experimental::coroutine_handle<> coro) noexcept + void await_suspend(stdcoro::coroutine_handle<> coro) noexcept { fork_join_operation& forkJoin = m_forkJoin; FUNC& func = m_func;