2
0
mirror of https://github.com/boostorg/context.git synced 2026-01-19 04:02:17 +00:00

support ucontext_t in callcc()

This commit is contained in:
Oliver Kowalke
2017-05-15 21:29:41 +02:00
parent 50ebf5bd11
commit 4b8a730919
21 changed files with 1419 additions and 595 deletions

View File

@@ -25,6 +25,9 @@ feature.compose <valgrind>on : <define>BOOST_USE_VALGRIND ;
feature.feature context-switch : cc ec : optional propagated composite ;
feature.compose <context-switch>ec : <define>BOOST_USE_EXECUTION_CONTEXT ;
feature.feature context : ucontext : optional propagated composite ;
feature.compose <context>ucontext : <define>BOOST_USE_UCONTEXT ;
project boost/context
: requirements
<toolset>gcc,<segmented-stacks>on:<cxxflags>-fsplit-stack

View File

@@ -7,7 +7,8 @@
[section:architectures Architectures]
__boost_context__ supports following architectures:
__boost_context__, using [link implementation ['fcontext_t]], supports following
architectures:
[table Supported architectures (<ABI|binary format>)
[[Architecture] [LINUX (UNIX)] [Windows] [MacOS X] [iOS]]
@@ -21,6 +22,10 @@ __boost_context__ supports following architectures:
[[x86_64] [SYSV,X32|ELF] [MS|PE] [SYSV|MACH-O] [-]]
]
[note If the architecture is not supported but the platform provides
[link implementation __ucontext__], __boost_context__ should be
compiled with `BOOST_USE_UCONTEXT` and b2 property `context=ucontext`.]
[section:crosscompiling Cross compiling]
Cross compiling the library requires to specify the build properties

View File

@@ -14,7 +14,7 @@ continuation as a first-class object and pass it as an argument to another
continuation.
A continuation (abstract concept of functional programming languages)
represents the state of the cotnrol flow of a program at a given point in time.
represents the state of the control flow of a program at a given point in time.
Continuations can be suspended and resumed later in order to change the control
flow of a program.
@@ -52,7 +52,9 @@ not yet returned, the stack is traversed in order to access the control
structure (address stored at the first stack frame) and continuation's stack is
deallocated via the __stack_allocator__.
[important Segmented stacks are not supported by __callcc__.]
[note [link segmented ['Segmented stacks]] are supported by __cc__ using
[link implementation ['ucontext_t]].]
[heading __con__]
@@ -451,6 +453,24 @@ parser (via callback).
The data (character) is transferred between the two continuations.
[#implementation]
[heading Implementation: fcontext_t/ucontext_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 magnitutes of order less CPU cycles; see section
[link performance ['performance]]).
As an alternative, [@https://en.wikipedia.org/wiki/Setcontext __ucontext__]
can be used by compiling with `BOOST_USE_UCONTEXT` and b2 property `context=ucontext`.
__ucontext__ might be available on a broader range of POSIX-platforms but has
some [link ucontext ['disadvantages]] (for instance deprecated snce POSIX.1-2003).
[link segmented ['Segmented stacks]] are supported by __cc__ only with
__ucontext__ as its implementation.
[heading Class `continuation`]
#include <boost/context/continuation.hpp>

View File

@@ -134,7 +134,8 @@
[def __fixedsize__ ['fixedsize_stack]]
[def __pooled_fixedsize__ ['pooled_fixedsize_stack]]
[def __protected_fixedsize__ ['protected_fixedsize_stack]]
[def __segmented__ ['segmented_stack]]
[def __segmented__ [link segmented ['segmented_stack]]]
[def __segmented_stack__ ['segmented_stack]]
[def __stack_context__ ['stack_context]]
[def __fls_alloc__ ['::FlsAlloc()]]

View File

@@ -14,7 +14,7 @@
at compilers command-line in order to use __econtext__ (v1).]
[note Segmented stacks (['segmented-stacks=on]), e.g. on demand growing stacks,
can only be used with __econtext__ (v1).]
can be used with __econtext__ (v1).]
Class __econtext__ encapsulates context switching and manages the associated
context' stack (allocation/deallocation).

View File

@@ -11,23 +11,24 @@ __boost_context__ is a foundational library that provides a sort of cooperative
multitasking on a single thread. By providing an abstraction of the current
execution state in the current thread, including the stack (with local
variables) and stack pointer, all registers and CPU flags, and the instruction
pointer, a __econtext__ represents a specific point in the application's
pointer, a execution context represents a specific point in the application's
execution path. This is useful for building higher-level abstractions, like
__coroutines__, __coop_threads__ or an equivalent to
[@http://msdn.microsoft.com/en-us/library/9k7k7cf0%28v=vs.80%29.aspx C# keyword __yield__]
in C++.
__econtext__ provides the means to suspend the current execution path and to
__cc__/__con__ provides the means to suspend the current execution path and to
transfer execution control, thereby permitting another context to run on the
current thread. This state full transfer mechanism enables a context to suspend
execution from within nested functions and, later, to resume from where it was
suspended. While the execution path represented by a __econtext__ only runs on a
suspended. While the execution path represented by a __con__ only runs on a
single thread, it can be migrated to another thread at any given time.
A context switch between threads requires system calls (involving the OS
kernel), which can cost more than thousand CPU cycles on x86 CPUs. By contrast,
transferring control among them requires only few CPU cycles because it does not
involve system calls as it is done within a single thread.
A [@http://en.wikipedia.org/wiki/Context_switch context switch] between threads
requires system calls (involving the OS kernel), which can cost more than
thousand CPU cycles on x86 CPUs. By contrast, transferring control vias
__cc__/__con__ requires only few CPU cycles because it does not involve system
calls as it is done within a single thread.
In order to use the classes and functions described here, you can either include
the specific headers specified by the descriptions of each class or function, or

View File

@@ -5,23 +5,21 @@
http://www.boost.org/LICENSE_1_0.txt
]
[#performance]
[section:performance Performance]
Performance of __boost_context__ 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. The code was compiled using the build options,
'variant = release cxxflags = -DBOOST_DISABLE_ASSERTS'.
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 context switch
[[Platform] [ucontext_t] [execution_context (v1)] [execution_context (v2)]]
[[callcc()/continuation (fcontext_t)] [callcc()/continuation (ucontext_t)]]
[
[x86_64 [footnote Intel Core i7-4770S 3.10GHz]]
[547 ns / 1433 cycles]
[30 ns / 80 cycles]
[8 ns / 25 cycles]
[18 ns / 37 CPU cycles]
[547 ns / 1130 CPU cycles]
]
]

View File

@@ -39,9 +39,10 @@ into a function which was exited via a call to ['longjmp()] is undefined
[footnote ISO/IEC 9899:1999, 2005, 7.13.2.1:2].
[#ucontext]
[heading ucontext_t]
Since POSIX.1-2003 `ucontext_t` is deprecated and was removed in POSIX.1-2008!
Since POSIX.1-2004 `ucontext_t` is deprecated and was removed in POSIX.1-2008!
The function signature of `makecontext()` is:
void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);
@@ -56,8 +57,9 @@ in var-arg list is not guaranteed to work, especially it will fail for
architectures where pointers are larger than integers.
`ucontext_t` preserves signal mask between context switches which involves system
calls consuming a lot of CPU cycles (ucontext_t is slower by
perfomance_link[factor 13x] relative to `fcontext_t`).
calls consuming a lot of CPU cycles (ucontext_t is slower; a context switch
takes [link performance ['two magnitutes of order more CPU cycles]] more than
__fcontext__).
[heading Windows fibers]

View File

@@ -7,10 +7,12 @@
[section:requirements Requirements]
__boost_context__ must be built for the particular compiler(s) and CPU
architecture(s)s being targeted. __boost_context__ includes assembly code and,
therefore, requires GNU as and GNU preprocesspr for supported POSIX systems,
MASM for Windows/x86 systems and ARMasm for Windows/arm systems.
If __boost_context__ uses fcontext_t (the default) as its implementation,
it must be built for the particular compiler(s) and CPU architecture(s)
being targeted.
Using [link implementation ['fcontext_t]], __boost_context__ includes assembly
code and, therefore, requires GNU as and GNU preprocessor for supported POSIX
systems, MASM for Windows/x86 systems and ARMasm for Windows/arm systems.
[note MASM64 (ml64.exe) is a part of Microsoft's Windows Driver Kit.]

View File

@@ -199,6 +199,7 @@ address of the stack.]]
[endsect]
[#segmented]
[section:segmented Class ['segmented_stack]]
__boost_context__ supports usage of a __segmented__, e. g. the size of
@@ -211,10 +212,11 @@ stack which grows on demand.
[note Segmented stacks are currently only supported by [*gcc] from version
[*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] at b2/bjam
command line.]
property `segmented-stacks`, e.g. [*toolset=gcc segmented-stacks=on] and
applying `BOOST_USE_SEGMENTED_STACKS` at b2/bjam command line.]
[note Segmented stacks can only be used with __econtext__ (v1)].
[note Segmented stacks can only be used with __cc__ (using ucontext_t) and
__econtext__ (v1)].
#include <boost/context/segmented_stack.hpp>

View File

@@ -70,6 +70,10 @@ exe endless_loop
: endless_loop.cpp
;
exe segmented
: segmented.cpp
;
#exe backtrace
# : backtrace.cpp
# ;

View File

@@ -14,7 +14,7 @@ namespace ctx = boost::context;
ctx::continuation foo( ctx::continuation && c) {
do {
std::cout << "foo\n";
} while ( c = c.resume() );
} while ( ( c = c.resume() ) );
return std::move( c);
}
@@ -22,7 +22,7 @@ int main() {
ctx::continuation c = ctx::callcc( foo);
do {
std::cout << "bar\n";
} while ( c = c.resume() );
} while ( ( c = c.resume() ) );
std::cout << "main: done" << std::endl;
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,51 @@
// 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/context/all.hpp>
namespace ctx = boost::context;
#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
ctx::continuation c = ctx::callcc(
[count](ctx::continuation && c){
bar( count);
return std::move( c);
});
std::cout << "main: done" << std::endl;
return EXIT_SUCCESS;
}

View File

@@ -4,553 +4,8 @@
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_CONTEXT_CONTINUATION_H
#define BOOST_CONTEXT_CONTINUATION_H
#include <boost/context/detail/config.hpp>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <exception>
#include <functional>
#include <memory>
#include <ostream>
#include <tuple>
#include <utility>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/intrusive_ptr.hpp>
#if defined(BOOST_NO_CXX17_STD_APPLY)
#include <boost/context/detail/apply.hpp>
#endif
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
#include <boost/context/detail/exchange.hpp>
#endif
#if defined(BOOST_NO_CXX17_STD_INVOKE)
#include <boost/context/detail/invoke.hpp>
#endif
#include <boost/context/detail/disable_overload.hpp>
#include <boost/context/detail/exception.hpp>
#include <boost/context/detail/fcontext.hpp>
#include <boost/context/detail/tuple.hpp>
#include <boost/context/fixedsize_stack.hpp>
#include <boost/context/flags.hpp>
#include <boost/context/preallocated.hpp>
#include <boost/context/segmented_stack.hpp>
#include <boost/context/stack_context.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
#if defined(BOOST_MSVC)
# pragma warning(push)
# pragma warning(disable: 4702)
#endif
namespace boost {
namespace context {
namespace detail {
template< typename U >
struct helper {
template< typename T >
static T convert( T && t) noexcept {
return std::forward< T >( t);
}
};
template< typename U >
struct helper< std::tuple< U > > {
template< typename T >
static std::tuple< T > convert( T && t) noexcept {
return std::make_tuple( std::forward< T >( t) );
}
};
inline
transfer_t context_unwind( transfer_t t) {
throw forced_unwind( t.fctx);
return { nullptr, nullptr };
}
template< typename Rec >
transfer_t context_exit( transfer_t t) noexcept {
Rec * rec = static_cast< Rec * >( t.data);
// destroy context stack
rec->deallocate();
return { nullptr, nullptr };
}
template< typename Rec >
void context_entry( transfer_t t_) noexcept {
// transfer control structure to the context-stack
Rec * rec = static_cast< Rec * >( t_.data);
BOOST_ASSERT( nullptr != t_.fctx);
BOOST_ASSERT( nullptr != rec);
transfer_t t = { nullptr, nullptr };
try {
// jump back to `context_create()`
t = jump_fcontext( t_.fctx, nullptr);
// start executing
t = rec->run( t);
} catch ( forced_unwind const& e) {
t = { e.fctx, nullptr };
}
BOOST_ASSERT( nullptr != t.fctx);
// destroy context-stack of `this`context on next context
ontop_fcontext( t.fctx, rec, context_exit< Rec >);
BOOST_ASSERT_MSG( false, "context already terminated");
}
template<
typename Ctx,
typename StackAlloc,
typename Fn
>
class record {
private:
StackAlloc salloc_;
stack_context sctx_;
typename std::decay< Fn >::type fn_;
static void destroy( record * p) noexcept {
StackAlloc salloc = p->salloc_;
stack_context sctx = p->sctx_;
// deallocate record
p->~record();
// destroy stack with stack allocator
salloc.deallocate( sctx);
}
public:
record( stack_context sctx, StackAlloc const& salloc,
Fn && fn) noexcept :
salloc_( salloc),
sctx_( sctx),
fn_( std::forward< Fn >( fn) ) {
}
record( record const&) = delete;
record & operator=( record const&) = delete;
void deallocate() noexcept {
destroy( this);
}
transfer_t run( transfer_t t) {
Ctx from{ t };
// invoke context-function
#if defined(BOOST_NO_CXX17_STD_INVOKE)
Ctx cc = invoke( fn_, std::move( from) );
#if defined(BOOST_USE_UCONTEXT)
#include <boost/context/continuation_ucontext.hpp>
#else
Ctx cc = std::invoke( fn_, std::move( from) );
#include <boost/context/continuation_fcontext.hpp>
#endif
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
return { exchange( cc.t_.fctx, nullptr), nullptr };
#else
return { std::exchange( cc.t_.fctx, nullptr), nullptr };
#endif
}
};
template< typename Record, typename StackAlloc, typename Fn >
fcontext_t context_create( StackAlloc salloc, Fn && fn) {
auto sctx = salloc.allocate();
// reserve space for control structure
#if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
const std::size_t size = sctx.size - sizeof( Record);
void * sp = static_cast< char * >( sctx.sp) - sizeof( Record);
#else
constexpr std::size_t func_alignment = 64; // alignof( Record);
constexpr std::size_t func_size = sizeof( Record);
// reserve space on stack
void * sp = static_cast< char * >( sctx.sp) - func_size - func_alignment;
// align sp pointer
std::size_t space = func_size + func_alignment;
sp = std::align( func_alignment, func_size, sp, space);
BOOST_ASSERT( nullptr != sp);
// calculate remaining size
const std::size_t size = sctx.size - ( static_cast< char * >( sctx.sp) - static_cast< char * >( sp) );
#endif
// create fast-context
const fcontext_t fctx = make_fcontext( sp, size, & context_entry< Record >);
BOOST_ASSERT( nullptr != fctx);
// placment new for control structure on context-stack
auto rec = ::new ( sp) Record{
sctx, salloc, std::forward< Fn >( fn) };
// transfer control structure to context-stack
return jump_fcontext( fctx, rec).fctx;
}
template< typename Record, typename StackAlloc, typename Fn >
fcontext_t context_create( preallocated palloc, StackAlloc salloc, Fn && fn) {
// reserve space for control structure
#if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
const std::size_t size = palloc.size - sizeof( Record);
void * sp = static_cast< char * >( palloc.sp) - sizeof( Record);
#else
constexpr std::size_t func_alignment = 64; // alignof( Record);
constexpr std::size_t func_size = sizeof( Record);
// reserve space on stack
void * sp = static_cast< char * >( palloc.sp) - func_size - func_alignment;
// align sp pointer
std::size_t space = func_size + func_alignment;
sp = std::align( func_alignment, func_size, sp, space);
BOOST_ASSERT( nullptr != sp);
// calculate remaining size
const std::size_t size = palloc.size - ( static_cast< char * >( palloc.sp) - static_cast< char * >( sp) );
#endif
// create fast-context
const fcontext_t fctx = make_fcontext( sp, size, & context_entry< Record >);
BOOST_ASSERT( nullptr != fctx);
// placment new for control structure on context-stack
auto rec = ::new ( sp) Record{
palloc.sctx, salloc, std::forward< Fn >( fn) };
// transfer control structure to context-stack
return jump_fcontext( fctx, rec).fctx;
}
template< typename ... Arg >
struct result_type {
typedef std::tuple< Arg ... > type;
static
type get( detail::transfer_t & t) {
auto p = static_cast< std::tuple< Arg ... > * >( t.data);
return std::move( * p);
}
};
template< typename Arg >
struct result_type< Arg > {
typedef Arg type;
static
type get( detail::transfer_t & t) {
auto p = static_cast< std::tuple< Arg > * >( t.data);
return std::forward< Arg >( std::get< 0 >( * p) );
}
};
}
template< typename Ctx, typename Fn, typename Arg >
detail::transfer_t context_ontop( detail::transfer_t t) {
auto p = static_cast< Arg * >( t.data);
BOOST_ASSERT( nullptr != p);
typename std::decay< Fn >::type fn = std::forward< Fn >( std::get< 0 >( * p) );
t.data = & std::get< 1 >( * p);
Ctx c{ t };
// execute function, pass continuation via reference
typedef typename std::decay< decltype( std::get< 1 >( * p) )>::type Tpl;
std::get< 1 >( * p) = detail::helper< Tpl >::convert( fn( std::move( c) ) );
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
return { detail::exchange( c.t_.fctx, nullptr), & std::get< 1 >( * p) };
#else
return { std::exchange( c.t_.fctx, nullptr), & std::get< 1 >( * p) };
#endif
}
template< typename Ctx, typename Fn >
detail::transfer_t context_ontop_void( detail::transfer_t t) {
auto p = static_cast< std::tuple< Fn > * >( t.data);
BOOST_ASSERT( nullptr != p);
typename std::decay< Fn >::type fn = std::forward< Fn >( std::get< 0 >( * p) );
Ctx c{ t };
// execute function, pass continuation via reference
fn( std::move( c) );
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
return { detail::exchange( c.t_.fctx, nullptr), nullptr };
#else
return { std::exchange( c.t_.fctx, nullptr), nullptr };
#endif
}
class continuation {
private:
template< typename Ctx, typename StackAlloc, typename Fn >
friend class detail::record;
template< typename Ctx, typename Fn, typename Arg >
friend detail::transfer_t
context_ontop( detail::transfer_t);
template< typename Ctx, typename Fn >
friend detail::transfer_t
context_ontop_void( detail::transfer_t);
template< typename StackAlloc, typename Fn, typename ... Arg >
friend continuation
callcc( std::allocator_arg_t, StackAlloc, Fn &&, Arg ...);
template< typename StackAlloc, typename Fn, typename ... Arg >
friend continuation
callcc( std::allocator_arg_t, preallocated, StackAlloc, Fn &&, Arg ...);
template< typename StackAlloc, typename Fn >
friend continuation
callcc( std::allocator_arg_t, StackAlloc, Fn &&);
template< typename StackAlloc, typename Fn >
friend continuation
callcc( std::allocator_arg_t, preallocated, StackAlloc, Fn &&);
detail::transfer_t t_{ nullptr, nullptr };
continuation( detail::fcontext_t fctx) noexcept :
t_{ fctx, nullptr } {
}
continuation( detail::transfer_t t) noexcept :
t_{ t.fctx, t.data } {
}
public:
continuation() noexcept = default;
~continuation() {
if ( nullptr != t_.fctx) {
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::ontop_fcontext( detail::exchange( t_.fctx, nullptr), nullptr, detail::context_unwind);
#else
detail::ontop_fcontext( std::exchange( t_.fctx, nullptr), nullptr, detail::context_unwind);
#endif
}
}
continuation( continuation && other) noexcept :
t_{ other.t_.fctx, other.t_.data } {
other.t_ = { nullptr, nullptr };
}
continuation & operator=( continuation && other) noexcept {
if ( this != & other) {
continuation tmp = std::move( other);
swap( tmp);
}
return * this;
}
continuation( continuation const& other) noexcept = delete;
continuation & operator=( continuation const& other) noexcept = delete;
template< typename ... Arg >
continuation resume( Arg ... arg) {
BOOST_ASSERT( nullptr != t_.fctx);
auto tpl = std::make_tuple( std::forward< Arg >( arg) ... );
return detail::jump_fcontext(
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::exchange( t_.fctx, nullptr),
#else
std::exchange( t_.fctx, nullptr),
#endif
& tpl);
}
template< typename Fn, typename ... Arg >
continuation resume_with( Fn && fn, Arg ... arg) {
BOOST_ASSERT( nullptr != t_.fctx);
auto tpl = std::make_tuple( std::forward< Fn >( fn), std::make_tuple( std::forward< Arg >( arg) ... ));
return detail::ontop_fcontext(
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::exchange( t_.fctx, nullptr),
#else
std::exchange( t_.fctx, nullptr),
#endif
& tpl,
context_ontop< continuation, Fn, decltype(tpl) >);
}
continuation resume() {
BOOST_ASSERT( nullptr != t_.fctx);
return detail::jump_fcontext(
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::exchange( t_.fctx, nullptr),
#else
std::exchange( t_.fctx, nullptr),
#endif
nullptr);
}
template< typename Fn >
continuation resume_with( Fn && fn) {
BOOST_ASSERT( nullptr != t_.fctx);
auto p = std::make_tuple( std::forward< Fn >( fn) );
return detail::ontop_fcontext(
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::exchange( t_.fctx, nullptr),
#else
std::exchange( t_.fctx, nullptr),
#endif
& p,
context_ontop_void< continuation, Fn >);
}
bool data_available() noexcept {
return * this && nullptr != t_.data;
}
template< typename ... Arg >
typename detail::result_type< Arg ... >::type get_data() {
BOOST_ASSERT( nullptr != t_.data);
return detail::result_type< Arg ... >::get( t_);
}
explicit operator bool() const noexcept {
return nullptr != t_.fctx;
}
bool operator!() const noexcept {
return nullptr == t_.fctx;
}
bool operator==( continuation const& other) const noexcept {
return t_.fctx == other.t_.fctx;
}
bool operator!=( continuation const& other) const noexcept {
return t_.fctx != other.t_.fctx;
}
bool operator<( continuation const& other) const noexcept {
return t_.fctx < other.t_.fctx;
}
bool operator>( continuation const& other) const noexcept {
return other.t_.fctx < t_.fctx;
}
bool operator<=( continuation const& other) const noexcept {
return ! ( * this > other);
}
bool operator>=( continuation const& other) const noexcept {
return ! ( * this < other);
}
template< typename charT, class traitsT >
friend std::basic_ostream< charT, traitsT > &
operator<<( std::basic_ostream< charT, traitsT > & os, continuation const& other) {
if ( nullptr != other.t_.fctx) {
return os << other.t_.fctx;
} else {
return os << "{not-a-context}";
}
}
void swap( continuation & other) noexcept {
std::swap( t_, other.t_);
}
};
// Arg
template<
typename Fn,
typename ... Arg,
typename = detail::disable_overload< continuation, Fn >,
typename = detail::disable_overload< std::allocator_arg_t, Fn >
>
continuation
callcc( Fn && fn, Arg ... arg) {
return callcc(
std::allocator_arg, fixedsize_stack(),
std::forward< Fn >( fn), std::forward< Arg >( arg) ...);
}
template<
typename StackAlloc,
typename Fn,
typename ... Arg
>
continuation
callcc( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Arg ... arg) {
using Record = detail::record< continuation, StackAlloc, Fn >;
return continuation{
detail::context_create< Record >(
salloc, std::forward< Fn >( fn) ) }.resume(
std::forward< Arg >( arg) ... );
}
template<
typename StackAlloc,
typename Fn,
typename ... Arg
>
continuation
callcc( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn, Arg ... arg) {
using Record = detail::record< continuation, StackAlloc, Fn >;
return continuation{
detail::context_create< Record >(
palloc, salloc, std::forward< Fn >( fn) ) }.resume(
std::forward< Arg >( arg) ... );
}
// void
template<
typename Fn,
typename = detail::disable_overload< continuation, Fn >
>
continuation
callcc( Fn && fn) {
return callcc(
std::allocator_arg, fixedsize_stack(),
std::forward< Fn >( fn) );
}
template< typename StackAlloc, typename Fn >
continuation
callcc( std::allocator_arg_t, StackAlloc salloc, Fn && fn) {
using Record = detail::record< continuation, StackAlloc, Fn >;
return continuation{
detail::context_create< Record >(
salloc, std::forward< Fn >( fn) ) }.resume();
}
template< typename StackAlloc, typename Fn >
continuation
callcc( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn) {
using Record = detail::record< continuation, StackAlloc, Fn >;
return continuation{
detail::context_create< Record >(
palloc, salloc, std::forward< Fn >( fn) ) }.resume();
}
#if defined(BOOST_USE_SEGMENTED_STACKS)
template<
typename Fn,
typename ... Arg
>
continuation
callcc( std::allocator_arg_t, segmented_stack, Fn &&, Arg ...);
template<
typename StackAlloc,
typename Fn,
typename ... Arg
>
continuation
callcc( std::allocator_arg_t, preallocated, segmented_stack, Fn &&, Arg ...);
#endif
// swap
inline
void swap( continuation & l, continuation & r) noexcept {
l.swap( r);
}
}}
#if defined(BOOST_MSVC)
# pragma warning(pop)
#endif
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_CONTEXT_CONTINUATION_H

View File

@@ -0,0 +1,556 @@
// Copyright Oliver Kowalke 2017.
// 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_CONTEXT_CONTINUATION_H
#define BOOST_CONTEXT_CONTINUATION_H
#include <boost/context/detail/config.hpp>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <exception>
#include <functional>
#include <memory>
#include <ostream>
#include <tuple>
#include <utility>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/intrusive_ptr.hpp>
#if defined(BOOST_NO_CXX17_STD_APPLY)
#include <boost/context/detail/apply.hpp>
#endif
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
#include <boost/context/detail/exchange.hpp>
#endif
#if defined(BOOST_NO_CXX17_STD_INVOKE)
#include <boost/context/detail/invoke.hpp>
#endif
#include <boost/context/detail/disable_overload.hpp>
#include <boost/context/detail/exception.hpp>
#include <boost/context/detail/fcontext.hpp>
#include <boost/context/detail/tuple.hpp>
#include <boost/context/fixedsize_stack.hpp>
#include <boost/context/flags.hpp>
#include <boost/context/preallocated.hpp>
#include <boost/context/segmented_stack.hpp>
#include <boost/context/stack_context.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
#if defined(BOOST_MSVC)
# pragma warning(push)
# pragma warning(disable: 4702)
#endif
namespace boost {
namespace context {
namespace detail {
template< typename U >
struct helper {
template< typename T >
static T convert( T && t) noexcept {
return std::forward< T >( t);
}
};
template< typename U >
struct helper< std::tuple< U > > {
template< typename T >
static std::tuple< T > convert( T && t) noexcept {
return std::make_tuple( std::forward< T >( t) );
}
};
inline
transfer_t context_unwind( transfer_t t) {
throw forced_unwind( t.fctx);
return { nullptr, nullptr };
}
template< typename Rec >
transfer_t context_exit( transfer_t t) noexcept {
Rec * rec = static_cast< Rec * >( t.data);
// destroy context stack
rec->deallocate();
return { nullptr, nullptr };
}
template< typename Rec >
void context_entry( transfer_t t_) noexcept {
// transfer control structure to the context-stack
Rec * rec = static_cast< Rec * >( t_.data);
BOOST_ASSERT( nullptr != t_.fctx);
BOOST_ASSERT( nullptr != rec);
transfer_t t = { nullptr, nullptr };
try {
// jump back to `context_create()`
t = jump_fcontext( t_.fctx, nullptr);
// start executing
t = rec->run( t);
} catch ( forced_unwind const& e) {
t = { e.fctx, nullptr };
}
BOOST_ASSERT( nullptr != t.fctx);
// destroy context-stack of `this`context on next context
ontop_fcontext( t.fctx, rec, context_exit< Rec >);
BOOST_ASSERT_MSG( false, "context already terminated");
}
template<
typename Ctx,
typename StackAlloc,
typename Fn
>
class record {
private:
StackAlloc salloc_;
stack_context sctx_;
typename std::decay< Fn >::type fn_;
static void destroy( record * p) noexcept {
StackAlloc salloc = p->salloc_;
stack_context sctx = p->sctx_;
// deallocate record
p->~record();
// destroy stack with stack allocator
salloc.deallocate( sctx);
}
public:
record( stack_context sctx, StackAlloc const& salloc,
Fn && fn) noexcept :
salloc_( salloc),
sctx_( sctx),
fn_( std::forward< Fn >( fn) ) {
}
record( record const&) = delete;
record & operator=( record const&) = delete;
void deallocate() noexcept {
destroy( this);
}
transfer_t run( transfer_t t) {
Ctx from{ t };
// invoke context-function
#if defined(BOOST_NO_CXX17_STD_INVOKE)
Ctx cc = invoke( fn_, std::move( from) );
#else
Ctx cc = std::invoke( fn_, std::move( from) );
#endif
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
return { exchange( cc.t_.fctx, nullptr), nullptr };
#else
return { std::exchange( cc.t_.fctx, nullptr), nullptr };
#endif
}
};
template< typename Record, typename StackAlloc, typename Fn >
fcontext_t context_create( StackAlloc salloc, Fn && fn) {
auto sctx = salloc.allocate();
// reserve space for control structure
#if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
const std::size_t size = sctx.size - sizeof( Record);
void * sp = static_cast< char * >( sctx.sp) - sizeof( Record);
#else
constexpr std::size_t func_alignment = 64; // alignof( Record);
constexpr std::size_t func_size = sizeof( Record);
// reserve space on stack
void * sp = static_cast< char * >( sctx.sp) - func_size - func_alignment;
// align sp pointer
std::size_t space = func_size + func_alignment;
sp = std::align( func_alignment, func_size, sp, space);
BOOST_ASSERT( nullptr != sp);
// calculate remaining size
const std::size_t size = sctx.size - ( static_cast< char * >( sctx.sp) - static_cast< char * >( sp) );
#endif
// create fast-context
const fcontext_t fctx = make_fcontext( sp, size, & context_entry< Record >);
BOOST_ASSERT( nullptr != fctx);
// placment new for control structure on context-stack
auto rec = ::new ( sp) Record{
sctx, salloc, std::forward< Fn >( fn) };
// transfer control structure to context-stack
return jump_fcontext( fctx, rec).fctx;
}
template< typename Record, typename StackAlloc, typename Fn >
fcontext_t context_create( preallocated palloc, StackAlloc salloc, Fn && fn) {
// reserve space for control structure
#if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
const std::size_t size = palloc.size - sizeof( Record);
void * sp = static_cast< char * >( palloc.sp) - sizeof( Record);
#else
constexpr std::size_t func_alignment = 64; // alignof( Record);
constexpr std::size_t func_size = sizeof( Record);
// reserve space on stack
void * sp = static_cast< char * >( palloc.sp) - func_size - func_alignment;
// align sp pointer
std::size_t space = func_size + func_alignment;
sp = std::align( func_alignment, func_size, sp, space);
BOOST_ASSERT( nullptr != sp);
// calculate remaining size
const std::size_t size = palloc.size - ( static_cast< char * >( palloc.sp) - static_cast< char * >( sp) );
#endif
// create fast-context
const fcontext_t fctx = make_fcontext( sp, size, & context_entry< Record >);
BOOST_ASSERT( nullptr != fctx);
// placment new for control structure on context-stack
auto rec = ::new ( sp) Record{
palloc.sctx, salloc, std::forward< Fn >( fn) };
// transfer control structure to context-stack
return jump_fcontext( fctx, rec).fctx;
}
template< typename ... Arg >
struct result_type {
typedef std::tuple< Arg ... > type;
static
type get( detail::transfer_t & t) {
auto p = static_cast< std::tuple< Arg ... > * >( t.data);
return std::move( * p);
}
};
template< typename Arg >
struct result_type< Arg > {
typedef Arg type;
static
type get( detail::transfer_t & t) {
auto p = static_cast< std::tuple< Arg > * >( t.data);
return std::forward< Arg >( std::get< 0 >( * p) );
}
};
}
template< typename Ctx, typename Fn, typename Arg >
detail::transfer_t context_ontop( detail::transfer_t t) {
auto p = static_cast< Arg * >( t.data);
BOOST_ASSERT( nullptr != p);
typename std::decay< Fn >::type fn = std::forward< Fn >( std::get< 0 >( * p) );
t.data = & std::get< 1 >( * p);
Ctx c{ t };
// execute function, pass continuation via reference
typedef typename std::decay< decltype( std::get< 1 >( * p) )>::type Tpl;
std::get< 1 >( * p) = detail::helper< Tpl >::convert( fn( std::move( c) ) );
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
return { detail::exchange( c.t_.fctx, nullptr), & std::get< 1 >( * p) };
#else
return { std::exchange( c.t_.fctx, nullptr), & std::get< 1 >( * p) };
#endif
}
template< typename Ctx, typename Fn >
detail::transfer_t context_ontop_void( detail::transfer_t t) {
auto p = static_cast< std::tuple< Fn > * >( t.data);
BOOST_ASSERT( nullptr != p);
typename std::decay< Fn >::type fn = std::forward< Fn >( std::get< 0 >( * p) );
Ctx c{ t };
// execute function, pass continuation via reference
fn( std::move( c) );
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
return { detail::exchange( c.t_.fctx, nullptr), nullptr };
#else
return { std::exchange( c.t_.fctx, nullptr), nullptr };
#endif
}
class continuation {
private:
template< typename Ctx, typename StackAlloc, typename Fn >
friend class detail::record;
template< typename Ctx, typename Fn, typename Arg >
friend detail::transfer_t
context_ontop( detail::transfer_t);
template< typename Ctx, typename Fn >
friend detail::transfer_t
context_ontop_void( detail::transfer_t);
template< typename StackAlloc, typename Fn, typename ... Arg >
friend continuation
callcc( std::allocator_arg_t, StackAlloc, Fn &&, Arg ...);
template< typename StackAlloc, typename Fn, typename ... Arg >
friend continuation
callcc( std::allocator_arg_t, preallocated, StackAlloc, Fn &&, Arg ...);
template< typename StackAlloc, typename Fn >
friend continuation
callcc( std::allocator_arg_t, StackAlloc, Fn &&);
template< typename StackAlloc, typename Fn >
friend continuation
callcc( std::allocator_arg_t, preallocated, StackAlloc, Fn &&);
detail::transfer_t t_{ nullptr, nullptr };
continuation( detail::fcontext_t fctx) noexcept :
t_{ fctx, nullptr } {
}
continuation( detail::transfer_t t) noexcept :
t_{ t.fctx, t.data } {
}
public:
continuation() noexcept = default;
~continuation() {
if ( nullptr != t_.fctx) {
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::ontop_fcontext( detail::exchange( t_.fctx, nullptr), nullptr, detail::context_unwind);
#else
detail::ontop_fcontext( std::exchange( t_.fctx, nullptr), nullptr, detail::context_unwind);
#endif
}
}
continuation( continuation && other) noexcept :
t_{ other.t_.fctx, other.t_.data } {
other.t_ = { nullptr, nullptr };
}
continuation & operator=( continuation && other) noexcept {
if ( this != & other) {
continuation tmp = std::move( other);
swap( tmp);
}
return * this;
}
continuation( continuation const& other) noexcept = delete;
continuation & operator=( continuation const& other) noexcept = delete;
template< typename ... Arg >
continuation resume( Arg ... arg) {
BOOST_ASSERT( nullptr != t_.fctx);
auto tpl = std::make_tuple( std::forward< Arg >( arg) ... );
return detail::jump_fcontext(
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::exchange( t_.fctx, nullptr),
#else
std::exchange( t_.fctx, nullptr),
#endif
& tpl);
}
template< typename Fn, typename ... Arg >
continuation resume_with( Fn && fn, Arg ... arg) {
BOOST_ASSERT( nullptr != t_.fctx);
auto tpl = std::make_tuple( std::forward< Fn >( fn), std::make_tuple( std::forward< Arg >( arg) ... ));
return detail::ontop_fcontext(
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::exchange( t_.fctx, nullptr),
#else
std::exchange( t_.fctx, nullptr),
#endif
& tpl,
context_ontop< continuation, Fn, decltype(tpl) >);
}
continuation resume() {
BOOST_ASSERT( nullptr != t_.fctx);
return detail::jump_fcontext(
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::exchange( t_.fctx, nullptr),
#else
std::exchange( t_.fctx, nullptr),
#endif
nullptr);
}
template< typename Fn >
continuation resume_with( Fn && fn) {
BOOST_ASSERT( nullptr != t_.fctx);
auto p = std::make_tuple( std::forward< Fn >( fn) );
return detail::ontop_fcontext(
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::exchange( t_.fctx, nullptr),
#else
std::exchange( t_.fctx, nullptr),
#endif
& p,
context_ontop_void< continuation, Fn >);
}
bool data_available() noexcept {
return * this && nullptr != t_.data;
}
template< typename ... Arg >
typename detail::result_type< Arg ... >::type get_data() {
BOOST_ASSERT( nullptr != t_.data);
return detail::result_type< Arg ... >::get( t_);
}
explicit operator bool() const noexcept {
return nullptr != t_.fctx;
}
bool operator!() const noexcept {
return nullptr == t_.fctx;
}
bool operator==( continuation const& other) const noexcept {
return t_.fctx == other.t_.fctx;
}
bool operator!=( continuation const& other) const noexcept {
return t_.fctx != other.t_.fctx;
}
bool operator<( continuation const& other) const noexcept {
return t_.fctx < other.t_.fctx;
}
bool operator>( continuation const& other) const noexcept {
return other.t_.fctx < t_.fctx;
}
bool operator<=( continuation const& other) const noexcept {
return ! ( * this > other);
}
bool operator>=( continuation const& other) const noexcept {
return ! ( * this < other);
}
template< typename charT, class traitsT >
friend std::basic_ostream< charT, traitsT > &
operator<<( std::basic_ostream< charT, traitsT > & os, continuation const& other) {
if ( nullptr != other.t_.fctx) {
return os << other.t_.fctx;
} else {
return os << "{not-a-context}";
}
}
void swap( continuation & other) noexcept {
std::swap( t_, other.t_);
}
};
// Arg
template<
typename Fn,
typename ... Arg,
typename = detail::disable_overload< continuation, Fn >,
typename = detail::disable_overload< std::allocator_arg_t, Fn >
>
continuation
callcc( Fn && fn, Arg ... arg) {
return callcc(
std::allocator_arg, fixedsize_stack(),
std::forward< Fn >( fn), std::forward< Arg >( arg) ...);
}
template<
typename StackAlloc,
typename Fn,
typename ... Arg
>
continuation
callcc( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Arg ... arg) {
using Record = detail::record< continuation, StackAlloc, Fn >;
return continuation{
detail::context_create< Record >(
salloc, std::forward< Fn >( fn) ) }.resume(
std::forward< Arg >( arg) ... );
}
template<
typename StackAlloc,
typename Fn,
typename ... Arg
>
continuation
callcc( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn, Arg ... arg) {
using Record = detail::record< continuation, StackAlloc, Fn >;
return continuation{
detail::context_create< Record >(
palloc, salloc, std::forward< Fn >( fn) ) }.resume(
std::forward< Arg >( arg) ... );
}
// void
template<
typename Fn,
typename = detail::disable_overload< continuation, Fn >
>
continuation
callcc( Fn && fn) {
return callcc(
std::allocator_arg, fixedsize_stack(),
std::forward< Fn >( fn) );
}
template< typename StackAlloc, typename Fn >
continuation
callcc( std::allocator_arg_t, StackAlloc salloc, Fn && fn) {
using Record = detail::record< continuation, StackAlloc, Fn >;
return continuation{
detail::context_create< Record >(
salloc, std::forward< Fn >( fn) ) }.resume();
}
template< typename StackAlloc, typename Fn >
continuation
callcc( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn) {
using Record = detail::record< continuation, StackAlloc, Fn >;
return continuation{
detail::context_create< Record >(
palloc, salloc, std::forward< Fn >( fn) ) }.resume();
}
#if defined(BOOST_USE_SEGMENTED_STACKS)
template<
typename Fn,
typename ... Arg
>
continuation
callcc( std::allocator_arg_t, segmented_stack, Fn &&, Arg ...);
template<
typename StackAlloc,
typename Fn,
typename ... Arg
>
continuation
callcc( std::allocator_arg_t, preallocated, segmented_stack, Fn &&, Arg ...);
#endif
// swap
inline
void swap( continuation & l, continuation & r) noexcept {
l.swap( r);
}
}}
#if defined(BOOST_MSVC)
# pragma warning(pop)
#endif
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_CONTEXT_CONTINUATION_H

View File

@@ -0,0 +1,696 @@
// Copyright Oliver Kowalke 2017.
// 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_CONTEXT_CONTINUATION_H
#define BOOST_CONTEXT_CONTINUATION_H
extern "C" {
#include <ucontext.h>
}
#include <boost/context/detail/config.hpp>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <memory>
#include <ostream>
#include <system_error>
#include <tuple>
#include <utility>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#if defined(BOOST_NO_CXX17_STD_APPLY)
#include <boost/context/detail/apply.hpp>
#endif
#include <boost/context/detail/disable_overload.hpp>
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
#include <boost/context/detail/exchange.hpp>
#endif
#include <boost/context/fixedsize_stack.hpp>
#include <boost/context/flags.hpp>
#include <boost/context/preallocated.hpp>
#if defined(BOOST_USE_SEGMENTED_STACKS)
#include <boost/context/segmented_stack.hpp>
#endif
#include <boost/context/stack_context.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
#if defined(BOOST_USE_ASAN)
extern "C" {
void __sanitizer_start_switch_fiber( void **, const void *, size_t);
void __sanitizer_finish_switch_fiber( void *, const void **, size_t *);
}
#endif
#if defined(BOOST_USE_SEGMENTED_STACKS)
extern "C" {
void __splitstack_getcontext( void * [BOOST_CONTEXT_SEGMENTS]);
void __splitstack_setcontext( void * [BOOST_CONTEXT_SEGMENTS]);
}
#endif
namespace boost {
namespace context {
namespace detail {
template< typename U >
struct helper {
template< typename T >
static T convert( T && t) noexcept {
return std::forward< T >( t);
}
};
template< typename U >
struct helper< std::tuple< U > > {
template< typename T >
static std::tuple< T > convert( T && t) noexcept {
return std::make_tuple( std::forward< T >( t) );
}
};
// tampoline function
// entered if the execution context
// is resumed for the first time
template< typename Record >
static void entry_func( void * data) noexcept {
Record * record = static_cast< Record * >( data);
BOOST_ASSERT( nullptr != record);
// start execution of toplevel context-function
record->run();
}
struct BOOST_CONTEXT_DECL activation_record {
thread_local static activation_record * current_rec;
ucontext_t uctx{};
stack_context sctx{};
bool main_ctx{ true };
void * data{ nullptr };
activation_record * from{ nullptr };
std::function< void() > ontop{};
bool terminated{ false };
bool force_unwind{ false };
#if defined(BOOST_USE_ASAN)
void * fake_stack{ nullptr };
void * stack_bottom{ nullptr };
std::size_t stack_size{ 0 };
bool started{ false };
#endif
static activation_record *& current() noexcept;
// used for toplevel-context
// (e.g. main context, thread-entry context)
activation_record() {
if ( 0 != ::getcontext( & uctx) ) {
throw std::system_error(
std::error_code( errno, std::system_category() ),
"getcontext() failed");
}
#if defined(BOOST_USE_ASAN)
stack_bottom = uctx.uc_stack.ss_sp;
stack_size = uctx.uc_stack.ss_size;
#endif
}
activation_record( stack_context sctx_) noexcept :
sctx{ sctx_ },
main_ctx{ false } {
}
virtual ~activation_record() {
}
activation_record( activation_record const&) = delete;
activation_record & operator=( activation_record const&) = delete;
bool is_main_context() const noexcept {
return main_ctx;
}
detail::activation_record * resume( void * vp) {
data = vp;
from = current();
from->data = nullptr;
// store `this` in static, thread local pointer
// `this` will become the active (running) context
current() = this;
#if defined(BOOST_USE_SEGMENTED_STACKS)
// adjust segmented stack properties
__splitstack_getcontext( from->sctx.segments_ctx);
__splitstack_setcontext( sctx.segments_ctx);
#endif
#if defined(BOOST_USE_ASAN)
if ( from->started) {
__sanitizer_finish_switch_fiber( from->fake_stack, (const void **) & from->stack_bottom,
& from->stack_size);
from->started = false;
}
__sanitizer_start_switch_fiber( & fake_stack, stack_bottom, stack_size);
started = true;
#endif
// context switch from parent context to `this`-context
::swapcontext( & from->uctx, & uctx);
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
return detal::exchange( current()->from, nullptr);
#else
return std::exchange( current()->from, nullptr);
#endif
}
template< typename Ctx, typename Fn, typename Tpl >
detail::activation_record * resume_with( Fn && fn, Tpl * tpl) {
data = nullptr;
from = current();
from->data = nullptr;
// store `this` in static, thread local pointer
// `this` will become the active (running) context
// returned by continuation::current()
current() = this;
#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS)
auto from_ = current()->from;
current()->ontop = std::bind(
[tpl,from_](typename std::decay< Fn >::type & fn){
current()->data = tpl;
* tpl = helper< Tpl >::convert( fn( Ctx{ from_ } ) );
},
std::forward< Fn >( fn) );
#else
current()->ontop = [fn=std::forward<Fn>(fn),tpl,from=current()->from]() {
current()->data = tpl;
* tpl = helper< Tpl >::convert( fn( Ctx{ from } ) );
};
#endif
#if defined(BOOST_USE_SEGMENTED_STACKS)
// adjust segmented stack properties
__splitstack_getcontext( from->sctx.segments_ctx);
__splitstack_setcontext( sctx.segments_ctx);
#endif
#if defined(BOOST_USE_ASAN)
if ( from->started) {
__sanitizer_finish_switch_fiber( from->fake_stack, (const void **) & from->stack_bottom,
& from->stack_size);
from->started = false;
}
__sanitizer_start_switch_fiber( & fake_stack, stack_bottom, stack_size);
started = true;
#endif
// context switch from parent context to `this`-context
::swapcontext( & from->uctx, & uctx);
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
return detail::exchange( current()->from, nullptr);
#else
return std::exchange( current()->from, nullptr);
#endif
}
template< typename Ctx, typename Fn >
detail::activation_record * resume_with( Fn && fn) {
data = nullptr;
from = current();
from->data = nullptr;
// store `this` in static, thread local pointer
// `this` will become the active (running) context
// returned by continuation::current()
current() = this;
#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS)
auto from_ = current()->from;
current()->ontop = std::bind(
[from_](typename std::decay< Fn >::type & fn){
fn( Ctx{ from_ } );
current()->data = nullptr;
},
std::forward< Fn >( fn) );
#else
current()->ontop = [fn=std::forward<Fn>(fn),from=current()->from]() {
fn( Ctx{ from } );
current()->data = nullptr;
};
#endif
#if defined(BOOST_USE_SEGMENTED_STACKS)
// adjust segmented stack properties
__splitstack_getcontext( from->sctx.segments_ctx);
__splitstack_setcontext( sctx.segments_ctx);
#endif
#if defined(BOOST_USE_ASAN)
if ( from->started) {
__sanitizer_finish_switch_fiber( from->fake_stack, (const void **) & from->stack_bottom,
& from->stack_size);
from->started = false;
}
__sanitizer_start_switch_fiber( & fake_stack, stack_bottom, stack_size);
started = true;
#endif
// context switch from parent context to `this`-context
::swapcontext( & from->uctx, & uctx);
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
return detail::exchange( current()->from, nullptr);
#else
return std::exchange( current()->from, nullptr);
#endif
}
virtual void deallocate() noexcept {
}
};
struct BOOST_CONTEXT_DECL activation_record_initializer {
activation_record_initializer() noexcept;
~activation_record_initializer();
};
struct forced_unwind {
activation_record * from{ nullptr };
forced_unwind( activation_record * from_) noexcept :
from{ from_ } {
}
};
template< typename Ctx, typename StackAlloc, typename Fn, typename ... Arg >
class capture_record : public activation_record {
private:
typename std::decay< Fn >::type fn_;
std::tuple< Arg ... > arg_;
StackAlloc salloc_;
static void destroy( capture_record * p) noexcept {
StackAlloc salloc = p->salloc_;
stack_context sctx = p->sctx;
// deallocate activation record
p->~capture_record();
// destroy stack with stack allocator
salloc.deallocate( sctx);
}
public:
capture_record( stack_context sctx, StackAlloc const& salloc,
Fn && fn, Arg ... arg) noexcept :
activation_record{ sctx },
fn_( std::forward< Fn >( fn) ),
arg_( std::forward< Arg >( arg) ... ),
salloc_{ salloc } {
}
void deallocate() noexcept override final {
BOOST_ASSERT( main_ctx || ( ! main_ctx && terminated) );
destroy( this);
}
void run() {
Ctx c{ from };
auto tpl = std::tuple_cat(
std::forward_as_tuple( std::move( c) ),
std::move( arg_) );
try {
// invoke context-function
#if defined(BOOST_NO_CXX17_STD_APPLY)
c = apply( std::move( fn_), std::move( tpl) );
#else
c = std::apply( std::move( fn_), std::move( tpl) );
#endif
} catch ( forced_unwind const& ex) {
c = Ctx{ ex.from };
}
// this context has finished its task
data = nullptr;
from = nullptr;
ontop = nullptr;
terminated = true;
force_unwind = false;
c.resume();
BOOST_ASSERT_MSG( false, "continuation already terminated");
}
};
template< typename Ctx, typename StackAlloc, typename Fn, typename ... Arg >
static activation_record * create_context( StackAlloc salloc, Fn && fn, Arg ... arg) {
typedef capture_record< Ctx, StackAlloc, Fn, Arg ... > capture_t;
auto sctx = salloc.allocate();
// reserve space for control structure
void * storage = static_cast< char * >( sctx.sp) - sizeof( capture_t);
// placment new for control structure on fast-context stack
capture_t * record = new ( storage) capture_t{
sctx, salloc, std::forward< Fn >( fn), std::forward< Arg >( arg) ... };
// create user-context
if ( 0 != ::getcontext( & record->uctx) ) {
throw std::system_error(
std::error_code( errno, std::system_category() ),
"getcontext() failed");
}
record->uctx.uc_stack.ss_size = sctx.size - sizeof(capture_t) - 64;
record->uctx.uc_stack.ss_sp = static_cast< char * >( sctx.sp) - sctx.size;
record->uctx.uc_link = nullptr;
::makecontext( & record->uctx, ( void (*)() ) & detail::entry_func< capture_t >, 1, record);
#if defined(BOOST_USE_ASAN)
record->stack_bottom = record->uctx.uc_stack.ss_sp;
record->stack_size = record->uctx.uc_stack.ss_size;
#endif
return record;
}
template< typename Ctx, typename StackAlloc, typename Fn, typename ... Arg >
static activation_record * create_context( preallocated palloc, StackAlloc salloc,
Fn && fn, Arg ... arg) {
typedef capture_record< Ctx, StackAlloc, Fn, Arg ... > capture_t;
// reserve space for control structure
void * storage = static_cast< char * >( palloc.sp) - sizeof( capture_t);
// placment new for control structure on fast-context stack
capture_t * record = new ( storage) capture_t{
palloc.sctx, salloc, std::forward< Fn >( fn), std::forward< Arg >( arg) ... };
// create user-context
if ( 0 != ::getcontext( & record->uctx) ) {
throw std::system_error(
std::error_code( errno, std::system_category() ),
"getcontext() failed");
}
record->uctx.uc_stack.ss_size = palloc.size - sizeof(capture_t) - 64;
record->uctx.uc_stack.ss_sp = static_cast< char * >( palloc.sctx.sp) - palloc.sctx.size;
record->uctx.uc_link = nullptr;
::makecontext( & record->uctx, ( void (*)() ) & detail::entry_func< capture_t >, 1, record);
#if defined(BOOST_USE_ASAN)
record->stack_bottom = record->uctx.uc_stack.ss_sp;
record->stack_size = record->uctx.uc_stack.ss_size;
#endif
return record;
}
template< typename ... Arg >
struct result_type {
typedef std::tuple< Arg ... > type;
static
type get( void * data) {
auto p = static_cast< std::tuple< Arg ... > * >( data);
return std::move( * p);
}
};
template< typename Arg >
struct result_type< Arg > {
typedef Arg type;
static
type get( void * data) {
auto p = static_cast< std::tuple< Arg > * >( data);
return std::forward< Arg >( std::get< 0 >( * p) );
}
};
}
class BOOST_CONTEXT_DECL continuation {
private:
friend struct detail::activation_record;
template< typename Ctx, typename StackAlloc, typename Fn, typename ... Arg >
friend class detail::capture_record;
template< typename Ctx, typename StackAlloc, typename Fn, typename ... Arg >
friend detail::activation_record * detail::create_context( StackAlloc, Fn &&, Arg ...);
template< typename Ctx, typename StackAlloc, typename Fn, typename ... Arg >
friend detail::activation_record * detail::create_context( preallocated, StackAlloc, Fn &&, Arg ...);
template< typename StackAlloc, typename Fn, typename ... Arg >
friend continuation
callcc( std::allocator_arg_t, StackAlloc, Fn &&, Arg ...);
template< typename StackAlloc, typename Fn, typename ... Arg >
friend continuation
callcc( std::allocator_arg_t, preallocated, StackAlloc, Fn &&, Arg ...);
template< typename StackAlloc, typename Fn >
friend continuation
callcc( std::allocator_arg_t, StackAlloc, Fn &&);
template< typename StackAlloc, typename Fn >
friend continuation
callcc( std::allocator_arg_t, preallocated, StackAlloc, Fn &&);
detail::activation_record * ptr_{ nullptr };
continuation( detail::activation_record * ptr) noexcept :
ptr_{ ptr } {
}
public:
continuation() = default;
~continuation() {
if ( nullptr != ptr_ && ! ptr_->main_ctx) {
if ( ! ptr_->terminated) {
ptr_->force_unwind = true;
ptr_->resume( nullptr);
BOOST_ASSERT( ptr_->terminated);
}
ptr_->deallocate();
}
}
continuation( continuation const&) = delete;
continuation & operator=( continuation const&) = delete;
continuation( continuation && other) noexcept :
ptr_{ nullptr } {
swap( other);
}
continuation & operator=( continuation && other) noexcept {
if ( this == & other) return * this;
continuation tmp{ std::move( other) };
swap( tmp);
return * this;
}
template< typename ... Arg >
continuation resume( Arg ... arg) {
auto tpl = std::make_tuple( std::forward< Arg >( arg) ... );
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::activation_record * ptr = detail::exchange( ptr_, nullptr)->resume( & tpl);
#else
detail::activation_record * ptr = std::exchange( ptr_, nullptr)->resume( & tpl);
#endif
if ( detail::activation_record::current()->force_unwind) {
throw detail::forced_unwind{ ptr};
} else if ( detail::activation_record::current()->ontop) {
detail::activation_record::current()->ontop();
detail::activation_record::current()->ontop = nullptr;
}
return continuation{ ptr };
}
template< typename Fn, typename ... Arg >
continuation resume_with( Fn && fn, Arg ... arg) {
auto tpl = std::make_tuple( std::forward< Arg >( arg) ... );
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::activation_record * ptr =
detail::exchange( ptr_, nullptr)->resume_with< continuation >( std::forward< Fn >( fn), & tpl);
#else
detail::activation_record * ptr =
std::exchange( ptr_, nullptr)->resume_with< continuation >( std::forward< Fn >( fn), & tpl);
#endif
if ( detail::activation_record::current()->force_unwind) {
throw detail::forced_unwind{ ptr };
} else if ( detail::activation_record::current()->ontop) {
detail::activation_record::current()->ontop();
detail::activation_record::current()->ontop = nullptr;
}
return continuation{ ptr };
}
continuation resume() {
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::activation_record * ptr = detail::exchange( ptr_, nullptr)->resume( nullptr);
#else
detail::activation_record * ptr = std::exchange( ptr_, nullptr)->resume( nullptr);
#endif
if ( detail::activation_record::current()->force_unwind) {
throw detail::forced_unwind{ ptr };
} else if ( detail::activation_record::current()->ontop) {
detail::activation_record::current()->ontop();
detail::activation_record::current()->ontop = nullptr;
}
return continuation{ ptr };
}
template< typename Fn >
continuation resume_with( Fn && fn) {
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::activation_record * ptr =
detail::exchange( ptr_, nullptr)->resume_with< continuation >( std::forward< Fn >( fn) );
#else
detail::activation_record * ptr =
std::exchange( ptr_, nullptr)->resume_with< continuation >( std::forward< Fn >( fn) );
#endif
if ( detail::activation_record::current()->force_unwind) {
throw detail::forced_unwind{ ptr };
} else if ( detail::activation_record::current()->ontop) {
detail::activation_record::current()->ontop();
detail::activation_record::current()->ontop = nullptr;
}
return continuation{ ptr };
}
bool data_available() noexcept {
return * this && nullptr != detail::activation_record::current()->data;
}
template< typename ... Arg >
typename detail::result_type< Arg ... >::type get_data() {
BOOST_ASSERT( data_available() );;
return detail::result_type< Arg ... >::get( detail::activation_record::current()->data);
}
explicit operator bool() const noexcept {
return nullptr != ptr_ && ! ptr_->terminated;
}
bool operator!() const noexcept {
return nullptr == ptr_ || ptr_->terminated;
}
bool operator==( continuation const& other) const noexcept {
return ptr_ == other.ptr_;
}
bool operator!=( continuation const& other) const noexcept {
return ptr_ != other.ptr_;
}
bool operator<( continuation const& other) const noexcept {
return ptr_ < other.ptr_;
}
bool operator>( continuation const& other) const noexcept {
return other.ptr_ < ptr_;
}
bool operator<=( continuation const& other) const noexcept {
return ! ( * this > other);
}
bool operator>=( continuation const& other) const noexcept {
return ! ( * this < other);
}
template< typename charT, class traitsT >
friend std::basic_ostream< charT, traitsT > &
operator<<( std::basic_ostream< charT, traitsT > & os, continuation const& other) {
if ( nullptr != other.ptr_) {
return os << other.ptr_;
} else {
return os << "{not-a-context}";
}
}
void swap( continuation & other) noexcept {
std::swap( ptr_, other.ptr_);
}
};
// Arg
template<
typename Fn,
typename ... Arg,
typename = detail::disable_overload< continuation, Fn >,
typename = detail::disable_overload< std::allocator_arg_t, Fn >
>
continuation
callcc( Fn && fn, Arg ... arg) {
return callcc(
std::allocator_arg,
#if defined(BOOST_USE_SEGMENTED_STACKS)
segmented_stack(),
#else
fixedsize_stack(),
#endif
std::forward< Fn >( fn), std::forward< Arg >( arg) ...);
}
template<
typename StackAlloc,
typename Fn,
typename ... Arg
>
continuation
callcc( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Arg ... arg) {
return continuation{
detail::create_context< continuation >(
salloc, std::forward< Fn >( fn) ) }.resume(
std::forward< Arg >( arg) ... );
}
template<
typename StackAlloc,
typename Fn,
typename ... Arg
>
continuation
callcc( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn, Arg ... arg) {
return continuation{
detail::create_context< continuation >(
palloc, salloc, std::forward< Fn >( fn) ) }.resume(
std::forward< Arg >( arg) ... );
}
// void
template<
typename Fn,
typename = detail::disable_overload< continuation, Fn >
>
continuation
callcc( Fn && fn) {
return callcc(
std::allocator_arg,
#if defined(BOOST_USE_SEGMENTED_STACKS)
segmented_stack(),
#else
fixedsize_stack(),
#endif
std::forward< Fn >( fn) );
}
template< typename StackAlloc, typename Fn >
continuation
callcc( std::allocator_arg_t, StackAlloc salloc, Fn && fn) {
return continuation{
detail::create_context< continuation >(
salloc, std::forward< Fn >( fn) ) }.resume();
}
template< typename StackAlloc, typename Fn >
continuation
callcc( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn) {
return continuation{
detail::create_context< continuation >(
palloc, salloc, std::forward< Fn >( fn) ) }.resume();
}
inline
void swap( continuation & l, continuation & r) noexcept {
l.swap( r);
}
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_CONTEXT_CONTINUATION_H

View File

@@ -20,7 +20,9 @@ namespace context {
namespace detail {
struct forced_unwind {
fcontext_t fctx;
fcontext_t fctx{ nullptr };
forced_unwind() = default;
forced_unwind( fcontext_t fctx_) :
fctx( fctx_) {

View File

@@ -14,6 +14,7 @@
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/pool/pool.hpp>
#include <boost/context/detail/config.hpp>

View File

@@ -26,7 +26,6 @@ project boost/context/performance/fcontext
<optimization>speed
<threading>multi
<variant>release
<cxxflags>-DBOOST_DISABLE_ASSERTS
;
alias sources

View File

@@ -4,7 +4,11 @@
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#if defined(BOOST_USE_UCONTEXT)
#include "boost/context/continuation_ucontext.hpp"
#else
#include "boost/context/execution_context.hpp"
#endif
#include <boost/config.hpp>
@@ -12,14 +16,17 @@
# include BOOST_ABI_PREFIX
#endif
#if ! defined(BOOST_CONTEXT_NO_CXX11)
# if (defined(BOOST_EXECUTION_CONTEXT) && (BOOST_EXECUTION_CONTEXT == 1))
#if defined(BOOST_USE_UCONTEXT) || (defined(BOOST_EXECUTION_CONTEXT) && (BOOST_EXECUTION_CONTEXT == 1))
namespace boost {
namespace context {
namespace detail {
thread_local
#if defined(BOOST_USE_UCONTEXT)
activation_record *
#elif defined(BOOST_EXECUTION_CONTEXT) && (BOOST_EXECUTION_CONTEXT == 1)
activation_record::ptr_t
#endif
activation_record::current_rec;
// zero-initialization
@@ -28,30 +35,50 @@ thread_local static std::size_t counter;
// schwarz counter
activation_record_initializer::activation_record_initializer() noexcept {
if ( 0 == counter++) {
#if defined(BOOST_USE_UCONTEXT)
activation_record::current_rec = new activation_record();
#elif defined(BOOST_EXECUTION_CONTEXT) && (BOOST_EXECUTION_CONTEXT == 1)
activation_record::current_rec.reset( new activation_record() );
#endif
}
}
activation_record_initializer::~activation_record_initializer() {
if ( 0 == --counter) {
BOOST_ASSERT( activation_record::current_rec->is_main_context() );
#if defined(BOOST_USE_UCONTEXT)
delete activation_record::current_rec;
#elif defined(BOOST_EXECUTION_CONTEXT) && (BOOST_EXECUTION_CONTEXT == 1)
delete activation_record::current_rec.detach();
#endif
}
}
}
#if defined(BOOST_USE_UCONTEXT)
namespace detail {
activation_record *&
activation_record::current() noexcept {
// initialized the first time control passes; per thread
thread_local static activation_record_initializer initializer;
return activation_record::current_rec;
}
}
#elif defined(BOOST_EXECUTION_CONTEXT) && (BOOST_EXECUTION_CONTEXT == 1)
execution_context
execution_context::current() noexcept {
// initialized the first time control passes; per thread
thread_local static detail::activation_record_initializer initializer;
return execution_context();
}
}}
# endif
#endif
# ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
# endif
}}
#endif
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif

View File

@@ -634,7 +634,6 @@ boost::unit_test::test_suite * init_unit_test_suite( int, char* [])
test->add( BOOST_TEST_CASE( & test_exception) );
test->add( BOOST_TEST_CASE( & test_fp) );
test->add( BOOST_TEST_CASE( & test_stacked) );
test->add( BOOST_TEST_CASE( & test_stacked) );
test->add( BOOST_TEST_CASE( & test_prealloc) );
test->add( BOOST_TEST_CASE( & test_ontop) );
test->add( BOOST_TEST_CASE( & test_ontop_exception) );