You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
373 lines
12 KiB
373 lines
12 KiB
|
|
// 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 }; |
|
#ifndef BOOST_ASSERT_IS_VOID |
|
const_cast< forced_unwind & >( ex).caught = true; |
|
#endif |
|
} |
|
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
|
|
|