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 <windows.h> 15 16#include <cstddef> 17#include <cstdint> 18#include <cstdlib> 19#include <memory> 20#include <tuple> 21#include <utility> 22 23#include <boost/assert.hpp> 24#include <boost/config.hpp> 25#include <boost/intrusive_ptr.hpp> 26 27#include <boost/context/detail/invoke.hpp> 28#include <boost/context/stack_context.hpp> 29 30#ifdef BOOST_HAS_ABI_HEADERS 31# include BOOST_ABI_PREFIX 32#endif 33 34namespace boost { 35namespace context { 36 37struct preallocated { 38 void * sp; 39 std::size_t size; 40 stack_context sctx; 41 42 preallocated( void * sp_, std::size_t size_, stack_context sctx_) noexcept : 43 sp( sp_), size( size_), sctx( sctx_) { 44 } 45}; 46 47class BOOST_CONTEXT_DECL execution_context { 48private: 49 struct activation_record { 50 typedef boost::intrusive_ptr< activation_record > ptr_t; 51 52 enum flag_t { 53 flag_main_ctx = 1 << 1, 54 flag_preserve_fpu = 1 << 2, 55 flag_segmented_stack = 1 << 3 56 }; 57 58 thread_local static activation_record toplevel_rec; 59 thread_local static ptr_t current_rec; 60 61 std::size_t use_count; 62 LPVOID fiber; 63 stack_context sctx; 64 int flags; 65 66 // used for toplevel-context 67 // (e.g. main context, thread-entry context) 68 activation_record() noexcept : 69 use_count( 1), 70 fiber( nullptr), 71 sctx(), 72 flags( flag_main_ctx) { 73 } 74 75 activation_record( stack_context sctx_, bool use_segmented_stack) noexcept : 76 use_count( 0), 77 fiber( nullptr), 78 sctx( sctx_), 79 flags( use_segmented_stack ? flag_segmented_stack : 0) { 80 } 81 82 virtual ~activation_record() noexcept = default; 83 84 void resume() noexcept { 85 // store current activation record in local variable 86 activation_record * from = current_rec.get(); 87 // store `this` in static, thread local pointer 88 // `this` will become the active (running) context 89 // returned by execution_context::current() 90 current_rec = this; 91 // context switch from parent context to `this`-context 92#if ( _WIN32_WINNT > 0x0600) 93 if ( ::IsThreadAFiber() ) { 94 from->fiber = ::GetCurrentFiber(); 95 } else { 96 from->fiber = ::ConvertThreadToFiber( nullptr); 97 } 98#else 99 from->fiber = ::ConvertThreadToFiber( nullptr); 100 if ( nullptr == from->fiber) { 101 DWORD err = ::GetLastError(); 102 BOOST_ASSERT( ERROR_ALREADY_FIBER == err); 103 from->fiber = ::GetCurrentFiber(); 104 BOOST_ASSERT( nullptr != from->fiber); 105 BOOST_ASSERT( reinterpret_cast< LPVOID >( 0x1E00) != from->fiber); 106 } 107#endif 108 ::SwitchToFiber( fiber); 109 } 110 111 virtual void deallocate() {} 112 113 friend void intrusive_ptr_add_ref( activation_record * ar) { 114 ++ar->use_count; 115 } 116 117 friend void intrusive_ptr_release( activation_record * ar) { 118 BOOST_ASSERT( nullptr != ar); 119 120 if ( 0 == --ar->use_count) { 121 ar->deallocate(); 122 } 123 } 124 }; 125 126 template< typename Fn, typename StackAlloc > 127 class capture_record : public activation_record { 128 private: 129 StackAlloc salloc_; 130 Fn fn_; 131 132 static void destroy( capture_record * p) { 133 StackAlloc salloc( p->salloc_); 134 stack_context sctx( p->sctx); 135 // deallocate activation record 136 p->~capture_record(); 137 // destroy stack with stack allocator 138 salloc.deallocate( sctx); 139 } 140 141 public: 142 explicit capture_record( stack_context sctx, StackAlloc const& salloc, Fn && fn, bool use_segmented_stack) noexcept : 143 activation_record( sctx, use_segmented_stack), 144 salloc_( salloc), 145 fn_( std::forward< Fn >( fn) ) { 146 } 147 148 void deallocate() override final { 149 destroy( this); 150 } 151 152 void run() noexcept { 153 try { 154 fn_(); 155 } catch (...) { 156 std::terminate(); 157 } 158 BOOST_ASSERT( 0 == (flags & flag_main_ctx) ); 159 } 160 }; 161 162 // tampoline function 163 // entered if the execution context 164 // is resumed for the first time 165 template< typename AR > 166 static VOID WINAPI entry_func( LPVOID p) { 167 BOOST_ASSERT( 0 != p); 168 169 AR * ar( reinterpret_cast< AR * >( p) ); 170 // start execution of toplevel context-function 171 ar->run(); 172 //ctx->fn_(ctx->param_); 173 ::DeleteFiber( ar->fiber); 174 } 175 176 typedef boost::intrusive_ptr< activation_record > ptr_t; 177 178 ptr_t ptr_; 179 180 template< typename StackAlloc, typename Fn > 181 static activation_record * create_context( StackAlloc salloc, Fn && fn, bool use_segmented_stack) { 182 typedef capture_record< Fn, StackAlloc > capture_t; 183 184 // hackish 185 std::size_t fsize = salloc.size_; 186 salloc.size_ = sizeof( capture_t); 187 188 stack_context sctx( salloc.allocate() ); 189 // reserve space for control structure 190 void * sp = static_cast< char * >( sctx.sp) - sizeof( capture_t); 191 // placment new for control structure on fast-context stack 192 capture_t * cr = new ( sp) capture_t( sctx, salloc, std::forward< Fn >( fn), use_segmented_stack); 193 // create fiber 194 // use default stacksize 195 cr->fiber = ::CreateFiber( fsize, execution_context::entry_func< capture_t >, cr); 196 BOOST_ASSERT( nullptr != cr->fiber); 197 return cr; 198 } 199 200 template< typename StackAlloc, typename Fn > 201 static activation_record * create_context( preallocated palloc, StackAlloc salloc, Fn && fn, bool use_segmented_stack) { 202 typedef capture_record< Fn, StackAlloc > capture_t; 203 204 // hackish 205 std::size_t fsize = salloc.size_; 206 salloc.size_ = sizeof( capture_t); 207 208 // reserve space for control structure 209 void * sp = static_cast< char * >( palloc.sp) - sizeof( capture_t); 210 // placment new for control structure on fast-context stack 211 capture_t * cr = new ( sp) capture_t( palloc.sctx, salloc, std::forward< Fn >( fn), use_segmented_stack); 212 // create fiber 213 // use default stacksize 214 cr->fiber = ::CreateFiber( fsize, execution_context::entry_func< capture_t >, cr); 215 BOOST_ASSERT( nullptr != cr->fiber); 216 return cr; 217 } 218 219 template< typename StackAlloc, typename Fn, typename Tpl, std::size_t ... I > 220 static activation_record * create_capture_record( StackAlloc salloc, 221 Fn && fn_, Tpl && tpl_, 222 std::index_sequence< I ... >, 223 bool use_segmented_stack) { 224 return create_context( 225 salloc, 226 // lambda, executed in new execution context 227 [fn=std::forward< Fn >( fn_),tpl=std::forward< Tpl >( tpl_)] () mutable -> decltype( auto) { 228 detail::invoke( fn, 229 // non-type template parameter pack used to extract the 230 // parameters (arguments) from the tuple and pass them to fn 231 // via parameter pack expansion 232 // std::tuple_element<> does not perfect forwarding 233 std::forward< decltype( std::get< I >( std::declval< Tpl >() ) ) >( 234 std::get< I >( std::forward< Tpl >( tpl) ) ) ... ); 235 }, 236 use_segmented_stack); 237 } 238 239 template< typename StackAlloc, typename Fn, typename Tpl, std::size_t ... I > 240 static activation_record * create_capture_record( preallocated palloc, StackAlloc salloc, 241 Fn && fn_, Tpl && tpl_, 242 std::index_sequence< I ... >, 243 bool use_segmented_stack) { 244 return create_context( 245 palloc, salloc, 246 // lambda, executed in new execution context 247 [fn=std::forward< Fn >( fn_),tpl=std::forward< Tpl >( tpl_)] () mutable -> decltype( auto) { 248 detail::invoke( fn, 249 // non-type template parameter pack used to extract the 250 // parameters (arguments) from the tuple and pass them to fn 251 // via parameter pack expansion 252 // std::tuple_element<> does not perfect forwarding 253 std::forward< decltype( std::get< I >( std::declval< Tpl >() ) ) >( 254 std::get< I >( std::forward< Tpl >( tpl) ) ) ... ); 255 }, 256 use_segmented_stack); 257 } 258 259 execution_context() : 260 // default constructed with current activation_record 261 ptr_( activation_record::current_rec) { 262 } 263 264public: 265 static execution_context current() noexcept { 266 return execution_context(); 267 } 268 269 template< typename StackAlloc, typename Fn, typename ... Args > 270 explicit execution_context( StackAlloc salloc, Fn && fn, Args && ... args) : 271 // deferred execution of fn and its arguments 272 // arguments are stored in std::tuple<> 273 // non-type template parameter pack via std::index_sequence_for<> 274 // preserves the number of arguments 275 // used to extract the function arguments from std::tuple<> 276 ptr_( create_capture_record( salloc, 277 std::forward< Fn >( fn), 278 std::make_tuple( std::forward< Args >( args) ... ), 279 std::index_sequence_for< Args ... >(), false) ) { 280 } 281 282 template< typename StackAlloc, typename Fn, typename ... Args > 283 explicit execution_context( preallocated palloc, StackAlloc salloc, Fn && fn, Args && ... args) : 284 // deferred execution of fn and its arguments 285 // arguments are stored in std::tuple<> 286 // non-type template parameter pack via std::index_sequence_for<> 287 // preserves the number of arguments 288 // used to extract the function arguments from std::tuple<> 289 ptr_( create_capture_record( palloc, salloc, 290 std::forward< Fn >( fn), 291 std::make_tuple( std::forward< Args >( args) ... ), 292 std::index_sequence_for< Args ... >(), false) ) { 293 } 294 295 void operator()( bool preserve_fpu = false) noexcept { 296 ptr_->resume(); 297 } 298}; 299 300}} 301 302# ifdef BOOST_HAS_ABI_HEADERS 303# include BOOST_ABI_SUFFIX 304# endif 305 306#endif 307 308#endif // BOOST_CONTEXT_EXECUTION_CONTEXT_H 309