2
0
mirror of https://github.com/boostorg/context.git synced 2026-01-19 04:02:17 +00:00
This commit is contained in:
Oliver Kowalke
2021-09-05 15:34:47 +02:00
parent 561a31508f
commit e721688e1a
16 changed files with 168 additions and 2161 deletions

View File

@@ -821,8 +821,7 @@ alias impl_sources
# ucontext_t
alias impl_sources
: continuation.cpp
fiber.cpp
: fiber.cpp
: <context-impl>ucontext
[ requires cxx11_auto_declarations
cxx11_constexpr
@@ -841,8 +840,7 @@ alias impl_sources
# WinFiber
alias impl_sources
: continuation.cpp
fiber.cpp
: fiber.cpp
: <context-impl>winfib
[ requires cxx11_auto_declarations
cxx11_constexpr

View File

@@ -1,13 +0,0 @@
// 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)
#if defined(BOOST_USE_UCONTEXT)
#include <boost/context/continuation_ucontext.hpp>
#elif defined(BOOST_USE_WINFIB)
#include <boost/context/continuation_winfib.hpp>
#else
#include <boost/context/continuation_fcontext.hpp>
#endif

View File

@@ -1,370 +0,0 @@
// 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_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 {
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);
try {
// jump back to `create_context()`
t = jump_fcontext( t.fctx, nullptr);
// start executing
t.fctx = rec->run( t.fctx);
} catch ( forced_unwind const& ex) {
t = { ex.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 Fn >
transfer_t context_ontop( transfer_t t) {
auto p = static_cast< std::tuple< Fn > * >( t.data);
BOOST_ASSERT( nullptr != p);
typename std::decay< Fn >::type fn = std::get< 0 >( * p);
t.data = nullptr;
Ctx c{ t.fctx };
// execute function, pass continuation via reference
c = fn( std::move( c) );
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
return { exchange( c.fctx_, nullptr), nullptr };
#else
return { std::exchange( c.fctx_, nullptr), nullptr };
#endif
}
template< typename Ctx, typename StackAlloc, typename Fn >
class record {
private:
stack_context sctx_;
typename std::decay< StackAlloc >::type salloc_;
typename std::decay< Fn >::type fn_;
static void destroy( record * p) noexcept {
typename std::decay< StackAlloc >::type salloc = std::move( 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 && salloc,
Fn && fn) noexcept :
sctx_( sctx),
salloc_( std::forward< StackAlloc >( salloc)),
fn_( std::forward< Fn >( fn) ) {
}
record( record const&) = delete;
record & operator=( record const&) = delete;
void deallocate() noexcept {
destroy( this);
}
fcontext_t run( fcontext_t fctx) {
Ctx c{ fctx };
// invoke context-function
#if defined(BOOST_NO_CXX17_STD_INVOKE)
c = boost::context::detail::invoke( fn_, std::move( c) );
#else
c = std::invoke( fn_, std::move( c) );
#endif
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
return exchange( c.fctx_, nullptr);
#else
return std::exchange( c.fctx_, nullptr);
#endif
}
};
template< typename Record, typename StackAlloc, typename Fn >
fcontext_t create_context1( StackAlloc && salloc, Fn && fn) {
auto sctx = salloc.allocate();
// reserve space for control structure
void * storage = reinterpret_cast< void * >(
( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( Record) ) )
& ~static_cast< uintptr_t >( 0xff) );
// placment new for control structure on context stack
Record * record = new ( storage) Record{
sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
// 64byte gab between control structure and stack top
// should be 16byte aligned
void * stack_top = reinterpret_cast< void * >(
reinterpret_cast< uintptr_t >( storage) - static_cast< uintptr_t >( 64) );
void * stack_bottom = reinterpret_cast< void * >(
reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sctx.size) );
// create fast-context
const std::size_t size = reinterpret_cast< uintptr_t >( stack_top) - reinterpret_cast< uintptr_t >( stack_bottom);
const fcontext_t fctx = make_fcontext( stack_top, size, & context_entry< Record >);
BOOST_ASSERT( nullptr != fctx);
// transfer control structure to context-stack
return jump_fcontext( fctx, record).fctx;
}
template< typename Record, typename StackAlloc, typename Fn >
fcontext_t create_context2( preallocated palloc, StackAlloc && salloc, Fn && fn) {
// reserve space for control structure
void * storage = reinterpret_cast< void * >(
( reinterpret_cast< uintptr_t >( palloc.sp) - static_cast< uintptr_t >( sizeof( Record) ) )
& ~ static_cast< uintptr_t >( 0xff) );
// placment new for control structure on context-stack
Record * record = new ( storage) Record{
palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
// 64byte gab between control structure and stack top
void * stack_top = reinterpret_cast< void * >(
reinterpret_cast< uintptr_t >( storage) - static_cast< uintptr_t >( 64) );
void * stack_bottom = reinterpret_cast< void * >(
reinterpret_cast< uintptr_t >( palloc.sctx.sp) - static_cast< uintptr_t >( palloc.sctx.size) );
// create fast-context
const std::size_t size = reinterpret_cast< uintptr_t >( stack_top) - reinterpret_cast< uintptr_t >( stack_bottom);
const fcontext_t fctx = make_fcontext( stack_top, size, & context_entry< Record >);
BOOST_ASSERT( nullptr != fctx);
// transfer control structure to context-stack
return jump_fcontext( fctx, record).fctx;
}
}
class continuation {
private:
template< typename Ctx, typename StackAlloc, typename Fn >
friend class detail::record;
template< typename Ctx, typename Fn >
friend detail::transfer_t
detail::context_ontop( detail::transfer_t);
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::fcontext_t fctx_{ nullptr };
continuation( detail::fcontext_t fctx) noexcept :
fctx_{ fctx } {
}
public:
continuation() noexcept = default;
~continuation() {
if ( BOOST_UNLIKELY( nullptr != fctx_) ) {
detail::ontop_fcontext(
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::exchange( fctx_, nullptr),
#else
std::exchange( fctx_, nullptr),
#endif
nullptr,
detail::context_unwind);
}
}
continuation( continuation && other) noexcept {
swap( other);
}
continuation & operator=( continuation && other) noexcept {
if ( BOOST_LIKELY( this != & other) ) {
continuation tmp = std::move( other);
swap( tmp);
}
return * this;
}
continuation( continuation const& other) noexcept = delete;
continuation & operator=( continuation const& other) noexcept = delete;
continuation resume() & {
return std::move( * this).resume();
}
continuation resume() && {
BOOST_ASSERT( nullptr != fctx_);
return { detail::jump_fcontext(
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::exchange( fctx_, nullptr),
#else
std::exchange( fctx_, nullptr),
#endif
nullptr).fctx };
}
template< typename Fn >
continuation resume_with( Fn && fn) & {
return std::move( * this).resume_with( std::forward< Fn >( fn) );
}
template< typename Fn >
continuation resume_with( Fn && fn) && {
BOOST_ASSERT( nullptr != fctx_);
auto p = std::make_tuple( std::forward< Fn >( fn) );
return { detail::ontop_fcontext(
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::exchange( fctx_, nullptr),
#else
std::exchange( fctx_, nullptr),
#endif
& p,
detail::context_ontop< continuation, Fn >).fctx };
}
explicit operator bool() const noexcept {
return nullptr != fctx_;
}
bool operator!() const noexcept {
return nullptr == fctx_;
}
bool operator<( continuation const& other) const noexcept {
return fctx_ < other.fctx_;
}
template< typename charT, class traitsT >
friend std::basic_ostream< charT, traitsT > &
operator<<( std::basic_ostream< charT, traitsT > & os, continuation const& other) {
if ( nullptr != other.fctx_) {
return os << other.fctx_;
} else {
return os << "{not-a-context}";
}
}
void swap( continuation & other) noexcept {
std::swap( fctx_, other.fctx_);
}
};
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::create_context1< Record >(
std::forward< StackAlloc >( 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::create_context2< Record >(
palloc, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) }.resume();
}
#if defined(BOOST_USE_SEGMENTED_STACKS)
template< typename Fn >
continuation
callcc( std::allocator_arg_t, segmented_stack, Fn &&);
template< typename StackAlloc, typename Fn >
continuation
callcc( std::allocator_arg_t, preallocated, segmented_stack, Fn &&);
#endif
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

@@ -1,538 +0,0 @@
// 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/predef.h>
#if BOOST_OS_MACOS
#define _XOPEN_SOURCE 600
#endif
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>
#include <boost/context/detail/disable_overload.hpp>
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
#include <boost/context/detail/exchange.hpp>
#endif
#include <boost/context/detail/externc.hpp>
#if defined(BOOST_NO_CXX17_STD_INVOKE)
#include <boost/context/detail/invoke.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
namespace boost {
namespace context {
namespace detail {
// 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 {
ucontext_t uctx{};
stack_context sctx{};
bool main_ctx{ true };
activation_record * from{ nullptr };
std::function< activation_record*(activation_record*&) > 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 };
#endif
static activation_record *& current() noexcept;
// used for toplevel-context
// (e.g. main context, thread-entry context)
activation_record() {
if ( BOOST_UNLIKELY( 0 != ::getcontext( & uctx) ) ) {
throw std::system_error(
std::error_code( errno, std::system_category() ),
"getcontext() failed");
}
}
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;
}
activation_record * resume() {
from = current();
// 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 ( terminated) {
__sanitizer_start_switch_fiber( nullptr, stack_bottom, stack_size);
} else {
__sanitizer_start_switch_fiber( & from->fake_stack, stack_bottom, stack_size);
}
#endif
// context switch from parent context to `this`-context
::swapcontext( & from->uctx, & uctx);
#if defined(BOOST_USE_ASAN)
__sanitizer_finish_switch_fiber( current()->fake_stack,
(const void **) & current()->from->stack_bottom,
& current()->from->stack_size);
#endif
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
return exchange( current()->from, nullptr);
#else
return std::exchange( current()->from, nullptr);
#endif
}
template< typename Ctx, typename Fn >
activation_record * resume_with( Fn && fn) {
from = current();
// 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)
current()->ontop = std::bind(
[](typename std::decay< Fn >::type & fn, activation_record *& ptr){
Ctx c{ ptr };
c = fn( std::move( c) );
if ( ! c) {
ptr = nullptr;
}
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
return exchange( c.ptr_, nullptr);
#else
return std::exchange( c.ptr_, nullptr);
#endif
},
std::forward< Fn >( fn),
std::placeholders::_1);
#else
current()->ontop = [fn=std::forward<Fn>(fn)](activation_record *& ptr){
Ctx c{ ptr };
c = fn( std::move( c) );
if ( ! c) {
ptr = nullptr;
}
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
return exchange( c.ptr_, nullptr);
#else
return std::exchange( c.ptr_, nullptr);
#endif
};
#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)
__sanitizer_start_switch_fiber( & from->fake_stack, stack_bottom, stack_size);
#endif
// context switch from parent context to `this`-context
::swapcontext( & from->uctx, & uctx);
#if defined(BOOST_USE_ASAN)
__sanitizer_finish_switch_fiber( current()->fake_stack,
(const void **) & current()->from->stack_bottom,
& current()->from->stack_size);
#endif
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
return 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 >
class capture_record : public activation_record {
private:
typename std::decay< StackAlloc >::type salloc_;
typename std::decay< Fn >::type fn_;
static void destroy( capture_record * p) noexcept {
typename std::decay< StackAlloc >::type salloc = std::move( 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 && salloc, Fn && fn) noexcept :
activation_record{ sctx },
salloc_{ std::forward< StackAlloc >( salloc) },
fn_( std::forward< Fn >( fn) ) {
}
void deallocate() noexcept override final {
BOOST_ASSERT( main_ctx || ( ! main_ctx && terminated) );
destroy( this);
}
void run() {
#if defined(BOOST_USE_ASAN)
__sanitizer_finish_switch_fiber( fake_stack,
(const void **) & from->stack_bottom,
& from->stack_size);
#endif
Ctx c{ from };
try {
// invoke context-function
#if defined(BOOST_NO_CXX17_STD_INVOKE)
c = boost::context::detail::invoke( fn_, std::move( c) );
#else
c = std::invoke( fn_, std::move( c) );
#endif
} catch ( forced_unwind const& ex) {
c = Ctx{ ex.from };
}
// this context has finished its task
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 >
static activation_record * create_context1( StackAlloc && salloc, Fn && fn) {
typedef capture_record< Ctx, StackAlloc, Fn > capture_t;
auto sctx = salloc.allocate();
// reserve space for control structure
void * storage = reinterpret_cast< void * >(
( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( capture_t) ) )
& ~ static_cast< uintptr_t >( 0xff) );
// placment new for control structure on context stack
capture_t * record = new ( storage) capture_t{
sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
// stack bottom
void * stack_bottom = reinterpret_cast< void * >(
reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sctx.size) );
// create user-context
if ( BOOST_UNLIKELY( 0 != ::getcontext( & record->uctx) ) ) {
record->~capture_t();
salloc.deallocate( sctx);
throw std::system_error(
std::error_code( errno, std::system_category() ),
"getcontext() failed");
}
record->uctx.uc_stack.ss_sp = stack_bottom;
// 64byte gap between control structure and stack top
record->uctx.uc_stack.ss_size = reinterpret_cast< uintptr_t >( storage) -
reinterpret_cast< uintptr_t >( stack_bottom) - static_cast< uintptr_t >( 64);
record->uctx.uc_link = nullptr;
::makecontext( & record->uctx, ( void (*)() ) & 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 >
static activation_record * create_context2( preallocated palloc, StackAlloc && salloc, Fn && fn) {
typedef capture_record< Ctx, StackAlloc, Fn > capture_t;
// reserve space for control structure
void * storage = reinterpret_cast< void * >(
( reinterpret_cast< uintptr_t >( palloc.sp) - static_cast< uintptr_t >( sizeof( capture_t) ) )
& ~ static_cast< uintptr_t >( 0xff) );
// placment new for control structure on context stack
capture_t * record = new ( storage) capture_t{
palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
// stack bottom
void * stack_bottom = reinterpret_cast< void * >(
reinterpret_cast< uintptr_t >( palloc.sctx.sp) - static_cast< uintptr_t >( palloc.sctx.size) );
// create user-context
if ( BOOST_UNLIKELY( 0 != ::getcontext( & record->uctx) ) ) {
record->~capture_t();
salloc.deallocate( palloc.sctx);
throw std::system_error(
std::error_code( errno, std::system_category() ),
"getcontext() failed");
}
record->uctx.uc_stack.ss_sp = stack_bottom;
// 64byte gap between control structure and stack top
record->uctx.uc_stack.ss_size = reinterpret_cast< uintptr_t >( storage) -
reinterpret_cast< uintptr_t >( stack_bottom) - static_cast< uintptr_t >( 64);
record->uctx.uc_link = nullptr;
::makecontext( & record->uctx, ( void (*)() ) & 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;
}
}
class BOOST_CONTEXT_DECL continuation {
private:
friend struct detail::activation_record;
template< typename Ctx, typename StackAlloc, typename Fn >
friend class detail::capture_record;
template< typename Ctx, typename StackAlloc, typename Fn >
friend detail::activation_record * detail::create_context1( StackAlloc &&, Fn &&);
template< typename Ctx, typename StackAlloc, typename Fn >
friend detail::activation_record * detail::create_context2( preallocated, StackAlloc &&, Fn &&);
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 ( BOOST_UNLIKELY( nullptr != ptr_) && ! ptr_->main_ctx) {
if ( BOOST_LIKELY( ! ptr_->terminated) ) {
ptr_->force_unwind = true;
ptr_->resume();
BOOST_ASSERT( ptr_->terminated);
}
ptr_->deallocate();
}
}
continuation( continuation const&) = delete;
continuation & operator=( continuation const&) = delete;
continuation( continuation && other) noexcept {
swap( other);
}
continuation & operator=( continuation && other) noexcept {
if ( BOOST_LIKELY( this != & other) ) {
continuation tmp = std::move( other);
swap( tmp);
}
return * this;
}
continuation resume() & {
return std::move( * this).resume();
}
continuation resume() && {
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::activation_record * ptr = detail::exchange( ptr_, nullptr)->resume();
#else
detail::activation_record * ptr = std::exchange( ptr_, nullptr)->resume();
#endif
if ( BOOST_UNLIKELY( detail::activation_record::current()->force_unwind) ) {
throw detail::forced_unwind{ ptr};
} else if ( BOOST_UNLIKELY( nullptr != detail::activation_record::current()->ontop) ) {
ptr = detail::activation_record::current()->ontop( ptr);
detail::activation_record::current()->ontop = nullptr;
}
return { ptr };
}
template< typename Fn >
continuation resume_with( Fn && fn) & {
return std::move( * this).resume_with( std::forward< Fn >( fn) );
}
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 ( BOOST_UNLIKELY( detail::activation_record::current()->force_unwind) ) {
throw detail::forced_unwind{ ptr};
} else if ( BOOST_UNLIKELY( nullptr != detail::activation_record::current()->ontop) ) {
ptr = detail::activation_record::current()->ontop( ptr);
detail::activation_record::current()->ontop = nullptr;
}
return { ptr };
}
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_;
}
#if !defined(BOOST_EMBTC)
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}";
}
}
#else
template< typename charT, class traitsT >
friend std::basic_ostream< charT, traitsT > &
operator<<( std::basic_ostream< charT, traitsT > & os, continuation const& other);
#endif
void swap( continuation & other) noexcept {
std::swap( ptr_, other.ptr_);
}
};
#if defined(BOOST_EMBTC)
template< typename charT, class traitsT >
inline 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}";
}
}
#endif
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_context1< continuation >(
std::forward< StackAlloc >( 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_context2< continuation >(
palloc, std::forward< StackAlloc >( 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

@@ -1,473 +0,0 @@
// 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 <windows.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>
#include <boost/context/detail/disable_overload.hpp>
#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/fixedsize_stack.hpp>
#include <boost/context/flags.hpp>
#include <boost/context/preallocated.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 {
// tampoline function
// entered if the execution context
// is resumed for the first time
template< typename Record >
static VOID WINAPI entry_func( LPVOID 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 {
LPVOID fiber{ nullptr };
stack_context sctx{};
bool main_ctx{ true };
activation_record * from{ nullptr };
std::function< activation_record*(activation_record*&) > ontop{};
bool terminated{ false };
bool force_unwind{ false };
static activation_record *& current() noexcept;
// used for toplevel-context
// (e.g. main context, thread-entry context)
activation_record() noexcept {
#if ( _WIN32_WINNT > 0x0600)
if ( ::IsThreadAFiber() ) {
fiber = ::GetCurrentFiber();
} else {
fiber = ::ConvertThreadToFiber( nullptr);
}
#else
fiber = ::ConvertThreadToFiber( nullptr);
if ( BOOST_UNLIKELY( nullptr == fiber) ) {
DWORD err = ::GetLastError();
BOOST_ASSERT( ERROR_ALREADY_FIBER == err);
fiber = ::GetCurrentFiber();
BOOST_ASSERT( nullptr != fiber);
BOOST_ASSERT( reinterpret_cast< LPVOID >( 0x1E00) != fiber);
}
#endif
}
activation_record( stack_context sctx_) noexcept :
sctx{ sctx_ },
main_ctx{ false } {
}
virtual ~activation_record() {
if ( BOOST_UNLIKELY( main_ctx) ) {
::ConvertFiberToThread();
} else {
::DeleteFiber( fiber);
}
}
activation_record( activation_record const&) = delete;
activation_record & operator=( activation_record const&) = delete;
bool is_main_context() const noexcept {
return main_ctx;
}
activation_record * resume() {
from = current();
// store `this` in static, thread local pointer
// `this` will become the active (running) context
current() = this;
// context switch from parent context to `this`-context
// context switch
::SwitchToFiber( fiber);
#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 >
activation_record * resume_with( Fn && fn) {
from = current();
// 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)
current()->ontop = std::bind(
[](typename std::decay< Fn >::type & fn, activation_record *& ptr){
Ctx c{ ptr };
c = fn( std::move( c) );
if ( ! c) {
ptr = nullptr;
}
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
return exchange( c.ptr_, nullptr);
#else
return std::exchange( c.ptr_, nullptr);
#endif
},
std::forward< Fn >( fn),
std::placeholders::_1);
#else
current()->ontop = [fn=std::forward<Fn>(fn)](activation_record *& ptr){
Ctx c{ ptr };
c = fn( std::move( c) );
if ( ! c) {
ptr = nullptr;
}
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
return exchange( c.ptr_, nullptr);
#else
return std::exchange( c.ptr_, nullptr);
#endif
};
#endif
// context switch
::SwitchToFiber( fiber);
#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 };
explicit forced_unwind( activation_record * from_) :
from{ from_ } {
}
};
template< typename Ctx, typename StackAlloc, typename Fn >
class capture_record : public activation_record {
private:
typename std::decay< StackAlloc >::type salloc_;
typename std::decay< Fn >::type fn_;
static void destroy( capture_record * p) noexcept {
typename std::decay< StackAlloc >::type salloc = std::move( 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 && salloc, Fn && fn) noexcept :
activation_record( sctx),
salloc_( std::forward< StackAlloc >( salloc)),
fn_( std::forward< Fn >( fn) ) {
}
void deallocate() noexcept override final {
BOOST_ASSERT( main_ctx || ( ! main_ctx && terminated) );
destroy( this);
}
void run() {
Ctx c{ from };
try {
// invoke context-function
#if defined(BOOST_NO_CXX17_STD_INVOKE)
c = boost::context::detail::invoke( fn_, std::move( c) );
#else
c = std::invoke( fn_, std::move( c) );
#endif
} catch ( forced_unwind const& ex) {
c = Ctx{ ex.from };
}
// this context has finished its task
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 >
static activation_record * create_context1( StackAlloc && salloc, Fn && fn) {
typedef capture_record< Ctx, StackAlloc, Fn > capture_t;
auto sctx = salloc.allocate();
BOOST_ASSERT( ( sizeof( capture_t) ) < sctx.size);
// reserve space for control structure
void * storage = reinterpret_cast< void * >(
( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( capture_t) ) )
& ~ static_cast< uintptr_t >( 0xff) );
// placment new for control structure on context stack
capture_t * record = new ( storage) capture_t{
sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
// create user-context
record->fiber = ::CreateFiber( sctx.size, & detail::entry_func< capture_t >, record);
return record;
}
template< typename Ctx, typename StackAlloc, typename Fn >
static activation_record * create_context2( preallocated palloc, StackAlloc && salloc, Fn && fn) {
typedef capture_record< Ctx, StackAlloc, Fn > capture_t;
BOOST_ASSERT( ( sizeof( capture_t) ) < palloc.size);
// reserve space for control structure
void * storage = reinterpret_cast< void * >(
( reinterpret_cast< uintptr_t >( palloc.sp) - static_cast< uintptr_t >( sizeof( capture_t) ) )
& ~ static_cast< uintptr_t >( 0xff) );
// placment new for control structure on context stack
capture_t * record = new ( storage) capture_t{
palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
// create user-context
record->fiber = ::CreateFiber( palloc.sctx.size, & detail::entry_func< capture_t >, record);
return record;
}
}
class BOOST_CONTEXT_DECL continuation {
private:
friend struct detail::activation_record;
template< typename Ctx, typename StackAlloc, typename Fn >
friend class detail::capture_record;
template< typename Ctx, typename StackAlloc, typename Fn >
friend detail::activation_record * detail::create_context1( StackAlloc &&, Fn &&);
template< typename Ctx, typename StackAlloc, typename Fn >
friend detail::activation_record * detail::create_context2( preallocated, StackAlloc &&, Fn &&);
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 ( BOOST_UNLIKELY( nullptr != ptr_) && ! ptr_->main_ctx) {
if ( BOOST_LIKELY( ! ptr_->terminated) ) {
ptr_->force_unwind = true;
ptr_->resume();
BOOST_ASSERT( ptr_->terminated);
}
ptr_->deallocate();
}
}
continuation( continuation const&) = delete;
continuation & operator=( continuation const&) = delete;
continuation( continuation && other) noexcept {
swap( other);
}
continuation & operator=( continuation && other) noexcept {
if ( BOOST_LIKELY( this != & other) ) {
continuation tmp = std::move( other);
swap( tmp);
}
return * this;
}
continuation resume() & {
return std::move( * this).resume();
}
continuation resume() && {
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::activation_record * ptr = detail::exchange( ptr_, nullptr)->resume();
#else
detail::activation_record * ptr = std::exchange( ptr_, nullptr)->resume();
#endif
if ( BOOST_UNLIKELY( detail::activation_record::current()->force_unwind) ) {
throw detail::forced_unwind{ ptr};
} else if ( BOOST_UNLIKELY( nullptr != detail::activation_record::current()->ontop) ) {
ptr = detail::activation_record::current()->ontop( ptr);
detail::activation_record::current()->ontop = nullptr;
}
return { ptr };
}
template< typename Fn >
continuation resume_with( Fn && fn) & {
return std::move( * this).resume_with( std::forward< Fn >( fn) );
}
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 ( BOOST_UNLIKELY( detail::activation_record::current()->force_unwind) ) {
throw detail::forced_unwind{ ptr};
} else if ( BOOST_UNLIKELY( nullptr != detail::activation_record::current()->ontop) ) {
ptr = detail::activation_record::current()->ontop( ptr);
detail::activation_record::current()->ontop = nullptr;
}
return { ptr };
}
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_;
}
#if !defined(BOOST_EMBTC)
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}";
}
}
#else
template< typename charT, class traitsT >
friend std::basic_ostream< charT, traitsT > &
operator<<( std::basic_ostream< charT, traitsT > & os, continuation const& other);
#endif
void swap( continuation & other) noexcept {
std::swap( ptr_, other.ptr_);
}
};
#if defined(BOOST_EMBTC)
template< typename charT, class traitsT >
inline 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}";
}
}
#endif
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) {
return continuation{
detail::create_context1< continuation >(
std::forward< StackAlloc >( 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_context2< continuation >(
palloc, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) }.resume();
}
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

@@ -133,4 +133,10 @@ static constexpr std::size_t prefetch_stride{ 4 * cacheline_length };
# define BOOST_CONTEXT_USE_MAP_STACK
#endif
#if defined(BOOST_NO_CXX11_NULLPTR)
# define BOOST_CONTEXT_NULLPTR 0
#else
# define BOOST_CONTEXT_NULLPTR nullptr
#endif
#endif // BOOST_CONTEXT_DETAIL_CONFIG_H

View File

@@ -7,9 +7,14 @@
#ifndef BOOST_CONTEXT_DETAIL_DISABLE_OVERLOAD_H
#define BOOST_CONTEXT_DETAIL_DISABLE_OVERLOAD_H
#if ! defined(BOOST_NO_CXX11_HDR_TYPE_TRAITS)
#include <type_traits>
#endif
#include <boost/config.hpp>
#if defined(BOOST_NO_CXX11_HDR_TYPE_TRAITS)
#include <boost/type_traits.hpp>
#endif
#include <boost/context/detail/config.hpp>
@@ -21,15 +26,15 @@ namespace boost {
namespace context {
namespace detail {
// http://ericniebler.com/2013/08/07/universal-references-and-the-copy-constructo/
template< typename X, typename Y >
using disable_overload =
typename std::enable_if<
! std::is_base_of<
struct disable_overload : public
boost::enable_if<
! boost::is_base_of<
X,
typename std::decay< Y >::type
>::value
>::type;
typename boost::decay< Y >::type
>
>
{};
}}}

View File

@@ -21,9 +21,11 @@ namespace context {
namespace detail {
struct forced_unwind {
fcontext_t fctx{ nullptr };
fcontext_t fctx;
forced_unwind() = default;
forced_unwind() BOOST_NOEXCEPT_OR_NOTHROW :
fctx( BOOST_CONTEXT_NULLPTR) {
}
forced_unwind( fcontext_t fctx_) :
fctx( fctx_) {

View File

@@ -11,6 +11,7 @@
#include <utility>
#include <boost/config.hpp>
#include <boost/move/move.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
@@ -21,9 +22,9 @@ namespace context {
namespace detail {
template< typename T, typename U = T >
T exchange( T & t, U && nv) {
T ov = std::move( t);
t = std::forward< U >( nv);
T exchange( T & t, BOOST_RV_REF( U) nv) {
T ov = boost::move( t);
t = boost::forward< U >( nv);
return ov;
}

View File

@@ -25,6 +25,16 @@ typedef void* fcontext_t;
struct transfer_t {
fcontext_t fctx;
void * data;
transfer_t() BOOST_NOEXCEPT :
fctx( BOOST_CONTEXT_NULLPTR),
data( BOOST_CONTEXT_NULLPTR) {
}
transfer_t( fcontext_t fctx_, void * data_) BOOST_NOEXCEPT :
fctx( fctx_),
data( data_) {
}
};
extern "C" BOOST_CONTEXT_DECL

View File

@@ -17,12 +17,13 @@
#include <functional>
#include <memory>
#include <ostream>
#include <tuple>
#include <utility>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/move/move.hpp>
#include <boost/type_traits.hpp>
#include <boost/utility/explicit_operator_bool.hpp>
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
#include <boost/context/detail/exchange.hpp>
@@ -33,7 +34,6 @@
#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>
@@ -56,32 +56,32 @@ namespace detail {
inline
transfer_t fiber_unwind( transfer_t t) {
throw forced_unwind( t.fctx);
return { nullptr, nullptr };
return transfer_t( BOOST_CONTEXT_NULLPTR, BOOST_CONTEXT_NULLPTR);
}
template< typename Rec >
transfer_t fiber_exit( transfer_t t) noexcept {
transfer_t fiber_exit( transfer_t t) BOOST_NOEXCEPT_OR_NOTHROW {
Rec * rec = static_cast< Rec * >( t.data);
// destroy context stack
rec->deallocate();
return { nullptr, nullptr };
return transfer_t( BOOST_CONTEXT_NULLPTR, BOOST_CONTEXT_NULLPTR);
}
template< typename Rec >
void fiber_entry( transfer_t t) noexcept {
void fiber_entry( transfer_t t) BOOST_NOEXCEPT_OR_NOTHROW {
// transfer control structure to the context-stack
Rec * rec = static_cast< Rec * >( t.data);
BOOST_ASSERT( nullptr != t.fctx);
BOOST_ASSERT( nullptr != rec);
BOOST_ASSERT( BOOST_CONTEXT_NULLPTR != t.fctx);
BOOST_ASSERT( BOOST_CONTEXT_NULLPTR != rec);
try {
// jump back to `create_context()`
t = jump_fcontext( t.fctx, nullptr);
t = jump_fcontext( t.fctx, BOOST_CONTEXT_NULLPTR);
// start executing
t.fctx = rec->run( t.fctx);
} catch ( forced_unwind const& ex) {
t = { ex.fctx, nullptr };
t = transfer_t( ex.fctx, BOOST_CONTEXT_NULLPTR);
}
BOOST_ASSERT( nullptr != t.fctx);
BOOST_ASSERT( BOOST_CONTEXT_NULLPTR != t.fctx);
// destroy context-stack of `this`context on next context
ontop_fcontext( t.fctx, rec, fiber_exit< Rec >);
BOOST_ASSERT_MSG( false, "context already terminated");
@@ -89,15 +89,15 @@ void fiber_entry( transfer_t t) noexcept {
template< typename Ctx, typename Fn >
transfer_t fiber_ontop( transfer_t t) {
BOOST_ASSERT( nullptr != t.data);
BOOST_ASSERT( BOOST_CONTEXT_NULLPTR != t.data);
auto p = *static_cast< Fn * >( t.data);
t.data = nullptr;
t.data = BOOST_CONTEXT_NULLPTR;
// execute function, pass fiber via reference
Ctx c = p( Ctx{ t.fctx } );
Ctx c = p( Ctx( t.fctx) );
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
return { exchange( c.fctx_, nullptr), nullptr };
return transfer_t( exchange( c.fctx_, BOOST_CONTEXT_NULLPTR), BOOST_CONTEXT_NULLPTR);
#else
return { std::exchange( c.fctx_, nullptr), nullptr };
return transfer_t( std::exchange( c.fctx_, BOOST_CONTEXT_NULLPTR), BOOST_CONTEXT_NULLPTR);
#endif
}
@@ -105,11 +105,11 @@ template< typename Ctx, typename StackAlloc, typename Fn >
class fiber_record {
private:
stack_context sctx_;
typename std::decay< StackAlloc >::type salloc_;
typename std::decay< Fn >::type fn_;
typename boost::decay< StackAlloc >::type salloc_;
typename boost::decay< Fn >::type fn_;
static void destroy( fiber_record * p) noexcept {
typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_);
static void destroy( fiber_record * p) BOOST_NOEXCEPT_OR_NOTHROW {
typename boost::decay< StackAlloc >::type salloc = boost::move( p->salloc_);
stack_context sctx = p->sctx_;
// deallocate fiber_record
p->~fiber_record();
@@ -118,45 +118,45 @@ private:
}
public:
fiber_record( stack_context sctx, StackAlloc && salloc,
Fn && fn) noexcept :
fiber_record( stack_context sctx, BOOST_RV_REF( StackAlloc) salloc,
BOOST_RV_REF( Fn) fn) BOOST_NOEXCEPT_OR_NOTHROW :
sctx_( sctx),
salloc_( std::forward< StackAlloc >( salloc)),
fn_( std::forward< Fn >( fn) ) {
salloc_( boost::forward< StackAlloc >( salloc) ),
fn_( boost::forward< Fn >( fn) ) {
}
fiber_record( fiber_record const&) = delete;
fiber_record & operator=( fiber_record const&) = delete;
BOOST_DELETED_FUNCTION( fiber_record( fiber_record const&) );
BOOST_DELETED_FUNCTION( fiber_record & operator=( fiber_record const&) );
void deallocate() noexcept {
void deallocate() BOOST_NOEXCEPT_OR_NOTHROW {
destroy( this);
}
fcontext_t run( fcontext_t fctx) {
// invoke context-function
#if defined(BOOST_NO_CXX17_STD_INVOKE)
Ctx c = boost::context::detail::invoke( fn_, Ctx{ fctx } );
Ctx c = boost::context::detail::invoke( fn_, Ctx( fctx) );
#else
Ctx c = std::invoke( fn_, Ctx{ fctx } );
#endif
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
return exchange( c.fctx_, nullptr);
return exchange( c.fctx_, BOOST_CONTEXT_NULLPTR);
#else
return std::exchange( c.fctx_, nullptr);
return std::exchange( c.fctx_, BOOST_CONTEXT_NULLPTR);
#endif
}
};
template< typename Record, typename StackAlloc, typename Fn >
fcontext_t create_fiber1( StackAlloc && salloc, Fn && fn) {
fcontext_t create_fiber1( BOOST_RV_REF( StackAlloc) salloc, BOOST_RV_REF( Fn) fn) {
auto sctx = salloc.allocate();
// reserve space for control structure
void * storage = reinterpret_cast< void * >(
( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( Record) ) )
& ~static_cast< uintptr_t >( 0xff) );
( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( Record) ) )
& ~static_cast< uintptr_t >( 0xff) );
// placment new for control structure on context stack
Record * record = new ( storage) Record{
sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
Record * record = new ( storage) Record(
sctx, boost::forward< StackAlloc >( salloc), boost::forward< Fn >( fn) );
// 64byte gab between control structure and stack top
// should be 16byte aligned
void * stack_top = reinterpret_cast< void * >(
@@ -164,31 +164,35 @@ fcontext_t create_fiber1( StackAlloc && salloc, Fn && fn) {
void * stack_bottom = reinterpret_cast< void * >(
reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sctx.size) );
// create fast-context
const std::size_t size = reinterpret_cast< uintptr_t >( stack_top) - reinterpret_cast< uintptr_t >( stack_bottom);
const std::size_t size = reinterpret_cast< uintptr_t >( stack_top) -
reinterpret_cast< uintptr_t >( stack_bottom);
const fcontext_t fctx = make_fcontext( stack_top, size, & fiber_entry< Record >);
BOOST_ASSERT( nullptr != fctx);
BOOST_ASSERT( BOOST_CONTEXT_NULLPTR != fctx);
// transfer control structure to context-stack
return jump_fcontext( fctx, record).fctx;
}
template< typename Record, typename StackAlloc, typename Fn >
fcontext_t create_fiber2( preallocated palloc, StackAlloc && salloc, Fn && fn) {
fcontext_t create_fiber2( preallocated palloc, BOOST_RV_REF( StackAlloc) salloc,
BOOST_RV_REF( Fn) fn) {
// reserve space for control structure
void * storage = reinterpret_cast< void * >(
( reinterpret_cast< uintptr_t >( palloc.sp) - static_cast< uintptr_t >( sizeof( Record) ) )
& ~ static_cast< uintptr_t >( 0xff) );
( reinterpret_cast< uintptr_t >( palloc.sp) - static_cast< uintptr_t >( sizeof( Record) ) )
& ~ static_cast< uintptr_t >( 0xff) );
// placment new for control structure on context-stack
Record * record = new ( storage) Record{
palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
Record * record = new ( storage) Record(
palloc.sctx, boost::forward< StackAlloc >( salloc), boost::forward< Fn >( fn) );
// 64byte gab between control structure and stack top
void * stack_top = reinterpret_cast< void * >(
reinterpret_cast< uintptr_t >( storage) - static_cast< uintptr_t >( 64) );
void * stack_bottom = reinterpret_cast< void * >(
reinterpret_cast< uintptr_t >( palloc.sctx.sp) - static_cast< uintptr_t >( palloc.sctx.size) );
reinterpret_cast< uintptr_t >( palloc.sctx.sp) -
static_cast< uintptr_t >( palloc.sctx.size) );
// create fast-context
const std::size_t size = reinterpret_cast< uintptr_t >( stack_top) - reinterpret_cast< uintptr_t >( stack_bottom);
const std::size_t size = reinterpret_cast< uintptr_t >( stack_top) -
reinterpret_cast< uintptr_t >( stack_bottom);
const fcontext_t fctx = make_fcontext( stack_top, size, & fiber_entry< Record >);
BOOST_ASSERT( nullptr != fctx);
BOOST_ASSERT( BOOST_CONTEXT_NULLPTR != fctx);
// transfer control structure to context-stack
return jump_fcontext( fctx, record).fctx;
}
@@ -204,110 +208,105 @@ private:
friend detail::transfer_t
detail::fiber_ontop( detail::transfer_t);
template< typename StackAlloc, typename Fn >
friend fiber
callcc( std::allocator_arg_t, StackAlloc &&, Fn &&);
detail::fcontext_t fctx_;
template< typename StackAlloc, typename Fn >
friend fiber
callcc( std::allocator_arg_t, preallocated, StackAlloc &&, Fn &&);
detail::fcontext_t fctx_{ nullptr };
fiber( detail::fcontext_t fctx) noexcept :
fctx_{ fctx } {
fiber( detail::fcontext_t fctx) BOOST_NOEXCEPT_OR_NOTHROW :
fctx_( fctx) {
}
public:
fiber() noexcept = default;
fiber() BOOST_NOEXCEPT_OR_NOTHROW :
fctx_( BOOST_CONTEXT_NULLPTR) {
}
template< typename Fn, typename = detail::disable_overload< fiber, Fn > >
fiber( Fn && fn) :
fiber{ std::allocator_arg, fixedsize_stack(), std::forward< Fn >( fn) } {
fiber( BOOST_RV_REF( Fn) fn) :
fctx_( detail::create_fiber1< detail::fiber_record< fiber, fixedsize_stack, Fn > >(
fixedsize_stack(), boost::forward< Fn >( fn) ) ) {
}
template< typename StackAlloc, typename Fn >
fiber( std::allocator_arg_t, StackAlloc && salloc, Fn && fn) :
fctx_{ detail::create_fiber1< detail::fiber_record< fiber, StackAlloc, Fn > >(
std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) } {
fiber( std::allocator_arg_t, BOOST_RV_REF( StackAlloc) salloc, BOOST_RV_REF( Fn) fn) :
fctx_( detail::create_fiber1< detail::fiber_record< fiber, StackAlloc, Fn > >(
boost::forward< StackAlloc >( salloc), boost::forward< Fn >( fn) ) ) {
}
template< typename StackAlloc, typename Fn >
fiber( std::allocator_arg_t, preallocated palloc, StackAlloc && salloc, Fn && fn) :
fctx_{ detail::create_fiber2< detail::fiber_record< fiber, StackAlloc, Fn > >(
palloc, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) } {
fiber( std::allocator_arg_t, preallocated palloc, BOOST_RV_REF( StackAlloc) salloc,
BOOST_RV_REF( Fn) fn) :
fctx_( detail::create_fiber2< detail::fiber_record< fiber, StackAlloc, Fn > >(
palloc, boost::forward< StackAlloc >( salloc), boost::forward< Fn >( fn) ) ) {
}
#if defined(BOOST_USE_SEGMENTED_STACKS)
template< typename Fn >
fiber( std::allocator_arg_t, segmented_stack, Fn &&);
template< typename StackAlloc, typename Fn >
fiber( std::allocator_arg_t, preallocated, segmented_stack, Fn &&);
#endif
~fiber() {
if ( BOOST_UNLIKELY( nullptr != fctx_) ) {
if ( BOOST_UNLIKELY( BOOST_CONTEXT_NULLPTR != fctx_) ) {
detail::ontop_fcontext(
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::exchange( fctx_, nullptr),
detail::exchange( fctx_, BOOST_CONTEXT_NULLPTR),
#else
std::exchange( fctx_, nullptr),
std::exchange( fctx_, BOOST_CONTEXT_NULLPTR),
#endif
nullptr,
BOOST_CONTEXT_NULLPTR,
detail::fiber_unwind);
}
}
fiber( fiber && other) noexcept {
fiber( BOOST_RV_REF( fiber) other) BOOST_NOEXCEPT_OR_NOTHROW :
fctx_( BOOST_CONTEXT_NULLPTR) {
swap( other);
}
fiber & operator=( fiber && other) noexcept {
fiber & operator=( BOOST_RV_REF( fiber) other) BOOST_NOEXCEPT_OR_NOTHROW {
if ( BOOST_LIKELY( this != & other) ) {
fiber tmp = std::move( other);
fiber tmp = boost::move( other);
swap( tmp);
}
return * this;
}
fiber( fiber const& other) noexcept = delete;
fiber & operator=( fiber const& other) noexcept = delete;
BOOST_DELETED_FUNCTION( fiber( fiber const& other) BOOST_NOEXCEPT_OR_NOTHROW);
BOOST_DELETED_FUNCTION( fiber & operator=( fiber const& other) BOOST_NOEXCEPT_OR_NOTHROW);
fiber resume() && {
BOOST_ASSERT( nullptr != fctx_);
return { detail::jump_fcontext(
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::exchange( fctx_, nullptr),
#else
std::exchange( fctx_, nullptr),
fiber resume()
#if ! defined(BOOST_NO_CXX11_REF_QUALIFIERS)
&&
#endif
nullptr).fctx };
{
BOOST_ASSERT( BOOST_CONTEXT_NULLPTR != fctx_);
return fiber( detail::jump_fcontext(
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::exchange( fctx_, BOOST_CONTEXT_NULLPTR),
#else
std::exchange( fctx_, BOOST_CONTEXT_NULLPTR),
#endif
BOOST_CONTEXT_NULLPTR).fctx);
}
template< typename Fn >
fiber resume_with( Fn && fn) && {
BOOST_ASSERT( nullptr != fctx_);
auto p = std::forward< Fn >( fn);
return { detail::ontop_fcontext(
fiber resume_with( BOOST_RV_REF( Fn) fn)
#if ! defined(BOOST_NO_CXX11_REF_QUALIFIERS)
&&
#endif
{
BOOST_ASSERT( BOOST_CONTEXT_NULLPTR != fctx_);
auto p = boost::forward< Fn >( fn);
return fiber( detail::ontop_fcontext(
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
detail::exchange( fctx_, nullptr),
detail::exchange( fctx_, BOOST_CONTEXT_NULLPTR),
#else
std::exchange( fctx_, nullptr),
std::exchange( fctx_, BOOST_CONTEXT_NULLPTR),
#endif
& p,
detail::fiber_ontop< fiber, decltype(p) >).fctx };
detail::fiber_ontop< fiber, decltype(p) >).fctx); // FIXME: decltype
}
explicit operator bool() const noexcept {
return nullptr != fctx_;
BOOST_EXPLICIT_OPERATOR_BOOL();
bool operator!() const BOOST_NOEXCEPT_OR_NOTHROW {
return BOOST_CONTEXT_NULLPTR == fctx_;
}
bool operator!() const noexcept {
return nullptr == fctx_;
}
bool operator<( fiber const& other) const noexcept {
bool operator<( fiber const& other) const BOOST_NOEXCEPT_OR_NOTHROW {
return fctx_ < other.fctx_;
}
@@ -316,7 +315,7 @@ public:
template< typename charT, class traitsT >
friend std::basic_ostream< charT, traitsT > &
operator<<( std::basic_ostream< charT, traitsT > & os, fiber const& other) {
if ( nullptr != other.fctx_) {
if ( BOOST_CONTEXT_NULLPTR != other.fctx_) {
return os << other.fctx_;
} else {
return os << "{not-a-context}";
@@ -331,7 +330,7 @@ public:
#endif
void swap( fiber & other) noexcept {
void swap( fiber & other) BOOST_NOEXCEPT_OR_NOTHROW {
std::swap( fctx_, other.fctx_);
}
};
@@ -341,7 +340,7 @@ public:
template< typename charT, class traitsT >
inline std::basic_ostream< charT, traitsT > &
operator<<( std::basic_ostream< charT, traitsT > & os, fiber const& other) {
if ( nullptr != other.fctx_) {
if ( BOOST_CONTEXT_NULLPTR != other.fctx_) {
return os << other.fctx_;
} else {
return os << "{not-a-context}";
@@ -351,7 +350,7 @@ public:
#endif
inline
void swap( fiber & l, fiber & r) noexcept {
void swap( fiber & l, fiber & r) BOOST_NOEXCEPT_OR_NOTHROW {
l.swap( r);
}

View File

@@ -4,15 +4,20 @@
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_CONTEXT_POOLED_pooled_fixedsize_H
#define BOOST_CONTEXT_POOLED_pooled_fixedsize_H
#ifndef BOOST_CONTEXT_POOLED_POOLED_FIXEDSIZE_H
#define BOOST_CONTEXT_POOLED_POOLED_FIXEDSIZE_H
#if ! defined(BOOST_NO_CXX11_HDR_ATOMIC)
#include <atomic>
#endif
#include <cstddef>
#include <cstdlib>
#include <new>
#include <boost/assert.hpp>
#if defined(BOOST_NO_CXX11_HDR_ATOMIC)
#include <boost/atomic.hpp>
#endif
#include <boost/config.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/pool/pool.hpp>
@@ -43,20 +48,21 @@ namespace context {
namespace detail {
template< typename traitsT >
struct map_stack_allocator {
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
static char * malloc( const size_type bytes) {
void * block;
if ( ::posix_memalign( &block, traitsT::page_size(), bytes) != 0) {
static char * malloc( size_type bytes) {
void * block = BOOST_CONTEXT_NULLPTR;
if ( 0 != ::posix_memalign( &block, traitsT::page_size(), bytes) ) {
return 0;
}
if ( mmap( block, bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_STACK, -1, 0) == MAP_FAILED) {
if ( MAP_FAILED == ::mmap( block, bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_STACK, -1, 0) ) {
std::free( block);
return 0;
return BOOST_CONTEXT_NULLPTR;
}
return reinterpret_cast< char * >( block);
}
static void free( char * const block) {
std::free( block);
}
@@ -69,7 +75,11 @@ class basic_pooled_fixedsize_stack {
private:
class storage {
private:
#if ! defined(BOOST_NO_CXX11_HDR_ATOMIC)
std::atomic< std::size_t > use_count_;
#else
boost::atomic< std::size_t > use_count_;
#endif
std::size_t stack_size_;
#if defined(BOOST_CONTEXT_USE_MAP_STACK)
boost::pool< detail::map_stack_allocator< traitsT > > storage_;
@@ -149,4 +159,4 @@ typedef basic_pooled_fixedsize_stack< stack_traits > pooled_fixedsize_stack;
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_CONTEXT_POOLED_pooled_fixedsize_H
#endif // BOOST_CONTEXT_POOLED_POOLED_FIXEDSIZE_H

View File

@@ -25,7 +25,7 @@ struct preallocated {
std::size_t size;
stack_context sctx;
preallocated( void * sp_, std::size_t size_, stack_context sctx_) noexcept :
preallocated( void * sp_, std::size_t size_, stack_context sctx_) BOOST_NOEXCEPT_OR_NOTHROW :
sp( sp_), size( size_), sctx( sctx_) {
}
};

View File

@@ -1,58 +0,0 @@
// 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)
#if defined(BOOST_USE_UCONTEXT)
#include "boost/context/continuation_ucontext.hpp"
#elif defined(BOOST_USE_WINFIB)
#include "boost/context/continuation_winfib.hpp"
#endif
#include <boost/config.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace context {
namespace detail {
// zero-initialization
thread_local activation_record * current_rec;
thread_local static std::size_t counter;
// schwarz counter
activation_record_initializer::activation_record_initializer() noexcept {
if ( 0 == counter++) {
current_rec = new activation_record();
}
}
activation_record_initializer::~activation_record_initializer() {
if ( 0 == --counter) {
BOOST_ASSERT( current_rec->is_main_context() );
delete current_rec;
}
}
}
namespace detail {
activation_record *&
activation_record::current() noexcept {
// initialized the first time control passes; per thread
thread_local static activation_record_initializer initializer;
return current_rec;
}
}
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif

View File

@@ -148,63 +148,7 @@ test-suite minimal :
cxx11_template_aliases
cxx11_thread_local
cxx11_variadic_templates ]
: test_fiber_segmented ]
[ run test_callcc.cpp :
: :
<context-impl>fcontext
[ requires cxx11_auto_declarations
cxx11_constexpr
cxx11_defaulted_functions
cxx11_final
cxx11_hdr_thread
cxx11_hdr_tuple
cxx11_lambdas
cxx11_noexcept
cxx11_nullptr
cxx11_rvalue_references
cxx11_template_aliases
cxx11_thread_local
cxx11_variadic_templates ]
: test_callcc_asm ]
[ run test_callcc.cpp :
: :
<conditional>@native-impl
[ requires cxx11_auto_declarations
cxx11_constexpr
cxx11_defaulted_functions
cxx11_final
cxx11_hdr_thread
cxx11_hdr_tuple
cxx11_lambdas
cxx11_noexcept
cxx11_nullptr
cxx11_rvalue_references
cxx11_template_aliases
cxx11_thread_local
cxx11_variadic_templates ]
: test_callcc_native ]
[ run test_callcc.cpp :
: :
<context-impl>ucontext
<conditional>@segmented-stack
[ requires cxx11_auto_declarations
cxx11_constexpr
cxx11_defaulted_functions
cxx11_final
cxx11_hdr_thread
cxx11_hdr_tuple
cxx11_lambdas
cxx11_noexcept
cxx11_nullptr
cxx11_rvalue_references
cxx11_template_aliases
cxx11_thread_local
cxx11_variadic_templates ]
: test_callcc_segmented ] ;
: test_fiber_segmented ] ;
test-suite full :
minimal ;

View File

@@ -1,516 +0,0 @@
// Copyright Oliver Kowalke 2009.
// 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 <stdio.h>
#include <stdlib.h>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <iostream>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#include <boost/array.hpp>
#include <boost/assert.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/utility.hpp>
#include <boost/variant.hpp>
#include <boost/context/continuation.hpp>
#include <boost/context/detail/config.hpp>
#ifdef BOOST_WINDOWS
#include <windows.h>
#endif
#if defined(BOOST_MSVC)
# pragma warning(push)
# pragma warning(disable: 4702 4723 4996)
#endif
typedef boost::variant<int,std::string> variant_t;
namespace ctx = boost::context;
int value1 = 0;
std::string value2;
double value3 = 0.;
struct X {
ctx::continuation foo( ctx::continuation && c, int i) {
value1 = i;
return std::move( c);
}
};
struct Y {
Y() {
value1 = 3;
}
Y( Y const&) = delete;
Y & operator=( Y const&) = delete;
~Y() {
value1 = 7;
}
};
class moveable {
public:
bool state;
int value;
moveable() :
state( false),
value( -1) {
}
moveable( int v) :
state( true),
value( v) {
}
moveable( moveable && other) :
state( other.state),
value( other.value) {
other.state = false;
other.value = -1;
}
moveable & operator=( moveable && other) {
if ( this == & other) return * this;
state = other.state;
value = other.value;
other.state = false;
other.value = -1;
return * this;
}
moveable( moveable const& other) = delete;
moveable & operator=( moveable const& other) = delete;
void operator()() {
value1 = value;
}
};
struct my_exception : public std::runtime_error {
ctx::continuation c;
my_exception( ctx::continuation && c_, char const* what) :
std::runtime_error( what),
c{ std::move( c_) } {
}
};
#ifdef BOOST_MSVC
// Optimizations can remove the integer-divide-by-zero here.
#pragma optimize("", off)
void seh( bool & catched) {
__try {
int i = 1;
i /= 0;
} __except( EXCEPTION_EXECUTE_HANDLER) {
catched = true;
}
}
#pragma optimize("", on)
#endif
void test_move() {
value1 = 0;
int i = 1;
BOOST_CHECK_EQUAL( 0, value1);
ctx::continuation c1 = ctx::callcc(
[&i](ctx::continuation && c) {
value1 = i;
c = c.resume();
value1 = i;
return std::move( c);
});
BOOST_CHECK_EQUAL( 1, value1);
BOOST_CHECK( c1);
ctx::continuation c2;
BOOST_CHECK( ! c2);
c2 = std::move( c1);
BOOST_CHECK( ! c1);
BOOST_CHECK( c2);
i = 3;
c2.resume();
BOOST_CHECK_EQUAL( 3, value1);
BOOST_CHECK( ! c1);
BOOST_CHECK( ! c2);
}
void test_bind() {
value1 = 0;
X x;
ctx::continuation c = ctx::callcc( std::bind( & X::foo, x, std::placeholders::_1, 7) );
BOOST_CHECK_EQUAL( 7, value1);
}
void test_exception() {
{
const char * what = "hello world";
ctx::continuation c = ctx::callcc(
[&what](ctx::continuation && c) {
try {
throw std::runtime_error( what);
} catch ( std::runtime_error const& e) {
value2 = e.what();
}
return std::move( c);
});
BOOST_CHECK_EQUAL( std::string( what), value2);
BOOST_CHECK( ! c);
}
#ifdef BOOST_MSVC
{
bool catched = false;
std::thread([&catched](){
ctx::continuation c = ctx::callcc([&catched](ctx::continuation && c){
c = c.resume();
seh( catched);
return std::move( c);
});
BOOST_CHECK( c );
c.resume();
}).join();
BOOST_CHECK( catched);
}
#endif
}
void test_fp() {
value3 = 0.;
double d = 7.13;
ctx::continuation c = ctx::callcc(
[&d]( ctx::continuation && c) {
d += 3.45;
value3 = d;
return std::move( c);
});
BOOST_CHECK_EQUAL( 10.58, value3);
BOOST_CHECK( ! c);
}
void test_stacked() {
value1 = 0;
value3 = 0.;
ctx::continuation c = ctx::callcc(
[](ctx::continuation && c) {
ctx::continuation c1 = ctx::callcc(
[](ctx::continuation && c) {
value1 = 3;
return std::move( c);
});
value3 = 3.14;
return std::move( c);
});
BOOST_CHECK_EQUAL( 3, value1);
BOOST_CHECK_EQUAL( 3.14, value3);
BOOST_CHECK( ! c );
}
void test_prealloc() {
value1 = 0;
ctx::default_stack alloc;
ctx::stack_context sctx( alloc.allocate() );
void * sp = static_cast< char * >( sctx.sp) - 10;
std::size_t size = sctx.size - 10;
int i = 7;
ctx::continuation c = ctx::callcc(
std::allocator_arg, ctx::preallocated( sp, size, sctx), alloc,
[&i]( ctx::continuation && c) {
value1 = i;
return std::move( c);
});
BOOST_CHECK_EQUAL( 7, value1);
BOOST_CHECK( ! c);
}
void test_ontop() {
{
int i = 3;
ctx::continuation c = ctx::callcc([&i](ctx::continuation && c) {
for (;;) {
i *= 10;
c = c.resume();
}
return std::move( c);
});
c = c.resume_with(
[&i](ctx::continuation && c){
i -= 10;
return std::move( c);
});
BOOST_CHECK( c);
BOOST_CHECK_EQUAL( i, 200);
}
{
ctx::continuation c1;
ctx::continuation c = ctx::callcc([&c1](ctx::continuation && c) {
c = c.resume();
BOOST_CHECK( ! c);
return std::move( c1);
});
c = c.resume_with(
[&c1](ctx::continuation && c){
c1 = std::move( c);
return std::move( c);
});
}
}
void test_ontop_exception() {
value1 = 0;
value2 = "";
ctx::continuation c = ctx::callcc([](ctx::continuation && c){
for (;;) {
value1 = 3;
try {
c = c.resume();
} catch ( my_exception & ex) {
value2 = ex.what();
return std::move( ex.c);
}
}
return std::move( c);
});
c = c.resume();
BOOST_CHECK_EQUAL( 3, value1);
const char * what = "hello world";
c.resume_with(
[what](ctx::continuation && c){
throw my_exception( std::move( c), what);
return std::move( c);
});
BOOST_CHECK_EQUAL( 3, value1);
BOOST_CHECK_EQUAL( std::string( what), value2);
}
void test_termination1() {
{
value1 = 0;
ctx::continuation c = ctx::callcc(
[](ctx::continuation && c){
Y y;
return c.resume();
});
BOOST_CHECK_EQUAL( 3, value1);
}
BOOST_CHECK_EQUAL( 7, value1);
{
value1 = 0;
BOOST_CHECK_EQUAL( 0, value1);
ctx::continuation c = ctx::callcc(
[](ctx::continuation && c) {
value1 = 3;
return std::move( c);
});
BOOST_CHECK_EQUAL( 3, value1);
BOOST_CHECK( ! c );
}
{
value1 = 0;
BOOST_CHECK_EQUAL( 0, value1);
int i = 3;
ctx::continuation c = ctx::callcc(
[&i](ctx::continuation && c){
value1 = i;
c = c.resume();
value1 = i;
return std::move( c);
});
BOOST_CHECK( c);
BOOST_CHECK_EQUAL( i, value1);
BOOST_CHECK( c);
i = 7;
c = c.resume();
BOOST_CHECK( ! c);
BOOST_CHECK_EQUAL( i, value1);
}
}
void test_termination2() {
{
value1 = 0;
value3 = 0.0;
ctx::continuation c = ctx::callcc(
[](ctx::continuation && c){
Y y;
value1 = 3;
value3 = 4.;
c = c.resume();
value1 = 7;
value3 = 8.;
c = c.resume();
return std::move( c);
});
BOOST_CHECK_EQUAL( 3, value1);
BOOST_CHECK_EQUAL( 4., value3);
c = c.resume();
}
BOOST_CHECK_EQUAL( 7, value1);
BOOST_CHECK_EQUAL( 8., value3);
}
void test_sscanf() {
ctx::continuation c = ctx::callcc(
[]( ctx::continuation && c) {
{
double n1 = 0;
double n2 = 0;
sscanf("3.14 7.13", "%lf %lf", & n1, & n2);
BOOST_CHECK( n1 == 3.14);
BOOST_CHECK( n2 == 7.13);
}
{
int n1=0;
int n2=0;
sscanf("1 23", "%d %d", & n1, & n2);
BOOST_CHECK( n1 == 1);
BOOST_CHECK( n2 == 23);
}
{
int n1=0;
int n2=0;
sscanf("1 jjj 23", "%d %*[j] %d", & n1, & n2);
BOOST_CHECK( n1 == 1);
BOOST_CHECK( n2 == 23);
}
return std::move( c);
});
}
void test_snprintf() {
ctx::continuation c = ctx::callcc(
[]( ctx::continuation && c) {
{
const char *fmt = "sqrt(2) = %f";
char buf[19];
snprintf( buf, sizeof( buf), fmt, std::sqrt( 2) );
BOOST_CHECK( 0 < sizeof( buf) );
BOOST_ASSERT( std::string("sqrt(2) = 1.41") == std::string( buf, 14) );
}
{
std::uint64_t n = 0xbcdef1234567890;
const char *fmt = "0x%016llX";
char buf[100];
snprintf( buf, sizeof( buf), fmt, n);
BOOST_ASSERT( std::string("0x0BCDEF1234567890") == std::string( buf, 18) );
}
return std::move( c);
});
}
#ifdef BOOST_WINDOWS
void test_bug12215() {
ctx::continuation c = ctx::callcc(
[](ctx::continuation && c) {
char buffer[MAX_PATH];
GetModuleFileName( nullptr, buffer, MAX_PATH);
return std::move( c);
});
}
#endif
void test_goodcatch() {
value1 = 0;
value3 = 0.0;
{
ctx::continuation c = ctx::callcc(
[](ctx::continuation && c) {
Y y;
value3 = 2.;
c = c.resume();
try {
value3 = 3.;
c = c.resume();
} catch ( boost::context::detail::forced_unwind const&) {
value3 = 4.;
throw;
} catch (...) {
value3 = 5.;
}
value3 = 6.;
return std::move( c);
});
BOOST_CHECK_EQUAL( 3, value1);
BOOST_CHECK_EQUAL( 2., value3);
c = c.resume();
BOOST_CHECK_EQUAL( 3, value1);
BOOST_CHECK_EQUAL( 3., value3);
}
BOOST_CHECK_EQUAL( 7, value1);
BOOST_CHECK_EQUAL( 4., value3);
}
void test_badcatch() {
#if 0
value1 = 0;
value3 = 0.;
{
ctx::continuation c = ctx::callcc(
[](ctx::continuation && c) {
Y y;
try {
value3 = 3.;
c = c.resume();
} catch (...) {
value3 = 5.;
}
return std::move( c);
});
BOOST_CHECK_EQUAL( 3, value1);
BOOST_CHECK_EQUAL( 3., value3);
// the destruction of ctx here will cause a forced_unwind to be thrown that is not caught
// in fn19. That will trigger the "not caught" assertion in ~forced_unwind. Getting that
// assertion to propogate bak here cleanly is non-trivial, and there seems to not be a good
// way to hook directly into the assertion when it happens on an alternate stack.
std::move( c);
}
BOOST_CHECK_EQUAL( 7, value1);
BOOST_CHECK_EQUAL( 4., value3);
#endif
}
boost::unit_test::test_suite * init_unit_test_suite( int, char* [])
{
boost::unit_test::test_suite * test =
BOOST_TEST_SUITE("Boost.Context: callcc test suite");
test->add( BOOST_TEST_CASE( & test_move) );
test->add( BOOST_TEST_CASE( & test_bind) );
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_prealloc) );
test->add( BOOST_TEST_CASE( & test_ontop) );
test->add( BOOST_TEST_CASE( & test_ontop_exception) );
test->add( BOOST_TEST_CASE( & test_termination1) );
test->add( BOOST_TEST_CASE( & test_termination2) );
test->add( BOOST_TEST_CASE( & test_sscanf) );
test->add( BOOST_TEST_CASE( & test_snprintf) );
#ifdef BOOST_WINDOWS
test->add( BOOST_TEST_CASE( & test_bug12215) );
#endif
test->add( BOOST_TEST_CASE( & test_goodcatch) );
test->add( BOOST_TEST_CASE( & test_badcatch) );
return test;
}
#if defined(BOOST_MSVC)
# pragma warning(pop)
#endif