mirror of
https://github.com/boostorg/fiber.git
synced 2026-02-11 23:52:29 +00:00
Merge pull request #52 from nat-goodspeed/nospawn
Reimplement boost::fibers::asio::yield with promise/future.
This commit is contained in:
@@ -21,7 +21,6 @@
|
||||
#include <boost/fiber/all.hpp>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <boost/fiber/all.hpp>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
164
examples/asio/detail/promise_handler.hpp
Normal file
164
examples/asio/detail/promise_handler.hpp
Normal file
@@ -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 <exception>
|
||||
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/exception/all.hpp>
|
||||
|
||||
#include <boost/fiber/all.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.
|
||||
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<T>
|
||||
{
|
||||
using promise_handler_base<T>::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<T>( 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<T>::get_promise;
|
||||
};
|
||||
|
||||
// specialize promise_handler for void
|
||||
template<>
|
||||
class promise_handler< void >: public promise_handler_base<void>
|
||||
{
|
||||
using promise_handler_base<void>::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<void>( 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<void>::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
|
||||
@@ -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 <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/detail/config.hpp>
|
||||
#include <boost/asio/detail/handler_alloc_helpers.hpp>
|
||||
#include <boost/asio/detail/handler_cont_helpers.hpp>
|
||||
#include <boost/asio/detail/handler_invoke_helpers.hpp>
|
||||
#include <boost/asio/detail/noncopyable.hpp>
|
||||
#include <boost/asio/detail/shared_ptr.hpp>
|
||||
#include <boost/asio/handler_type.hpp>
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#include <boost/fiber/all.hpp>
|
||||
|
||||
#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<Handler, T> *)
|
||||
{ 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 <Handler, void > 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
|
||||
@@ -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 <exception>
|
||||
#include <memory>
|
||||
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/detail/config.hpp>
|
||||
#include <boost/asio/handler_type.hpp>
|
||||
#include <boost/exception/all.hpp>
|
||||
#include <boost/thread/detail/memory.hpp>
|
||||
|
||||
#include <boost/fiber/all.hpp>
|
||||
|
||||
#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<T>;
|
||||
|
||||
} // 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<void>: 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
|
||||
|
||||
@@ -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 <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/detail/config.hpp>
|
||||
#include <boost/asio/handler_type.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <boost/system/system_error.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
#include <boost/fiber/all.hpp>
|
||||
|
||||
#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<T>;
|
||||
|
||||
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<T> 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<void>: 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
|
||||
|
||||
@@ -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 <host> <port>\n";
|
||||
std::cerr << "Usage: echo_client <host> <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#include <boost/fiber/all.hpp>
|
||||
|
||||
#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 <port>\n";
|
||||
std::cerr << "Usage: echo_server <port>\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);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <boost/fiber/all.hpp>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
88
examples/asio/promise_completion_token.hpp
Normal file
88
examples/asio/promise_completion_token.hpp
Normal file
@@ -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 <boost/config.hpp>
|
||||
|
||||
#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 <tt>yield_t</tt> and <tt>use_future_t</tt>. They
|
||||
* share a common handler class <tt>promise_handler</tt>.
|
||||
*
|
||||
* Each subclass (e.g. <tt>use_future_t</tt>) has a canonical instance
|
||||
* (<tt>use_future</tt>). These may be used in the following ways as a
|
||||
* Boost.Asio asynchronous operation completion token:
|
||||
*
|
||||
* <dl>
|
||||
* <dt><tt>boost::fibers::asio::use_future</tt></dt>
|
||||
* <dd>This is the canonical instance of <tt>use_future_t</tt>, provided
|
||||
* solely for convenience. It causes <tt>promise_handler</tt> to allocate its
|
||||
* internal <tt>boost::fibers::promise</tt> using a default-constructed
|
||||
* default allocator (<tt>std::allocator<void></tt>).</dd>
|
||||
* <dt><tt>boost::fibers::asio::use_future::with(alloc_instance)</tt></dt>
|
||||
* <dd>This usage specifies an alternate allocator instance
|
||||
* <tt>alloc_instance</tt>. It causes <tt>promise_handler</tt> to allocate its
|
||||
* internal <tt>boost::fibers::promise</tt> using the specified
|
||||
* allocator.</dd>
|
||||
* </dl>
|
||||
*/
|
||||
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
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <boost/fiber/all.hpp>
|
||||
|
||||
#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)
|
||||
|
||||
@@ -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 <boost/asio/detail/config.hpp>
|
||||
#include <boost/asio/detail/weak_ptr.hpp>
|
||||
#include <boost/asio/detail/wrapped_handler.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#include <boost/fiber/all.hpp>
|
||||
|
||||
#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
|
||||
@@ -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 <memory>
|
||||
|
||||
#include <memory> // std::allocator
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/asio/detail/config.hpp>
|
||||
#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<std::size_t> 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<Allocator>
|
||||
{
|
||||
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>( 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
|
||||
|
||||
@@ -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 <memory>
|
||||
|
||||
#include <boost/asio/detail/config.hpp>
|
||||
#include <memory> // std::allocator
|
||||
#include <boost/config.hpp>
|
||||
#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 <tt>read_some()</tt> blocks the entire calling @em thread, whereas
|
||||
* <tt>async_read_some(..., boost::fibers::asio::yield)</tt> blocks only the
|
||||
* calling @em fiber, permitting other fibers on the same thread to continue
|
||||
* running.
|
||||
*
|
||||
* To specify an alternate allocator for the internal
|
||||
* <tt>boost::fibers::promise</tt>:
|
||||
* @code
|
||||
* boost::fibers::asio::yield.with(alloc_instance)
|
||||
* @endcode
|
||||
*
|
||||
* To bind a <tt>boost::system::error_code</tt> @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<Allocator>
|
||||
{
|
||||
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>( 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;
|
||||
|
||||
}}}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user