Merge branch 'develop'

This commit is contained in:
Oliver Kowalke 2017-05-26 22:14:08 +02:00
commit bce46a83a9
18 changed files with 113 additions and 1655 deletions

View File

@ -1,107 +0,0 @@
[/
Copyright Oliver Kowalke 2014.
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
]
[section:attributes Attributes]
Class `attributes` is used to specify parameters required to setup a
coroutine's context.
enum flag_unwind_t
{
stack_unwind,
no_stack_unwind
};
enum flag_fpu_t
{
fpu_preserved,
fpu_not_preserved
};
struct attributes
{
std::size_t size;
flag_unwind_t do_unwind;
flag_fpu_t preserve_fpu;
attributes() noexcept;
explicit attributes( std::size_t size_) noexcept;
explicit attributes( flag_unwind_t do_unwind_) noexcept;
explicit attributes( flag_fpu_t preserve_fpu_) noexcept;
explicit attributes( std::size_t size_, flag_unwind_t do_unwind_) noexcept;
explicit attributes( std::size_t size_, flag_fpu_t preserve_fpu_) noexcept;
explicit attributes( flag_unwind_t do_unwind_, flag_fpu_t preserve_fpu_) noexcept;
explicit attributes( std::size_t size_, flag_unwind_t do_unwind_, flag_fpu_t preserve_fpu_) noexcept;
};
[heading `attributes()`]
[variablelist
[[Effects:] [Default constructor using `boost::context::default_stacksize()`, does unwind
the stack after coroutine/generator is complete and preserves FPU registers.]]
[[Throws:] [Nothing.]]
]
[heading `attributes( std::size_t size)`]
[variablelist
[[Effects:] [Argument `size` defines stack size of the new coroutine.
Stack unwinding after termination and preserving FPU registers is set by
default.]]
[[Throws:] [Nothing.]]
]
[heading `attributes( flag_unwind_t do_unwind)`]
[variablelist
[[Effects:] [Argument `do_unwind` determines if stack will be unwound after
termination or not. The default stacksize is used for the new coroutine
and FPU registers are preserved.]]
[[Throws:] [Nothing.]]
]
[heading `attributes( flag_fpu_t preserve_fpu)`]
[variablelist
[[Effects:] [Argument `preserve_fpu` determines if FPU register have to be
preserved across context switches. The default stacksize is used for the
new coroutine and its stack will be unwound after termination.]]
[[Throws:] [Nothing.]]
]
[heading `attributes( std::size_t size, flag_unwind_t do_unwind)`]
[variablelist
[[Effects:] [Arguments `size` and `do_unwind` are given by the user.
FPU registers are preserved across each context switch.]]
[[Throws:] [Nothing.]]
]
[heading `attributes( std::size_t size, flag_fpu_t preserve_fpu)`]
[variablelist
[[Effects:] [Arguments `size` and `preserve_fpu` are given by the user.
The stack is automatically unwound after coroutine/generator terminates.]]
[[Throws:] [Nothing.]]
]
[heading `attributes( flag_unwind_t do_unwind, flag_fpu_t preserve_fpu)`]
[variablelist
[[Effects:] [Arguments `do_unwind` and `preserve_fpu` are given by the user.
The stack gets a default value of `boost::context::default_stacksize()`.]]
[[Throws:] [Nothing.]]
]
[heading `attributes( std::size_t size, flag_unwind_t do_unwind, flag_fpu_t preserve_fpu)`]
[variablelist
[[Effects:] [Arguments `size`, `do_unwind` and `preserve_fpu` are given by the
user.]]
[[Throws:] [Nothing.]]
]
[endsect]

View File

