2
0
mirror of https://github.com/boostorg/fiber.git synced 2026-02-17 01:32:32 +00:00
Files
fiber/include/boost/fiber/context.hpp
2015-09-30 19:58:04 +02:00

541 lines
16 KiB
C++

// Copyright Oliver Kowalke 2013.
// 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_FIBERS_CONTEXT_H
#define BOOST_FIBERS_CONTEXT_H
#include <atomic>
#include <chrono>
#include <exception>
#include <map>
#include <memory>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/context/detail/invoke.hpp>
#include <boost/context/execution_context.hpp>
#include <boost/context/stack_context.hpp>
#include <boost/intrusive/list.hpp>
#include <boost/intrusive/parent_from_member.hpp>
#include <boost/intrusive/set.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/fss.hpp>
#include <boost/fiber/detail/spinlock.hpp>
#include <boost/fiber/exceptions.hpp>
#include <boost/fiber/fixedsize_stack.hpp>
#include <boost/fiber/properties.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
class context;
class fiber;
class scheduler;
namespace detail {
struct wait_tag;
typedef intrusive::list_member_hook<
intrusive::tag< wait_tag >,
intrusive::link_mode<
intrusive::auto_unlink
>
> wait_hook;
// declaration of the functor that converts between
// the context class and the wait-hook
struct wait_functor {
// required types
typedef wait_hook hook_type;
typedef hook_type * hook_ptr;
typedef const hook_type * const_hook_ptr;
typedef context value_type;
typedef value_type * pointer;
typedef const value_type * const_pointer;
// required static functions
static hook_ptr to_hook_ptr( value_type &value);
static const_hook_ptr to_hook_ptr( value_type const& value);
static pointer to_value_ptr( hook_ptr n);
static const_pointer to_value_ptr( const_hook_ptr n);
};
struct ready_tag;
typedef intrusive::list_member_hook<
intrusive::tag< ready_tag >,
intrusive::link_mode<
intrusive::auto_unlink
>
> ready_hook;
struct remote_ready_tag;
typedef intrusive::list_member_hook<
intrusive::tag< remote_ready_tag >,
intrusive::link_mode<
intrusive::auto_unlink
>
> remote_ready_hook;
struct sleep_tag;
typedef intrusive::set_member_hook<
intrusive::tag< sleep_tag >,
intrusive::link_mode<
intrusive::auto_unlink
>
> sleep_hook;
struct terminated_tag;
typedef intrusive::list_member_hook<
intrusive::tag< terminated_tag >,
intrusive::link_mode<
intrusive::auto_unlink
>
> terminated_hook;
struct managed_tag;
typedef intrusive::list_member_hook<
intrusive::tag< managed_tag >,
intrusive::link_mode<
intrusive::auto_unlink
>
> worker_hook;
}
struct main_context_t {};
const main_context_t main_context{};
struct dispatcher_context_t {};
const dispatcher_context_t dispatcher_context{};
struct worker_context_t {};
const worker_context_t worker_context{};
class BOOST_FIBERS_DECL context {
private:
enum flag_t {
flag_main_context = 1 << 1,
flag_dispatcher_context = 1 << 2,
flag_worker_context = 1 << 3,
flag_terminated = 1 << 4,
flag_interruption_blocked = 1 << 5,
flag_interruption_requested = 1 << 6,
flag_forced_unwind = 1 << 7
};
struct BOOST_FIBERS_DECL fss_data {
void * vp;
detail::fss_cleanup_function::ptr_t cleanup_function;
fss_data() :
vp( nullptr),
cleanup_function() {
}
fss_data( void * vp_,
detail::fss_cleanup_function::ptr_t const& fn) :
vp( vp_),
cleanup_function( fn) {
BOOST_ASSERT( cleanup_function);
}
void do_cleanup() {
( * cleanup_function)( vp);
}
};
typedef std::map< uintptr_t, fss_data > fss_data_t;
static thread_local context * active_;
#if ! defined(BOOST_FIBERS_NO_ATOMICS)
std::atomic< std::size_t > use_count_;
std::atomic< int > flags_;
#else
std::size_t use_count_;
int flags_;
#endif
scheduler * scheduler_;
boost::context::execution_context ctx_;
public:
detail::spinlock hook_splk_;
detail::worker_hook worker_hook_;
detail::terminated_hook terminated_hook_;
detail::ready_hook ready_hook_;
detail::remote_ready_hook remote_ready_hook_;
detail::sleep_hook sleep_hook_;
detail::wait_hook wait_hook_;
std::chrono::steady_clock::time_point tp_;
typedef intrusive::list<
context,
intrusive::function_hook< detail::wait_functor >,
intrusive::constant_time_size< false > > wait_queue_t;
private:
fss_data_t fss_data_;
wait_queue_t wait_queue_;
detail::spinlock splk_;
fiber_properties * properties_;
void set_terminated_() noexcept;
public:
class id {
private:
context * impl_;
public:
id() noexcept :
impl_( nullptr) {
}
explicit id( context * impl) noexcept :
impl_( impl) {
}
bool operator==( id const& other) const noexcept {
return impl_ == other.impl_;
}
bool operator!=( id const& other) const noexcept {
return impl_ != other.impl_;
}
bool operator<( id const& other) const noexcept {
return impl_ < other.impl_;
}
bool operator>( id const& other) const noexcept {
return other.impl_ < impl_;
}
bool operator<=( id const& other) const noexcept {
return ! ( * this > other);
}
bool operator>=( id const& other) const noexcept {
return ! ( * this < other);
}
template< typename charT, class traitsT >
friend std::basic_ostream< charT, traitsT > &
operator<<( std::basic_ostream< charT, traitsT > & os, id const& other) {
if ( nullptr != other.impl_) {
return os << other.impl_;
} else {
return os << "{not-valid}";
}
}
explicit operator bool() const noexcept {
return nullptr != impl_;
}
bool operator!() const noexcept {
return nullptr == impl_;
}
};
static context * active() noexcept;
static void active( context * active) noexcept;
static void reset_active() noexcept;
// main fiber context
context( main_context_t);
// dispatcher fiber context
context( dispatcher_context_t, boost::context::preallocated const&,
fixedsize_stack const&, scheduler *);
// worker fiber context
template< typename StackAlloc, typename Fn, typename ... Args >
context( worker_context_t,
boost::context::preallocated palloc, StackAlloc salloc,
Fn && fn, Args && ... args) :
use_count_( 1), // fiber instance or scheduler owner
flags_( flag_worker_context),
scheduler_( nullptr),
ctx_( std::allocator_arg, palloc, salloc,
// mutable: generated operator() is not const -> enables std::move( fn)
// std::make_tuple: stores decayed copies of its args, implicitly unwraps std::reference_wrapper
[=,fn_=std::forward< Fn >( fn),tpl_=std::make_tuple( std::forward< Args >( args) ...),
ctx=boost::context::execution_context::current()] () mutable -> void {
try {
auto fn( std::move( fn_) );
auto tpl( std::move( tpl_) );
// jump back after initialization
ctx();
// check for unwinding
if ( ! unwinding_requested() ) {
boost::context::detail::do_invoke( fn, tpl);
}
} catch ( fiber_interrupted const&) {
} catch ( forced_unwind const&) {
} catch ( ... ) {
std::terminate();
}
// mark fiber as terminated
set_terminated_();
// notify waiting (joining) fibers
release();
// switch to another fiber
suspend();
BOOST_ASSERT_MSG( false, "fiber already terminated");
}),
hook_splk_(),
worker_hook_(),
terminated_hook_(),
ready_hook_(),
remote_ready_hook_(),
sleep_hook_(),
wait_hook_(),
tp_( (std::chrono::steady_clock::time_point::max)() ),
fss_data_(),
wait_queue_(),
splk_(),
properties_( nullptr) {
// switch for initialization
ctx_();
}
virtual ~context();
void set_scheduler( scheduler *);
scheduler * get_scheduler() const noexcept;
id get_id() const noexcept;
void resume();
void suspend() noexcept;
void release() noexcept;
void join();
void yield() noexcept;
bool wait_until( std::chrono::steady_clock::time_point const&) noexcept;
void set_ready( context *) noexcept;
bool is_main_context() const noexcept {
return 0 != ( flags_ & flag_main_context);
}
bool is_dispatcher_context() const noexcept {
return 0 != ( flags_ & flag_dispatcher_context);
}
bool is_worker_context() const noexcept {
return 0 != ( flags_ & flag_worker_context);
}
bool is_terminated() const noexcept {
return 0 != ( flags_ & flag_terminated);
}
bool interruption_blocked() const noexcept {
return 0 != ( flags_ & flag_interruption_blocked);
}
void interruption_blocked( bool blck) noexcept;
bool interruption_requested() const noexcept {
return 0 != ( flags_ & flag_interruption_requested);
}
void request_interruption( bool req) noexcept;
bool unwinding_requested() const noexcept {
return 0 != ( flags_ & flag_forced_unwind);
}
void request_unwinding() noexcept;
void * get_fss_data( void const * vp) const;
void set_fss_data(
void const * vp,
detail::fss_cleanup_function::ptr_t const& cleanup_fn,
void * data,
bool cleanup_existing);
void set_properties( fiber_properties * props);
fiber_properties * get_properties() const noexcept {
return properties_;
}
bool worker_is_linked();
bool terminated_is_linked();
bool ready_is_linked();
bool remote_ready_is_linked();
bool sleep_is_linked();
bool wait_is_linked();
template< typename List >
void worker_link( List & lst) {
std::unique_lock< detail::spinlock > lk( hook_splk_);
lst.push_back( * this);
}
template< typename List >
void terminated_link( List & lst) {
std::unique_lock< detail::spinlock > lk( hook_splk_);
lst.push_back( * this);
}
template< typename List >
void ready_link( List & lst) {
std::unique_lock< detail::spinlock > lk( hook_splk_);
lst.push_back( * this);
}
template< typename List >
void remote_ready_link( List & lst) {
std::unique_lock< detail::spinlock > lk( hook_splk_);
lst.push_back( * this);
}
template< typename Set >
void sleep_link( Set & set) {
std::unique_lock< detail::spinlock > lk( hook_splk_);
set.insert( * this);
}
template< typename List >
void wait_link( List & lst) {
std::unique_lock< detail::spinlock > lk( hook_splk_);
lst.push_back( * this);
}
void worker_unlink();
void ready_unlink();
void remote_ready_unlink();
void sleep_unlink();
void wait_unlink();
friend void intrusive_ptr_add_ref( context * ctx) {
BOOST_ASSERT( nullptr != ctx);
++ctx->use_count_;
}
friend void intrusive_ptr_release( context * ctx) {
BOOST_ASSERT( nullptr != ctx);
if ( 0 == --ctx->use_count_) {
ctx->~context();
}
}
};
inline
static intrusive_ptr< context > make_dispatcher_context( scheduler * sched) {
fixedsize_stack salloc; // use default satck-size
boost::context::stack_context sctx = salloc.allocate();
#if defined(BOOST_NO_CXX14_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
// reserve space for control structure
std::size_t size = sctx.size - sizeof( context);
void * sp = static_cast< char * >( sctx.sp) - sizeof( context);
#else
constexpr std::size_t func_alignment = 64; // alignof( context);
constexpr std::size_t func_size = sizeof( context);
// reserve space on stack
void * sp = static_cast< char * >( sctx.sp) - func_size - func_alignment;
// align sp pointer
std::size_t space = func_size + func_alignment;
sp = std::align( func_alignment, func_size, sp, space);
BOOST_ASSERT( nullptr != sp);
// calculate remaining size
std::size_t size = sctx.size - ( static_cast< char * >( sctx.sp) - static_cast< char * >( sp) );
#endif
// placement new of context on top of fiber's stack
return intrusive_ptr< context >(
new ( sp) context(
dispatcher_context,
boost::context::preallocated( sp, size, sctx),
salloc,
sched) );
}
template< typename StackAlloc, typename Fn, typename ... Args >
static intrusive_ptr< context > make_worker_context( StackAlloc salloc, Fn && fn, Args && ... args) {
boost::context::stack_context sctx = salloc.allocate();
#if defined(BOOST_NO_CXX14_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
// reserve space for control structure
std::size_t size = sctx.size - sizeof( context);
void * sp = static_cast< char * >( sctx.sp) - sizeof( context);
#else
constexpr std::size_t func_alignment = 64; // alignof( context);
constexpr std::size_t func_size = sizeof( context);
// reserve space on stack
void * sp = static_cast< char * >( sctx.sp) - func_size - func_alignment;
// align sp pointer
std::size_t space = func_size + func_alignment;
sp = std::align( func_alignment, func_size, sp, space);
BOOST_ASSERT( nullptr != sp);
// calculate remaining size
std::size_t size = sctx.size - ( static_cast< char * >( sctx.sp) - static_cast< char * >( sp) );
#endif
// placement new of context on top of fiber's stack
return intrusive_ptr< context >(
new ( sp) context(
worker_context,
boost::context::preallocated( sp, size, sctx),
salloc,
std::forward< Fn >( fn),
std::forward< Args >( args) ... ) );
}
namespace detail {
inline
wait_functor::hook_ptr wait_functor::to_hook_ptr( wait_functor::value_type & value) {
return & value.wait_hook_;
}
inline
wait_functor::const_hook_ptr wait_functor::to_hook_ptr( wait_functor::value_type const& value) {
return & value.wait_hook_;
}
inline
wait_functor::pointer wait_functor::to_value_ptr( wait_functor::hook_ptr n) {
return intrusive::get_parent_from_member< context >( n, & context::wait_hook_);
}
inline
wait_functor::const_pointer wait_functor::to_value_ptr( wait_functor::const_hook_ptr n) {
return intrusive::get_parent_from_member< context >( n, & context::wait_hook_);
}
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_CONTEXT_H