1 2// Copyright Oliver Kowalke 2014. 3// Distributed under the Boost Software License, Version 1.0. 4// (See accompanying file LICENSE_1_0.txt or copy at 5// http://www.boost.org/LICENSE_1_0.txt) 6 7#ifndef BOOST_CONTEXT_EXECUTION_CONTEXT_H 8#define BOOST_CONTEXT_EXECUTION_CONTEXT_H 9 10#include <boost/context/detail/config.hpp> 11 12#if ! defined(BOOST_CONTEXT_NO_EXECUTION_CONTEXT) 13 14# include <algorithm> 15# include <cstddef> 16# include <cstdint> 17# include <cstdlib> 18# include <functional> 19# include <memory> 20# include <tuple> 21# include <utility> 22 23# include <boost/assert.hpp> 24# include <boost/config.hpp> 25# include <boost/context/fcontext.hpp> 26# include <boost/intrusive_ptr.hpp> 27 28# include <boost/context/detail/invoke.hpp> 29# include <boost/context/stack_context.hpp> 30# include <boost/context/segmented_stack.hpp> 31 32# ifdef BOOST_HAS_ABI_HEADERS 33# include BOOST_ABI_PREFIX 34# endif 35 36# if defined(BOOST_USE_SEGMENTED_STACKS) 37extern "C" { 38 39void __splitstack_getcontext( void * [BOOST_CONTEXT_SEGMENTS]); 40 41void __splitstack_setcontext( void * [BOOST_CONTEXT_SEGMENTS]); 42 43} 44# endif 45 46namespace boost { 47namespace context { 48 49struct preallocated { 50 void * sp; 51 std::size_t size; 52 stack_context sctx; 53 54 preallocated( void * sp_, std::size_t size_, stack_context sctx_) noexcept : 55 sp( sp_), size( size_), sctx( sctx_) { 56 } 57}; 58 59class BOOST_CONTEXT_DECL execution_context { 60private: 61 struct activation_record { 62 typedef boost::intrusive_ptr< activation_record > ptr_t; 63 64 enum flag_t { 65 flag_main_ctx = 1 << 1, 66 flag_preserve_fpu = 1 << 2, 67 flag_segmented_stack = 1 << 3 68 }; 69 70 thread_local static activation_record toplevel_rec; 71 thread_local static ptr_t current_rec; 72 73 std::size_t use_count; 74 fcontext_t fctx; 75 stack_context sctx; 76 int flags; 77 78 // used for toplevel-context 79 // (e.g. main context, thread-entry context) 80 activation_record() noexcept : 81 use_count( 1), 82 fctx( nullptr), 83 sctx(), 84 flags( flag_main_ctx) { 85 } 86 87 activation_record( fcontext_t fctx_, stack_context sctx_, bool use_segmented_stack) noexcept : 88 use_count( 0), 89 fctx( fctx_), 90 sctx( sctx_), 91 flags( use_segmented_stack ? flag_segmented_stack : 0) { 92 } 93 94 virtual ~activation_record() noexcept = default; 95 96 void resume( bool fpu = false) noexcept { 97 // store current activation record in local variable 98 activation_record * from = current_rec.get(); 99 // store `this` in static, thread local pointer 100 // `this` will become the active (running) context 101 // returned by execution_context::current() 102 current_rec = this; 103 // set FPU flag 104 if (fpu) { 105 from->flags |= flag_preserve_fpu; 106 this->flags |= flag_preserve_fpu; 107 } else { 108 from->flags &= ~flag_preserve_fpu; 109 this->flags &= ~flag_preserve_fpu; 110 } 111# if defined(BOOST_USE_SEGMENTED_STACKS) 112 if ( 0 != (flags & flag_segmented_stack) ) { 113 // adjust segmented stack properties 114 __splitstack_getcontext( from->sctx.segments_ctx); 115 __splitstack_setcontext( sctx.segments_ctx); 116 // context switch from parent context to `this`-context 117 jump_fcontext( & from->fctx, fctx, reinterpret_cast< intptr_t >( this), fpu); 118 // parent context resumed 119 // adjust segmented stack properties 120 __splitstack_setcontext( from->sctx.segments_ctx); 121 } else { 122 // context switch from parent context to `this`-context 123 jump_fcontext( & from->fctx, fctx, reinterpret_cast< intptr_t >( this), fpu); 124 // parent context resumed 125 } 126# else 127 // context switch from parent context to `this`-context 128 jump_fcontext( & from->fctx, fctx, reinterpret_cast< intptr_t >( this), fpu); 129 // parent context resumed 130# endif 131 } 132 133 virtual void deallocate() {} 134 135 friend void intrusive_ptr_add_ref( activation_record * ar) { 136 ++ar->use_count; 137 } 138 139 friend void intrusive_ptr_release( activation_record * ar) { 140 BOOST_ASSERT( nullptr != ar); 141 142 if ( 0 == --ar->use_count) { 143 ar->deallocate(); 144 } 145 } 146 }; 147 148 template< typename Fn, typename StackAlloc > 149 class capture_record : public activation_record { 150 private: 151 StackAlloc salloc_; 152 Fn fn_; 153 154 static void destroy( capture_record * p) { 155 StackAlloc salloc( p->salloc_); 156 stack_context sctx( p->sctx); 157 // deallocate activation record 158 p->~capture_record(); 159 // destroy stack with stack allocator 160 salloc.deallocate( sctx); 161 } 162 163 public: 164 explicit capture_record( stack_context sctx, StackAlloc const& salloc, fcontext_t fctx, Fn && fn, bool use_segmented_stack) noexcept : 165 activation_record( fctx, sctx, use_segmented_stack), 166 salloc_( salloc), 167 fn_( std::forward< Fn >( fn) ) { 168 } 169 170 void deallocate() override final { 171 destroy( this); 172 } 173 174 void run() noexcept { 175 try { 176 fn_(); 177 } catch (...) { 178 std::terminate(); 179 } 180 BOOST_ASSERT( 0 == (flags & flag_main_ctx) ); 181 } 182 }; 183 184 // tampoline function 185 // entered if the execution context 186 // is resumed for the first time 187 template< typename AR > 188 static void entry_func( intptr_t p) noexcept { 189 BOOST_ASSERT( 0 != p); 190 191 AR * ar( reinterpret_cast< AR * >( p) ); 192 BOOST_ASSERT( nullptr != ar); 193 194 // start execution of toplevel context-function 195 ar->run(); 196 } 197 198 typedef boost::intrusive_ptr< activation_record > ptr_t; 199 200 ptr_t ptr_; 201 202 template< typename StackAlloc, typename Fn > 203 static activation_record * create_context( StackAlloc salloc, Fn && fn, bool use_segmented_stack) { 204 typedef capture_record< Fn, StackAlloc > capture_t; 205 206 stack_context sctx( salloc.allocate() ); 207 // reserve space for control structure 208#if defined(BOOST_NO_CXX14_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN) 209 std::size_t size = sctx.size - sizeof( capture_t); 210 void * sp = static_cast< char * >( sctx.sp) - sizeof( capture_t); 211#else 212 constexpr std::size_t func_alignment = 64; // alignof( capture_t); 213 constexpr std::size_t func_size = sizeof( capture_t); 214 // reserve space on stack 215 void * sp = static_cast< char * >( sctx.sp) - func_size - func_alignment; 216 // align sp pointer 217 std::size_t space = func_size + func_alignment; 218 sp = std::align( func_alignment, func_size, sp, space); 219 BOOST_ASSERT( nullptr != sp); 220 // calculate remaining size 221 std::size_t size = sctx.size - ( static_cast< char * >( sctx.sp) - static_cast< char * >( sp) ); 222#endif 223 // create fast-context 224 fcontext_t fctx = make_fcontext( sp, size, & execution_context::entry_func< capture_t >); 225 BOOST_ASSERT( nullptr != fctx); 226 // placment new for control structure on fast-context stack 227 return new ( sp) capture_t( sctx, salloc, fctx, std::forward< Fn >( fn), use_segmented_stack); 228 } 229 230 template< typename StackAlloc, typename Fn > 231 static activation_record * create_context( preallocated palloc, StackAlloc salloc, Fn && fn, bool use_segmented_stack) { 232 typedef capture_record< Fn, StackAlloc > capture_t; 233 234 // reserve space for control structure 235#if defined(BOOST_NO_CXX14_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN) 236 std::size_t size = palloc.size - sizeof( capture_t); 237 void * sp = static_cast< char * >( palloc.sp) - sizeof( capture_t); 238#else 239 constexpr std::size_t func_alignment = 64; // alignof( capture_t); 240 constexpr std::size_t func_size = sizeof( capture_t); 241 // reserve space on stack 242 void * sp = static_cast< char * >( palloc.sp) - func_size - func_alignment; 243 // align sp pointer 244 std::size_t space = func_size + func_alignment; 245 sp = std::align( func_alignment, func_size, sp, space); 246 BOOST_ASSERT( nullptr != sp); 247 // calculate remaining size 248 std::size_t size = palloc.size - ( static_cast< char * >( palloc.sp) - static_cast< char * >( sp) ); 249#endif 250 // create fast-context 251 fcontext_t fctx = make_fcontext( sp, size, & execution_context::entry_func< capture_t >); 252 BOOST_ASSERT( nullptr != fctx); 253 // placment new for control structure on fast-context stack 254 return new ( sp) capture_t( palloc.sctx, salloc, fctx, std::forward< Fn >( fn), use_segmented_stack); 255 } 256 257 template< typename StackAlloc, typename Fn, typename Tpl, std::size_t ... I > 258 static activation_record * create_capture_record( StackAlloc salloc, 259 Fn && fn_, Tpl && tpl_, 260 std::index_sequence< I ... >, 261 bool use_segmented_stack) { 262 return create_context( 263 salloc, 264 // lambda, executed in new execution context 265 [fn=std::forward< Fn >( fn_),tpl=std::forward< Tpl >( tpl_)] () mutable -> decltype( auto) { 266 detail::invoke( fn, 267 // non-type template parameter pack used to extract the 268 // parameters (arguments) from the tuple and pass them to fn 269 // via parameter pack expansion 270 // std::tuple_element<> does not perfect forwarding 271 std::forward< decltype( std::get< I >( std::declval< Tpl >() ) ) >( 272 std::get< I >( std::forward< Tpl >( tpl) ) ) ... ); 273 }, 274 use_segmented_stack); 275 } 276 277 template< typename StackAlloc, typename Fn, typename Tpl, std::size_t ... I > 278 static activation_record * create_capture_record( preallocated palloc, StackAlloc salloc, 279 Fn && fn_, Tpl && tpl_, 280 std::index_sequence< I ... >, 281 bool use_segmented_stack) { 282 return create_context( 283 palloc, salloc, 284 // lambda, executed in new execution context 285 [fn=std::forward< Fn >( fn_),tpl=std::forward< Tpl >( tpl_)] () mutable -> decltype( auto) { 286 detail::invoke( fn, 287 // non-type template parameter pack used to extract the 288 // parameters (arguments) from the tuple and pass them to fn 289 // via parameter pack expansion 290 // std::tuple_element<> does not perfect forwarding 291 std::forward< decltype( std::get< I >( std::declval< Tpl >() ) ) >( 292 std::get< I >( std::forward< Tpl >( tpl) ) ) ... ); 293 }, 294 use_segmented_stack); 295 } 296 297 execution_context() : 298 // default constructed with current activation_record 299 ptr_( activation_record::current_rec) { 300 } 301 302public: 303 static execution_context current() noexcept { 304 return execution_context(); 305 } 306 307# if defined(BOOST_USE_SEGMENTED_STACKS) 308 template< typename Fn, typename ... Args > 309 explicit execution_context( segmented_stack salloc, Fn && fn, Args && ... args) : 310 // deferred execution of fn and its arguments 311 // arguments are stored in std::tuple<> 312 // non-type template parameter pack via std::index_sequence_for<> 313 // preserves the number of arguments 314 // used to extract the function arguments from std::tuple<> 315 ptr_( create_capture_record( salloc, 316 std::forward< Fn >( fn), 317 std::make_tuple( std::forward< Args >( args) ... ), 318 std::index_sequence_for< Args ... >(), true) ) { 319 } 320 321 template< typename Fn, typename ... Args > 322 explicit execution_context( preallocated palloc, segmented_stack salloc, Fn && fn, Args && ... args) : 323 // deferred execution of fn and its arguments 324 // arguments are stored in std::tuple<> 325 // non-type template parameter pack via std::index_sequence_for<> 326 // preserves the number of arguments 327 // used to extract the function arguments from std::tuple<> 328 ptr_( create_capture_record( palloc, salloc, 329 std::forward< Fn >( fn), 330 std::make_tuple( std::forward< Args >( args) ... ), 331 std::index_sequence_for< Args ... >(), true) ) { 332 } 333# endif 334 335 template< typename StackAlloc, typename Fn, typename ... Args > 336 explicit execution_context( StackAlloc salloc, Fn && fn, Args && ... args) : 337 // deferred execution of fn and its arguments 338 // arguments are stored in std::tuple<> 339 // non-type template parameter pack via std::index_sequence_for<> 340 // preserves the number of arguments 341 // used to extract the function arguments from std::tuple<> 342 ptr_( create_capture_record( salloc, 343 std::forward< Fn >( fn), 344 std::make_tuple( std::forward< Args >( args) ... ), 345 std::index_sequence_for< Args ... >(), false) ) { 346 } 347 348 template< typename StackAlloc, typename Fn, typename ... Args > 349 explicit execution_context( preallocated palloc, StackAlloc salloc, Fn && fn, Args && ... args) : 350 // deferred execution of fn and its arguments 351 // arguments are stored in std::tuple<> 352 // non-type template parameter pack via std::index_sequence_for<> 353 // preserves the number of arguments 354 // used to extract the function arguments from std::tuple<> 355 ptr_( create_capture_record( palloc, salloc, 356 std::forward< Fn >( fn), 357 std::make_tuple( std::forward< Args >( args) ... ), 358 std::index_sequence_for< Args ... >(), false) ) { 359 } 360 361 void operator()( bool preserve_fpu = false) noexcept { 362 ptr_->resume( preserve_fpu); 363 } 364}; 365 366}} 367 368# ifdef BOOST_HAS_ABI_HEADERS 369# include BOOST_ABI_SUFFIX 370# endif 371 372#endif 373 374#endif // BOOST_CONTEXT_EXECUTION_CONTEXT_H 375