mirror of
https://github.com/boostorg/coroutine2.git
synced 2025-05-09 15:14:01 +00:00
call/cc supports ucontext_t and WinFiber
This commit is contained in:
parent
c588717142
commit
bcb2b415ce
@ -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]
|
@ -46,7 +46,7 @@
|
||||
[def __coro_ns__ ['boost::coroutines2]]
|
||||
[def __econtext__ ['execution_context]]
|
||||
[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 __fixedsize__ ['fixedsize_stack]]
|
||||
[def __forced_unwind__ ['detail::forced_unwind]]
|
||||
@ -73,6 +73,9 @@
|
||||
[def __standard_allocator__ ['fixedsize]]
|
||||
[def __start__ ['session::start()]]
|
||||
[def __terminate__ ['std::terminate()]]
|
||||
[def __ucontext__ ['ucontext_t]]
|
||||
[def __winfib__ ['WinFiber]]
|
||||
[def __cc__ ['call/cc]]
|
||||
[def __underflow__ ['stream_buf::underflow()]]
|
||||
[def __yield_context__ ['boost::asio::yield_context]]
|
||||
|
||||
|
@ -57,7 +57,7 @@ stack (macro SIGSTKSZ on POSIX).
|
||||
At this time of writing only GCC (4.7)
|
||||
[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__
|
||||
provides support for segmented stacks.
|
||||
provides support for [link segmented ['segmented stacks]].
|
||||
|
||||
The destructor releases the associated stack. The implementer is free to
|
||||
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
|
||||
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__.
|
||||
|
||||
[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>::pull_type& yield){
|
||||
coro();
|
||||
yield();
|
||||
});
|
||||
coro();
|
||||
|
||||
|
||||
[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]
|
||||
|
@ -7,24 +7,18 @@
|
||||
|
||||
[section:performance Performance]
|
||||
|
||||
Performance of __boost_coroutine__ was measured on the platforms shown in the
|
||||
following table. Performance measurements were taken using `rdtsc` and
|
||||
`boost::chrono::high_resolution_clock`, with overhead corrections, on x86
|
||||
platforms. In each case, cache warm-up was accounted for, and the one
|
||||
running thread was pinned to a single CPU.
|
||||
Performance measurements were taken using `std::chrono::highresolution_clock`,
|
||||
with overhead corrections.
|
||||
The code was compiled with gcc-6.3.1, using build options:
|
||||
variant = release, optimization = speed.
|
||||
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]
|
||||
[switch]
|
||||
[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]
|
||||
[26 ns / 56 CPU cycles]
|
||||
[542 ns / 1146 CPU cycles]
|
||||
]
|
||||
]
|
||||
|
||||
|
@ -183,6 +183,7 @@ address of the stack.]]
|
||||
[endsect]
|
||||
|
||||
|
||||
[#segmented]
|
||||
[section:segmented Class ['segmented_stack]]
|
||||
|
||||
__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.
|
||||
|
||||
[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
|
||||
__segmented_stack__ __boost_coroutine__ must be built with
|
||||
property `segmented-stacks`, e.g. [*toolset=gcc segmented-stacks=on] at b2/bjam
|
||||
[*4.7] [*clang] from version [*3.4] onwards. In order to use a
|
||||
__segmented_stack__ __boost_context__ must be built with
|
||||
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.]
|
||||
|
||||
|
||||
#include <boost/coroutine2/segmented_stack.hpp>
|
||||
|
||||
struct segmented_stack {
|
||||
|
@ -39,3 +39,7 @@ exe layout
|
||||
exe parser
|
||||
: parser.cpp
|
||||
;
|
||||
|
||||
exe segmented
|
||||
: segmented.cpp
|
||||
;
|
||||
|
48
example/segmented.cpp
Normal file
48
example/segmented.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
|
||||
// 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 <memory>
|
||||
|
||||
#include <boost/coroutine2/all.hpp>
|
||||
|
||||
#ifdef BOOST_MSVC //MS VisualStudio
|
||||
__declspec(noinline) void access( char *buf);
|
||||
#else // GCC
|
||||
void access( char *buf) __attribute__ ((noinline));
|
||||
#endif
|
||||
void access( char *buf) {
|
||||
buf[0] = '\0';
|
||||
}
|
||||
|
||||
void bar( int i) {
|
||||
char buf[4 * 1024];
|
||||
if ( i > 0) {
|
||||
access( buf);
|
||||
std::cout << i << ". iteration" << std::endl;
|
||||
bar( i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
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 << "initial stack size = " << boost::context::segmented_stack::traits_type::default_size() / 1024 << "kB" << std::endl;
|
||||
std::cout << "application should not fail" << std::endl;
|
||||
#else
|
||||
std::cout << "using standard stacks: allocates " << count << " * 4kB == " << 4 * count << "kB on stack, ";
|
||||
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);
|
||||
}};
|
||||
std::cout << "main: done" << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
@ -29,26 +29,14 @@ class push_coroutine;
|
||||
#include <boost/coroutine2/detail/pull_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/push_control_block_cc.hpp>
|
||||
# endif
|
||||
#include <boost/coroutine2/detail/pull_control_block_cc.hpp>
|
||||
#include <boost/coroutine2/detail/push_control_block_cc.hpp>
|
||||
|
||||
# include <boost/coroutine2/detail/pull_coroutine.ipp>
|
||||
# include <boost/coroutine2/detail/push_coroutine.ipp>
|
||||
#include <boost/coroutine2/detail/pull_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/push_control_block_cc.ipp>
|
||||
# endif
|
||||
#endif
|
||||
#include <boost/coroutine2/detail/pull_control_block_cc.ipp>
|
||||
#include <boost/coroutine2/detail/push_control_block_cc.ipp>
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_SUFFIX
|
||||
|
@ -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();
|
||||
|
||||
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();
|
||||
|
||||
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();
|
||||
|
||||
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
|
@ -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
|
@ -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();
|
||||
|
||||
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();
|
||||
|
||||
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();
|
||||
|
||||
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
|
@ -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
|
@ -11,11 +11,7 @@
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/context/detail/invoke.hpp>
|
||||
#if (BOOST_EXECUTION_CONTEXT==1)
|
||||
# include <boost/context/execution_context.hpp>
|
||||
#else
|
||||
# include <boost/context/continuation.hpp>
|
||||
#endif
|
||||
#include <boost/context/continuation.hpp>
|
||||
|
||||
#include <boost/fiber/detail/config.hpp>
|
||||
#include <boost/fiber/detail/data.hpp>
|
||||
@ -28,45 +24,6 @@ namespace boost {
|
||||
namespace coroutines2 {
|
||||
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 >
|
||||
class wrapper {
|
||||
private:
|
||||
@ -101,7 +58,6 @@ wrap( Fn1 && fn1, Fn2 && fn2) {
|
||||
std::forward< Fn1 >( fn1),
|
||||
std::forward< Fn2 >( fn2) );
|
||||
}
|
||||
#endif
|
||||
|
||||
}}}
|
||||
|
||||
|
@ -61,17 +61,7 @@ alias sources
|
||||
|
||||
explicit sources ;
|
||||
|
||||
exe performance_create_protected
|
||||
exe performance
|
||||
: sources
|
||||
performance_create_protected.cpp
|
||||
;
|
||||
|
||||
exe performance_create_standard
|
||||
: sources
|
||||
performance_create_standard.cpp
|
||||
;
|
||||
|
||||
exe performance_switch
|
||||
: sources
|
||||
performance_switch.cpp
|
||||
performance.cpp
|
||||
;
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include <boost/chrono.hpp>
|
||||
#include <boost/coroutine2/all.hpp>
|
||||
@ -17,57 +18,52 @@
|
||||
#include "clock.hpp"
|
||||
#include "cycle.hpp"
|
||||
|
||||
typedef boost::coroutines2::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;
|
||||
void fn( boost::coroutines2::coroutine< void >::push_type & c) {
|
||||
while ( true) {
|
||||
c();
|
||||
}
|
||||
}
|
||||
|
||||
duration_type measure_time_void( duration_type overhead) {
|
||||
boost::coroutines2::coroutine< void >::pull_type c{ fn };
|
||||
time_point_type start( clock_type::now() );
|
||||
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;
|
||||
total -= overhead_clock(); // overhead of measurement
|
||||
total /= jobs; // loops
|
||||
total /= 2; // 2x jump_fcontext
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
# ifdef BOOST_CONTEXT_CYCLE
|
||||
cycle_type measure_cycles( cycle_type overhead)
|
||||
{
|
||||
stack_allocator stack_alloc;
|
||||
|
||||
cycle_type measure_cycles_void( cycle_type overhead) {
|
||||
boost::coroutines2::coroutine< void >::pull_type c{ fn };
|
||||
cycle_type start( cycles() );
|
||||
for ( std::size_t i = 0; i < jobs; ++i) {
|
||||
coro_type::push_type c( stack_alloc, fn);
|
||||
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
|
||||
{
|
||||
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(
|
||||
@ -76,28 +72,27 @@ int main( int argc, char * 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);
|
||||
|
||||
if ( bind) {
|
||||
bind_to_processor( 0);
|
||||
}
|
||||
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;
|
||||
#ifdef BOOST_CONTEXT_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;
|
||||
#endif
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
catch ( std::exception const& e)
|
||||
{ std::cerr << "exception: " << e.what() << std::endl; }
|
||||
catch (...)
|
||||
{ std::cerr << "unhandled exception" << std::endl; }
|
||||
catch ( std::exception const& e) {
|
||||
std::cerr << "exception: " << e.what() << std::endl;
|
||||
} catch (...) {
|
||||
std::cerr << "unhandled exception" << std::endl;
|
||||
}
|
||||
return EXIT_FAILURE;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user