diff --git a/doc/acknowledgements.qbk b/doc/acknowledgements.qbk index 12dd356..df69bd5 100644 --- a/doc/acknowledgements.qbk +++ b/doc/acknowledgements.qbk @@ -7,7 +7,7 @@ [section:acknowledgements Acknowledgments] -I'd like to thank Adreas Fett, Artyom Beilis, Casey Bodley, Daniel Larimer, David Deakins, +I'd like to thank Adreas Fett, Artyom Beilis, Daniel Larimer, David Deakins, Evgeny Shapovalov, Fernando Pelliccioni, Giovanni Piero Deretta, Gordon Woodhull, Helge Bahmann, Holger Grund, Jeffrey Lee Hellrung (Jr.), Keith Jeffery, Martin Husemann, Phil Endecott, Robert Stewart, Sergey Cheban, Steven diff --git a/doc/context.qbk b/doc/context.qbk index d65bc34..b1327a3 100644 --- a/doc/context.qbk +++ b/doc/context.qbk @@ -20,7 +20,6 @@ ] -[def __boost_asio__ [*Boost.Asio]] [def __boost_build__ [*Boost.Build]] [def __boost_context__ [*Boost.Context]] @@ -138,7 +137,6 @@ [def __resume_with__ ['continuation::resume_with()]] [def __segmented__ [link segmented ['segmented_stack]]] [def __segmented_stack__ ['segmented_stack]] -[def __spawn__ [link spawn ['spawn()]]] [def __stack_context__ ['stack_context]] [def __winfib__ ['WinFiber]] @@ -167,7 +165,6 @@ [include callcc.qbk] [include stack.qbk] [include preallocated.qbk] -[include spawn.qbk] [include performance.qbk] [include architectures.qbk] [include rationale.qbk] diff --git a/doc/context.xml b/doc/context.xml index 775789a..d83439d 100644 --- a/doc/context.xml +++ b/doc/context.xml @@ -1,6 +1,6 @@ - @@ -319,7 +319,8 @@ - Do not jump from inside a catch block. + Do not jump from inside a catch block and then re-throw the exception in + another fiber. @@ -331,7 +332,7 @@ Sometimes it is useful to execute a new function on top of a resumed fiber. For this purpose continuation::resume_with() has to be used. The function passed as argument must accept a rvalue reference to fiber and return fiber. + linkend="ff">fiber and return void. namespace ctx=boost::context; int data=0; @@ -351,7 +352,7 @@ f1=std::move(f1).resume(); std::cout << "f1: returned second time: " << data << std::endl; data+=1; -f1=std::move(f1).resume_with([&data](ctx::fiber&& f2){ +f1=f1.resume_with([&data](ctx::fiber&& f2){ std::cout << "f2: entered: " << data << std::endl; data=-1; return std::move(f2); @@ -583,7 +584,7 @@ source=std::move(source).resume(); while(!done){ printf("Parsed: %c\n",c); - source=std::move(source).resume(); + source=std::Move(source).resume(); } output: @@ -1401,7 +1402,8 @@ - Do not jump from inside a catch block. + Do not jump from inside a catch block and then re-throw the exception in + another continuation. @@ -1414,7 +1416,8 @@ Sometimes it is useful to execute a new function on top of a resumed continuation. For this purpose continuation::resume_with() has to be used. The function passed as argument must accept a rvalue reference to continuation and return continuation. + linkend="cc">continuation and return void. namespace ctx=boost::context; int data=0; @@ -3202,188 +3205,6 @@ -
- <anchor id="spawn"/><link linkend="context.spawn">Using fibers in Boost.Asio - with spawn()</link> - - spawn() creates a new fiber - and starts new stackful thread of execution. The spawn() - function is a high-level wrapper over the Boost.Context - library. This function enables programs to implement asynchronous logic in - a synchronous manner. Suspending/resuming of the spawned fiber - is controlled by Boost.Asio. - - - - In contrast to Boost.Asio this implementation - of spawn() is based only - on fiber. - - - - Usage - -void do_echo(boost::context::yield_context yield) { - try { - char data[128]; - for (;;) { - std::size_t length = my_socket.async_read_some(boost::asio::buffer(data), yield); - boost::asio::async_write(my_socket, boost::asio::buffer(data, length), yield); - } - } catch (std::exception const& e) { - // ... - } -} -// ... -boost::context::spawn(my_strand, do_echo); - - - This simple example demonstrates the basic usage of spawn(). - Function do_echo is executed - by a new fiber that has been created by spawn(). - - - do_echo gets suspended while - asynchronous operations like async_read_some - and async_write are started - and resumed after the asynchronous operation completed. Therefore do_echo does not require callbacks (the code - looks like synchronous). - - - Call - with current continuation - -#include <boost/context/spawn.hpp> - -template< typename Function, typename StackAllocator = boost::context::default_stack > -auto spawn( Function && fn, StackAllocator && salloc = StackAllocator() ); - - - - - Effects: - - - This function is used to launch a new execution context on behalf of - spawned context. Parameter fn - is the fiber function and must have signature void(basic_yield_context<Handler>). - - - - -template< typename Handler, typename Function, typename StackAllocator = boost::context::default_stack > -auto spawn( Handler && hndlr, Function && fn, StackAllocator && salloc = StackAllocator() ); - - - - - Effects: - - - This function is used to launch a new execution context on behalf of - spawned context, calling the specified handler hndlr - when the fiber completes. hndlr - provides an execution context (via the the handler invocation hook) for - the fiber. The handler must have the signature void(). Parameter fn - is the fiber function and must have signature void(basic_yield_context<Handler>). - - - - -template< typename Handler, typename Function, typename StackAllocator = boost::context::default_stack > -auto spawn( context::basic_yield_context< Handler > ctx, Function && function, StackAllocator && salloc = StackAllocator() ) - - - - - Effects: - - - This function is used to launch a new execution context on behalf of - spawned context. Parameter fn - is the fiber function and must have signature void(basic_yield_context<Handler>). ctx - identifies the current execution context as a parent of the new fiber. - This specifies that the new fiber should inherit the execution context - of the parent. For example, if the parent fiber is executing in a particular - strand, then the new fiber will execute in the same strand. - - - - -template< typename Function, typename Executor, typename StackAllocator = boost::context::default_stack > -auto spawn( Executor const& ex, Function && function, StackAllocator && salloc = StackAllocator() ) - - - - - Effects: - - - This function is used to launch a new execution context on behalf of - spawned context. Parameter fn - is the fiber function and must have signature void(basic_yield_context<Handler>). ex - identifies the executor that will run the fiber. The new fiber is implicitly - given its own strand within this executor. - - - - -template< typename Function, typename Executor, typename StackAllocator = boost::context::default_stack > -auto spawn( context::detail::net::strand< Executor > const& ex, Function && function, StackAllocator && salloc = StackAllocator() ) - - - - - Effects: - - - This function is used to launch a new execution context on behalf of - spawned context. Parameter fn - is the fiber function and must have signature void(basic_yield_context<Handler>). ex - identifies the strand that will run the fiber. - - - - -template< typename Function, typename ExecutionContext, typename StackAllocator = boost::context::default_stack > -auto spawn( ExecutionContext & ctx, Function && function, StackAllocator && salloc = StackAllocator() ) - - - - - Effects: - - - This function is used to launch a new execution context on behalf of - spawned context. Parameter fn - is the fiber function and must have signature void(basic_yield_context<Handler>). ctx - identifies the execution context that will run the fiber. The new fiber - is implicitly given its own strand within this execution context. - - - - -
<anchor id="performance"/><link linkend="context.performance">Performance</link> @@ -3522,7 +3343,7 @@ - AAPCS|MACH-O + - @@ -3566,7 +3387,7 @@ - O32|N64|ELF + O32,N64|ELF @@ -3593,7 +3414,7 @@ - SYSV|ELF|XCOFF + SYSV|ELF,XCOFF @@ -3620,7 +3441,7 @@ - SYSV|ELF|XCOFF + SYSV|ELF,XCOFF @@ -4055,11 +3876,11 @@
<link linkend="context.acknowledgements">Acknowledgments</link> - I'd like to thank Adreas Fett, Artyom Beilis, Casey Bodley, Daniel Larimer, - David Deakins, Evgeny Shapovalov, Fernando Pelliccioni, Giovanni Piero Deretta, - Gordon Woodhull, Helge Bahmann, Holger Grund, Jeffrey Lee Hellrung (Jr.), Keith - Jeffery, Martin Husemann, Phil Endecott, Robert Stewart, Sergey Cheban, Steven - Watanabe, Vicente J. Botet Escriba, Wayne Piekarski. + I'd like to thank Adreas Fett, Artyom Beilis, Daniel Larimer, David Deakins, + Evgeny Shapovalov, Fernando Pelliccioni, Giovanni Piero Deretta, Gordon Woodhull, + Helge Bahmann, Holger Grund, Jeffrey Lee Hellrung (Jr.), Keith Jeffery, Martin + Husemann, Phil Endecott, Robert Stewart, Sergey Cheban, Steven Watanabe, Vicente + J. Botet Escriba, Wayne Piekarski.
diff --git a/doc/spawn.qbk b/doc/spawn.qbk deleted file mode 100644 index 4b98aeb..0000000 --- a/doc/spawn.qbk +++ /dev/null @@ -1,106 +0,0 @@ -[/ - Copyright Oliver Kowalke 2021. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt -] - -[#spawn] -[section:spawn Using fibers in Boost.Asio with spawn()] - -__spawn__ creates a new __fiber__ and starts new stackful thread of execution. -The __spawn__ function is a high-level wrapper over the __boost_context__ library. This function -enables programs to implement asynchronous logic in a synchronous manner. -Suspending/resuming of the spawned __fiber__ is controlled by __boost_asio__. - -[note In contrast to __boost_asio__ this implementation of __spawn__ is based only on __fib__.] - -[heading Usage] - - void do_echo(boost::context::yield_context yield) { - try { - char data[128]; - for (;;) { - std::size_t length = my_socket.async_read_some(boost::asio::buffer(data), yield); - boost::asio::async_write(my_socket, boost::asio::buffer(data, length), yield); - } - } catch (std::exception const& e) { - // ... - } - } - // ... - boost::context::spawn(my_strand, do_echo); - -This simple example demonstrates the basic usage of __spawn__. -Function `do_echo` is executed by a new __fiber__ that has been created by __spawn__. - -`do_echo` gets suspended while asynchronous operations like `async_read_some` and `async_write` are -started and resumed after the asynchronous operation completed. -Therefore `do_echo` does not require callbacks (the code looks like synchronous). - - -[heading Call with current continuation] - - #include - - template< typename Function, typename StackAllocator = boost::context::default_stack > - auto spawn( Function && fn, StackAllocator && salloc = StackAllocator() ); - -[variablelist -[[Effects:] [This function is used to launch a new execution context on behalf of spawned context. -Parameter `fn` is the fiber function and must have signature `void(basic_yield_context)`.]] -] - - template< typename Handler, typename Function, typename StackAllocator = boost::context::default_stack > - auto spawn( Handler && hndlr, Function && fn, StackAllocator && salloc = StackAllocator() ); - -[variablelist -[[Effects:] [This function is used to launch a new execution context on behalf of spawned context, -calling the specified handler `hndlr` when the fiber completes. `hndlr` provides an execution -context (via the the handler invocation hook) for the fiber. The handler must have the signature -`void()`. -Parameter `fn` is the fiber function and must have signature `void(basic_yield_context)`.]] -] - - template< typename Handler, typename Function, typename StackAllocator = boost::context::default_stack > - auto spawn( context::basic_yield_context< Handler > ctx, Function && function, StackAllocator && salloc = StackAllocator() ) - -[variablelist -[[Effects:] [This function is used to launch a new execution context on behalf of spawned context. -Parameter `fn` is the fiber function and must have signature `void(basic_yield_context)`. -`ctx` identifies the current execution context as a parent of the new fiber. This specifies that the -new fiber should inherit the execution context of the parent. For example, if the parent fiber is -executing in a particular strand, then the new fiber will execute in the same strand.]] -] - - template< typename Function, typename Executor, typename StackAllocator = boost::context::default_stack > - auto spawn( Executor const& ex, Function && function, StackAllocator && salloc = StackAllocator() ) - -[variablelist -[[Effects:] [This function is used to launch a new execution context on behalf of spawned context. -Parameter `fn` is the fiber function and must have signature `void(basic_yield_context)`. -`ex` identifies the executor that will run the fiber. The new fiber is implicitly given its own -strand within this executor.]] -] - - template< typename Function, typename Executor, typename StackAllocator = boost::context::default_stack > - auto spawn( context::detail::net::strand< Executor > const& ex, Function && function, StackAllocator && salloc = StackAllocator() ) - -[variablelist -[[Effects:] [This function is used to launch a new execution context on behalf of spawned context. -Parameter `fn` is the fiber function and must have signature `void(basic_yield_context)`. -`ex` identifies the strand that will run the fiber.]] -] - - template< typename Function, typename ExecutionContext, typename StackAllocator = boost::context::default_stack > - auto spawn( ExecutionContext & ctx, Function && function, StackAllocator && salloc = StackAllocator() ) - -[variablelist -[[Effects:] [This function is used to launch a new execution context on behalf of spawned context. -Parameter `fn` is the fiber function and must have signature `void(basic_yield_context)`. -`ctx` identifies the execution context that will run the fiber. The new fiber is implicitly given -its own strand within this execution context.]] -] - - -[endsect] diff --git a/example/fiber/Jamfile.v2 b/example/fiber/Jamfile.v2 index 3fb9fee..bf3c1bd 100644 --- a/example/fiber/Jamfile.v2 +++ b/example/fiber/Jamfile.v2 @@ -81,37 +81,3 @@ exe circle #exe echosse # : echosse.cpp # ; - -exe server - : echo_server.cpp - /boost/system//boost_system - /boost/chrono//boost_chrono - : BOOST_ALL_NO_LIB=1 - multi - solaris:socket - solaris:nsl - windows:_WIN32_WINNT=0x0501 - windows,gcc:ws2_32 - windows,gcc:mswsock - windows,gcc-cygwin:__USE_W32_SOCKETS - hpux,gcc:_XOPEN_SOURCE_EXTENDED - hpux:ipv6 - haiku:network - ; - - exe client - : echo_client.cpp - /boost/system//boost_system - /boost/chrono//boost_chrono - : BOOST_ALL_NO_LIB=1 - multi - solaris:socket - solaris:nsl - windows:_WIN32_WINNT=0x0501 - windows,gcc:ws2_32 - windows,gcc:mswsock - windows,gcc-cygwin:__USE_W32_SOCKETS - hpux,gcc:_XOPEN_SOURCE_EXTENDED - hpux:ipv6 - haiku:network - ; diff --git a/example/fiber/echo_client.cpp b/example/fiber/echo_client.cpp deleted file mode 100644 index 6754db8..0000000 --- a/example/fiber/echo_client.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// -// blocking_tcp_echo_client.cpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#include -#include -#include - -#include - -using boost::asio::ip::tcp; - -enum { max_length = 1024 }; - -int main(int argc, char* argv[]) -{ - try - { - if (argc != 3) - { - std::cerr << "Usage: blocking_tcp_echo_client \n"; - return 1; - } - - boost::asio::io_context io_context; - - tcp::resolver resolver(io_context); - tcp::resolver::results_type endpoints = - resolver.resolve(tcp::v4(), argv[1], argv[2]); - - tcp::socket s(io_context); - boost::asio::connect(s, endpoints); - - using namespace std; // For strlen. - std::cout << "Enter message: "; - char request[max_length]; - std::cin.getline(request, max_length); - size_t request_length = strlen(request); - boost::asio::write(s, boost::asio::buffer(request, request_length)); - - char reply[max_length]; - size_t reply_length = boost::asio::read(s, - boost::asio::buffer(reply, request_length)); - std::cout << "Reply is: "; - std::cout.write(reply, reply_length); - std::cout << "\n"; - } - catch (std::exception& e) - { - std::cerr << "Exception: " << e.what() << "\n"; - } - - return 0; -} diff --git a/example/fiber/echo_server.cpp b/example/fiber/echo_server.cpp deleted file mode 100644 index a194f2d..0000000 --- a/example/fiber/echo_server.cpp +++ /dev/null @@ -1,124 +0,0 @@ -// -// echo_server.cpp -// ~~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -using boost::asio::ip::tcp; - -class session : public boost::enable_shared_from_this -{ -public: - explicit session(boost::asio::io_context& io_context) - : strand_(boost::asio::make_strand(io_context)), - socket_(io_context), - timer_(io_context) - { - } - - tcp::socket& socket() - { - return socket_; - } - - void go() - { - boost::context::spawn(strand_, - boost::bind(&session::echo, - shared_from_this(), boost::placeholders::_1)); - boost::context::spawn(strand_, - boost::bind(&session::timeout, - shared_from_this(), boost::placeholders::_1)); - } - -private: - void echo(boost::context::yield_context yield) - { - try - { - char data[128]; - for (;;) - { - timer_.expires_after(boost::asio::chrono::seconds(10)); - std::size_t n = socket_.async_read_some(boost::asio::buffer(data), yield); - boost::asio::async_write(socket_, boost::asio::buffer(data, n), yield); - } - } - catch (std::exception& e) - { - socket_.close(); - timer_.cancel(); - } - } - - void timeout(boost::context::yield_context yield) - { - while (socket_.is_open()) - { - boost::system::error_code ignored_ec; - timer_.async_wait(yield[ignored_ec]); - if (timer_.expiry() <= boost::asio::steady_timer::clock_type::now()) - socket_.close(); - } - } - - boost::asio::strand strand_; - tcp::socket socket_; - boost::asio::steady_timer timer_; -}; - -void do_accept(boost::asio::io_context& io_context, - unsigned short port, boost::context::yield_context yield) -{ - tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), port)); - - for (;;) - { - boost::system::error_code ec; - boost::shared_ptr new_session(new session(io_context)); - acceptor.async_accept(new_session->socket(), yield[ec]); - if (!ec) new_session->go(); - } -} - -int main(int argc, char* argv[]) -{ - try - { - if (argc != 2) - { - std::cerr << "Usage: echo_server \n"; - return 1; - } - - boost::asio::io_context io_context; - - boost::context::spawn(io_context, - boost::bind(do_accept, - boost::ref(io_context), atoi(argv[1]), boost::placeholders::_1)); - - io_context.run(); - } - catch (std::exception& e) - { - std::cerr << "Exception: " << e.what() << "\n"; - } - - return 0; -} diff --git a/include/boost/context/detail/is_stack_allocator.hpp b/include/boost/context/detail/is_stack_allocator.hpp deleted file mode 100644 index 17d4034..0000000 --- a/include/boost/context/detail/is_stack_allocator.hpp +++ /dev/null @@ -1,40 +0,0 @@ - -// Copyright Casey Brodley 2019. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#ifndef BOOST_CONTEXT_DETAIL_ALLOCATOR_H -#define BOOST_CONTEXT_DETAIL_ALLOCATOR_H - -#include - -#include - -#include - -namespace boost { -namespace context { -namespace detail { - -template< typename T, typename = void > -struct is_stack_allocator : public std::false_type { -}; - -template< typename T > -struct is_stack_allocator< - T, - boost::void_t< - decltype( - // boost::context::stack_context c = salloc.allocate(); - std::declval() = std::declval().allocate(), - // salloc.deallocate(c); - std::declval().deallocate(std::declval()) - ) - > -> : public std::true_type { -}; - -}}} - -#endif // BOOST_CONTEXT_DETAIL_ALLOCATOR_H diff --git a/include/boost/context/detail/net.hpp b/include/boost/context/detail/net.hpp deleted file mode 100644 index c21041e..0000000 --- a/include/boost/context/detail/net.hpp +++ /dev/null @@ -1,40 +0,0 @@ - -// Copyright Casey Brodley 2019. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#ifndef BOOST_CONTEXT_DETAIL_NET_H -#define BOOST_CONTEXT_DETAIL_NET_H - -#include -#include -#include -#include -#include -#include -#include - -#define CONTEXT_NET_NAMESPACE boost::asio - -namespace boost { -namespace context { -namespace detail { -namespace net { - -using boost::asio::associated_executor_t; -using boost::asio::get_associated_executor; - -using boost::asio::associated_allocator_t; -using boost::asio::get_associated_allocator; - -using boost::asio::execution_context; -using boost::asio::executor; -using boost::asio::executor_binder; -using boost::asio::is_executor; - -using boost::asio::strand; - -}}}} - -#endif // BOOST_CONTEXT_DETAIL_NET_H diff --git a/include/boost/context/impl/spawn.hpp b/include/boost/context/impl/spawn.hpp deleted file mode 100644 index 9beda34..0000000 --- a/include/boost/context/impl/spawn.hpp +++ /dev/null @@ -1,487 +0,0 @@ - -// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// Copyright (c) 2017,2021 Oliver Kowalke (oliver dot kowalke at gmail dot com) -// Copyright (c) 2019 Casey Bodley (cbodley at redhat dot com) -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#ifndef BOOST_CONTEXT_IMPL_SPAWN_H -#define BOOST_CONTEXT_IMPL_SPAWN_H - -#include -#include -#include - -#include -#include - -#include -#include -#include - -namespace boost { -namespace context { -namespace detail { - -class spawn_context { -public: - fiber_context ctx_; - std::exception_ptr eptr_{}; - - spawn_context() = default; - - template< typename StackAlloc, typename Fn > - spawn_context( std::allocator_arg_t, StackAlloc && salloc, Fn && fn) : - ctx_{ - std::allocator_arg, - std::forward< StackAlloc >( salloc), - std::forward< Fn >( fn) } { - } - - void resume() { - ctx_ = std::move( ctx_).resume(); - if ( eptr_) { - std::rethrow_exception( std::move( eptr_) ); - } - } -}; - -template< typename Handler, typename ...Ts > -class coro_handler { -public: - coro_handler( basic_yield_context< Handler > ctx) : - callee_{ ctx.callee_.lock() }, - caller_{ ctx.caller_ }, - handler_{ ctx.handler_ }, - ready_{ 0 }, - ec_{ ctx.ec_ }, - value_{ 0 } { - } - - void operator()( Ts... values) { - *ec_ = boost::system::error_code{}; - *value_ = std::forward_as_tuple( std::move( values) ...); - if ( --*ready_ == 0) { - callee_->resume(); - } - } - - void operator()( boost::system::error_code ec, Ts... values) { - *ec_ = ec; - *value_ = std::forward_as_tuple( std::move( values) ...); - if ( --*ready_ == 0) { - callee_->resume(); - } - } - -//private: - std::shared_ptr< spawn_context > callee_; - spawn_context & caller_; - Handler handler_; - std::atomic< long > * ready_; - boost::system::error_code * ec_; - boost::optional< std::tuple< Ts... > > * value_; -}; - -template< typename Handler, typename T > -class coro_handler< Handler, T > { -public: - coro_handler( basic_yield_context< Handler > ctx) : - callee_{ ctx.callee_.lock() }, - caller_{ ctx.caller_ }, - handler_{ ctx.handler_ }, - ready_{ 0 }, - ec_{ ctx.ec_ }, - value_{ 0 } { - } - - void operator()( T value) { - *ec_ = boost::system::error_code(); - *value_ = std::move( value); - if ( --*ready_ == 0) { - callee_->resume(); - } - } - - void operator()( boost::system::error_code ec, T value) { - *ec_ = ec; - *value_ = std::move( value); - if ( --*ready_ == 0) { - callee_->resume(); - } - } - -//private: - std::shared_ptr< spawn_context > callee_; - spawn_context & caller_; - Handler handler_; - std::atomic< long > * ready_; - boost::system::error_code * ec_; - boost::optional< T > * value_; -}; - -template< typename Handler > -class coro_handler< Handler, void > { -public: - coro_handler( basic_yield_context< Handler > ctx) : - callee_{ ctx.callee_.lock() }, - caller_{ ctx.caller_ }, - handler_{ ctx.handler_ }, - ready_{ 0 }, - ec_{ ctx.ec_ } { - } - - void operator()() { - *ec_ = boost::system::error_code(); - if ( --*ready_ == 0) { - callee_->resume(); - } - } - - void operator()( boost::system::error_code ec) { - *ec_ = ec; - if ( --*ready_ == 0) { - callee_->resume(); - } - } - -//private: - std::shared_ptr< spawn_context > callee_; - spawn_context & caller_; - Handler handler_; - std::atomic< long > * ready_; - boost::system::error_code * ec_; -}; - -template< typename Handler, typename ...Ts > -class coro_async_result { -public: - using completion_handler_type = coro_handler< Handler, Ts... >; - using return_type = std::tuple< Ts... >; - - explicit coro_async_result( completion_handler_type & h) : - handler_{ h }, - caller_{ h.caller_ }, - ready_{ 2 } { - h.ready_ = & ready_; - out_ec_ = h.ec_; - if ( ! out_ec_) { - h.ec_ = & ec_; - } - h.value_ = & value_; - } - - return_type get() { - // Must not hold shared_ptr while suspended. - handler_.callee_.reset(); - if ( --ready_ != 0) { - caller_.resume(); // suspend caller - } - if ( ! out_ec_ && ec_) { - throw boost::system::system_error( ec_); - } - return std::move( * value_); - } - -private: - completion_handler_type & handler_; - spawn_context & caller_; - std::atomic< long > ready_; - boost::system::error_code * out_ec_; - boost::system::error_code ec_; - boost::optional< return_type > value_; -}; - -template< typename Handler, typename T > -class coro_async_result< Handler, T > { -public: - using completion_handler_type = coro_handler< Handler, T >; - using return_type = T; - - explicit coro_async_result( completion_handler_type & h) : - handler_{ h }, - caller_{ h.caller_ }, - ready_{ 2 } { - h.ready_ = & ready_; - out_ec_ = h.ec_; - if ( ! out_ec_) { - h.ec_ = & ec_; - } - h.value_ = & value_; - } - - return_type get() { - // Must not hold shared_ptr while suspended. - handler_.callee_.reset(); - if ( --ready_ != 0) { - caller_.resume(); // suspend caller - } - if ( ! out_ec_ && ec_) { - throw boost::system::system_error( ec_); - } - return std::move( * value_); - } - -private: - completion_handler_type & handler_; - spawn_context & caller_; - std::atomic< long > ready_; - boost::system::error_code * out_ec_; - boost::system::error_code ec_; - boost::optional< return_type > value_; -}; - -template< typename Handler > -class coro_async_result< Handler, void > { -public: - using completion_handler_type = coro_handler< Handler, void >; - using return_type = void; - - explicit coro_async_result( completion_handler_type & h) : - handler_{ h }, - caller_{ h.caller_ }, - ready_{ 2 } { - h.ready_ = & ready_; - out_ec_ = h.ec_; - if ( ! out_ec_) { - h.ec_ = & ec_; - } - } - - void get() { - // Must not hold shared_ptr while suspended. - handler_.callee_.reset(); - if ( --ready_ != 0) { - caller_.resume(); // suspend caller - } - if ( ! out_ec_ && ec_) { - throw boost::system::system_error( ec_); - } - } - -private: - completion_handler_type & handler_; - spawn_context & caller_; - std::atomic< long > ready_; - boost::system::error_code * out_ec_; - boost::system::error_code ec_; -}; - -} // namespace detail -} // namespace context - -#if !defined(GENERATING_DOCUMENTATION) - -template< typename Handler, typename ReturnType > -class CONTEXT_NET_NAMESPACE::async_result< context::basic_yield_context< Handler >, ReturnType() > : - public context::detail::coro_async_result< Handler, void > { -public: - explicit async_result( - typename context::detail::coro_async_result< Handler, void >::completion_handler_type & h) : - context::detail::coro_async_result< Handler, void >{ h } { - } -}; - -template< typename Handler, typename ReturnType, typename ...Args > -class CONTEXT_NET_NAMESPACE::async_result< context::basic_yield_context< Handler >, ReturnType( Args...) > : - public context::detail::coro_async_result< Handler, typename std::decay< Args >::type... > { -public: - explicit async_result( - typename context::detail::coro_async_result< Handler, typename std::decay< Args >::type... >::completion_handler_type & h) : - context::detail::coro_async_result< Handler, typename std::decay< Args >::type... >{ h } { - } -}; - -template< typename Handler, typename ReturnType > -class CONTEXT_NET_NAMESPACE::async_result< context::basic_yield_context< Handler >, ReturnType( boost::system::error_code) > : - public context::detail::coro_async_result< Handler, void > { -public: - explicit async_result( - typename context::detail::coro_async_result< Handler, void>::completion_handler_type & h) : - context::detail::coro_async_result< Handler, void >{ h } { - } -}; - -template< typename Handler, typename ReturnType, typename ...Args > -class CONTEXT_NET_NAMESPACE::async_result< context::basic_yield_context< Handler >, ReturnType( boost::system::error_code, Args...) > : - public context::detail::coro_async_result< Handler, typename std::decay< Args >::type... > { -public: - explicit async_result( - typename context::detail::coro_async_result< Handler, typename std::decay< Args >::type...>::completion_handler_type & h) : - context::detail::coro_async_result< Handler, typename std::decay< Args >::type... >{ h } { - } -}; - -template< typename Handler, typename Allocator, typename ...Ts > -struct CONTEXT_NET_NAMESPACE::associated_allocator< context::detail::coro_handler< Handler, Ts... >, Allocator > { - using type = associated_allocator_t< Handler, Allocator >; - - static type get( context::detail::coro_handler< Handler, Ts... > const& h, Allocator const& a = Allocator{} ) noexcept { - return associated_allocator< Handler, Allocator >::get( h.handler_, a); - } -}; - -template< typename Handler, typename Executor, typename ...Ts > -struct CONTEXT_NET_NAMESPACE::associated_executor< context::detail::coro_handler< Handler, Ts... >, Executor> { - using type = associated_executor_t< Handler, Executor >; - - static type get( context::detail::coro_handler< Handler, Ts... > const& h, Executor const& ex = Executor{} ) noexcept { - return associated_executor< Handler, Executor >::get( h.handler_, ex); - } -}; - -namespace context { -namespace detail { - -template< typename Handler, typename Function, typename StackAllocator > -struct spawn_data { - template< typename Hand, typename Func, typename Stack > - spawn_data( Hand && handler, bool call_handler, Func && function, Stack && salloc) : - handler_{ std::forward< Hand >( handler) }, - call_handler_{ call_handler }, - function_{ std::forward< Func >( function) }, - salloc_{ std::forward< Stack >( salloc) } { - } - - spawn_data( spawn_data const&) = delete; - spawn_data & operator=( spawn_data const&) = delete; - - Handler handler_; - bool call_handler_; - Function function_; - StackAllocator salloc_; - spawn_context caller_; -}; - -template< typename Handler, typename Function, typename StackAllocator > -struct spawn_helper { - void operator()() { - callee_.reset( - new spawn_context{ - std::allocator_arg, - std::move( data_->salloc_), - [this] (fiber_context && f) { - std::shared_ptr< spawn_data< Handler, Function, StackAllocator > > data = data_; - data->caller_.ctx_ = std::move( f); - const basic_yield_context< Handler > yh{ callee_, data->caller_, data->handler_ }; - try { - ( data->function_)( yh); - if ( data->call_handler_) { - ( data->handler_)(); - } - } catch ( boost::context::detail::forced_unwind const& e) { - throw; // must allow forced_unwind to propagate - } catch (...) { - auto callee = yh.callee_.lock(); - if ( callee) { - callee->eptr_ = std::current_exception(); - } - } - fiber_context caller = std::move( data->caller_.ctx_); - data.reset(); - return caller; - } } ); - callee_->ctx_ = std::move( callee_->ctx_).resume(); - if ( callee_->eptr_) { - std::rethrow_exception( std::move( callee_->eptr_) ); - } - } - - std::shared_ptr< spawn_context > callee_; - std::shared_ptr< spawn_data< Handler, Function, StackAllocator > > data_; -}; - -inline -void default_spawn_handler() { -} - -} // namespace detail - -template< typename Function, typename StackAllocator > -auto spawn( Function && function, StackAllocator && salloc) - -> typename std::enable_if< - context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value - >::type { - auto ex = context::detail::net::get_associated_executor( function); - spawn( ex, std::forward< Function >( function), std::forward< StackAllocator >( salloc) ); -} - -template< typename Handler, typename Function, typename StackAllocator > -auto spawn( Handler && handler, Function && function, StackAllocator && salloc) - -> typename std::enable_if< - ! context::detail::net::is_executor< typename std::decay< Handler >::type >::value && - ! std::is_convertible< Handler &, context::detail::net::execution_context & >::value && - ! context::detail::is_stack_allocator< typename std::decay< Function >::type >::value && - context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value - >::type { - using handler_type = typename std::decay< Handler >::type; - using function_type = typename std::decay< Function >::type; - - auto ex = context::detail::net::get_associated_executor( handler); - auto a = context::detail::net::get_associated_allocator( handler); - context::detail::spawn_helper< handler_type, function_type, StackAllocator > helper; - helper.data_ = std::make_shared< - context::detail::spawn_data< handler_type, function_type, StackAllocator > >( - std::forward< Handler >( handler), true, - std::forward< Function >( function), - std::forward< StackAllocator >( salloc) ); - ex.dispatch( helper, a); -} - -template< typename Handler, typename Function, typename StackAllocator > -auto spawn( context::basic_yield_context< Handler > ctx, Function && function, StackAllocator && salloc) - -> typename std::enable_if< - context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value - >::type { - using function_type = typename std::decay< Function >::type; - - Handler handler{ ctx.handler_ }; // Explicit copy that might be moved from. - auto ex = context::detail::net::get_associated_executor( handler); - auto a = context::detail::net::get_associated_allocator( handler); - context::detail::spawn_helper< Handler, function_type, StackAllocator > helper; - helper.data_ = std::make_shared< - context::detail::spawn_data< Handler, function_type, StackAllocator > >( - std::forward< Handler >( handler), false, - std::forward< Function >( function), - std::forward< StackAllocator >( salloc) ); - ex.dispatch( helper, a); -} - -template< typename Function, typename Executor, typename StackAllocator > -auto spawn( Executor const& ex, Function && function, StackAllocator && salloc) - -> typename std::enable_if< - context::detail::net::is_executor< Executor >::value && - context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value - >::type { - spawn( context::detail::net::strand< Executor >{ ex }, - std::forward< Function >( function), - std::forward< StackAllocator >( salloc) ); -} - -template< typename Function, typename Executor, typename StackAllocator > -auto spawn( context::detail::net::strand< Executor > const& ex, Function && function, StackAllocator && salloc) - -> typename std::enable_if< - context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value - >::type { - spawn( bind_executor( ex, & context::detail::default_spawn_handler), - std::forward< Function >( function), - std::forward< StackAllocator >( salloc) ); -} - -template< typename Function, typename ExecutionContext, typename StackAllocator > -auto spawn( ExecutionContext & ctx, Function && function, StackAllocator && salloc) - -> typename std::enable_if< - std::is_convertible< ExecutionContext &, context::detail::net::execution_context & >::value && - context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value - >::type { - spawn( ctx.get_executor(), - std::forward< Function >( function), - std::forward< StackAllocator >( salloc) ); -} - -#endif // !defined(GENERATING_DOCUMENTATION) - -}} - -#endif // BOOST_CONTEXT_IMPL_SPAWN_H diff --git a/include/boost/context/spawn.hpp b/include/boost/context/spawn.hpp deleted file mode 100644 index f884ae7..0000000 --- a/include/boost/context/spawn.hpp +++ /dev/null @@ -1,129 +0,0 @@ - -// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// Copyright (c) 2017,2021 Oliver Kowalke (oliver dot kowalke at gmail dot com) -// Copyright (c) 2019 Casey Bodley (cbodley at redhat dot com) -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#ifndef BOOST_CONTEXT_SPAWN_H -#define BOOST_CONTEXT_SPAWN_H - -#include - -#include - -#include -#include -#include -#include - -namespace boost { -namespace context { -namespace detail { - -class spawn_context; - -} - -// Context object represents the current execution context. -// The basic_yield_context class is used to represent the current execution -// context. A basic_yield_context may be passed as a handler to an -// asynchronous operation. For example: -template< typename Handler > -class basic_yield_context { -public: - // Construct a yield context to represent the specified execution context. - // Most applications do not need to use this constructor. Instead, the - // spawn() function passes a yield context as an argument to the fiber - // function. - basic_yield_context( - std::weak_ptr< detail::spawn_context > const& callee, - detail::spawn_context & caller, - Handler & handler) : - callee_{ callee }, - caller_{ caller }, - handler_{ handler }, - ec_{ 0 } { - } - - // Construct a yield context from another yield context type. - // Requires that OtherHandler be convertible to Handler. - template< typename OtherHandler > - basic_yield_context( - basic_yield_context< OtherHandler > const& other) : - callee_{ other.callee_ }, - caller_{ other.caller_ }, - handler_{ other.handler_ }, - ec_{ other.ec_ } { - } - - // Return a yield context that sets the specified error_code. - // By default, when a yield context is used with an asynchronous operation, a - // non-success error_code is converted to system_error and thrown. This - // operator may be used to specify an error_code object that should instead be - // set with the asynchronous operation's result. For example: - basic_yield_context operator[]( boost::system::error_code & ec) const { - basic_yield_context tmp{ * this }; - tmp.ec_ = & ec; - return tmp; - } - -//private: - std::weak_ptr< detail::spawn_context > callee_; - detail::spawn_context & caller_; - Handler handler_; - boost::system::error_code * ec_; -}; - -using yield_context = basic_yield_context< detail::net::executor_binder< void(*)(), detail::net::executor > >; - -// The spawn() function is a high-level wrapper over the Boost.Context -// library (spawn_context). This function enables programs to -// implement asynchronous logic in a synchronous manner. -template< typename Function, typename StackAllocator = boost::context::default_stack > -auto spawn( Function && fn, StackAllocator && salloc = StackAllocator() ) - -> typename std::enable_if< - boost::context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value - >::type; - -template< typename Handler, typename Function, typename StackAllocator = boost::context::default_stack > -auto spawn( Handler && hndlr, Function && fn, StackAllocator && salloc = StackAllocator() ) - -> typename std::enable_if< - ! context::detail::net::is_executor< typename std::decay< Handler >::type >::value && - ! std::is_convertible< Handler &, context::detail::net::execution_context & >::value && - ! context::detail::is_stack_allocator< typename std::decay< Function >::type >::value && - context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value - >::type; - -template< typename Handler, typename Function, typename StackAllocator = boost::context::default_stack > -auto spawn( context::basic_yield_context< Handler > ctx, Function && fn, StackAllocator && salloc = StackAllocator() ) - -> typename std::enable_if< - context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value - >::type; - -template< typename Function, typename Executor, typename StackAllocator = boost::context::default_stack > -auto spawn( Executor const& ex, Function && function, StackAllocator && salloc = StackAllocator() ) - -> typename std::enable_if< - context::detail::net::is_executor< Executor >::value && - context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value - >::type; - -template< typename Function, typename Executor, typename StackAllocator = boost::context::default_stack > -auto spawn( context::detail::net::strand< Executor > const& ex, Function && function, StackAllocator && salloc = StackAllocator() ) - -> typename std::enable_if< - context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value - >::type; - -template< typename Function, typename ExecutionContext, typename StackAllocator = boost::context::default_stack > -auto spawn( ExecutionContext & ctx, Function && function, StackAllocator && salloc = StackAllocator() ) - -> typename std::enable_if< - std::is_convertible< ExecutionContext &, context::detail::net::execution_context & >::value && - context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value - >::type; - -}} - -#include - -#endif // BOOST_CONTEXT_SPAWN_H diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 3c5f9cd..78b9129 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -203,43 +203,7 @@ test-suite minimal : cxx11_template_aliases cxx11_thread_local cxx11_variadic_templates ] - : test_callcc_segmented ] - -[ run test_spawn.cpp : - : : - fcontext - [ requires cxx11_auto_declarations - cxx11_constexpr - cxx11_defaulted_functions - cxx11_final - cxx11_hdr_thread - cxx11_hdr_tuple - cxx11_lambdas - cxx11_noexcept - cxx11_nullptr - cxx11_rvalue_references - cxx11_template_aliases - cxx11_thread_local - cxx11_variadic_templates ] - : test_spawn_asm ] - -[ run test_spawn.cpp : - : : - @native-impl - [ requires cxx11_auto_declarations - cxx11_constexpr - cxx11_defaulted_functions - cxx11_final - cxx11_hdr_thread - cxx11_hdr_tuple - cxx11_lambdas - cxx11_noexcept - cxx11_nullptr - cxx11_rvalue_references - cxx11_template_aliases - cxx11_thread_local - cxx11_variadic_templates ] - : test_spawn_native ] ; + : test_callcc_segmented ] ; test-suite full : diff --git a/test/test_spawn.cpp b/test/test_spawn.cpp deleted file mode 100644 index b07091e..0000000 --- a/test/test_spawn.cpp +++ /dev/null @@ -1,596 +0,0 @@ - -// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// Copyright (c) 2019 Casey Bodley (cbodley at redhat dot com) -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#include - -#include -#include -#include -#include -#include - -// make assertions about async_result::return_type with different signatures -// this is a compilation test only - -template< typename Sig> -struct yield_result : boost::asio::async_result {}; - -template< typename T, typename Sig> -struct yield_returns : std::is_same::return_type> {}; - -// no return value -static_assert(yield_returns< void, void() >::value, - "wrong return value for void()"); -static_assert(yield_returns< void, void(boost::system::error_code) >::value, - "wrong return value for void(error_code)"); -// single-parameter return value -static_assert(yield_returns::value, - "wrong return value for void(int)"); -static_assert(yield_returns::value, - "wrong return value for void(error_code, int)"); -// multiple-parameter return value -static_assert(yield_returns, - void(int, std::string) >::value, - "wrong return value for void(int, string)"); -static_assert(yield_returns, - void(boost::system::error_code, int, std::string) >::value, - "wrong return value for void(error_code, int, string)"); -// single-tuple-parameter return value -static_assert(yield_returns, - void(std::tuple) >::value, - "wrong return value for void(std::tuple)"); -static_assert(yield_returns, - void(boost::system::error_code, std::tuple) >::value, - "wrong return value for void(error_code, std::tuple)"); -// single-pair-parameter return value -static_assert(yield_returns, - void(std::pair) >::value, - "wrong return value for void(std::tuple)"); -static_assert(yield_returns, - void(boost::system::error_code, std::pair) >::value, - "wrong return value for void(error_code, std::tuple)"); - -boost::context::protected_fixedsize_stack with_stack_allocator() { - return boost::context::protected_fixedsize_stack{ 65536 }; -} - -struct counting_handler { - int & count; - - counting_handler( int & count) : - count(count) { - } - - void operator()() { - ++count; - } - - template< typename T > - void operator()( boost::context::basic_yield_context< T >) { - ++count; - } -}; - -void spawnFunction() { - boost::asio::io_context ioc; - int called = 0; - boost::context::spawn( counting_handler{ called } ); - BOOST_CHECK_EQUAL(0, ioc.run()); // runs in system executor - BOOST_CHECK(ioc.stopped()); - BOOST_CHECK_EQUAL(1, called); -} - -void spawnBoundFunction() { - boost::asio::io_context ioc; - int called = 0; - boost::context::spawn( bind_executor( ioc.get_executor(), counting_handler{ called } ) ); - BOOST_CHECK_EQUAL(1, ioc.run()); - BOOST_CHECK(ioc.stopped()); - BOOST_CHECK_EQUAL(1, called); -} - -void spawnFunctionStackAllocator() { - boost::asio::io_context ioc; - int called = 0; - boost::context::spawn( - counting_handler{ called }, - with_stack_allocator() ); - BOOST_CHECK_EQUAL(0, ioc.run()); - BOOST_CHECK(ioc.stopped()); - BOOST_CHECK_EQUAL(1, called); -} - -void spawnHandler() { - boost::asio::io_context ioc; - boost::asio::strand< boost::asio::io_context::executor_type > strand{ ioc.get_executor() }; - int called = 0; - boost::context::spawn( - bind_executor( strand, counting_handler{ called } ), - counting_handler{ called } ); - BOOST_CHECK_EQUAL(1, ioc.run()); - BOOST_CHECK(ioc.stopped()); - BOOST_CHECK_EQUAL(2, called); -} - -void spawnHandlerStackAllocator() { - boost::asio::io_context ioc; - typedef boost::asio::io_context::executor_type executor_type; - boost::asio::strand< executor_type > strand{ ioc.get_executor() }; - int called = 0; - boost::context::spawn( - bind_executor( strand, counting_handler{ called } ), - counting_handler{ called }, - with_stack_allocator() ); - BOOST_CHECK_EQUAL(1, ioc.run()); - BOOST_CHECK(ioc.stopped()); - BOOST_CHECK_EQUAL(2, called); -} - -struct spawn_counting_handler { - int & count; - - spawn_counting_handler( int & count) : - count{ count } { - } - - template< typename T > - void operator()( boost::context::basic_yield_context< T > y) { - boost::context::spawn( y, counting_handler{ count } ); - ++count; - } -}; - -void spawnYieldContext() { - boost::asio::io_context ioc; - int called = 0; - boost::context::spawn( - bind_executor( ioc.get_executor(), counting_handler{ called } ), - spawn_counting_handler{ called } ); - BOOST_CHECK_EQUAL(1, ioc.run()); - BOOST_CHECK(ioc.stopped()); - BOOST_CHECK_EQUAL(3, called); -} - -struct spawn_alloc_counting_handler { - int & count; - - spawn_alloc_counting_handler( int & count) : - count{ count } { - } - - template< typename T > - void operator()( boost::context::basic_yield_context< T > y) { - boost::context::spawn( - y, - counting_handler{ count }, - with_stack_allocator() ); - ++count; - } -}; - -void spawnYieldContextStackAllocator() { - boost::asio::io_context ioc; - int called = 0; - boost::context::spawn( - bind_executor( ioc.get_executor(), counting_handler{ called } ), - spawn_alloc_counting_handler{ called } ); - BOOST_CHECK_EQUAL(1, ioc.run()); - BOOST_CHECK(ioc.stopped()); - BOOST_CHECK_EQUAL(3, called); -} - -void spawnExecutor() { - boost::asio::io_context ioc; - int called = 0; - boost::context::spawn( ioc.get_executor(), counting_handler{ called } ); - BOOST_CHECK_EQUAL(1, ioc.run()); - BOOST_CHECK(ioc.stopped()); - BOOST_CHECK_EQUAL(1, called); -} - -void spawnExecutorStackAllocator() { - boost::asio::io_context ioc; - int called = 0; - boost::context::spawn( - ioc.get_executor(), - counting_handler{ called }, - with_stack_allocator() ); - BOOST_CHECK_EQUAL(1, ioc.run()); - BOOST_CHECK(ioc.stopped()); - BOOST_CHECK_EQUAL(1, called); -} - -void spawnStrand() { - boost::asio::io_context ioc; - typedef boost::asio::io_context::executor_type executor_type; - int called = 0; - boost::context::spawn( - boost::asio::strand< executor_type >{ ioc.get_executor() }, - counting_handler{ called } ); - BOOST_CHECK_EQUAL(1, ioc.run()); - BOOST_CHECK(ioc.stopped()); - BOOST_CHECK_EQUAL(1, called); -} - -void spawnStrandStackAllocator() { - boost::asio::io_context ioc; - typedef boost::asio::io_context::executor_type executor_type; - int called = 0; - boost::context::spawn( - boost::asio::strand< executor_type >{ ioc.get_executor() }, - counting_handler{ called }, - with_stack_allocator() ); - BOOST_CHECK_EQUAL(1, ioc.run()); - BOOST_CHECK(ioc.stopped()); - BOOST_CHECK_EQUAL(1, called); -} - -void spawnExecutionContext() { - boost::asio::io_context ioc; - int called = 0; - boost::context::spawn( ioc, counting_handler{ called } ); - BOOST_CHECK_EQUAL(1, ioc.run()); - BOOST_CHECK(ioc.stopped()); - BOOST_CHECK_EQUAL(1, called); -} - -void spawnExecutionContextStackAllocator() { - boost::asio::io_context ioc; - int called = 0; - boost::context::spawn( - ioc, - counting_handler{ called }, - with_stack_allocator() ); - BOOST_CHECK_EQUAL(1, ioc.run()); - BOOST_CHECK(ioc.stopped()); - BOOST_CHECK_EQUAL(1, called); -} - -typedef boost::asio::system_timer timer_type; - -struct spawn_wait_handler { - timer_type & timer; - - spawn_wait_handler( timer_type & timer) : - timer{ timer } { - } - - template< typename T > - void operator()( boost::context::basic_yield_context< T > yield) { - timer.async_wait( yield); - } -}; - -void spawnTimer() { - int called = 0; - { - boost::asio::io_context ioc; - timer_type timer{ ioc, boost::asio::chrono::hours{ 0 } }; - boost::context::spawn( - bind_executor( ioc.get_executor(), counting_handler{ called } ), - spawn_wait_handler{ timer } ); - BOOST_CHECK_EQUAL(2, ioc.run() ); - BOOST_CHECK( ioc.stopped() ); - } - BOOST_CHECK_EQUAL(1, called); -} - -void spawnTimerDestruct() { - int called = 0; - { - boost::asio::io_context ioc; - timer_type timer{ ioc, boost::asio::chrono::hours{ 65536 } }; - boost::context::spawn( - bind_executor( ioc.get_executor(), counting_handler{ called } ), - spawn_wait_handler{ timer } ); - BOOST_CHECK_EQUAL(1, ioc.run_one() ); - BOOST_CHECK(!ioc.stopped() ); - } - BOOST_CHECK_EQUAL(0, called); -} - -using boost::system::error_code; - -template< typename Handler, typename ...Args > -void post( Handler & h, Args && ...args) { - auto ex = boost::asio::get_associated_executor( h); - //auto a = boost::asio::get_associated_allocator( h); - auto b = std::bind( std::move( h), std::forward< Args >( args) ...); - boost::asio::post( ex, std::move( b)); - //ex.post(std::move(b), a); -} - -struct single_tuple_handler { - std::tuple< int, std::string > & result; - - void operator()( boost::context::yield_context y) { - using Signature = void(std::tuple); - boost::asio::async_completion< boost::context::yield_context, Signature > init{ y }; - post( - init.completion_handler, - std::make_tuple( 42, std::string{ "test" } ) ); - result = init.result.get(); - } -}; - -void returnSingleTuple() { - boost::asio::io_context ioc; - std::tuple< int, std::string > result; - boost::context::spawn( ioc, single_tuple_handler{ result } ); - BOOST_CHECK_EQUAL(2, ioc.poll() ); - BOOST_CHECK(std::make_tuple(42, std::string{"test"}) == result); -} - -struct multiple2_handler { - std::tuple& result; - void operator()( boost::context::yield_context y) { - using Signature = void(error_code, int, std::string); - boost::asio::async_completion< boost::context::yield_context, Signature > init{ y }; - post( - init.completion_handler, - error_code{}, - 42, - std::string{"test"} ); - result = init.result.get(); - } -}; - -void returnMultiple2() { - boost::asio::io_context ioc; - std::tuple< int, std::string > result; - boost::context::spawn( ioc, multiple2_handler{result}); - BOOST_CHECK_EQUAL(2, ioc.poll() ); - BOOST_CHECK(std::make_tuple(42, std::string{"test"}) == result); -} - -struct multiple2_with_moveonly_handler { - std::tuple< int, std::unique_ptr< int > > & result; - - void operator()( boost::context::yield_context y) { - using Signature = void(int, std::unique_ptr); - boost::asio::async_completion< boost::context::yield_context, Signature > init{ y }; - std::unique_ptr< int > ptr{ new int(42) }; - init.completion_handler( 42, std::move( ptr) ); - result = init.result.get(); - } -}; - -void returnMultiple2MoveOnly() { - boost::asio::io_context ioc; - std::tuple< int, std::unique_ptr< int > > result; - boost::context::spawn( ioc, multiple2_with_moveonly_handler{ result } ); - BOOST_CHECK_EQUAL(1, ioc.poll() ); - BOOST_CHECK_EQUAL(42, std::get<0>(result) ); - BOOST_CHECK(std::get<1>(result) ); - BOOST_CHECK_EQUAL(42, *std::get<1>(result) ); -} - -struct multiple3_handler { - std::tuple< int, std::string, double > & result; - - void operator()( boost::context::yield_context y) { - using Signature = void(error_code, int, std::string, double); - boost::asio::async_completion< boost::context::yield_context, Signature > init{ y }; - post( - init.completion_handler, - error_code{}, - 42, - std::string{"test"}, - 2.0); - result = init.result.get(); - } -}; - -void returnMultiple3() { - boost::asio::io_context ioc; - std::tuple< int, std::string, double > result; - boost::context::spawn( ioc, multiple3_handler{result}); - BOOST_CHECK_EQUAL(2, ioc.poll() ); - BOOST_CHECK(std::make_tuple( 42, std::string{"test"}, 2.0) == result); -} - -struct non_default_constructible { - non_default_constructible() = delete; - non_default_constructible( std::nullptr_t) { - } -}; - -struct non_default_constructible_handler { - boost::optional< non_default_constructible > & result; - - void operator()( boost::context::yield_context y) { - using Signature = void(non_default_constructible); - boost::asio::async_completion< boost::context::yield_context, Signature > init{ y }; - post( - init.completion_handler, - non_default_constructible{ nullptr } ); - result = init.result.get(); - } -}; - -void returnNonDefaultConstructible() { - boost::asio::io_context ioc; - boost::optional< non_default_constructible > result; - boost::context::spawn( ioc, non_default_constructible_handler{ result } ); - BOOST_CHECK_EQUAL(2, ioc.poll() ); - BOOST_CHECK(result); -} - -struct multiple_non_default_constructible_handler { - boost::optional< std::tuple< int, non_default_constructible > > & result; - - void operator()( boost::context::yield_context y) { - using Signature = void(error_code, int, non_default_constructible); - boost::asio::async_completion< boost::context::yield_context, Signature > init{ y }; - post( - init.completion_handler, - error_code{}, - 42, - non_default_constructible{ nullptr } ); - result = init.result.get(); - } -}; - -void returnMultipleNonDefaultConstructible() { - boost::asio::io_context ioc; - boost::optional< std::tuple< int, non_default_constructible > > result; - boost::context::spawn( ioc, multiple_non_default_constructible_handler{ result } ); - BOOST_CHECK_EQUAL(2, ioc.poll() ); - BOOST_CHECK(result); -} - -struct throwing_handler { - template< typename T > - void operator()( boost::context::basic_yield_context< T >) { - throw std::runtime_error{ "" }; - } -}; - -void spawnThrowInHelper() { - boost::asio::io_context ioc; - boost::context::spawn( ioc, throwing_handler{} ); - BOOST_CHECK_THROW(ioc.run_one(), std::runtime_error); // spawn->throw -} - -struct noop_handler { - template< typename T > - void operator()( boost::context::basic_yield_context< T >) { - } -}; - -struct throwing_completion_handler { - void operator()() { - throw std::runtime_error{ "" }; - } -}; - -void spawnHandlerThrowInHelper() { - boost::asio::io_context ioc; - boost::context::spawn( - bind_executor( ioc.get_executor(), throwing_completion_handler{} ), - noop_handler{} ); - BOOST_CHECK_THROW( ioc.run_one(), std::runtime_error); // spawn->throw -} - -template< typename CompletionToken > -auto async_yield( CompletionToken && token) -> BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void() ) { - boost::asio::async_completion< CompletionToken, void() > init{ token }; - boost::asio::post( std::move( init.completion_handler) ); - return init.result.get(); -} - -struct yield_throwing_handler { - template< typename T > - void operator()( boost::context::basic_yield_context< T > y) { - async_yield( y); // suspend and resume before throwing - throw std::runtime_error{ "" }; - } -}; - -void spawnThrowAfterYield() { - boost::asio::io_context ioc; - boost::context::spawn( ioc, yield_throwing_handler{} ); - BOOST_CHECK_NO_THROW(ioc.run_one() ); // yield_throwing_handler suspend - BOOST_CHECK_THROW(ioc.run_one(), std::runtime_error); // resume + throw -} - -struct yield_handler { - template< typename T > - void operator()( boost::context::basic_yield_context< T > y) { - async_yield( y); - } -}; - -void spawnHandlerThrowAfterYield() { - boost::asio::io_context ioc; - boost::context::spawn( - bind_executor( ioc.get_executor(), throwing_completion_handler{} ), - yield_handler{} ); - BOOST_CHECK_NO_THROW(ioc.run_one() ); // yield_handler suspend - BOOST_CHECK_THROW(ioc.run_one(), std::runtime_error); // resume + throw -} - -struct nested_throwing_handler { - template< typename T > - void operator()( boost::context::basic_yield_context< T > y) { - boost::context::spawn( y, throwing_handler{} ); - } -}; - -void spawnThrowInNestedHelper() { - boost::asio::io_context ioc; - boost::context::spawn( ioc, nested_throwing_handler{} ); - BOOST_CHECK_THROW(ioc.run_one(), std::runtime_error); // spawn->spawn->throw -} - -struct yield_nested_throwing_handler { - template< typename T > - void operator()( boost::context::basic_yield_context< T > y) { - async_yield( y); // suspend and resume before spawning - boost::context::spawn( y, yield_throwing_handler{} ); - } -}; - -void spawnThrowAfterNestedYield() { - boost::asio::io_context ioc; - boost::context::spawn( ioc, yield_nested_throwing_handler{} ); - BOOST_CHECK_NO_THROW(ioc.run_one() ); // yield_nested_throwing_handler suspend - BOOST_CHECK_NO_THROW(ioc.run_one() ); // yield_throwing_handler suspend - BOOST_CHECK_THROW(ioc.run_one(), std::runtime_error); // resume + throw -} - -struct yield_throw_after_nested_handler { - template< typename T > - void operator()( boost::context::basic_yield_context< T > y) { - async_yield( y); // suspend and resume before spawning - boost::context::spawn( y, yield_handler{} ); - throw std::runtime_error{ "" }; - } -}; - -void spawnThrowAfterNestedSpawn() { - boost::asio::io_context ioc; - boost::context::spawn( ioc, yield_throw_after_nested_handler() ); - BOOST_CHECK_NO_THROW(ioc.run_one() ); // yield_throw_after_nested_handler suspend - BOOST_CHECK_THROW(ioc.run_one(), std::runtime_error); // resume + throw - BOOST_CHECK_EQUAL(1, ioc.poll() ); // yield_handler resume - BOOST_CHECK(ioc.stopped() ); -} - -boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { - boost::unit_test::test_suite * test = - BOOST_TEST_SUITE("Boost.Context: spawn test suite"); - test->add( BOOST_TEST_CASE( & spawnFunction) ); - test->add( BOOST_TEST_CASE( & spawnBoundFunction) ); - test->add( BOOST_TEST_CASE( & spawnFunctionStackAllocator) ); - test->add( BOOST_TEST_CASE( & spawnHandler) ); - test->add( BOOST_TEST_CASE( & spawnHandlerStackAllocator) ); - test->add( BOOST_TEST_CASE( & spawnYieldContext) ); - test->add( BOOST_TEST_CASE( & spawnYieldContextStackAllocator) ); - test->add( BOOST_TEST_CASE( & spawnExecutor) ); - test->add( BOOST_TEST_CASE( & spawnExecutorStackAllocator) ); - test->add( BOOST_TEST_CASE( & spawnStrand) ); - test->add( BOOST_TEST_CASE( & spawnStrandStackAllocator) ); - test->add( BOOST_TEST_CASE( & spawnExecutionContext) ); - test->add( BOOST_TEST_CASE( & spawnExecutionContextStackAllocator) ); - test->add( BOOST_TEST_CASE( & spawnTimer) ); - test->add( BOOST_TEST_CASE( & spawnTimerDestruct) ); - test->add( BOOST_TEST_CASE( & returnSingleTuple) ); - test->add( BOOST_TEST_CASE( & returnMultiple2) ); - test->add( BOOST_TEST_CASE( & returnMultiple2MoveOnly) ); - test->add( BOOST_TEST_CASE( & returnMultiple3) ); - test->add( BOOST_TEST_CASE( & returnNonDefaultConstructible) ); - test->add( BOOST_TEST_CASE( & returnMultipleNonDefaultConstructible) ); - test->add( BOOST_TEST_CASE( & spawnThrowInHelper) ); - test->add( BOOST_TEST_CASE( & spawnHandlerThrowInHelper) ); - test->add( BOOST_TEST_CASE( & spawnThrowAfterYield) ); - test->add( BOOST_TEST_CASE( & spawnHandlerThrowAfterYield) ); - test->add( BOOST_TEST_CASE( & spawnThrowInNestedHelper) ); - test->add( BOOST_TEST_CASE( & spawnThrowAfterNestedYield) ); - test->add( BOOST_TEST_CASE( & spawnThrowAfterNestedSpawn) ); - return test; -}