mirror of
https://github.com/boostorg/context.git
synced 2026-01-19 04:02:17 +00:00
C++03
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
>
|
||||
>
|
||||
{};
|
||||
|
||||
}}}
|
||||
|
||||
|
||||
@@ -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_) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_) {
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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
|
||||
@@ -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 ;
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user