diff --git a/examples/asio/daytime_client.cpp b/examples/asio/daytime_client.cpp index acd5653b..c6801e3e 100644 --- a/examples/asio/daytime_client.cpp +++ b/examples/asio/daytime_client.cpp @@ -21,7 +21,6 @@ #include #include "loop.hpp" -#include "spawn.hpp" #include "use_future.hpp" using boost::asio::ip::udp; @@ -84,9 +83,9 @@ int main( int argc, char* argv[]) return 1; } - boost::fibers::asio::spawn( io_service, + boost::fibers::fiber( boost::bind( get_daytime, - boost::ref( io_service), argv[1]) ); + boost::ref( io_service), argv[1]) ).detach(); boost::fibers::asio::run_service( io_service); } diff --git a/examples/asio/daytime_client2.cpp b/examples/asio/daytime_client2.cpp index b59f6ec7..0460aca9 100644 --- a/examples/asio/daytime_client2.cpp +++ b/examples/asio/daytime_client2.cpp @@ -21,7 +21,6 @@ #include #include "loop.hpp" -#include "spawn.hpp" #include "yield.hpp" using boost::asio::ip::udp; @@ -75,9 +74,9 @@ int main( int argc, char* argv[]) return 1; } - boost::fibers::asio::spawn( io_service, + boost::fibers::fiber( boost::bind( get_daytime, - boost::ref( io_service), argv[1]) ); + boost::ref( io_service), argv[1]) ).detach(); boost::fibers::asio::run_service( io_service); } diff --git a/examples/asio/detail/promise_handler.hpp b/examples/asio/detail/promise_handler.hpp new file mode 100644 index 00000000..990a9d6d --- /dev/null +++ b/examples/asio/detail/promise_handler.hpp @@ -0,0 +1,164 @@ +// +// promise_handler.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2013 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) +// +// modified by Oliver Kowalke and Nat Goodspeed +// + +#ifndef BOOST_FIBERS_ASIO_DETAIL_PROMISE_HANDLER_HPP +#define BOOST_FIBERS_ASIO_DETAIL_PROMISE_HANDLER_HPP + +#include + +#include +#include + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace asio { +namespace detail { + +// Completion handler to adapt a promise as a completion handler. +template< typename T > +class promise_handler_base +{ + typedef boost::shared_ptr< boost::fibers::promise< T > > promise_ptr; + +public: + // Construct from any promise_completion_token subclass special value. + template< typename Allocator > + promise_handler_base( const boost::fibers::asio::promise_completion_token< Allocator >& pct) : + promise_( new boost::fibers::promise< T >( std::allocator_arg, pct.get_allocator() ) ), + ecp_( pct.ec_) + {} + + bool should_set_value( boost::system::error_code const& ec) + { + if (! ec) + { + // whew, success + return true; + } + + // ec indicates error + if (ecp_) + { + // promise_completion_token bound an error_code variable: set it + *ecp_ = ec; + // This is the odd case: although there's an error, user code + // expressly forbid us to call set_exception(). We've set the + // bound error code -- but future::get() will wait forever unless + // we kick the promise SOMEHOW. Tell subclass to call set_value() + // anyway. + return true; + } + + // no bound error_code: cause promise_ to throw an exception + promise_->set_exception( + std::make_exception_ptr( + boost::system::system_error( ec) ) ); + // caller should NOT call set_value() + return false; + } + + promise_ptr get_promise() const + { return promise_; } + +private: + promise_ptr promise_; + boost::system::error_code * ecp_; +}; + +// generic promise_handler for arbitrary value +template< typename T > +class promise_handler: public promise_handler_base +{ + using promise_handler_base::should_set_value; + +public: + // Construct from any promise_completion_token subclass special value. + template< typename Allocator > + promise_handler( const boost::fibers::asio::promise_completion_token< Allocator >& pct) : + promise_handler_base( pct) + {} + + void operator()( T t) + { + get_promise()->set_value( t); + } + + void operator()( boost::system::error_code const& ec, T t) + { + if (should_set_value(ec)) + get_promise()->set_value( t); + } + + using promise_handler_base::get_promise; +}; + +// specialize promise_handler for void +template<> +class promise_handler< void >: public promise_handler_base +{ + using promise_handler_base::should_set_value; + +public: + // Construct from any promise_completion_token subclass special value. + template< typename Allocator > + promise_handler( const boost::fibers::asio::promise_completion_token< Allocator >& pct) : + promise_handler_base( pct) + {} + + void operator()() + { + get_promise()->set_value(); + } + + void operator()( boost::system::error_code const& ec) + { + if (should_set_value( ec)) + get_promise()->set_value(); + } + + using promise_handler_base::get_promise; +}; + +} // namespace detail +} // namespace asio +} // namespace fibers + +namespace asio { +namespace detail { + +// Specialize asio_handler_invoke hook to ensure that any exceptions thrown +// from the handler are propagated back to the caller via the future. +template< typename Function, typename T > +void asio_handler_invoke( Function f, fibers::asio::detail::promise_handler< T > * h) +{ + boost::shared_ptr< boost::fibers::promise< T > > p( h->get_promise()); + try + { f(); } + catch (...) + { p->set_exception( std::current_exception() ); } +} + +} // namespace detail +} // namespace asio +} // namespace boost + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBERS_ASIO_DETAIL_PROMISE_HANDLER_HPP diff --git a/examples/asio/detail/spawn.hpp b/examples/asio/detail/spawn.hpp deleted file mode 100644 index a44488b0..00000000 --- a/examples/asio/detail/spawn.hpp +++ /dev/null @@ -1,342 +0,0 @@ -// -// detail/spawn.hpp -// ~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2013 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) -// - -#ifndef BOOST_FIBERS_ASIO_DETAIL_SPAWN_HPP -#define BOOST_FIBERS_ASIO_DETAIL_SPAWN_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifdef BOOST_HAS_ABI_HEADERS -# include BOOST_ABI_PREFIX -#endif - -namespace boost { -namespace fibers { -namespace asio { - -namespace detail { - -template< typename Handler, typename T > -class fiber_handler -{ -public: - fiber_handler( basic_yield_context< Handler > ctx) : - fiber_( ctx.fiber_), - handler_( ctx.handler_), - ec_( ctx.ec_), - value_( 0) - {} - - void operator()( T value) - { - * ec_ = boost::system::error_code(); - * value_ = value; - fiber_->set_ready(); - } - - void operator()( boost::system::error_code ec, T value) - { - * ec_ = ec; - * value_ = value; - fiber_->set_ready(); - } - -//private: - boost::fibers::fiber_context * fiber_; - Handler & handler_; - boost::system::error_code * ec_; - T * value_; -}; - -template< typename Handler > -class fiber_handler< Handler, void > - -{ -public: - fiber_handler( basic_yield_context< Handler > ctx) : - fiber_( ctx.fiber_), - handler_( ctx.handler_), - ec_( ctx.ec_) - {} - - void operator()() - { - * ec_ = boost::system::error_code(); - fiber_->set_ready(); - } - - void operator()( boost::system::error_code ec) - { - * ec_ = ec; - fiber_->set_ready(); - } - -//private: - boost::fibers::fiber_context * fiber_; - Handler & handler_; - boost::system::error_code * ec_; -}; - -template< typename Handler, typename T > -void* asio_handler_allocate( std::size_t size, - fiber_handler< Handler, T > * this_handler) -{ - return boost_asio_handler_alloc_helpers::allocate( - size, this_handler->handler_); -} - -template< typename Handler, typename T > -void asio_handler_deallocate( void* pointer, std::size_t size, - fiber_handler< Handler, T > * this_handler) -{ - boost_asio_handler_alloc_helpers::deallocate( - pointer, size, this_handler->handler_); -} - -template< typename Handler, typename T > -bool asio_handler_is_continuation( fiber_handler *) -{ return true; } - -template< typename Function, typename Handler, typename T > -void asio_handler_invoke( Function & function, - fiber_handler< Handler, T > * this_handler) -{ - boost_asio_handler_invoke_helpers::invoke( - function, this_handler->handler_); -} - -template< typename Function, typename Handler, typename T > -void asio_handler_invoke( Function const& function, - fiber_handler< Handler, T > * this_handler) -{ - boost_asio_handler_invoke_helpers::invoke( - function, this_handler->handler_); -} - -} // namespace detail -} // namespace asio -} // namespace fibers - -namespace asio { - -#if !defined(GENERATING_DOCUMENTATION) - -template< typename Handler, typename ReturnType > -struct handler_type< - boost::fibers::asio::basic_yield_context< Handler >, - ReturnType() -> -{ typedef boost::fibers::asio::detail::fiber_handler< Handler, void > type; }; - -template< typename Handler, typename ReturnType, typename Arg1 > -struct handler_type< - boost::fibers::asio::basic_yield_context< Handler >, - ReturnType( Arg1) -> -{ typedef boost::fibers::asio::detail::fiber_handler< Handler, Arg1 > type; }; - -template< typename Handler, typename ReturnType > -struct handler_type< - boost::fibers::asio::basic_yield_context< Handler >, - ReturnType( boost::system::error_code) -> -{ typedef boost::fibers::asio::detail::fiber_handler type; }; - -template< typename Handler, typename ReturnType, typename Arg2 > -struct handler_type< - boost::fibers::asio::basic_yield_context< Handler >, - ReturnType( boost::system::error_code, Arg2) -> -{ typedef boost::fibers::asio::detail::fiber_handler< Handler, Arg2 > type; }; - -template< typename Handler, typename T > -class async_result< boost::fibers::asio::detail::fiber_handler< Handler, T > > -{ -public: - typedef T type; - - explicit async_result( boost::fibers::asio::detail::fiber_handler< Handler, T > & h) : - out_ec_( 0), ec_(), value_() - { - out_ec_ = h.ec_; - if ( ! out_ec_) h.ec_ = & ec_; - h.value_ = & value_; - } - - type get() - { - fibers::detail::spinlock splk; - std::unique_lock< fibers::detail::spinlock > lk( splk); - boost::fibers::detail::scheduler::instance()->wait(lk); - if ( ! out_ec_ && ec_) throw boost::system::system_error( ec_); - return value_; - } - -private: - boost::system::error_code * out_ec_; - boost::system::error_code ec_; - type value_; -}; - -template< typename Handler > -class async_result< boost::fibers::asio::detail::fiber_handler< Handler, void > > -{ -public: - typedef void type; - - explicit async_result( boost::fibers::asio::detail::fiber_handler< Handler, void > & h) : - out_ec_( 0), ec_() - { - out_ec_ = h.ec_; - if (!out_ec_) h.ec_ = &ec_; - } - - void get() - { - fibers::detail::spinlock splk; - std::unique_lock< fibers::detail::spinlock > lk( splk); - boost::fibers::detail::scheduler::instance()->wait(lk); - if ( ! out_ec_ && ec_) throw boost::system::system_error( ec_); - } - -private: - boost::system::error_code * out_ec_; - boost::system::error_code ec_; -}; - -} // namespace asio - -namespace fibers { -namespace asio { -namespace detail { - -template< typename Handler, typename Function > -struct spawn_data : private noncopyable -{ - spawn_data( boost::asio::io_service& io_svc, BOOST_ASIO_MOVE_ARG( Handler) handler, - bool call_handler, BOOST_ASIO_MOVE_ARG( Function) function) : - io_svc_(io_svc), - handler_( BOOST_ASIO_MOVE_CAST( Handler)( handler) ), - call_handler_( call_handler), - function_( BOOST_ASIO_MOVE_CAST( Function)( function) ) - {} - - boost::asio::io_service& io_svc_; - boost::fibers::fiber_context* fiber_; - Handler handler_; - bool call_handler_; - Function function_; -}; - -template< typename Handler, typename Function > -struct fiber_entry_point -{ - void operator()() - { - shared_ptr< spawn_data< Handler, Function > > data( data_); - data->fiber_ = boost::fibers::detail::scheduler::instance()->active(); - const basic_yield_context< Handler > yield( - data->fiber_, data->handler_); - - boost::asio::io_service::work w(data->io_svc_); - ( data->function_)( yield); - if ( data->call_handler_) - ( data->handler_)(); - } - - shared_ptr< spawn_data< Handler, Function > > data_; -}; - -template< typename Handler, typename Function > -struct spawn_helper -{ - void operator()() - { - fiber_entry_point< Handler, Function > entry_point = { data_ }; - boost::fibers::fiber fiber( entry_point); - fiber.detach(); - } - - shared_ptr< spawn_data< Handler, Function > > data_; -}; - -inline void default_spawn_handler() {} - -} // namespace detail - -template< typename Handler, typename Function > -void spawn( boost::asio::io_service& io_service, - BOOST_ASIO_MOVE_ARG( Handler) handler, - BOOST_ASIO_MOVE_ARG( Function) function) -{ - detail::spawn_helper< Handler, Function > helper; - helper.data_.reset( - new detail::spawn_data< Handler, Function >( - io_service, - BOOST_ASIO_MOVE_CAST( Handler)( handler), true, - BOOST_ASIO_MOVE_CAST( Function)( function) ) ); - boost_asio_handler_invoke_helpers::invoke( - helper, helper.data_->handler_); -} - -template< typename Handler, typename Function > -void spawn( basic_yield_context< Handler > ctx, - BOOST_ASIO_MOVE_ARG( Function) function) -{ - Handler handler( ctx.handler_); // Explicit copy that might be moved from. - detail::spawn_helper< Handler, Function > helper; - helper.data_.reset( - new detail::spawn_data< Handler, Function >( - BOOST_ASIO_MOVE_CAST( Handler)( handler), false, - BOOST_ASIO_MOVE_CAST( Function)( function) ) ); - boost_asio_handler_invoke_helpers::invoke( - helper, helper.data_->handler_); -} - -template< typename Function > -void spawn( boost::asio::io_service::strand strand, - BOOST_ASIO_MOVE_ARG( Function) function) -{ - boost::fibers::asio::spawn( - strand.get_io_service(), - strand.wrap( & detail::default_spawn_handler), - BOOST_ASIO_MOVE_CAST( Function)( function)); -} - -template< typename Function > -void spawn( boost::asio::io_service & io_service, - BOOST_ASIO_MOVE_ARG( Function) function) -{ - boost::fibers::asio::spawn( - boost::asio::io_service::strand( io_service), - BOOST_ASIO_MOVE_CAST( Function)( function)); -} - -#endif // !defined(GENERATING_DOCUMENTATION) - -} // namespace asio -} // namespace fibers -} // namespace boost - -#ifdef BOOST_HAS_ABI_HEADERS -# include BOOST_ABI_SUFFIX -#endif - -#endif // BOOST_FIBERS_ASIO_DETAIL_SPAWN_HPP diff --git a/examples/asio/detail/use_future.hpp b/examples/asio/detail/use_future.hpp index da10f278..decd1e1b 100644 --- a/examples/asio/detail/use_future.hpp +++ b/examples/asio/detail/use_future.hpp @@ -7,120 +7,42 @@ // 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) // -// modified by Oliver Kowalke +// modified by Oliver Kowalke and Nat Goodspeed // #ifndef BOOST_FIBERS_ASIO_DETAIL_USE_FUTURE_HPP #define BOOST_FIBERS_ASIO_DETAIL_USE_FUTURE_HPP -#include -#include - #include -#include #include -#include -#include #include +#include "promise_handler.hpp" + #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif namespace boost { +namespace fibers { namespace asio { namespace detail { -// Completion handler to adapt a promise as a completion handler. +// use_future_handler is just an alias for promise_handler -- but we must +// distinguish this case to specialize async_result below. +template < typename T > +using use_future_handler = promise_handler; + +} // detail +} // asio +} // fibers + +namespace asio { + +// Handler traits specialisation for use_future_handler. template< typename T > -class promise_handler -{ -public: - // Construct from use_future special value. - template< typename Allocator > - promise_handler( boost::fibers::asio::use_future_t< Allocator > uf) : - promise_( new boost::fibers::promise< T >( std::allocator_arg, uf.get_allocator() ) ) - {} - - void operator()( T t) - { - promise_->set_value( t); - //boost::fibers::detail::scheduler::instance()->run(); - } - - void operator()( boost::system::error_code const& ec, T t) - { - if (ec) - promise_->set_exception( - std::make_exception_ptr( - boost::system::system_error( ec) ) ); - else - promise_->set_value( t); - - // scheduler::run() resumes a ready fiber - // invoke scheduler::run() until no fiber was resumed - //boost::fibers::detail::scheduler::instance()->run(); - } - - //private: - boost::shared_ptr< boost::fibers::promise< T > > promise_; -}; - -// Completion handler to adapt a void promise as a completion handler. -template<> -class promise_handler< void > -{ -public: - // Construct from use_future special value. Used during rebinding. - template< typename Allocator > - promise_handler( boost::fibers::asio::use_future_t< Allocator > uf) : - promise_( new boost::fibers::promise< void > >( std::allocator_arg, uf.get_allocator() ) ) - {} - - void operator()() - { - promise_->set_value(); - //boost::fibers::detail::scheduler::instance()->run(); - } - - void operator()( boost::system::error_code const& ec) - { - if ( ec) - promise_->set_exception( - std::make_exception_ptr( - boost::system::system_error( ec) ) ); - else - promise_->set_value(); - - // scheduler::run() resumes a ready fiber - // invoke scheduler::run() until no fiber was resumed - //boost::fibers::detail::scheduler::instance()->run(); - } - - //private: - boost::shared_ptr< boost::fibers::promise< void > > promise_; -}; - -// Ensure any exceptions thrown from the handler are propagated back to the -// caller via the future. -template< typename Function, typename T > -void asio_handler_invoke( Function f, promise_handler< T > * h) -{ - boost::shared_ptr< boost::fibers::promise< T > > p( h->promise_); - try - { f(); } - catch (...) - { p->set_exception( std::current_exception() ); } -} - -} // namespace detail - -#if !defined(GENERATING_DOCUMENTATION) - -// Handler traits specialisation for promise_handler. -template< typename T > -class async_result< detail::promise_handler< T > > +class async_result< fibers::asio::detail::use_future_handler< T > > { public: // The initiating function will return a future. @@ -128,8 +50,8 @@ public: // Constructor creates a new promise for the async operation, and obtains the // corresponding future. - explicit async_result( detail::promise_handler< T > & h) - { value_ = h.promise_->get_future(); } + explicit async_result( fibers::asio::detail::use_future_handler< T > & h) + { value_ = h.get_promise()->get_future(); } // Obtain the future to be returned from the initiating function. type get() @@ -139,39 +61,43 @@ private: type value_; }; -// Handler type specialisation for use_future. +// Handler type specialisation for use_future for a nullary callback. template< typename Allocator, typename ReturnType > struct handler_type< boost::fibers::asio::use_future_t< Allocator>, ReturnType() > -{ typedef detail::promise_handler< void > type; }; +{ typedef fibers::asio::detail::use_future_handler< void > type; }; -// Handler type specialisation for use_future. +// Handler type specialisation for use_future for a single-argument callback. template< typename Allocator, typename ReturnType, typename Arg1 > struct handler_type< boost::fibers::asio::use_future_t< Allocator >, ReturnType( Arg1) > -{ typedef detail::promise_handler< Arg1 > type; }; +{ typedef fibers::asio::detail::use_future_handler< Arg1 > type; }; -// Handler type specialisation for use_future. +// Handler type specialisation for use_future for a callback passed only +// boost::system::error_code. Note the use of use_future_handler: an +// error_code indicating error will be conveyed to consumer code via +// set_exception(). template< typename Allocator, typename ReturnType > struct handler_type< boost::fibers::asio::use_future_t< Allocator >, ReturnType( boost::system::error_code) > -{ typedef detail::promise_handler< void > type; }; +{ typedef fibers::asio::detail::use_future_handler< void > type; }; -// Handler type specialisation for use_future. +// Handler type specialisation for use_future for a callback passed +// boost::system::error_code plus an arbitrary value. Note the use of a +// single-argument use_future_handler: an error_code indicating error will be +// conveyed to consumer code via set_exception(). template< typename Allocator, typename ReturnType, typename Arg2 > struct handler_type< boost::fibers::asio::use_future_t< Allocator >, ReturnType( boost::system::error_code, Arg2) > -{ typedef detail::promise_handler< Arg2 > type; }; - -#endif // !defined(GENERATING_DOCUMENTATION) +{ typedef fibers::asio::detail::use_future_handler< Arg2 > type; }; } // namespace asio } // namespace boost diff --git a/examples/asio/detail/yield.hpp b/examples/asio/detail/yield.hpp index a06e41b4..c2365daa 100644 --- a/examples/asio/detail/yield.hpp +++ b/examples/asio/detail/yield.hpp @@ -1,15 +1,25 @@ +// +// yield.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2013 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) +// +// modified by Oliver Kowalke and Nat Goodspeed +// + #ifndef BOOST_FIBERS_ASIO_DETAIL_YIELD_HPP #define BOOST_FIBERS_ASIO_DETAIL_YIELD_HPP #include -#include #include -#include -#include -#include #include +#include "promise_handler.hpp" + #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif @@ -19,156 +29,77 @@ namespace fibers { namespace asio { namespace detail { -template< typename T > -class yield_handler -{ -public: - yield_handler( yield_t const& y) : - fiber_( boost::fibers::detail::scheduler::instance()->active() ), - ec_( y.ec_), value_( 0) - {} +// yield_handler is just an alias for promise_handler -- but we must +// distinguish this case to specialize async_result below. +template < typename T > +using yield_handler = promise_handler; - void operator()( T t) - { - * ec_ = boost::system::error_code(); - * value_ = t; - fiber_->set_ready(); - } +} // detail +} // asio +} // fibers - void operator()( boost::system::error_code const& ec, T t) - { - * ec_ = ec; - * value_ = t; - fiber_->set_ready(); - } - -//private: - boost::fibers::fiber_context * fiber_; - boost::system::error_code * ec_; - T * value_; -}; - -// Completion handler to adapt a void promise as a completion handler. -template<> -class yield_handler< void > -{ -public: - yield_handler( yield_t const& y) : - fiber_( boost::fibers::detail::scheduler::instance()->active() ), - ec_( y.ec_) - {} - - void operator()() - { - * ec_ = boost::system::error_code(); - fiber_->set_ready(); - } - - void operator()( boost::system::error_code const& ec) - { - * ec_ = ec; - fiber_->set_ready(); - } - -//private: - boost::fibers::fiber_context * fiber_; - boost::system::error_code * ec_; -}; - -} // namespace detail -} // namespace asio -} // namespace fibers -} // namespace boost - -namespace boost { namespace asio { +// Handler traits specialisation for yield_handler. template< typename T > -class async_result< boost::fibers::asio::detail::yield_handler< T > > +class async_result< fibers::asio::detail::yield_handler< T > > { public: + // The initiating function will return a value of type T. typedef T type; + + // Constructor creates a new promise for the async operation, and obtains the + // corresponding future. + explicit async_result( fibers::asio::detail::yield_handler< T > & h) + { future_ = h.get_promise()->get_future(); } - explicit async_result( boost::fibers::asio::detail::yield_handler< T > & h) - { - out_ec_ = h.ec_; - if ( ! out_ec_) h.ec_ = & ec_; - h.value_ = & value_; - } - + // This blocks the calling fiber until the handler sets either a value or + // an exception. type get() - { - fibers::detail::spinlock splk; - std::unique_lock< fibers::detail::spinlock > lk( splk); - boost::fibers::detail::scheduler::instance()->wait(lk); - if ( ! out_ec_ && ec_) - throw_exception( boost::system::system_error( ec_) ); - return value_; - } + { return future_.get(); } private: - boost::system::error_code * out_ec_; - boost::system::error_code ec_; - type value_; + fibers::future future_; }; -template<> -class async_result< boost::fibers::asio::detail::yield_handler< void > > -{ -public: - typedef void type; - - explicit async_result( boost::fibers::asio::detail::yield_handler< void > & h) - { - out_ec_ = h.ec_; - if ( ! out_ec_) h.ec_ = & ec_; - } - - void get() - { - fibers::detail::spinlock splk; - std::unique_lock< fibers::detail::spinlock > lk( splk); - boost::fibers::detail::scheduler::instance()->wait(lk); - if ( ! out_ec_ && ec_) - throw_exception( boost::system::system_error( ec_) ); - } - -private: - boost::system::error_code * out_ec_; - boost::system::error_code ec_; -}; - -// Handler type specialisation for use_future. -template< typename ReturnType > +// Handler type specialisation for yield for a nullary callback. +template< typename Allocator, typename ReturnType > struct handler_type< - boost::fibers::asio::yield_t, + boost::fibers::asio::yield_t< Allocator>, ReturnType() > { typedef boost::fibers::asio::detail::yield_handler< void > type; }; -// Handler type specialisation for use_future. -template< typename ReturnType, typename Arg1 > +// Handler type specialisation for yield for a single-argument callback. +template< typename Allocator, typename ReturnType, typename Arg1 > struct handler_type< - boost::fibers::asio::yield_t, + boost::fibers::asio::yield_t< Allocator>, ReturnType( Arg1) > -{ typedef boost::fibers::asio::detail::yield_handler< Arg1 > type; }; +{ typedef fibers::asio::detail::yield_handler< Arg1 > type; }; -// Handler type specialisation for use_future. -template< typename ReturnType > +// Handler type specialisation for yield for a callback passed only +// boost::system::error_code. Note the use of yield_handler: an +// error_code indicating error will be conveyed to consumer code via an +// exception. Normal return implies (! error_code). +template< typename Allocator, typename ReturnType > struct handler_type< - boost::fibers::asio::yield_t, + boost::fibers::asio::yield_t< Allocator>, ReturnType( boost::system::error_code) > -{ typedef boost::fibers::asio::detail::yield_handler< void > type; }; +{ typedef fibers::asio::detail::yield_handler< void > type; }; -// Handler type specialisation for use_future. -template< typename ReturnType, typename Arg2 > +// Handler type specialisation for yield for a callback passed +// boost::system::error_code plus an arbitrary value. Note the use of a +// single-argument yield_handler: an error_code indicating error will be +// conveyed to consumer code via an exception. Normal return implies (! +// error_code). +template< typename Allocator, typename ReturnType, typename Arg2 > struct handler_type< - boost::fibers::asio::yield_t, + boost::fibers::asio::yield_t< Allocator>, ReturnType( boost::system::error_code, Arg2) > -{ typedef boost::fibers::asio::detail::yield_handler< Arg2 > type; }; +{ typedef fibers::asio::detail::yield_handler< Arg2 > type; }; } // namespace asio } // namespace boost diff --git a/examples/asio/echo_client.cpp b/examples/asio/echo_client.cpp index 629718cc..e59f6683 100644 --- a/examples/asio/echo_client.cpp +++ b/examples/asio/echo_client.cpp @@ -1,6 +1,6 @@ // -// blocking_tcp_echo_client.cpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// echo_client.cpp +// ~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) // @@ -23,7 +23,7 @@ int main(int argc, char* argv[]) { if (argc != 3) { - std::cerr << "Usage: blocking_tcp_echo_client \n"; + std::cerr << "Usage: echo_client \n"; return 1; } diff --git a/examples/asio/echo_server.cpp b/examples/asio/echo_server.cpp index b766a71b..3b2b134d 100644 --- a/examples/asio/echo_server.cpp +++ b/examples/asio/echo_server.cpp @@ -18,7 +18,7 @@ #include -#include "loop.hpp" +#include "loop.hpp" // run_service() #include "yield.hpp" using boost::asio::ip::tcp; @@ -40,7 +40,7 @@ void session( socket_ptr sock) boost::asio::buffer( data), boost::fibers::asio::yield[ec]); if ( ec == boost::asio::error::eof) - break; //connection closed cleanlyby peer + break; //connection closed cleanly by peer else if ( ec) throw boost::system::system_error( ec); //some other error @@ -49,7 +49,7 @@ void session( socket_ptr sock) boost::asio::buffer( data, length), boost::fibers::asio::yield[ec]); if ( ec == boost::asio::error::eof) - break; //connection closed cleanlyby peer + break; //connection closed cleanly by peer else if ( ec) throw boost::system::system_error( ec); //some other error } @@ -83,15 +83,14 @@ int main( int argc, char* argv[]) { if ( argc != 2) { - std::cerr << "Usage: blocking_tcp_echo_server \n"; + std::cerr << "Usage: echo_server \n"; return 1; } boost::asio::io_service io_service; - using namespace std; // For atoi. boost::fibers::fiber( - boost::bind( server, boost::ref( io_service), atoi( argv[1]) ) ).detach(); + boost::bind( server, boost::ref( io_service), std::atoi( argv[1]) ) ).detach(); boost::fibers::asio::run_service( io_service); } diff --git a/examples/asio/echo_server2.cpp b/examples/asio/echo_server2.cpp index e3cee140..20d81207 100644 --- a/examples/asio/echo_server2.cpp +++ b/examples/asio/echo_server2.cpp @@ -21,7 +21,6 @@ #include #include "loop.hpp" -#include "spawn.hpp" #include "yield.hpp" using boost::asio::ip::tcp; @@ -42,16 +41,16 @@ public: void go() { - boost::fibers::asio::spawn(strand_, + boost::fibers::fiber( boost::bind(&session::echo, - shared_from_this(), _1)); - boost::fibers::asio::spawn(strand_, + shared_from_this())).detach(); + boost::fibers::fiber( boost::bind(&session::timeout, - shared_from_this(), _1)); + shared_from_this())).detach(); } private: - void echo( boost::fibers::asio::yield_context yield) + void echo() { try { @@ -76,7 +75,7 @@ private: } } - void timeout( boost::fibers::asio::yield_context yield) + void timeout() { while ( socket_.is_open() ) { @@ -95,7 +94,7 @@ private: }; void do_accept(boost::asio::io_service& io_service, - unsigned short port, boost::fibers::asio::yield_context yield) + unsigned short port) { tcp::acceptor acceptor( io_service, tcp::endpoint( tcp::v4(), port) ); @@ -125,9 +124,9 @@ int main( int argc, char* argv[]) boost::asio::io_service io_service; using namespace std; // For atoi. - boost::fibers::asio::spawn( io_service, + boost::fibers::fiber( boost::bind( do_accept, - boost::ref( io_service), atoi( argv[1]), _1) ); + boost::ref( io_service), atoi( argv[1])) ).detach(); boost::fibers::asio::run_service( io_service); } diff --git a/examples/asio/promise_completion_token.hpp b/examples/asio/promise_completion_token.hpp new file mode 100644 index 00000000..97663680 --- /dev/null +++ b/examples/asio/promise_completion_token.hpp @@ -0,0 +1,88 @@ +// +// promise_completion_token.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2013 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) +// +// modified by Oliver Kowalke and Nat Goodspeed +// + +#ifndef BOOST_FIBERS_ASIO_PROMISE_COMPLETION_TOKEN_HPP +#define BOOST_FIBERS_ASIO_PROMISE_COMPLETION_TOKEN_HPP + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace asio { + +/// Common base class for yield_t and use_future_t. See also yield.hpp and +/// use_future.hpp. +/** + * The awkward name of this class is because it's not intended to be used + * directly in user code: it's the common base class for a couple of user- + * facing placeholder classes yield_t and use_future_t. They + * share a common handler class promise_handler. + * + * Each subclass (e.g. use_future_t) has a canonical instance + * (use_future). These may be used in the following ways as a + * Boost.Asio asynchronous operation completion token: + * + *
+ *
boost::fibers::asio::use_future
+ *
This is the canonical instance of use_future_t, provided + * solely for convenience. It causes promise_handler to allocate its + * internal boost::fibers::promise using a default-constructed + * default allocator (std::allocator).
+ *
boost::fibers::asio::use_future::with(alloc_instance)
+ *
This usage specifies an alternate allocator instance + * alloc_instance. It causes promise_handler to allocate its + * internal boost::fibers::promise using the specified + * allocator.
+ *
+ */ +template< typename Allocator > +class promise_completion_token +{ +public: + typedef Allocator allocator_type; + + /// Construct using default-constructed allocator. + BOOST_CONSTEXPR promise_completion_token() : + ec_( nullptr) + {} + + /// Construct using specified allocator. + explicit promise_completion_token( Allocator const& allocator) : + ec_( nullptr), + allocator_( allocator) + {} + + /// Obtain allocator. + allocator_type get_allocator() const + { return allocator_; } + +//private: + // used by some subclasses to bind an error_code to suppress exceptions + boost::system::error_code * ec_; + +private: + Allocator allocator_; +}; + +} // namespace asio +} // namespace fibers +} // namespace boost + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBERS_ASIO_PROMISE_COMPLETION_TOKEN_HPP diff --git a/examples/asio/publish_subscribe/server.cpp b/examples/asio/publish_subscribe/server.cpp index 6a47fb31..8c92b7d8 100644 --- a/examples/asio/publish_subscribe/server.cpp +++ b/examples/asio/publish_subscribe/server.cpp @@ -28,7 +28,6 @@ #include #include "../loop.hpp" -#include "../spawn.hpp" #include "../yield.hpp" using boost::asio::ip::tcp; @@ -160,7 +159,7 @@ public: { return socket_; } // this function is executed inside the fiber - void run( boost::fibers::asio::yield_context yield) + void run() { std::string channel; try @@ -174,7 +173,7 @@ public: boost::asio::async_read( socket_, boost::asio::buffer( data_), - yield[ec]); + boost::fibers::asio::yield[ec]); if ( ec) throw std::runtime_error("no channel from subscriber"); // first message ist equal to the channel name the publisher // publishes to @@ -206,7 +205,7 @@ public: boost::asio::async_write( socket_, boost::asio::buffer( data, data.size() ), - yield[ec]); + boost::fibers::asio::yield[ec]); if ( ec == boost::asio::error::eof) break; //connection closed cleanly by peer else if ( ec) @@ -269,7 +268,7 @@ public: { return socket_; } // this function is executed inside the fiber - void run( boost::fibers::asio::yield_context yield) + void run() { std::string channel; try @@ -286,7 +285,7 @@ public: boost::asio::async_read( socket_, boost::asio::buffer( data), - yield[ec]); + boost::fibers::asio::yield[ec]); if ( ec) throw std::runtime_error("no channel from publisher"); // first message ist equal to the channel name the publisher // publishes to @@ -304,7 +303,7 @@ public: boost::asio::async_read( socket_, boost::asio::buffer( data), - yield[ec]); + boost::fibers::asio::yield[ec]); if ( ec == boost::asio::error::eof) break; //connection closed cleanly by peer else if ( ec) @@ -333,8 +332,7 @@ typedef boost::shared_ptr< publisher_session > publisher_session_ptr; // function accepts connections requests from clients acting as a publisher void accept_publisher( boost::asio::io_service& io_service, unsigned short port, - registry & reg, - boost::fibers::asio::yield_context yield) + registry & reg) { // create TCP-acceptor tcp::acceptor acceptor( io_service, tcp::endpoint( tcp::v4(), port) ); @@ -353,11 +351,11 @@ void accept_publisher( boost::asio::io_service& io_service, // is connected acceptor.async_accept( new_publisher_session->socket(), - yield[ec]); + boost::fibers::asio::yield[ec]); if ( ! ec) { // run the new publisher in its own fiber (one fiber for one client) - boost::fibers::asio::spawn( io_service, - boost::bind( & publisher_session::run, new_publisher_session, _1) ); + boost::fibers::fiber( + boost::bind( & publisher_session::run, new_publisher_session) ).detach(); } } } @@ -365,8 +363,7 @@ void accept_publisher( boost::asio::io_service& io_service, // function accepts connections requests from clients acting as a subscriber void accept_subscriber( boost::asio::io_service& io_service, unsigned short port, - registry & reg, - boost::fibers::asio::yield_context yield) + registry & reg) { // create TCP-acceptor tcp::acceptor acceptor( io_service, tcp::endpoint( tcp::v4(), port) ); @@ -385,11 +382,11 @@ void accept_subscriber( boost::asio::io_service& io_service, // is connected acceptor.async_accept( new_subscriber_session->socket(), - yield[ec]); + boost::fibers::asio::yield[ec]); if ( ! ec) { // run the new subscriber in its own fiber (one fiber for one client) - boost::fibers::asio::spawn( io_service, - boost::bind( & subscriber_session::run, new_subscriber_session, _1) ); + boost::fibers::fiber( + boost::bind( & subscriber_session::run, new_subscriber_session) ).detach(); } } } @@ -406,15 +403,15 @@ int main( int argc, char* argv[]) registry reg; // create an acceptor for publishers, run it as fiber - boost::fibers::asio::spawn( io_service, + boost::fibers::fiber( boost::bind( accept_publisher, - boost::ref( io_service), 9997, boost::ref( reg), _1) ); + boost::ref( io_service), 9997, boost::ref( reg)) ).detach(); - // create an acceptor for subscribersm, run it as fiber - boost::fibers::asio::spawn( io_service, + // create an acceptor for subscribers, run it as fiber + boost::fibers::fiber( boost::bind( accept_subscriber, - boost::ref( io_service), 9998, boost::ref( reg), _1) ); - + boost::ref( io_service), 9998, boost::ref( reg)) ).detach(); + boost::fibers::asio::run_service( io_service); } catch ( std::exception const& e) diff --git a/examples/asio/spawn.hpp b/examples/asio/spawn.hpp deleted file mode 100644 index 235a1171..00000000 --- a/examples/asio/spawn.hpp +++ /dev/null @@ -1,225 +0,0 @@ -// -// spawn.hpp -// ~~~~~~~~~ -// -// Copyright (c) 2003-2013 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) -// - -#ifndef BOOST_FIBERS_ASIO_SPAWN_HPP -#define BOOST_FIBERS_ASIO_SPAWN_HPP - -#include -#include -#include -#include -#include -#include - -#include - -#ifdef BOOST_HAS_ABI_HEADERS -# include BOOST_ABI_PREFIX -#endif - -namespace boost { -namespace fibers { -namespace asio { - -/// Context object the represents the currently executing fiber. -/** - * The basic_yield_context class is used to represent the currently executing - * fiber. A basic_yield_context may be passed as a handler to an * asynchronous - * operation. For example: - * - * @code template< typename Handler > - * void my_fiber( basic_yield_context< Handler > yield) - * { - * ... - * std::size_t n = my_socket.async_read_some( buffer, yield); - * ... - * } @endcode - * - * The initiating function (async_read_some in the above example) suspends the - * current fiber. The fiber is resumed when the asynchronous operation - * completes, and the result of the operation is returned. - */ -template< typename Handler > -class basic_yield_context -{ -public: - /// Construct a yield context to represent the specified fiber. - /** - * 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( - boost::fibers::fiber_context * fib, - Handler& handler) : - fiber_( fib), - handler_( handler), - ec_( 0) - {} - - /// 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: - * - * @code template< typename Handler > - * void my_fiber( basic_yield_context< Handler > yield) - * { - * ... - * std::size_t n = my_socket.async_read_some( buffer, yield[ec]); - * if ( ec) - * { - * // An error occurred. - * } - * ... - * } @endcode - */ - basic_yield_context operator[]( boost::system::error_code & ec) - { - basic_yield_context tmp( * this); - tmp.ec_ = & ec; - return tmp; - } - -#if defined(GENERATING_DOCUMENTATION) -private: -#endif // defined(GENERATING_DOCUMENTATION) - boost::fibers::fiber_context * fiber_; - Handler & handler_; - boost::system::error_code * ec_; -}; - -#if defined(GENERATING_DOCUMENTATION) -/// Context object the represents the currently executing fiber. -typedef basic_yield_context< unspecified > yield_context; -#else // defined(GENERATING_DOCUMENTATION) -typedef basic_yield_context< - boost::asio::detail::wrapped_handler< - boost::asio::io_service::strand, void(*)(), - boost::asio::detail::is_continuation_if_running> > yield_context; -#endif // defined(GENERATING_DOCUMENTATION) - -/** - * @defgroup spawn boost::fibers::asio::spawn - * - * @brief Start a new stackful fiber. - * - * The spawn() function is a high-level wrapper over the Boost.Fiber - * library. This function enables programs to implement asynchronous logic in a - * synchronous manner, as illustrated by the following example: - * - * @code boost::asio::spawn( my_strand, do_echo); - * - * // ... - * - * void do_echo( boost::fibers::asio::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) - * { - * // ... - * } - * } @endcode - */ -/*@{*/ - -/// Start a new fiber, calling the specified handler when it completes. -/** - * This function is used to launch a new fiber. - * - * @param handler A handler to be called when the fiber exits. More - * importantly, the handler provides an execution context (via the handler - * invocation hook) for the fiber. The handler must have the signature: - * @code void handler(); @endcode - * - * @param function The fiber function. The function must have the signature: - * @code void function( basic_yield_context< Handler > yield); @endcode - * - */ -template< typename Handler, typename Function > -void spawn( boost::asio::io_service & io_service, - BOOST_ASIO_MOVE_ARG( Handler) handler, - BOOST_ASIO_MOVE_ARG( Function) function); - -/// Start a new fiber, inheriting the execution context of another. -/** - * This function is used to launch a new fiber. - * - * @param ctx Identifies the current fiber 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. - * - * @param function The fiber function. The function must have the signature: - * @code void function( basic_yield_context< Handler > yield); @endcode - * - */ -template< typename Handler, typename Function > -void spawn( boost::asio::io_service & io_service, - basic_yield_context< Handler > ctx, - BOOST_ASIO_MOVE_ARG( Function) function); - -/// Start a new fiber that executes in the contex of a strand. -/** - * This function is used to launch a new fiber. - * - * @param strand Identifies a strand. By starting multiple fibers on the - * same strand, the implementation ensures that none of those fibers can - * execute simultaneously. - * - * @param function The fiber function. The function must have the signature: - * @code void function( yield_context yield); @endcode - * - */ -template< typename Function > -void spawn( boost::asio::io_service::strand strand, - BOOST_ASIO_MOVE_ARG( Function) function); - -/// Start a new fiber that executes on a given io_service. -/** - * This function is used to launch a new fiber. - * - * @param io_service Identifies the io_service that will run the fiber. The - * new fiber is implicitly given its own strand within this io_service. - * - * @param function The fiber function. The function must have the signature: - * @code void function( yield_context yield); @endcode - * - */ -template< typename Function > -void spawn( boost::asio::io_service & io_service, - BOOST_ASIO_MOVE_ARG( Function) function); - -} // namespace asio -} // namespace fibers -} // namespace boost - -#ifdef BOOST_HAS_ABI_HEADERS -# include BOOST_ABI_SUFFIX -#endif - -#include "detail/spawn.hpp" - -#endif // BOOST_FIBERS_ASIO_SPAWN_HPP diff --git a/examples/asio/use_future.hpp b/examples/asio/use_future.hpp index 0c0a491a..ce0f9a69 100644 --- a/examples/asio/use_future.hpp +++ b/examples/asio/use_future.hpp @@ -7,16 +7,15 @@ // 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) // -// modified by Oliver Kowalke +// modified by Oliver Kowalke and Nat Goodspeed // #ifndef BOOST_FIBERS_ASIO_USE_FUTURE_HPP #define BOOST_FIBERS_ASIO_USE_FUTURE_HPP -#include - +#include // std::allocator #include -#include +#include "promise_completion_token.hpp" #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX @@ -26,12 +25,13 @@ namespace boost { namespace fibers { namespace asio { -/// Class used to specify that an asynchronous operation should return a future. +/// Class used to specify that a Boost.Asio asynchronous operation should +/// return a future. /** - * The use_future_t class is used to indicate that an asynchronous operation - * should return a boost::fibers::future object. A use_future_t object may be passed as a - * handler to an asynchronous operation, typically using the special value @c - * boost::asio::use_future. For example: + * The use_future_t class is used to indicate that a Boost.Asio asynchronous + * operation should return a boost::fibers::future object. A use_future_t + * object may be passed as a handler to an asynchronous operation, typically + * using the special value @c boost::fibers::asio::use_future. For example: * * @code boost::fibers::future my_future * = my_socket.async_read_some(my_buffer, boost::fibers::asio::use_future); @endcode @@ -42,37 +42,26 @@ namespace asio { * system_error and passed back to the caller via the future. */ template< typename Allocator = std::allocator< void > > -class use_future_t +class use_future_t: public promise_completion_token { public: - typedef Allocator allocator_type; - /// Construct using default-constructed allocator. BOOST_CONSTEXPR use_future_t() {} /// Construct using specified allocator. explicit use_future_t( Allocator const& allocator) : - allocator_( allocator) + promise_completion_token( allocator) {} /// Specify an alternate allocator. template< typename OtherAllocator > - use_future_t< OtherAllocator > operator[]( OtherAllocator const& allocator) const - { return use_future_t< OtherAllocator >( allocator); } - - /// Obtain allocator. - allocator_type get_allocator() const - { return allocator_; } - -private: - Allocator allocator_; + use_future_t< OtherAllocator > + operator[]( OtherAllocator const& allocator) const + { return use_future_t< OtherAllocator >( allocator); } }; /// A special value, similar to std::nothrow. -/** - * See the documentation for boost::asio::use_future_t for a usage example. - */ BOOST_CONSTEXPR_OR_CONST use_future_t<> use_future; } // namespace asio diff --git a/examples/asio/yield.hpp b/examples/asio/yield.hpp index 51caedbb..7431adfa 100644 --- a/examples/asio/yield.hpp +++ b/examples/asio/yield.hpp @@ -7,15 +7,15 @@ // 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) // -// modified by Oliver Kowalke +// modified by Oliver Kowalke and Nat Goodspeed // #ifndef BOOST_FIBERS_ASIO_YIELD_HPP #define BOOST_FIBERS_ASIO_YIELD_HPP -#include - -#include +#include // std::allocator +#include +#include "promise_completion_token.hpp" #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX @@ -25,25 +25,92 @@ namespace boost { namespace fibers { namespace asio { -class yield_t +/// Class used to specify that a Boost.Asio asynchronous operation should +/// suspend the calling fiber until completion. +/** + * The yield_t class is used to indicate that a Boost.Asio asynchronous + * operation should suspend the calling fiber until its completion. The + * asynchronous function will either return a suitable value, or will throw an + * exception indicating the error. A yield_t object may be passed as a handler + * to an asynchronous operation, typically using the special value @c + * boost::fibers::asio::yield. For example: + * + * @code std::size_t length_read + * = my_socket.async_read_some(my_buffer, boost::fibers::asio::yield); @endcode + * + * The initiating function (async_read_some in the above example) does not + * return to the calling fiber until the asynchronous read has completed. Like + * its synchronous counterpart, it returns the result of the operation. If the + * operation completes with an error_code indicating failure, it is converted + * into a system_error and thrown as an exception. + * + * To suppress a possible error exception: + * @code + * boost::system::error_code ec; + * std::size_t length_read = + * my_socket.async_read_some(my_buffer, boost::fibers::asio::yield[ec]); + * // test ec for success + * @endcode + * + * The crucial distinction between + * @code + * std::size_t length_read = my_socket.read_some(my_buffer); + * @endcode + * and + * @code + * std::size_t length_read = + * my_socket.async_read_some(my_buffer, boost::fibers::asio::yield); + * @code + * is that read_some() blocks the entire calling @em thread, whereas + * async_read_some(..., boost::fibers::asio::yield) blocks only the + * calling @em fiber, permitting other fibers on the same thread to continue + * running. + * + * To specify an alternate allocator for the internal + * boost::fibers::promise: + * @code + * boost::fibers::asio::yield.with(alloc_instance) + * @endcode + * + * To bind a boost::system::error_code @a ec as well as using an + * alternate allocator: + * @code + * boost::fibers::asio::yield.with(alloc_instance)[ec] + * @endcode + */ +template< typename Allocator = std::allocator< void > > +class yield_t: public promise_completion_token { public: - BOOST_CONSTEXPR yield_t() : - ec_( 0) + /// Construct with default-constructed allocator. + BOOST_CONSTEXPR yield_t() {} + /// Construct using specified allocator. + explicit yield_t( Allocator const& allocator) : + promise_completion_token( allocator) + {} + + /// Specify an alternate allocator. + template< typename OtherAllocator > + yield_t< OtherAllocator > + with( OtherAllocator const& allocator) const + { return yield_t< OtherAllocator >( allocator); } + + /// Bind an error_code to suppress error exception. yield_t operator[]( boost::system::error_code & ec) const { + // Return a copy because typical usage will be on our canonical + // instance. Don't leave the canonical instance with a dangling + // binding to a transient error_code! yield_t tmp; tmp.ec_ = & ec; return tmp; } - -//private: - boost::system::error_code * ec_; }; -BOOST_CONSTEXPR_OR_CONST yield_t yield; +/// A special value, similar to std::nothrow. +BOOST_CONSTEXPR_OR_CONST yield_t<> yield; }}}