@ -46,7 +46,7 @@
[def __coro_ns__ ['boost::coroutines2]] [def __coro_ns__ ['boost::coroutines2]]
[def __econtext__ ['execution_context]] [def __econtext__ ['execution_context]]
[def __end__ ['std::end()]] [def __end__ ['std::end()]]
[def __fcontext__ ['boost::contexts::fcontext_t]] [def __fcontext__ [@boost:/libs/context/doc/html/context/cc.html#implementation fcontext_t]]
[def __fetch__ ['inbuf::fetch()]] [def __fetch__ ['inbuf::fetch()]]
[def __fixedsize__ ['fixedsize_stack]] [def __fixedsize__ ['fixedsize_stack]]
[def __forced_unwind__ ['detail::forced_unwind]] [def __forced_unwind__ ['detail::forced_unwind]]
@ -73,6 +73,9 @@
[def __standard_allocator__ ['fixedsize]] [def __standard_allocator__ ['fixedsize]]
[def __start__ ['session::start()]] [def __start__ ['session::start()]]
[def __terminate__ ['std::terminate()]] [def __terminate__ ['std::terminate()]]
[def __ucontext__ ['ucontext_t]]
[def __winfib__ ['WinFiber]]
[def __cc__ ['call/cc]]
[def __underflow__ ['stream_buf::underflow()]] [def __underflow__ ['stream_buf::underflow()]]
[def __yield_context__ ['boost::asio::yield_context]] [def __yield_context__ ['boost::asio::yield_context]]

View File

@ -57,7 +57,7 @@ stack (macro SIGSTKSZ on POSIX).
At this time of writing only GCC (4.7) At this time of writing only GCC (4.7)
[footnote [@http://gcc.gnu.org/wiki/SplitStacks Ian Lance Taylor, Split Stacks in GCC]] [footnote [@http://gcc.gnu.org/wiki/SplitStacks Ian Lance Taylor, Split Stacks in GCC]]
is known to support segmented stacks. With version 1.54 __boost_coroutine__ is known to support segmented stacks. With version 1.54 __boost_coroutine__
provides support for segmented stacks. provides support for [link segmented ['segmented stacks]].
The destructor releases the associated stack. The implementer is free to The destructor releases the associated stack. The implementer is free to
deallocate the stack or to cache it for later usage. deallocate the stack or to cache it for later usage.
@ -67,15 +67,6 @@ deallocate the stack or to cache it for later usage.
A coroutine saves and restores registers according to the underlying ABI on A coroutine saves and restores registers according to the underlying ABI on
each context switch (using __boost_context__). each context switch (using __boost_context__).
Some applications do not use floating-point registers and can disable preserving
FPU registers for performance reasons.
[note According to the calling convention the FPU registers are preserved by
default.]
On POSIX systems, the coroutine context switch does not preserve signal masks
for performance reasons.
A context switch is done via __push_coro_op__ and __pull_coro_op__. A context switch is done via __push_coro_op__ and __pull_coro_op__.
[warning Calling __push_coro_op__ and __pull_coro_op__ from inside the [_same] [warning Calling __push_coro_op__ and __pull_coro_op__ from inside the [_same]
@ -85,11 +76,48 @@ As an example, the code below will result in undefined behaviour:
boost::coroutines2::coroutine<void>::push_type coro( boost::coroutines2::coroutine<void>::push_type coro(
[&](boost::coroutines2::coroutine<void>::pull_type& yield){ [&](boost::coroutines2::coroutine<void>::pull_type& yield){
coro(); yield();
}); });
coro(); coro();
[include asymmetric.qbk] [include asymmetric.qbk]
[section Implementations: fcontext_t, ucontext_t and WinFiber]
[heading fcontext_t]
The implementation uses __fcontext__ per default. fcontext_t is based on
assembler and not available for all platforms. It provides a much better
performance than __ucontext__
(the context switch takes two magnitudes of order less CPU cycles) and __winfib__.
[heading ucontext_t]
As an alternative, [@https://en.wikipedia.org/wiki/Setcontext __ucontext__]
can be used by compiling with `BOOST_USE_UCONTEXT` and b2 property `context-impl=ucontext`.
__ucontext__ might be available on a broader range of POSIX-platforms but has
some (for instance deprecated since POSIX.1-2003, not C99 conform).
[note __cc__ supports [link segmented ['Segmented stacks]] only with
__ucontext__ as its implementation.]
[heading WinFiber]
With `BOOST_USE_WINFIB` and b2 property `context-impl=winfib` Win32-Fibers are used
as implementation for __cc__.
Because the TIB (thread information block) is not fully described in the MSDN,
it might be possible that not all required TIB-parts are swapped.
[note The first call of __cc__ converts the thread into a Windows fiber by
invoking `ConvertThreadToFiber()`. If desired, `ConvertFiberToThread()` has
to be called by the user explicitly in order to release resources allocated
by `ConvertThreadToFiber()` (e.g. after using boost.context). ]
[endsect]
[endsect] [endsect]

View File

@ -15,7 +15,7 @@ always before the parent. Coroutines (the term was introduced by Melvin
Conway [footnote Conway, Melvin E.. "Design of a Separable Transition-Diagram Compiler". Conway [footnote Conway, Melvin E.. "Design of a Separable Transition-Diagram Compiler".
Commun. ACM, Volume 6 Issue 7, July 1963, Article No. 7]), Commun. ACM, Volume 6 Issue 7, July 1963, Article No. 7]),
are a generalization of routines (Donald Knuth [footnote Knuth, Donald Ervin (1997). are a generalization of routines (Donald Knuth [footnote Knuth, Donald Ervin (1997).
"Fundamental Algorithms. The Art of Computer Programming 1", (3rd ed.)]. "Fundamental Algorithms. The Art of Computer Programming 1", (3rd ed.)]).
The principal difference between coroutines and routines The principal difference between coroutines and routines
is that a coroutine enables explicit suspend and resume of its progress via is that a coroutine enables explicit suspend and resume of its progress via
additional operations by preserving execution state and thus provides an additional operations by preserving execution state and thus provides an

View File

@ -7,24 +7,18 @@
[section:performance Performance] [section:performance Performance]
Performance of __boost_coroutine__ was measured on the platforms shown in the Performance measurements were taken using `std::chrono::highresolution_clock`,
following table. Performance measurements were taken using `rdtsc` and with overhead corrections.
`boost::chrono::high_resolution_clock`, with overhead corrections, on x86 The code was compiled with gcc-6.3.1, using build options:
platforms. In each case, cache warm-up was accounted for, and the one variant = release, optimization = speed.
running thread was pinned to a single CPU. Tests were executed on dual Intel XEON E5 2620 2.2GHz, 16C/32T, 64GB RAM,
running Linux (x86_64).
[table Performance of asymmetric coroutines (using execution_context) [table Performance of context switch
[[using __fcontext__] [using [@boost:/libs/context/doc/html/context/cc.html#implementation ucontext_t]]]
[ [
[Platform] [26 ns / 56 CPU cycles]
[switch] [542 ns / 1146 CPU cycles]
[construction (protected stack-allocator)]
[construction (standard stack-allocator)]
]
[
[x86_64 [footnote Intel Core i7-4770S 3.10GHz]]
[16 ns / 50 cycles]
[7 \u00b5s / 22133 cycles]
[1.7 \u00b5s / 5383 cycles]
] ]
] ]

View File

@ -183,6 +183,7 @@ address of the stack.]]
[endsect] [endsect]
[#segmented]
[section:segmented Class ['segmented_stack]] [section:segmented Class ['segmented_stack]]
__boost_coroutine__ supports usage of a __segmented__, e. g. the size of __boost_coroutine__ supports usage of a __segmented__, e. g. the size of
@ -193,11 +194,13 @@ In contrast to __protected_fixedsize__ and __fixedsize__ it creates a
stack which grows on demand. stack which grows on demand.
[note Segmented stacks are currently only supported by [*gcc] from version [note Segmented stacks are currently only supported by [*gcc] from version
[*4.7] and [*clang] from version [*3.4] onwards. In order to use a [*4.7] [*clang] from version [*3.4] onwards. In order to use a
__segmented_stack__ __boost_coroutine__ must be built with __segmented_stack__ __boost_context__ must be built with
property `segmented-stacks`, e.g. [*toolset=gcc segmented-stacks=on] at b2/bjam property `segmented-stacks`, e.g. [*toolset=gcc segmented-stacks=on] and
applying `BOOST_USE_SEGMENTED_STACKS` and `BOOST_USE_UCONTEXT` at b2/bjam
command line.] command line.]
#include <boost/coroutine2/segmented_stack.hpp> #include <boost/coroutine2/segmented_stack.hpp>
struct segmented_stack { struct segmented_stack {

View File

@ -40,6 +40,6 @@ exe parser
: parser.cpp : parser.cpp
; ;
#exe segmented exe segmented
# : segmented.cpp : segmented.cpp
# ; ;

View File

@ -6,8 +6,7 @@
#include <cstdlib> #include <cstdlib>
#include <iostream> #include <iostream>
#include <memory>
#include <boost/config.hpp>
#include <boost/coroutine2/all.hpp> #include <boost/coroutine2/all.hpp>
@ -16,17 +15,13 @@ __declspec(noinline) void access( char *buf);
#else // GCC #else // GCC
void access( char *buf) __attribute__ ((noinline)); void access( char *buf) __attribute__ ((noinline));
#endif #endif
void access( char *buf) void access( char *buf) {
{
buf[0] = '\0'; buf[0] = '\0';
} }
void bar( int i) void bar( int i) {
{
char buf[4 * 1024]; char buf[4 * 1024];
if ( i > 0) {
if ( i > 0)
{
access( buf); access( buf);
std::cout << i << ". iteration" << std::endl; std::cout << i << ". iteration" << std::endl;
bar( i - 1); bar( i - 1);
@ -35,20 +30,19 @@ void bar( int i)
int main() { int main() {
int count = 384; int count = 384;
#if defined(BOOST_USE_SEGMENTED_STACKS)
std::cout << "using segmented_stack stacks: allocates " << count << " * 4kB == " << 4 * count << "kB on stack, "; std::cout << "using segmented_stack stacks: allocates " << count << " * 4kB == " << 4 * count << "kB on stack, ";
std::cout << "initial stack size = " << boost::context::segmented_stack::traits_type::default_size() / 1024 << "kB" << std::endl; std::cout << "initial stack size = " << boost::context::segmented_stack::traits_type::default_size() / 1024 << "kB" << std::endl;
std::cout << "application should not fail" << std::endl; std::cout << "application should not fail" << std::endl;
#else
boost::coroutines2::coroutine< void >::push_type sink( std::cout << "using standard stacks: allocates " << count << " * 4kB == " << 4 * count << "kB on stack, ";
[&]( boost::coroutines2::coroutine< void >::pull_type & source) { std::cout << "initial stack size = " << boost::context::fixedsize_stack::traits_type::default_size() / 1024 << "kB" << std::endl;
std::cout << "application might fail" << std::endl;
#endif
boost::coroutines2::coroutine< void >::pull_type coro{
[count](boost::coroutines2::coroutine< void >::push_type & coro){
bar( count); bar( count);
source(); }};
}); std::cout << "main: done" << std::endl;
return EXIT_SUCCESS;
sink();
std::cout << "main: Done" << std::endl;
return 0;
} }

View File

@ -29,26 +29,14 @@ class push_coroutine;
#include <boost/coroutine2/detail/pull_coroutine.hpp> #include <boost/coroutine2/detail/pull_coroutine.hpp>
#include <boost/coroutine2/detail/push_coroutine.hpp> #include <boost/coroutine2/detail/push_coroutine.hpp>
#if defined(BOOST_EXECUTION_CONTEXT)
# if (BOOST_EXECUTION_CONTEXT==1)
# include <boost/coroutine2/detail/pull_control_block_ecv1.hpp>
# include <boost/coroutine2/detail/push_control_block_ecv1.hpp>
# else
#include <boost/coroutine2/detail/pull_control_block_cc.hpp> #include <boost/coroutine2/detail/pull_control_block_cc.hpp>
#include <boost/coroutine2/detail/push_control_block_cc.hpp> #include <boost/coroutine2/detail/push_control_block_cc.hpp>
# endif
#include <boost/coroutine2/detail/pull_coroutine.ipp> #include <boost/coroutine2/detail/pull_coroutine.ipp>
#include <boost/coroutine2/detail/push_coroutine.ipp> #include <boost/coroutine2/detail/push_coroutine.ipp>
# if (BOOST_EXECUTION_CONTEXT==1)
# include <boost/coroutine2/detail/pull_control_block_ecv1.ipp>
# include <boost/coroutine2/detail/push_control_block_ecv1.ipp>
# else
#include <boost/coroutine2/detail/pull_control_block_cc.ipp> #include <boost/coroutine2/detail/pull_control_block_cc.ipp>
#include <boost/coroutine2/detail/push_control_block_cc.ipp> #include <boost/coroutine2/detail/push_control_block_cc.ipp>
# endif
#endif
#ifdef BOOST_HAS_ABI_HEADERS #ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX # include BOOST_ABI_SUFFIX

View File

@ -1,119 +0,0 @@
// Copyright Oliver Kowalke 2014.
// 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_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_HPP
#define BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_HPP
#include <exception>
#include <functional>
#include <type_traits>
#include <boost/config.hpp>
#include <boost/context/execution_context.hpp>
#include <boost/coroutine2/detail/state.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace coroutines2 {
namespace detail {
template< typename T >
struct pull_coroutine< T >::control_block {
boost::context::execution_context ctx;
typename push_coroutine< T >::control_block * other;
state_t state;
std::exception_ptr except;
bool bvalid;
typename std::aligned_storage< sizeof( T), alignof( T) >::type storage;
static void destroy( control_block * cb) noexcept;
template< typename StackAllocator, typename Fn >
control_block( context::preallocated, StackAllocator, Fn &&);
control_block( typename push_coroutine< T >::control_block *, boost::context::execution_context const&) noexcept;
~control_block() noexcept;
control_block( control_block &) = delete;
control_block & operator=( control_block &) = delete;
void deallocate() noexcept;
void resume();
void set( T *);
T & get() noexcept;
bool valid() const noexcept;
};
template< typename T >
struct pull_coroutine< T & >::control_block {
boost::context::execution_context ctx;
typename push_coroutine< T & >::control_block * other;
state_t state;
std::exception_ptr except;
T * t;
static void destroy( control_block * cb) noexcept;
template< typename StackAllocator, typename Fn >
control_block( context::preallocated, StackAllocator, Fn &&);
control_block( typename push_coroutine< T & >::control_block *, boost::context::execution_context const&) noexcept;
~control_block() noexcept;
control_block( control_block &) = delete;
control_block & operator=( control_block &) = delete;
void deallocate() noexcept;
void resume();
T & get() noexcept;
bool valid() const noexcept;
};
struct pull_coroutine< void >::control_block {
boost::context::execution_context ctx;
push_coroutine< void >::control_block * other;
state_t state;
std::exception_ptr except;
static void destroy( control_block * cb) noexcept;
template< typename StackAllocator, typename Fn >
control_block( context::preallocated, StackAllocator, Fn &&);
control_block( push_coroutine< void >::control_block *, boost::context::execution_context const&) noexcept;
~control_block() noexcept;
control_block( control_block &) = delete;
control_block & operator=( control_block &) = delete;
void deallocate() noexcept;
void resume();
bool valid() const noexcept;
};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_HPP

View File

@ -1,431 +0,0 @@
// Copyright Oliver Kowalke 2014.
// 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_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_IPP
#define BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_IPP
#include <exception>
#include <functional>
#include <memory>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/context/execution_context.hpp>
#include <boost/coroutine2/detail/config.hpp>
#include <boost/coroutine2/detail/decay_copy.hpp>
#include <boost/coroutine2/detail/forced_unwind.hpp>
#include <boost/coroutine2/detail/state.hpp>
#include <boost/coroutine2/detail/wrap.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace coroutines2 {
namespace detail {
// pull_coroutine< T >
template< typename T >
void
pull_coroutine< T >::control_block::destroy( control_block * cb) noexcept {
boost::context::execution_context ctx = cb->ctx;
// destroy control structure
cb->state |= state_t::destroy;
cb->~control_block();
}
template< typename T >
template< typename StackAllocator, typename Fn >
pull_coroutine< T >::control_block::control_block( context::preallocated palloc, StackAllocator salloc,
Fn && fn) :
#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS)
ctx{ std::allocator_arg, palloc, salloc,
wrap( [this]( typename std::decay< Fn >::type & fn_, boost::context::execution_context & ctx, void *) mutable noexcept {
// create synthesized push_coroutine< T >
typename push_coroutine< T >::control_block synthesized_cb{ this, ctx };
push_coroutine< T > synthesized{ & synthesized_cb };
other = & synthesized_cb;
if ( state_t::none == ( state & state_t::destroy) ) {
try {
auto fn = std::move( fn_);
// call coroutine-fn with synthesized push_coroutine as argument
fn( synthesized);
} catch ( forced_unwind const&) {
// do nothing for unwinding exception
} catch (...) {
// store other exceptions in exception-pointer
except = std::current_exception();
}
}
// set termination flags
state |= state_t::complete;
// jump back to ctx
other->ctx();
BOOST_ASSERT_MSG( false, "pull_coroutine is complete");
},
std::forward< Fn >( fn),
boost::context::execution_context::current() ) },
#else
ctx{ std::allocator_arg, palloc, salloc,
[this,fn_=decay_copy( std::forward< Fn >( fn) ),ctx=boost::context::execution_context::current()] (void *) mutable noexcept {
// create synthesized push_coroutine< T >
typename push_coroutine< T >::control_block synthesized_cb{ this, ctx };
push_coroutine< T > synthesized{ & synthesized_cb };
other = & synthesized_cb;
if ( state_t::none == ( state & state_t::destroy) ) {
try {
auto fn = std::move( fn_);
// call coroutine-fn with synthesized push_coroutine as argument
fn( synthesized);
} catch ( forced_unwind const&) {
// do nothing for unwinding exception
} catch (...) {
// store other exceptions in exception-pointer
except = std::current_exception();
}
}
// set termination flags
state |= state_t::complete;
// jump back to ctx
other->ctx();
BOOST_ASSERT_MSG( false, "pull_coroutine is complete");
}},
#endif
other{ nullptr },
state{ state_t::unwind },
except{},
bvalid{ false },
storage{} {
// enter coroutine-fn in order to have first value available after ctor (of `*this`) returns
set( static_cast< T * >( ctx() ) );
}
template< typename T >
pull_coroutine< T >::control_block::control_block( typename push_coroutine< T >::control_block * cb,
boost::context::execution_context const& ctx_) noexcept :
ctx{ ctx_ },
other{ cb },
state{ state_t::none },
except{},
bvalid{ false },
storage{} {
}
template< typename T >
pull_coroutine< T >::control_block::~control_block() {
if ( state_t::none == ( state & state_t::complete) &&
state_t::none != ( state & state_t::unwind) ) {
// unwind coroutine stack
other->ctx = boost::context::execution_context::current();
ctx( context::exec_ontop_arg, unwind_coroutine);
}
// destroy data if it set
if ( bvalid) {
reinterpret_cast< T * >( std::addressof( storage) )->~T();
}
}
template< typename T >
void
pull_coroutine< T >::control_block::deallocate() noexcept {
if ( state_t::none != ( state & state_t::unwind) ) {
destroy( this);
}
}
template< typename T >
void
pull_coroutine< T >::control_block::resume() {
other->ctx = boost::context::execution_context::current();
set( static_cast< T * >( ctx() ) );
if ( except) {
std::rethrow_exception( except);
}
}
template< typename T >
void
pull_coroutine< T >::control_block::set( T * t) {
// destroy data if it set
if ( bvalid) {
reinterpret_cast< T * >( std::addressof( storage) )->~T();
}
if ( nullptr != t) {
::new ( static_cast< void * >( std::addressof( storage) ) ) T( std::move( * t) );
bvalid = true;
} else {
bvalid = false;
}
}
template< typename T >
T &
pull_coroutine< T >::control_block::get() noexcept {
return * reinterpret_cast< T * >( std::addressof( storage) );
}
template< typename T >
bool
pull_coroutine< T >::control_block::valid() const noexcept {
return nullptr != other && state_t::none == ( state & state_t::complete) && bvalid;
}
// pull_coroutine< T & >
template< typename T >
void
pull_coroutine< T & >::control_block::destroy( control_block * cb) noexcept {
boost::context::execution_context ctx = cb->ctx;
// destroy control structure
cb->state |= state_t::destroy;
cb->~control_block();
}
template< typename T >
template< typename StackAllocator, typename Fn >
pull_coroutine< T & >::control_block::control_block( context::preallocated palloc, StackAllocator salloc,
Fn && fn) :
#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS)
ctx{ std::allocator_arg, palloc, salloc,
wrap( [this]( typename std::decay< Fn >::type & fn_, boost::context::execution_context & ctx, void *) mutable noexcept {
// create synthesized push_coroutine< T >
typename push_coroutine< T & >::control_block synthesized_cb{ this, ctx };
push_coroutine< T & > synthesized{ & synthesized_cb };
other = & synthesized_cb;
if ( state_t::none == ( state & state_t::destroy) ) {
try {
auto fn = std::move( fn_);
// call coroutine-fn with synthesized push_coroutine as argument
fn( synthesized);
} catch ( forced_unwind const&) {
// do nothing for unwinding exception
} catch (...) {
// store other exceptions in exception-pointer
except = std::current_exception();
}
}
// set termination flags
state |= state_t::complete;
// jump back to ctx
other->ctx();
BOOST_ASSERT_MSG( false, "pull_coroutine is complete");
},
std::forward< Fn >( fn),
boost::context::execution_context::current() ) },
#else
ctx{ std::allocator_arg, palloc, salloc,
[this,fn_=decay_copy( std::forward< Fn >( fn) ),ctx=boost::context::execution_context::current()] (void *) mutable noexcept {
// create synthesized push_coroutine< T >
typename push_coroutine< T & >::control_block synthesized_cb{ this, ctx };
push_coroutine< T & > synthesized{ & synthesized_cb };
other = & synthesized_cb;
if ( state_t::none == ( state & state_t::destroy) ) {
try {
auto fn = std::move( fn_);
// call coroutine-fn with synthesized push_coroutine as argument
fn( synthesized);
} catch ( forced_unwind const&) {
// do nothing for unwinding exception
} catch (...) {
// store other exceptions in exception-pointer
except = std::current_exception();
}
}
// set termination flags
state |= state_t::complete;
// jump back to ctx
other->ctx();
BOOST_ASSERT_MSG( false, "pull_coroutine is complete");
}},
#endif
other{ nullptr },
state{ state_t::unwind },
except{},
t{ nullptr } {
// enter coroutine-fn in order to have first value available after ctor (of `*this`) returns
t = static_cast< T * >( ctx() );
}
template< typename T >
pull_coroutine< T & >::control_block::control_block( typename push_coroutine< T & >::control_block * cb,
boost::context::execution_context const& ctx_) noexcept :
ctx{ ctx_ },
other{ cb },
state{ state_t::none },
except{},
t( nullptr) {
}
template< typename T >
pull_coroutine< T & >::control_block::~control_block() {
if ( state_t::none == ( state & state_t::complete) &&
state_t::none != ( state & state_t::unwind) ) {
// unwind coroutine stack
other->ctx = boost::context::execution_context::current();
ctx( context::exec_ontop_arg, unwind_coroutine);
}
}
template< typename T >
void
pull_coroutine< T & >::control_block::deallocate() noexcept {
if ( state_t::none != ( state & state_t::unwind) ) {
destroy( this);
}
}
template< typename T >
void
pull_coroutine< T & >::control_block::resume() {
other->ctx = boost::context::execution_context::current();
t = static_cast< T * >( ctx() );
if ( except) {
std::rethrow_exception( except);
}
}
template< typename T >
T &
pull_coroutine< T & >::control_block::get() noexcept {
return * static_cast< T * >( t);
}
template< typename T >
bool
pull_coroutine< T & >::control_block::valid() const noexcept {
return nullptr != other && state_t::none == ( state & state_t::complete) && nullptr != t;
}
// pull_coroutine< void >
inline
void
pull_coroutine< void >::control_block::destroy( control_block * cb) noexcept {
boost::context::execution_context ctx = cb->ctx;
// destroy control structure
cb->state |= state_t::destroy;
cb->~control_block();
}
template< typename StackAllocator, typename Fn >
pull_coroutine< void >::control_block::control_block( context::preallocated palloc, StackAllocator salloc,
Fn && fn) :
#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS)
ctx{ std::allocator_arg, palloc, salloc,
wrap( [this]( typename std::decay< Fn >::type & fn_, boost::context::execution_context & ctx, void *) mutable noexcept {
// create synthesized push_coroutine< T >
typename push_coroutine< void >::control_block synthesized_cb{ this, ctx };
push_coroutine< void > synthesized{ & synthesized_cb };
other = & synthesized_cb;
if ( state_t::none == ( state & state_t::destroy) ) {
try {
auto fn = std::move( fn_);
// call coroutine-fn with synthesized push_coroutine as argument
fn( synthesized);
} catch ( forced_unwind const&) {
// do nothing for unwinding exception
} catch (...) {
// store other exceptions in exception-pointer
except = std::current_exception();
}
}
// set termination flags
state |= state_t::complete;
// jump back to ctx
other->ctx();
BOOST_ASSERT_MSG( false, "pull_coroutine is complete");
},
std::forward< Fn >( fn),
boost::context::execution_context::current() ) },
#else
ctx{ std::allocator_arg, palloc, salloc,
[this,fn_=decay_copy( std::forward< Fn >( fn) ),ctx=boost::context::execution_context::current()] (void *) mutable noexcept {
// create synthesized push_coroutine< T >
typename push_coroutine< void >::control_block synthesized_cb{ this, ctx };
push_coroutine< void > synthesized{ & synthesized_cb };
other = & synthesized_cb;
if ( state_t::none == ( state & state_t::destroy) ) {
try {
auto fn = std::move( fn_);
// call coroutine-fn with synthesized push_coroutine as argument
fn( synthesized);
} catch ( forced_unwind const&) {
// do nothing for unwinding exception
} catch (...) {
// store other exceptions in exception-pointer
except = std::current_exception();
}
}
// set termination flags
state |= state_t::complete;
// jump back to ctx
other->ctx();
BOOST_ASSERT_MSG( false, "pull_coroutine is complete");
}},
#endif
other{ nullptr },
state{ state_t::unwind },
except{} {
// enter coroutine-fn in order to have first value available after ctor returns
ctx();
}
inline
pull_coroutine< void >::control_block::control_block( push_coroutine< void >::control_block * cb,
boost::context::execution_context const& ctx_) noexcept :
ctx{ ctx_ },
other{ cb },
state{ state_t::none },
except{} {
}
inline
pull_coroutine< void >::control_block::~control_block() {
if ( state_t::none == ( state & state_t::complete) &&
state_t::none != ( state & state_t::unwind) ) {
// unwind coroutine stack
other->ctx = boost::context::execution_context::current();
ctx( context::exec_ontop_arg, unwind_coroutine);
}
}
inline
void
pull_coroutine< void >::control_block::deallocate() noexcept {
if ( state_t::none != ( state & state_t::unwind) ) {
destroy( this);
}
}
inline
void
pull_coroutine< void >::control_block::resume() {
other->ctx = boost::context::execution_context::current();
ctx();
if ( except) {
std::rethrow_exception( except);
}
}
inline
bool
pull_coroutine< void >::control_block::valid() const noexcept {
return nullptr != other && state_t::none == ( state & state_t::complete);
}
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_IPP

View File

@ -1,111 +0,0 @@
// Copyright Oliver Kowalke 2014.
// 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_COROUTINES2_DETAIL_PUSH_CONTROL_BLOCK_HPP
#define BOOST_COROUTINES2_DETAIL_PUSH_CONTROL_BLOCK_HPP
#include <functional>
#include <exception>
#include <boost/config.hpp>
#include <boost/context/execution_context.hpp>
#include <boost/coroutine2/detail/state.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace coroutines2 {
namespace detail {
template< typename T >
struct push_coroutine< T >::control_block {
boost::context::execution_context ctx;
typename pull_coroutine< T >::control_block * other;
state_t state;
std::exception_ptr except;
static void destroy( control_block * cb) noexcept;
template< typename StackAllocator, typename Fn >
control_block( context::preallocated, StackAllocator, Fn &&);
control_block( typename pull_coroutine< T >::control_block *, boost::context::execution_context const&) noexcept;
~control_block() noexcept;
control_block( control_block &) = delete;
control_block & operator=( control_block &) = delete;
void deallocate() noexcept;
void resume( T const&);
void resume( T &&);
bool valid() const noexcept;
};
template< typename T >
struct push_coroutine< T & >::control_block {
boost::context::execution_context ctx;
typename pull_coroutine< T & >::control_block * other;
state_t state;
std::exception_ptr except;
static void destroy( control_block * cb) noexcept;
template< typename StackAllocator, typename Fn >
control_block( context::preallocated, StackAllocator, Fn &&);
control_block( typename pull_coroutine< T & >::control_block *, boost::context::execution_context const&) noexcept;
~control_block() noexcept;
control_block( control_block &) = delete;
control_block & operator=( control_block &) = delete;
void deallocate() noexcept;
void resume( T &);
bool valid() const noexcept;
};
struct push_coroutine< void >::control_block {
boost::context::execution_context ctx;
pull_coroutine< void >::control_block * other;
state_t state;
std::exception_ptr except;
static void destroy( control_block * cb) noexcept;
template< typename StackAllocator, typename Fn >
control_block( context::preallocated, StackAllocator, Fn &&);
control_block( pull_coroutine< void >::control_block *, boost::context::execution_context const&) noexcept;
~control_block() noexcept;
control_block( control_block &) = delete;
control_block & operator=( control_block &) = delete;
void deallocate() noexcept;
void resume();
bool valid() const noexcept;
};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_COROUTINES2_DETAIL_PUSH_CONTROL_BLOCK_HPP

View File

@ -1,428 +0,0 @@
// Copyright Oliver Kowalke 2014.
// 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_COROUTINES2_DETAIL_PUSH_CONTROL_BLOCK_IPP
#define BOOST_COROUTINES2_DETAIL_PUSH_CONTROL_BLOCK_IPP
#include <algorithm>
#include <exception>
#include <functional>
#include <memory>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/context/execution_context.hpp>
#include <boost/coroutine2/detail/config.hpp>
#include <boost/coroutine2/detail/decay_copy.hpp>
#include <boost/coroutine2/detail/forced_unwind.hpp>
#include <boost/coroutine2/detail/state.hpp>
#include <boost/coroutine2/detail/wrap.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace coroutines2 {
namespace detail {
// push_coroutine< T >
template< typename T >
void
push_coroutine< T >::control_block::destroy( control_block * cb) noexcept {
boost::context::execution_context ctx = cb->ctx;
// destroy control structure
cb->state |= state_t::destroy;
cb->~control_block();
}
template< typename T >
template< typename StackAllocator, typename Fn >
push_coroutine< T >::control_block::control_block( context::preallocated palloc, StackAllocator salloc,
Fn && fn) :
#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS)
ctx{ std::allocator_arg, palloc, salloc,
wrap( [this]( typename std::decay< Fn >::type & fn_, boost::context::execution_context & ctx, void * vp) mutable noexcept {
// create synthesized pull_coroutine< T >
typename pull_coroutine< T >::control_block synthesized_cb{ this, ctx };
pull_coroutine< T > synthesized{ & synthesized_cb };
other = & synthesized_cb;
if ( state_t::none == ( state & state_t::destroy) ) {
try {
// jump back to ctor
T * t = static_cast< T * >( ctx() );
// set transferred value
synthesized_cb.set( t);
auto fn = std::move( fn_);
// call coroutine-fn with synthesized pull_coroutine as argument
fn( synthesized);
} catch ( forced_unwind const&) {
// do nothing for unwinding exception
} catch (...) {
// store other exceptions in exception-pointer
except = std::current_exception();
}
}
// set termination flags
state |= state_t::complete;
// jump back to ctx
other->ctx();
BOOST_ASSERT_MSG( false, "push_coroutine is complete");
},
std::forward< Fn >( fn),
boost::context::execution_context::current() ) },
#else
ctx{ std::allocator_arg, palloc, salloc,
[this,fn_=decay_copy( std::forward< Fn >( fn) ),ctx=boost::context::execution_context::current()] (void *) mutable noexcept {
// create synthesized pull_coroutine< T >
typename pull_coroutine< T >::control_block synthesized_cb{ this, ctx };
pull_coroutine< T > synthesized{ & synthesized_cb };
other = & synthesized_cb;
if ( state_t::none == ( state & state_t::destroy) ) {
try {
// jump back to ctor
T * t = static_cast< T * >( ctx() );
// set transferred value
synthesized_cb.set( t);
auto fn = std::move( fn_);
// call coroutine-fn with synthesized pull_coroutine as argument
fn( synthesized);
} catch ( forced_unwind const&) {
// do nothing for unwinding exception
} catch (...) {
// store other exceptions in exception-pointer
except = std::current_exception();
}
}
// set termination flags
state |= state_t::complete;
// jump back to ctx
other->ctx();
BOOST_ASSERT_MSG( false, "push_coroutine is complete");
}},
#endif
other{ nullptr },
state{ state_t::unwind },
except{} {
// enter coroutine-fn in order to get other set
ctx();
}
template< typename T >
push_coroutine< T >::control_block::control_block( typename pull_coroutine< T >::control_block * cb,
boost::context::execution_context const& ctx_) noexcept :
ctx{ ctx_ },
other{ cb },
state{ state_t::none },
except{} {
}
template< typename T >
push_coroutine< T >::control_block::~control_block() {
if ( state_t::none == ( state & state_t::complete) &&
state_t::none != ( state & state_t::unwind) ) {
// unwind coroutine stack
other->ctx = boost::context::execution_context::current();
ctx( context::exec_ontop_arg, unwind_coroutine);
}
}
template< typename T >
void
push_coroutine< T >::control_block::deallocate() noexcept {
if ( state_t::none != ( state & state_t::unwind) ) {
destroy( this);
}
}
template< typename T >
void
push_coroutine< T >::control_block::resume( T const& t) {
other->ctx = boost::context::execution_context::current();
// pass an pointer to other context
ctx( const_cast< T * >( & t) );
if ( except) {
std::rethrow_exception( except);
}
}
template< typename T >
void
push_coroutine< T >::control_block::resume( T && t) {
other->ctx = boost::context::execution_context::current();
// pass an pointer to other context
ctx( std::addressof( t) );
if ( except) {
std::rethrow_exception( except);
}
}
template< typename T >
bool
push_coroutine< T >::control_block::valid() const noexcept {
return state_t::none == ( state & state_t::complete );
}
// push_coroutine< T & >
template< typename T >
void
push_coroutine< T & >::control_block::destroy( control_block * cb) noexcept {
boost::context::execution_context ctx = cb->ctx;
// destroy control structure
cb->state |= state_t::destroy;
cb->~control_block();
}
template< typename T >
template< typename StackAllocator, typename Fn >
push_coroutine< T & >::control_block::control_block( context::preallocated palloc, StackAllocator salloc,
Fn && fn) :
#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS)
ctx{ std::allocator_arg, palloc, salloc,
wrap( [this]( typename std::decay< Fn >::type & fn_, boost::context::execution_context & ctx, void * vp) mutable noexcept {
// create synthesized pull_coroutine< T >
typename pull_coroutine< T & >::control_block synthesized_cb{ this, ctx };
pull_coroutine< T & > synthesized{ & synthesized_cb };
other = & synthesized_cb;
if ( state_t::none == ( state & state_t::destroy) ) {
try {
// jump back to ctor
T * t = static_cast< T * >( ctx() );
// set transferred value
synthesized_cb.t = t;
auto fn = std::move( fn_);
// call coroutine-fn with synthesized pull_coroutine as argument
fn( synthesized);
} catch ( forced_unwind const&) {
// do nothing for unwinding exception
} catch (...) {
// store other exceptions in exception-pointer
except = std::current_exception();
}
}
// set termination flags
state |= state_t::complete;
// jump back to ctx
other->ctx();
BOOST_ASSERT_MSG( false, "push_coroutine is complete");
},
std::forward< Fn >( fn),
boost::context::execution_context::current() ) },
#else
ctx{ std::allocator_arg, palloc, salloc,
[this,fn_=decay_copy( std::forward< Fn >( fn) ),ctx=boost::context::execution_context::current()] (void *) mutable noexcept {
// create synthesized pull_coroutine< T >
typename pull_coroutine< T & >::control_block synthesized_cb{ this, ctx };
pull_coroutine< T & > synthesized{ & synthesized_cb };
other = & synthesized_cb;
if ( state_t::none == ( state & state_t::destroy) ) {
try {
// jump back to ctor
T * t = static_cast< T * >( ctx() );
// set transferred value
synthesized_cb.t = t;
auto fn = std::move( fn_);
// call coroutine-fn with synthesized pull_coroutine as argument
fn( synthesized);
} catch ( forced_unwind const&) {
// do nothing for unwinding exception
} catch (...) {
// store other exceptions in exception-pointer
except = std::current_exception();
}
}
// set termination flags
state |= state_t::complete;
// jump back to ctx
other->ctx();
BOOST_ASSERT_MSG( false, "push_coroutine is complete");
}},
#endif
other{ nullptr },
state{ state_t::unwind },
except{} {
// enter coroutine-fn in order to get other set
ctx();
}
template< typename T >
push_coroutine< T & >::control_block::control_block( typename pull_coroutine< T & >::control_block * cb,
boost::context::execution_context const& ctx_) noexcept :
ctx{ ctx_ },
other{ cb },
state{ state_t::none },
except{} {
}
template< typename T >
push_coroutine< T & >::control_block::~control_block() {
if ( state_t::none == ( state & state_t::complete) &&
state_t::none != ( state & state_t::unwind) ) {
// unwind coroutine stack
other->ctx = boost::context::execution_context::current();
ctx( context::exec_ontop_arg, unwind_coroutine);
}
}
template< typename T >
void
push_coroutine< T & >::control_block::deallocate() noexcept {
if ( state_t::none != ( state & state_t::unwind) ) {
destroy( this);
}
}
template< typename T >
void
push_coroutine< T & >::control_block::resume( T & t) {
other->ctx = boost::context::execution_context::current();
// pass an pointer to other context
ctx( const_cast< typename std::remove_const< T >::type * >( std::addressof( t) ) );
if ( except) {
std::rethrow_exception( except);
}
}
template< typename T >
bool
push_coroutine< T & >::control_block::valid() const noexcept {
return state_t::none == ( state & state_t::complete );
}
// push_coroutine< void >
inline
void
push_coroutine< void >::control_block::destroy( control_block * cb) noexcept {
boost::context::execution_context ctx = cb->ctx;
// destroy control structure
cb->state |= state_t::destroy;
cb->~control_block();
}
template< typename StackAllocator, typename Fn >
push_coroutine< void >::control_block::control_block( context::preallocated palloc, StackAllocator salloc, Fn && fn) :
#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS)
ctx{ std::allocator_arg, palloc, salloc,
wrap( [this]( typename std::decay< Fn >::type & fn_, boost::context::execution_context & ctx,
void * vp) mutable noexcept {
// create synthesized pull_coroutine< T >
typename pull_coroutine< void >::control_block synthesized_cb{ this, ctx };
pull_coroutine< void > synthesized{ & synthesized_cb };
other = & synthesized_cb;
if ( state_t::none == ( state & state_t::destroy) ) {
try {
// jump back to ctor
ctx();
auto fn = std::move( fn_);
// call coroutine-fn with synthesized pull_coroutine as argument
fn( synthesized);
} catch ( forced_unwind const&) {
// do nothing for unwinding exception
} catch (...) {
// store other exceptions in exception-pointer
except = std::current_exception();
}
}
// set termination flags
state |= state_t::complete;
// jump back to ctx
other->ctx();
BOOST_ASSERT_MSG( false, "push_coroutine is complete");
},
std::forward< Fn >( fn),
boost::context::execution_context::current() ) },
#else
ctx{ std::allocator_arg, palloc, salloc,
[this,fn_=decay_copy( std::forward< Fn >( fn) ),ctx=boost::context::execution_context::current()] (void *) mutable noexcept {
// create synthesized pull_coroutine< T >
typename pull_coroutine< void >::control_block synthesized_cb{ this, ctx };
pull_coroutine< void > synthesized{ & synthesized_cb };
other = & synthesized_cb;
if ( state_t::none == ( state & state_t::destroy) ) {
try {
// jump back to ctor
ctx();
auto fn = std::move( fn_);
// call coroutine-fn with synthesized pull_coroutine as argument
fn( synthesized);
} catch ( forced_unwind const&) {
// do nothing for unwinding exception
} catch (...) {
// store other exceptions in exception-pointer
except = std::current_exception();
}
}
// set termination flags
state |= state_t::complete;
// jump back to ctx
other->ctx();
BOOST_ASSERT_MSG( false, "push_coroutine is complete");
}},
#endif
other{ nullptr },
state{ state_t::unwind },
except{} {
// enter coroutine-fn in order to get other set
ctx();
}
inline
push_coroutine< void >::control_block::control_block( pull_coroutine< void >::control_block * cb,
boost::context::execution_context const& ctx_) noexcept :
ctx{ ctx_ },
other{ cb },
state{ state_t::none },
except{} {
}
inline
push_coroutine< void >::control_block::~control_block() {
if ( state_t::none == ( state & state_t::complete) &&
state_t::none != ( state & state_t::unwind) ) {
// unwind coroutine stack
other->ctx = boost::context::execution_context::current();
ctx( context::exec_ontop_arg, unwind_coroutine);
}
}
inline
void
push_coroutine< void >::control_block::deallocate() noexcept {
if ( state_t::none != ( state & state_t::unwind) ) {
destroy( this);
}
}
inline
void
push_coroutine< void >::control_block::resume() {
other->ctx = boost::context::execution_context::current();
ctx();
if ( except) {
std::rethrow_exception( except);
}
}
inline
bool
push_coroutine< void >::control_block::valid() const noexcept {
return state_t::none == ( state & state_t::complete );
}
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_COROUTINES2_DETAIL_PUSH_CONTROL_BLOCK_IPP

View File

@ -11,11 +11,7 @@
#include <boost/config.hpp> #include <boost/config.hpp>
#include <boost/context/detail/invoke.hpp> #include <boost/context/detail/invoke.hpp>
#if (BOOST_EXECUTION_CONTEXT==1)
# include <boost/context/execution_context.hpp>
#else
#include <boost/context/continuation.hpp> #include <boost/context/continuation.hpp>
#endif
#include <boost/fiber/detail/config.hpp> #include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/data.hpp> #include <boost/fiber/detail/data.hpp>
@ -28,45 +24,6 @@ namespace boost {
namespace coroutines2 { namespace coroutines2 {
namespace detail { namespace detail {
#if (BOOST_EXECUTION_CONTEXT==1)
template< typename Fn1, typename Fn2 >
class wrapper {
private:
typename std::decay< Fn1 >::type fn1_;
typename std::decay< Fn2 >::type fn2_;
boost::context::execution_context ctx_;
public:
wrapper( Fn1 && fn1, Fn2 && fn2,
boost::context::execution_context const& ctx) :
fn1_( std::move( fn1) ),
fn2_( std::move( fn2) ),
ctx_{ ctx } {
}
wrapper( wrapper const&) = delete;
wrapper & operator=( wrapper const&) = delete;
wrapper( wrapper && other) = default;
wrapper & operator=( wrapper && other) = default;
void operator()( void * vp) {
boost::context::detail::invoke(
std::move( fn1_),
fn2_, ctx_, vp);
}
};
template< typename Fn1, typename Fn2 >
wrapper< Fn1, Fn2 >
wrap( Fn1 && fn1, Fn2 && fn2,
boost::context::execution_context const& ctx) {
return wrapper< Fn1, Fn2 >(
std::forward< Fn1 >( fn1),
std::forward< Fn2 >( fn2),
ctx);
}
#else
template< typename Fn1, typename Fn2 > template< typename Fn1, typename Fn2 >
class wrapper { class wrapper {
private: private:
@ -101,7 +58,6 @@ wrap( Fn1 && fn1, Fn2 && fn2) {
std::forward< Fn1 >( fn1), std::forward< Fn1 >( fn1),
std::forward< Fn2 >( fn2) ); std::forward< Fn2 >( fn2) );
} }
#endif
}}} }}}

View File

@ -61,17 +61,7 @@ alias sources
explicit sources ; explicit sources ;
exe performance_create_protected exe performance
: sources : sources
performance_create_protected.cpp performance.cpp
;
exe performance_create_standard
: sources
performance_create_standard.cpp
;
exe performance_switch
: sources
performance_switch.cpp
; ;

View File

@ -7,6 +7,7 @@
#include <cstdlib> #include <cstdlib>
#include <iostream> #include <iostream>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <boost/chrono.hpp> #include <boost/chrono.hpp>
#include <boost/coroutine2/all.hpp> #include <boost/coroutine2/all.hpp>
@ -17,57 +18,52 @@
#include "clock.hpp" #include "clock.hpp"
#include "cycle.hpp" #include "cycle.hpp"
typedef boost::coroutines2::fixedsize_stack stack_allocator;
typedef boost::coroutines2::coroutine< void > coro_type;
boost::uint64_t jobs = 1000; boost::uint64_t jobs = 1000;
void fn( coro_type::pull_type & c) void fn( boost::coroutines2::coroutine< void >::push_type & c) {
{ while ( true) c(); } while ( true) {
c();
duration_type measure_time( duration_type overhead) }
{ }
stack_allocator stack_alloc;
duration_type measure_time_void( duration_type overhead) {
boost::coroutines2::coroutine< void >::pull_type c{ fn };
time_point_type start( clock_type::now() ); time_point_type start( clock_type::now() );
for ( std::size_t i = 0; i < jobs; ++i) { for ( std::size_t i = 0; i < jobs; ++i) {
coro_type::push_type c( stack_alloc, fn); c();
} }
duration_type total = clock_type::now() - start; duration_type total = clock_type::now() - start;
total -= overhead_clock(); // overhead of measurement total -= overhead_clock(); // overhead of measurement
total /= jobs; // loops total /= jobs; // loops
total /= 2; // 2x jump_fcontext
return total; return total;
} }
# ifdef BOOST_CONTEXT_CYCLE # ifdef BOOST_CONTEXT_CYCLE
cycle_type measure_cycles( cycle_type overhead) cycle_type measure_cycles_void( cycle_type overhead) {
{ boost::coroutines2::coroutine< void >::pull_type c{ fn };
stack_allocator stack_alloc;
cycle_type start( cycles() ); cycle_type start( cycles() );
for ( std::size_t i = 0; i < jobs; ++i) { for ( std::size_t i = 0; i < jobs; ++i) {
coro_type::push_type c( stack_alloc, fn); c();
} }
cycle_type total = cycles() - start; cycle_type total = cycles() - start;
total -= overhead; // overhead of measurement total -= overhead; // overhead of measurement
total /= jobs; // loops total /= jobs; // loops
total /= 2; // 2x jump_fcontext
return total; return total;
} }
# endif # endif
int main( int argc, char * argv[]) int main( int argc, char * argv[]) {
{ try {
try
{
bool bind = false; bool bind = false;
boost::program_options::options_description desc("allowed options"); boost::program_options::options_description desc("allowed options");
desc.add_options() desc.add_options()
("help", "help message") ("help", "help message")
("bind,b", boost::program_options::value< bool >( & bind), "bind thread to CPU") ("bind,b", boost::program_options::value< bool >( & bind), "bind thread to CPU")
("jobs,j", boost::program_options::value< boost::uint64_t >( & jobs), "jobs to run"); ("jobs,j", boost::program_options::value< boost::uint64_t >( & jobs), "jobs to run");
boost::program_options::variables_map vm; boost::program_options::variables_map vm;
boost::program_options::store( boost::program_options::store(
boost::program_options::parse_command_line( boost::program_options::parse_command_line(
@ -76,28 +72,27 @@ int main( int argc, char * argv[])
desc), desc),
vm); vm);
boost::program_options::notify( vm); boost::program_options::notify( vm);
if ( vm.count("help") ) { if ( vm.count("help") ) {
std::cout << desc << std::endl; std::cout << desc << std::endl;
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
if ( bind) {
if ( bind) bind_to_processor( 0); bind_to_processor( 0);
}
duration_type overhead_c = overhead_clock(); duration_type overhead_c = overhead_clock();
boost::uint64_t res = measure_time( overhead_c).count(); boost::uint64_t res = measure_time_void( overhead_c).count();
std::cout << "average of " << res << " nano seconds" << std::endl; std::cout << "average of " << res << " nano seconds" << std::endl;
#ifdef BOOST_CONTEXT_CYCLE #ifdef BOOST_CONTEXT_CYCLE
cycle_type overhead_y = overhead_cycle(); cycle_type overhead_y = overhead_cycle();
res = measure_cycles( overhead_y); res = measure_cycles_void( overhead_y);
std::cout << "average of " << res << " cpu cycles" << std::endl; std::cout << "average of " << res << " cpu cycles" << std::endl;
#endif #endif
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
catch ( std::exception const& e) catch ( std::exception const& e) {
{ std::cerr << "exception: " << e.what() << std::endl; } std::cerr << "exception: " << e.what() << std::endl;
catch (...) } catch (...) {
{ std::cerr << "unhandled exception" << std::endl; } std::cerr << "unhandled exception" << std::endl;
}
return EXIT_FAILURE; return EXIT_FAILURE;
} }

View File

@ -1,103 +0,0 @@
// Copyright Oliver Kowalke 2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <cstdlib>
#include <iostream>
#include <stdexcept>
#include <boost/chrono.hpp>
#include <boost/coroutine2/all.hpp>
#include <boost/cstdint.hpp>
#include <boost/program_options.hpp>
#include "bind_processor.hpp"
#include "clock.hpp"
#include "cycle.hpp"
typedef boost::coroutines2::protected_fixedsize_stack stack_allocator;
typedef boost::coroutines2::coroutine< void > coro_type;
boost::uint64_t jobs = 1000;
void fn( coro_type::pull_type & c)
{ while ( true) c(); }
duration_type measure_time( duration_type overhead)
{
stack_allocator stack_alloc;
time_point_type start( clock_type::now() );
for ( std::size_t i = 0; i < jobs; ++i) {
coro_type::push_type c( stack_alloc, fn);
}
duration_type total = clock_type::now() - start;
total -= overhead_clock(); // overhead of measurement
total /= jobs; // loops
return total;
}
# ifdef BOOST_CONTEXT_CYCLE
cycle_type measure_cycles( cycle_type overhead)
{
stack_allocator stack_alloc;
cycle_type start( cycles() );
for ( std::size_t i = 0; i < jobs; ++i) {
coro_type::push_type c( stack_alloc, fn);
}
cycle_type total = cycles() - start;
total -= overhead; // overhead of measurement
total /= jobs; // loops
return total;
}
# endif
int main( int argc, char * argv[])
{
try
{
bool bind = false;
boost::program_options::options_description desc("allowed options");
desc.add_options()
("help", "help message")
("bind,b", boost::program_options::value< bool >( & bind), "bind thread to CPU")
("jobs,j", boost::program_options::value< boost::uint64_t >( & jobs), "jobs to run");
boost::program_options::variables_map vm;
boost::program_options::store(
boost::program_options::parse_command_line(
argc,
argv,
desc),
vm);
boost::program_options::notify( vm);
if ( vm.count("help") ) {
std::cout << desc << std::endl;
return EXIT_SUCCESS;
}
if ( bind) bind_to_processor( 0);
duration_type overhead_c = overhead_clock();
boost::uint64_t res = measure_time( overhead_c).count();
std::cout << "average of " << res << " nano seconds" << std::endl;
#ifdef BOOST_CONTEXT_CYCLE
cycle_type overhead_y = overhead_cycle();
res = measure_cycles( overhead_y);
std::cout << "average of " << res << " cpu cycles" << std::endl;
#endif
return EXIT_SUCCESS;
}
catch ( std::exception const& e)
{ std::cerr << "exception: " << e.what() << std::endl; }
catch (...)
{ std::cerr << "unhandled exception" << std::endl; }
return EXIT_FAILURE;
}

View File

@ -1,194 +0,0 @@
// Copyright Oliver Kowalke 2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <cstdlib>
#include <iostream>
#include <stdexcept>
#include <string>
#include <boost/chrono.hpp>
#include <boost/coroutine2/all.hpp>
#include <boost/cstdint.hpp>
#include <boost/program_options.hpp>
#include "bind_processor.hpp"
#include "clock.hpp"
#include "cycle.hpp"
boost::uint64_t jobs = 1000;
struct X
{
std::string str;
X( std::string const& str_) :
str( str_)
{}
};
const X x("abc");
void fn_void( boost::coroutines2::coroutine< void >::push_type & c)
{ while ( true) c(); }
void fn_int( boost::coroutines2::coroutine< int >::push_type & c)
{ while ( true) c( 7); }
void fn_x( boost::coroutines2::coroutine< X >::push_type & c)
{
while ( true) c( x);
}
duration_type measure_time_void( duration_type overhead)
{
boost::coroutines2::coroutine< void >::pull_type c( fn_void);
time_point_type start( clock_type::now() );
for ( std::size_t i = 0; i < jobs; ++i) {
c();
}
duration_type total = clock_type::now() - start;
total -= overhead_clock(); // overhead of measurement
total /= jobs; // loops
total /= 2; // 2x jump_fcontext
return total;
}
duration_type measure_time_int( duration_type overhead)
{
boost::coroutines2::coroutine< int >::pull_type c( fn_int);
time_point_type start( clock_type::now() );
for ( std::size_t i = 0; i < jobs; ++i) {
c();
}
duration_type total = clock_type::now() - start;
total -= overhead_clock(); // overhead of measurement
total /= jobs; // loops
total /= 2; // 2x jump_fcontext
return total;
}
duration_type measure_time_x( duration_type overhead)
{
boost::coroutines2::coroutine< X >::pull_type c( fn_x);
time_point_type start( clock_type::now() );
for ( std::size_t i = 0; i < jobs; ++i) {
c();
}
duration_type total = clock_type::now() - start;
total -= overhead_clock(); // overhead of measurement
total /= jobs; // loops
total /= 2; // 2x jump_fcontext
return total;
}
# ifdef BOOST_CONTEXT_CYCLE
cycle_type measure_cycles_void( cycle_type overhead)
{
boost::coroutines2::coroutine< void >::pull_type c( fn_void);
cycle_type start( cycles() );
for ( std::size_t i = 0; i < jobs; ++i) {
c();
}
cycle_type total = cycles() - start;
total -= overhead; // overhead of measurement
total /= jobs; // loops
total /= 2; // 2x jump_fcontext
return total;
}
cycle_type measure_cycles_int( cycle_type overhead)
{
boost::coroutines2::coroutine< int >::pull_type c( fn_int);
cycle_type start( cycles() );
for ( std::size_t i = 0; i < jobs; ++i) {
c();
}
cycle_type total = cycles() - start;
total -= overhead; // overhead of measurement
total /= jobs; // loops
total /= 2; // 2x jump_fcontext
return total;
}
cycle_type measure_cycles_x( cycle_type overhead)
{
boost::coroutines2::coroutine< X >::pull_type c( fn_x);
cycle_type start( cycles() );
for ( std::size_t i = 0; i < jobs; ++i) {
c();
}
cycle_type total = cycles() - start;
total -= overhead; // overhead of measurement
total /= jobs; // loops
total /= 2; // 2x jump_fcontext
return total;
}
# endif
int main( int argc, char * argv[])
{
try
{
bool bind = false;
boost::program_options::options_description desc("allowed options");
desc.add_options()
("help", "help message")
("bind,b", boost::program_options::value< bool >( & bind), "bind thread to CPU")
("jobs,j", boost::program_options::value< boost::uint64_t >( & jobs), "jobs to run");
boost::program_options::variables_map vm;
boost::program_options::store(
boost::program_options::parse_command_line(
argc,
argv,
desc),
vm);
boost::program_options::notify( vm);
if ( vm.count("help") ) {
std::cout << desc << std::endl;
return EXIT_SUCCESS;
}
if ( bind) bind_to_processor( 0);
duration_type overhead_c = overhead_clock();
boost::uint64_t res = measure_time_void( overhead_c).count();
std::cout << "void: average of " << res << " nano seconds" << std::endl;
res = measure_time_int( overhead_c).count();
std::cout << "int: average of " << res << " nano seconds" << std::endl;
res = measure_time_x( overhead_c).count();
std::cout << "X: average of " << res << " nano seconds" << std::endl;
#ifdef BOOST_CONTEXT_CYCLE
cycle_type overhead_y = overhead_cycle();
res = measure_cycles_void( overhead_y);
std::cout << "void: average of " << res << " cpu cycles" << std::endl;
res = measure_cycles_int( overhead_y);
std::cout << "int: average of " << res << " cpu cycles" << std::endl;
res = measure_cycles_x( overhead_y);
std::cout << "X: average of " << res << " cpu cycles" << std::endl;
#endif
return EXIT_SUCCESS;
}
catch ( std::exception const& e)
{ std::cerr << "exception: " << e.what() << std::endl; }
catch (...)
{ std::cerr << "unhandled exception" << std::endl; }
return EXIT_FAILURE;
}