| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526 | 
//          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#endifextern "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#endifnamespace boost {namespace context {namespace detail {// tampoline function// entered if the execution context// is resumed for the first timetemplate< 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 };#ifndef BOOST_ASSERT_IS_VOID    bool                    caught{ false };#endif    forced_unwind( activation_record * from_) noexcept :        from{ from_ } {    }#ifndef BOOST_ASSERT_IS_VOID    ~forced_unwind() {        BOOST_ASSERT( caught);    }#endif};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 };#ifndef BOOST_ASSERT_IS_VOID            const_cast< forced_unwind & >( ex).caught = true;#endif        }        // 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_;    }    template< typename charT, class traitsT >    friend std::basic_ostream< charT, traitsT > &    operator<<( std::basic_ostream< charT, traitsT > & os, continuation const& other) {        if ( nullptr != other.ptr_) {            return os << other.ptr_;        } else {            return os << "{not-a-context}";        }    }    void swap( continuation & other) noexcept {        std::swap( ptr_, other.ptr_);    }};template<    typename Fn,    typename = detail::disable_overload< continuation, Fn >>continuationcallcc( 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 >continuationcallcc( 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 >continuationcallcc( 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();}inlinevoid swap( continuation & l, continuation & r) noexcept {    l.swap( r);}}}#ifdef BOOST_HAS_ABI_HEADERS# include BOOST_ABI_SUFFIX#endif#endif // BOOST_CONTEXT_CONTINUATION_H
 |