2
0
mirror of https://github.com/boostorg/fiber.git synced 2026-02-19 02:12:24 +00:00

Merge pull request #75 from nat-goodspeed/develop

Sync up with recent doc and Asio integration changes
This commit is contained in:
Oliver Kowalke
2016-03-24 07:51:18 +01:00
7 changed files with 322 additions and 276 deletions

View File

@@ -64,7 +64,7 @@ Numbers of the [@https://github.com/atemerev/skynet microbenchmark ['syknet]] fr
[footnote Intel Core2 Q6700, x86_64, 3GHz]:
[table performance of N=100000 actors/goroutines/fibers
[[Haskel | stack-1.0.4] [fiber (single threaded/raw) | gcc-5.2.1] [fiber (single threaded/atomics) | gcc-5.2.1] [Erlang | erts-7.0] [Go | go1.4.2]]
[[Haskell | stack-1.0.4] [fiber (single threaded/raw) | gcc-5.2.1] [fiber (single threaded/atomics) | gcc-5.2.1] [Erlang | erts-7.0] [Go | go1.4.2]]
[[58ms - 108ms] [205ms - 263ms] [221ms - 278ms] [237ms- 470ms] [614ms - 883ms]]
]

View File

@@ -418,11 +418,11 @@ of typical STL containers.
#include <boost/fiber/context.hpp>
enum class type {
none = 0,
main_context = 1 << 1, // fiber, associated with thread's stack
dispatcher_context = 1 << 2, // special fiber for maintanance operations
worker_context = 1 << 3, // fiber not special to the library
pinned_context = main_context | dispatcher_context // context of fiber must not be migrated to another thread
none,
main_context, // fiber associated with thread's stack
dispatcher_context, // special fiber for maintenance operations
worker_context, // fiber not special to the library
pinned_context // fiber must not be migrated to another thread
};
class context {
@@ -456,6 +456,9 @@ of typical STL containers.
void ready_unlink() noexcept;
void remote_ready_unlink() noexcept;
void wait_unlink() noexcept;
void suspend() noexcept;
void set_ready( context *) noexcept;
};
bool operator<( context const& l, context const& r) noexcept;
@@ -495,20 +498,18 @@ default-constructed __fiber_id__.]]
bool is_context( type t) const noexcept;
[variablelist
[[Returns:] [`true` if `*this` is of the specific type.]]
[[Returns:] [`true` if `*this` is of the specified type.]]
[[Throws:] [Nothing]]
[[Note:] [For `type::main_context` the `context` is associated with the ["main] fiber of the thread:
the one implicitly created by the thread itself, rather than one explicitly
created by __boost_fiber__.
For `type::disaptcher_context` the `context` is associated with a ["dispatching] fiber, responsible
for dispatching awakened fibers to a scheduler's ready-queue. The
["dispatching] fiber is an implementation detail of the fiber manager.
The context of ["main] and ["dispatching] fiber must never be passed to
[member_link context..migrate] for any other thread.
true` if `*this` is a worker context, that is, a context
associated with a fiber explicitly launched by the application rather than
implicitly.]]]
[[Note:] [The term ["worker] here means any fiber not special to the library.]]
[[Note:] [`type::worker_context` here means any fiber not special to the
library. For `type::main_context` the `context` is associated with the ["main]
fiber of the thread: the one implicitly created by the thread itself, rather
than one explicitly created by __boost_fiber__. For `type::dispatcher_context`
the `context` is associated with a ["dispatching] fiber, responsible for
dispatching awakened fibers to a scheduler's ready-queue. The ["dispatching]
fiber is an implementation detail of the fiber manager. The context of the
["main] or ["dispatching] fiber [mdash] any fiber for which
`is_context(pinned_context)` is `true` [mdash] must never be passed to
[member_link context..migrate] for any other thread.]]
]
[member_heading context..is_terminated]
@@ -630,6 +631,44 @@ __boost_intrusive__.]]
[[Throws:] [Nothing]]
]
[member_heading context..suspend]
void suspend() noexcept;
[variablelist
[[Effects:] [Suspends the running fiber (the fiber associated with `*this`)
until some other fiber passes `this` to [member_link context..set_ready].
`*this` is marked as not-ready, and control passes to the scheduler to select
another fiber to run.]]
[[Throws:] [Nothing]]
[[Note:] [This is a low-level API potentially useful for integration with
other frameworks. It is not intended to be directly invoked by a typical
application program.]]
[[Note:] [The burden is on the caller to arrange for a call to `set_ready()`
with a pointer to `this` at some future time.]]
]
[member_heading context..set_ready]
void set_ready( context * ctx ) noexcept;
[variablelist
[[Effects:] [Mark the fiber associated with context `*ctx` as being ready to
run. This does not immediately resume that fiber; rather it passes the fiber
to the scheduler for subsequent resumption. If the scheduler is idle (has not
returned from a call to [member_link sched_algorithm..suspend_until]),
[member_link sched_algorithm..notify] is called to wake it up.]]
[[Throws:] [Nothing]]
[[Note:] [This is a low-level API potentially useful for integration with
other frameworks. It is not intended to be directly invoked by a typical
application program.]]
[[Note:] [It is explicitly supported to call `set_ready(ctx)` from a thread
other than the one on which `*ctx` is currently suspended. The corresponding
fiber will be resumed on its original thread in due course.]]
[[Note:] [See [member_link context..migrate] for a way to migrate the
suspended thread to the thread calling `set_ready()`.]]
]
[hding context_less..Non-member function [`operator<()]]
bool operator<( context const& l, context const& r) noexcept;

View File

@@ -51,6 +51,9 @@ undefined behaviour.]
place inside __econtext__.]
See also [@http://www.boost.org/doc/libs/release/libs/context/doc/html/context/stack.html Boost.Context stack allocation].
In particular, `traits_type` methods are as described for
[@http://www.boost.org/doc/libs/release/libs/context/doc/html/context/stack/stack_traits.html
`boost::context::stack_traits`].
[class_heading protected_fixedsize_stack]
@@ -79,9 +82,9 @@ virtual addresses are used.]
[heading `stack_context allocate()`]
[variablelist
[[Preconditions:] [`traits_type::minimum:size() <= size` and
`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= size)`.]]
[[Effects:] [Allocates memory of at least `size` Bytes and stores a pointer
[[Preconditions:] [`traits_type::minimum_size() <= size` and
`traits_type::is_unbounded() || ( size <= traits_type::maximum_size() )`.]]
[[Effects:] [Allocates memory of at least `size` bytes and stores a pointer
to the stack and its actual size in `sctx`. Depending
on the architecture (the stack grows downwards/upwards) the stored address is
the highest/lowest address of the stack.]]
@@ -89,8 +92,8 @@ the highest/lowest address of the stack.]]
[heading `void deallocate( stack_context & sctx)`]
[variablelist
[[Preconditions:] [`sctx.sp` is valid, `traits_type::minimum:size() <= sctx.size` and
`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= sctx.size)`.]]
[[Preconditions:] [`sctx.sp` is valid, `traits_type::minimum_size() <= sctx.size` and
`traits_type::is_unbounded() || ( sctx.size <= traits_type::maximum_size() )`.]]
[[Effects:] [Deallocates the stack space.]]
]
@@ -115,21 +118,21 @@ end of each stack. The memory is managed internally by
[heading `pooled_fixedsize_stack(std::size_t stack_size, std::size_t next_size, std::size_t max_size)`]
[variablelist
[[Preconditions:] [`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= stack_size)`
and `0 < nest_size`.]]
[[Effects:] [Allocates memory of at least `stack_size` Bytes and stores a pointer to
[[Preconditions:] [`traits_type::is_unbounded() || ( traits_type::maximum_size() >= stack_size)`
and `0 < next_size`.]]
[[Effects:] [Allocates memory of at least `stack_size` bytes and stores a pointer to
the stack and its actual size in `sctx`. Depending on the architecture (the
stack grows downwards/upwards) the stored address is the highest/lowest
address of the stack. Argument `next_size` determines the number of stacks to
request from the system the first time that `*this` needs to allocate system
memory. The third argument `max_size` controls how many memory might be
allocated for stacks - a value of zero means no uper limit.]]
memory. The third argument `max_size` controls how much memory might be
allocated for stacks [mdash] a value of zero means no upper limit.]]
]
[heading `stack_context allocate()`]
[variablelis_stackt
[[Preconditions:] [`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= stack_size)`.]]
[[Effects:] [Allocates memory of at least `stack_size` Bytes and stores a pointer to
[variablelist
[[Preconditions:] [`traits_type::is_unbounded() || ( traits_type::maximum_size() >= stack_size)`.]]
[[Effects:] [Allocates memory of at least `stack_size` bytes and stores a pointer to
the stack and its actual size in `sctx`. Depending on the architecture (the
stack grows downwards/upwards) the stored address is the highest/lowest
address of the stack.]]
@@ -138,11 +141,11 @@ address of the stack.]]
[heading `void deallocate( stack_context & sctx)`]
[variablelist
[[Preconditions:] [`sctx.sp` is valid,
`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= sctx.size)`.]]
`traits_type::is_unbounded() || ( traits_type::maximum_size() >= sctx.size)`.]]
[[Effects:] [Deallocates the stack space.]]
]
[#class_fixedsize_stack]
[section:fixedsize Class ['fixedsize_stack]]
__boost_fiber__ provides the class __fixedsize__ which models
@@ -163,9 +166,9 @@ end of each stack. The memory is simply managed by `std::malloc()` and
[heading `stack_context allocate()`]
[variablelist
[[Preconditions:] [`traits_type::minimum:size() <= size` and
`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= size)`.]]
[[Effects:] [Allocates memory of at least `size` Bytes and stores a pointer to
[[Preconditions:] [`traits_type::minimum_size() <= size` and
`traits_type::is_unbounded() || ( traits_type::maximum_size() >= size)`.]]
[[Effects:] [Allocates memory of at least `size` bytes and stores a pointer to
the stack and its actual size in `sctx`. Depending on the architecture (the
stack grows downwards/upwards) the stored address is the highest/lowest
address of the stack.]]
@@ -173,8 +176,8 @@ address of the stack.]]
[heading `void deallocate( stack_context & sctx)`]
[variablelist
[[Preconditions:] [`sctx.sp` is valid, `traits_type::minimum:size() <= sctx.size` and
`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= sctx.size)`.]]
[[Preconditions:] [`sctx.sp` is valid, `traits_type::minimum_size() <= sctx.size` and
`traits_type::is_unbounded() || ( traits_type::maximum_size() >= sctx.size)`.]]
[[Effects:] [Deallocates the stack space.]]
]
@@ -208,9 +211,9 @@ command line.]
[heading `stack_context allocate()`]
[variablelist
[[Preconditions:] [`traits_type::minimum:size() <= size` and
`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= size)`.]]
[[Effects:] [Allocates memory of at least `size` Bytes and stores a pointer to
[[Preconditions:] [`traits_type::minimum_size() <= size` and
`traits_type::is_unbounded() || ( traits_type::maximum_size() >= size)`.]]
[[Effects:] [Allocates memory of at least `size` bytes and stores a pointer to
the stack and its actual size in `sctx`. Depending on the architecture (the
stack grows downwards/upwards) the stored address is the highest/lowest
address of the stack.]]
@@ -218,8 +221,8 @@ address of the stack.]]
[heading `void deallocate( stack_context & sctx)`]
[variablelist
[[Preconditions:] [`sctx.sp` is valid, `traits_type::minimum:size() <= sctx.size` and
`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= sctx.size)`.]]
[[Preconditions:] [`sctx.sp` is valid, `traits_type::minimum_size() <= sctx.size` and
`traits_type::is_unbounded() || ( traits_type::maximum_size() >= sctx.size)`.]]
[[Effects:] [Deallocates the stack space.]]
]

View File

@@ -7,9 +7,12 @@
#include <boost/system/error_code.hpp>
#include <boost/system/system_error.hpp>
#include <boost/throw_exception.hpp>
#include <boost/assert.hpp>
#include <boost/fiber/all.hpp>
#include <mutex> // std::unique_lock
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
@@ -19,81 +22,138 @@ namespace fibers {
namespace asio {
namespace detail {
template< typename T >
class yield_handler {
public:
yield_handler( yield_t const& y) :
ctx_( boost::fibers::context::active() ),
ec_( y.ec_) {
}
// Bundle a completion bool flag with a spinlock to protect it.
struct yield_completion {
typedef mutex_t fibers::detail::spinlock;
typedef lock_t std::unique_lock< mutex_t >;
void operator()( T t) {
std::unique_lock< fibers::detail::spinlock > lk( * mtx_);
* completed_ = true;
* ec_ = boost::system::error_code();
* value_ = std::move( t);
if ( ! ctx_->is_context( fibers::type::pinned_context) ) {
boost::fibers::context::active()->migrate( ctx_);
mutex_t mtx_{};
bool completed_{ false };
void wait() {
// yield_handler_base::operator()() will set completed_ true and
// attempt to wake a suspended fiber. It would be Bad if that call
// happened between our detecting (! completed_) and suspending.
lock_t lk( mtx_);
// If completed_ is already set, we're done here: don't suspend.
if ( ! completed_) {
// suspend(unique_lock<spinlock>) unlocks the lock in the act of
// resuming another fiber
fibers::context::active()->suspend( lk);
}
boost::fibers::context::active()->set_ready( ctx_);
}
void operator()( boost::system::error_code const& ec, T t) {
std::unique_lock< fibers::detail::spinlock > lk( * mtx_);
* completed_ = true;
* ec_ = ec;
* value_ = std::move( t);
if ( ! ctx_->is_context( fibers::type::pinned_context) &&
boost::fibers::context::active() != ctx_) {
boost::fibers::context::active()->migrate( ctx_);
}
boost::fibers::context::active()->set_ready( ctx_);
}
//private:
boost::fibers::context * ctx_;
boost::system::error_code * ec_;
T * value_{ nullptr };
fibers::detail::spinlock * mtx_{ nullptr };
bool * completed_{ nullptr };
};
// Completion handler to adapt a void promise as a completion handler.
template<>
class yield_handler< void >
{
// This class encapsulates common elements between yield_handler<T> (capturing
// a value to return from asio async function) and yield_handler<void> (no
// such value). See yield_handler<T> and its <void> specialization below. Both
// yield_handler<T> and yield_handler<void> are passed by value through
// various layers of asio functions. In other words, they're potentially
// copied multiple times. So key data such as the yield_completion instance
// must be stored in our async_result<yield_handler<>> specialization, which
// should be instantiated only once.
class yield_handler_base {
public:
yield_handler( yield_t const& y) :
yield_handler_base( yield_t const& y) :
// capture the context* associated with the running fiber
ctx_( boost::fibers::context::active() ),
ec_( y.ec_) {
}
void operator()() {
std::unique_lock< fibers::detail::spinlock > lk( * mtx_);
* completed_ = true;
* ec_ = boost::system::error_code();
if ( ! ctx_->is_context( fibers::type::pinned_context) ) {
boost::fibers::context::active()->migrate( ctx_);
// capture the passed yield_t
yt_( y )
{}
// completion callback passing only (error_code)
void operator()( boost::system::error_code const& ec)
{
BOOST_ASSERT_MSG( ycomp_,
"Must inject yield_completion* "
"before calling yield_handler_base::operator()()");
BOOST_ASSERT_MSG( yt_.ec_,
"Must inject boost::system::error_code* "
"before calling yield_handler_base::operator()()");
// If originating fiber is busy testing completed_ flag, wait until it
// has observed (! completed_).
yield_completion::lock_t lk( ycomp_->mtx_);
// Notify a subsequent yield_completion::wait() call that it need not
// suspend.
ycomp_->completed_ = true;
// set the error_code bound by yield_t
* yt_.ec_ = ec;
// Are we permitted to wake up the suspended fiber on this thread, the
// thread that called the completion handler?
if ( (! ctx_->is_context( fibers::type::pinned_context )) && yt_.allow_hop_ ) {
// We must not migrate a pinned_context to another thread. If this
// isn't a pinned_context, and the application passed yield_hop
// rather than yield, migrate this fiber to the running thread.
fibers::context::active()->migrate( ctx_);
}
boost::fibers::context::active()->set_ready( ctx_);
}
void operator()( boost::system::error_code const& ec) {
std::unique_lock< fibers::detail::spinlock > lk( * mtx_);
* completed_ = true;
* ec_ = ec;
if ( ! ctx_->is_context( fibers::type::pinned_context) &&
boost::fibers::context::active() != ctx_) {
boost::fibers::context::active()->migrate( ctx_);
}
boost::fibers::context::active()->set_ready( ctx_);
// either way, wake the fiber
fibers::context::active()->set_ready( ctx_);
}
//private:
boost::fibers::context * ctx_;
boost::system::error_code * ec_;
fibers::detail::spinlock * mtx_{ nullptr };
bool * completed_{ nullptr };
yield_t yt_;
// We depend on this pointer to yield_completion, which will be injected
// by async_result.
yield_completion * ycomp_{ nullptr };
};
// asio uses handler_type<completion token type, signature>::type to decide
// what to instantiate as the actual handler. Below, we specialize
// handler_type< yield_t, ... > to indicate yield_handler<>. So when you pass
// an instance of yield_t as an asio completion token, asio selects
// yield_handler<> as the actual handler class.
template< typename T >
class yield_handler: public yield_handler_base {
public:
// asio passes the completion token to the handler constructor
explicit yield_handler( yield_t const& y) :
yield_handler_base( y)
{}
// completion callback passing only value (T)
void operator()( T t)
{
// just like callback passing success error_code
(*this)( boost::system::error_code(), std::move(t) );
}
// completion callback passing (error_code, T)
void operator()( boost::system::error_code const& ec, T t)
{
BOOST_ASSERT_MSG( value_,
"Must inject value ptr "
"before caling yield_handler<T>::operator()()");
// move the value to async_result<> instance BEFORE waking up a
// suspended fiber
* value_ = std::move( t);
// forward the call to base-class completion handler
yield_handler_base::operator()( ec);
}
//private:
// pointer to destination for eventual value
// this must be injected by async_result before operator()() is called
T * value_{ nullptr };
};
// yield_handler<void> is like yield_handler<T> without value_. In fact it's
// just like yield_handler_base.
template<>
class yield_handler< void >: public yield_handler_base {
public:
explicit yield_handler( yield_t const& y) :
yield_handler_base( y)
{}
// nullary completion callback
void operator()()
{
(*this)( boost::system::error_code() );
}
// inherit operator()(error_code) overload from base class
using yield_handler_base::operator();
};
// Specialize asio_handler_invoke hook to ensure that any exceptions thrown
@@ -103,6 +163,44 @@ void asio_handler_invoke( Fn fn, yield_handler< T > * h) {
fn();
}
// Factor out commonality between async_result<yield_handler<T>> and
// async_result<yield_handler<void>>
class async_result_base {
public:
explicit async_result_base( yield_handler_base & h) {
// Inject ptr to our yield_completion instance into this
// yield_handler<>.
h->ycomp_ = &this->ycomp_;
// if yield_t didn't bind an error_code, make yield_handler_base's
// error_code* point to an error_code local to this object so
// yield_handler_base::operator() can unconditionally store through
// its error_code*
if ( ! h.yt_.ec_) {
h.yt_.ec_ = & ec_;
}
}
void get() {
// Unless yield_handler_base::operator() has already been called,
// suspend the calling fiber until that call.
ycomp_.wait();
// The only way our own ec_ member could have a non-default value is
// if our yield_handler did not have a bound error_code AND the
// completion callback passed a non-default error_code.
if ( ec_) {
throw_exception( boost::system::system_error( ec_) );
}
boost::this_fiber::interruption_point();
}
private:
// If yield_t does not bind an error_code instance, store into here.
boost::system::error_code ec_{};
// async_result_base owns the yield_completion because, unlike
// yield_handler<>, async_result<> is only instantiated once.
yield_completion ycomp_{};
};
} // namespace detail
} // namespace asio
} // namespace fibers
@@ -111,106 +209,79 @@ void asio_handler_invoke( Fn fn, yield_handler< T > * h) {
namespace boost {
namespace asio {
// asio constructs an async_result<> instance from the yield_handler specified
// by handler_type<>::type. A particular asio async method constructs the
// yield_handler, constructs this async_result specialization from it, then
// returns the result of calling its get() method.
template< typename T >
class async_result< boost::fibers::asio::detail::yield_handler< T > > {
class async_result< boost::fibers::asio::detail::yield_handler< T > >:
public boost::fibers::asio::detail::async_result_base {
public:
// type returned by get()
typedef T type;
explicit async_result( boost::fibers::asio::detail::yield_handler< T > & h) {
out_ec_ = h.ec_;
if ( ! out_ec_) {
h.ec_ = & ec_;
}
explicit async_result( boost::fibers::asio::detail::yield_handler< T > & h):
boost::fibers::asio::detail::async_result_base( h)
{
// Inject ptr to our value_ member into yield_handler<>: result will
// be stored here.
h.value_ = & value_;
h.mtx_ = & mtx_;
h.completed_ = & completed_;
}
// asio async method returns result of calling get()
type get() {
std::unique_lock< fibers::detail::spinlock > lk( mtx_);
if ( ! completed_) {
boost::fibers::context::active()->suspend( lk);
}
//lk.unlock();
if ( ! out_ec_ && ec_) {
throw_exception( boost::system::system_error( ec_) );
}
boost::this_fiber::interruption_point();
boost::fibers::asio::detail::async_result_base::get();
return std::move( value_);
}
private:
boost::system::error_code * out_ec_{ nullptr };
boost::system::error_code ec_{};
type value_{};
fibers::detail::spinlock mtx_{};
bool completed_{ false };
};
// Without the need to handle a passed value, our yield_handler<void>
// specialization is just like async_result_base.
template<>
class async_result< boost::fibers::asio::detail::yield_handler< void > > {
class async_result< boost::fibers::asio::detail::yield_handler< void > >:
public boost::fibers::asio::detail::async_result_base {
public:
typedef void type;
typedef void type;
explicit async_result( boost::fibers::asio::detail::yield_handler< void > & h) {
out_ec_ = h.ec_;
if ( ! out_ec_) {
h.ec_ = & ec_;
}
h.mtx_ = & mtx_;
h.completed_ = & completed_;
}
void get() {
std::unique_lock< fibers::detail::spinlock > lk( mtx_);
if ( ! completed_) {
boost::fibers::context::active()->suspend( lk);
}
//lk.unlock();
if ( ! out_ec_ && ec_) {
throw_exception( boost::system::system_error( ec_) );
}
boost::this_fiber::interruption_point();
}
private:
boost::system::error_code * out_ec_{ nullptr };
boost::system::error_code ec_{};
fibers::detail::spinlock mtx_{};
bool completed_{ false };
explicit async_result( boost::fibers::asio::detail::yield_handler< void > & h):
boost::fibers::asio::detail::async_result_base( h)
{}
};
// Handler type specialisation for use_future.
// Handler type specialisation for fibers::asio::yield.
// When 'yield' is passed as a completion handler which accepts no parameters,
// use yield_handler<void>.
template< typename ReturnType >
struct handler_type<
boost::fibers::asio::yield_t,
ReturnType()
>
{ typedef boost::fibers::asio::detail::yield_handler< void > type; };
struct handler_type< fibers::asio::yield_t, ReturnType() >
{ typedef fibers::asio::detail::yield_handler< void > type; };
// Handler type specialisation for use_future.
// Handler type specialisation for fibers::asio::yield.
// When 'yield' is passed as a completion handler which accepts a data
// parameter, use yield_handler<parameter type> to return that parameter to
// the caller.
template< typename ReturnType, typename Arg1 >
struct handler_type<
boost::fibers::asio::yield_t,
ReturnType( Arg1)
>
{ typedef boost::fibers::asio::detail::yield_handler< Arg1 > type; };
struct handler_type< fibers::asio::yield_t, ReturnType( Arg1) >
{ typedef fibers::asio::detail::yield_handler< Arg1 > type; };
// Handler type specialisation for use_future.
// Handler type specialisation for fibers::asio::yield.
// When 'yield' is passed as a completion handler which accepts only
// error_code, use yield_handler<void>. yield_handler will take care of the
// error_code one way or another.
template< typename ReturnType >
struct handler_type<
boost::fibers::asio::yield_t,
ReturnType( boost::system::error_code)
>
{ typedef boost::fibers::asio::detail::yield_handler< void > type; };
struct handler_type< fibers::asio::yield_t, ReturnType( boost::system::error_code) >
{ typedef fibers::asio::detail::yield_handler< void > type; };
// Handler type specialisation for use_future.
// Handler type specialisation for fibers::asio::yield.
// When 'yield' is passed as a completion handler which accepts a data
// parameter and an error_code, use yield_handler<parameter type> to return
// just the parameter to the caller. yield_handler will take care of the
// error_code one way or another.
template< typename ReturnType, typename Arg2 >
struct handler_type<
boost::fibers::asio::yield_t,
ReturnType( boost::system::error_code, Arg2)
>
{ typedef boost::fibers::asio::detail::yield_handler< Arg2 > type; };
struct handler_type< fibers::asio::yield_t, ReturnType( boost::system::error_code, Arg2) >
{ typedef fibers::asio::detail::yield_handler< Arg2 > type; };
} // namespace asio
} // namespace boost

View File

@@ -1,86 +0,0 @@
//
// 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>
*/
//[fibers_asio_promise_completion_token
template< typename Allocator >
class promise_completion_token {
public:
typedef Allocator allocator_type;
/// Construct using default-constructed allocator.
constexpr promise_completion_token() = default;
/// Construct using specified allocator.
explicit promise_completion_token( Allocator const& allocator) noexcept :
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_{ nullptr };
private:
Allocator allocator_{};
};
//]
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_ASIO_PROMISE_COMPLETION_TOKEN_HPP

View File

@@ -13,8 +13,6 @@
#ifndef BOOST_FIBERS_ASIO_YIELD_HPP
#define BOOST_FIBERS_ASIO_YIELD_HPP
#include <memory>
#include <boost/config.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
@@ -27,19 +25,40 @@ namespace asio {
class yield_t {
public:
constexpr yield_t() = default;
yield_t(bool hop):
allow_hop_( hop)
{}
/**
* @code
* static yield_t yield;
* boost::system::error_code myec;
* func(yield[myec]);
* @endcode
* @c yield[myec] returns an instance of @c yield_t whose @c ec_ points
* to @c myec. The expression @c yield[myec] "binds" @c myec to that
* (anonymous) @c yield_t instance, instructing @c func() to store any
* @c error_code it might produce into @c myec rather than throwing @c
* boost::system::system_error.
*/
yield_t operator[]( boost::system::error_code & ec) const {
yield_t tmp;
yield_t tmp{ *this };
tmp.ec_ = & ec;
return tmp;
}
//private:
// ptr to bound error_code instance if any
boost::system::error_code * ec_{ nullptr };
// allow calling fiber to "hop" to another thread if it could resume more
// quickly that way
bool allow_hop_;
};
thread_local yield_t yield{};
// canonical instance with allow_hop_ == false
thread_local yield_t yield(false);
// canonical instance with allow_hop_ == true
thread_local yield_t yield_hop(true);
}}}

View File

@@ -46,8 +46,8 @@ public:
// Call this method to alter priority, because we must notify
// priority_scheduler of any change.
void set_priority( int p) {
/*< It's important to call notify() on any
void set_priority( int p) { /*<
It's important to call `notify()` on any
change in a property that can affect the
scheduler's behavior. Therefore, such
modifications should only be performed
@@ -155,7 +155,7 @@ public:
// 'ctx' might not be in our queue at all, if caller is changing the
// priority of (say) the running fiber. If it's not there, no need to
// move it: we'll handle it next time it hits awakened().
if ( ! ctx->ready_is_linked()) {/*<
if ( ! ctx->ready_is_linked()) { /*<
Your `property_change()` override must be able to
handle the case in which the passed `ctx` is not in
your ready queue. It might be running, or it might be