• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 //          Copyright Oliver Kowalke 2017.
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_FIBER_H
8 #define BOOST_CONTEXT_FIBER_H
9 
10 #include <windows.h>
11 
12 #include <boost/context/detail/config.hpp>
13 
14 #include <algorithm>
15 #include <cstddef>
16 #include <cstdint>
17 #include <cstdlib>
18 #include <cstring>
19 #include <functional>
20 #include <memory>
21 #include <ostream>
22 #include <system_error>
23 #include <tuple>
24 #include <utility>
25 
26 #include <boost/assert.hpp>
27 #include <boost/config.hpp>
28 
29 #include <boost/context/detail/disable_overload.hpp>
30 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
31 #include <boost/context/detail/exchange.hpp>
32 #endif
33 #if defined(BOOST_NO_CXX17_STD_INVOKE)
34 #include <boost/context/detail/invoke.hpp>
35 #endif
36 #include <boost/context/fixedsize_stack.hpp>
37 #include <boost/context/flags.hpp>
38 #include <boost/context/preallocated.hpp>
39 #include <boost/context/stack_context.hpp>
40 
41 #ifdef BOOST_HAS_ABI_HEADERS
42 # include BOOST_ABI_PREFIX
43 #endif
44 
45 #if defined(BOOST_MSVC)
46 # pragma warning(push)
47 # pragma warning(disable: 4702)
48 #endif
49 
50 namespace boost {
51 namespace context {
52 namespace detail {
53 
54 // tampoline function
55 // entered if the execution context
56 // is resumed for the first time
57 template< typename Record >
fiber_entry_func(LPVOID data)58 static VOID WINAPI fiber_entry_func( LPVOID data) noexcept {
59     Record * record = static_cast< Record * >( data);
60     BOOST_ASSERT( nullptr != record);
61     // start execution of toplevel context-function
62     record->run();
63 }
64 
65 struct BOOST_CONTEXT_DECL fiber_activation_record {
66     LPVOID                                                      fiber{ nullptr };
67     stack_context                                               sctx{};
68     bool                                                        main_ctx{ true };
69     fiber_activation_record                                       *   from{ nullptr };
70     std::function< fiber_activation_record*(fiber_activation_record*&) >    ontop{};
71     bool                                                        terminated{ false };
72     bool                                                        force_unwind{ false };
73 
74     static fiber_activation_record *& current() noexcept;
75 
76     // used for toplevel-context
77     // (e.g. main context, thread-entry context)
fiber_activation_recordboost::context::detail::fiber_activation_record78     fiber_activation_record() noexcept {
79 #if ( _WIN32_WINNT > 0x0600)
80         if ( ::IsThreadAFiber() ) {
81             fiber = ::GetCurrentFiber();
82         } else {
83             fiber = ::ConvertThreadToFiber( nullptr);
84         }
85 #else
86         fiber = ::ConvertThreadToFiber( nullptr);
87         if ( BOOST_UNLIKELY( nullptr == fiber) ) {
88             BOOST_ASSERT( ERROR_ALREADY_FIBER == ::GetLastError());
89             fiber = ::GetCurrentFiber();
90             BOOST_ASSERT( nullptr != fiber);
91             BOOST_ASSERT( reinterpret_cast< LPVOID >( 0x1E00) != fiber);
92         }
93 #endif
94     }
95 
fiber_activation_recordboost::context::detail::fiber_activation_record96     fiber_activation_record( stack_context sctx_) noexcept :
97         sctx{ sctx_ },
98         main_ctx{ false } {
99     }
100 
~fiber_activation_recordboost::context::detail::fiber_activation_record101     virtual ~fiber_activation_record() {
102         if ( BOOST_UNLIKELY( main_ctx) ) {
103             ::ConvertFiberToThread();
104         } else {
105             ::DeleteFiber( fiber);
106         }
107     }
108 
109     fiber_activation_record( fiber_activation_record const&) = delete;
110     fiber_activation_record & operator=( fiber_activation_record const&) = delete;
111 
is_main_contextboost::context::detail::fiber_activation_record112     bool is_main_context() const noexcept {
113         return main_ctx;
114     }
115 
resumeboost::context::detail::fiber_activation_record116     fiber_activation_record * resume() {
117         from = current();
118         // store `this` in static, thread local pointer
119         // `this` will become the active (running) context
120         current() = this;
121         // context switch from parent context to `this`-context
122         // context switch
123         ::SwitchToFiber( fiber);
124 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
125         return detail::exchange( current()->from, nullptr);
126 #else
127         return std::exchange( current()->from, nullptr);
128 #endif
129     }
130 
131     template< typename Ctx, typename Fn >
resume_withboost::context::detail::fiber_activation_record132     fiber_activation_record * resume_with( Fn && fn) {
133         from = current();
134         // store `this` in static, thread local pointer
135         // `this` will become the active (running) context
136         // returned by fiber::current()
137         current() = this;
138 #if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS)
139         current()->ontop = std::bind(
140                 [](typename std::decay< Fn >::type & fn, fiber_activation_record *& ptr){
141                     Ctx c{ ptr };
142                     c = fn( std::move( c) );
143                     if ( ! c) {
144                         ptr = nullptr;
145                     }
146 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
147                     return exchange( c.ptr_, nullptr);
148 #else
149                     return std::exchange( c.ptr_, nullptr);
150 #endif
151                 },
152                 std::forward< Fn >( fn),
153                 std::placeholders::_1);
154 #else
155         current()->ontop = [fn=std::forward<Fn>(fn)](fiber_activation_record *& ptr){
156             Ctx c{ ptr };
157             c = fn( std::move( c) );
158             if ( ! c) {
159                 ptr = nullptr;
160             }
161 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
162             return exchange( c.ptr_, nullptr);
163 #else
164             return std::exchange( c.ptr_, nullptr);
165 #endif
166         };
167 #endif
168         // context switch
169         ::SwitchToFiber( fiber);
170 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
171         return detail::exchange( current()->from, nullptr);
172 #else
173         return std::exchange( current()->from, nullptr);
174 #endif
175     }
176 
deallocateboost::context::detail::fiber_activation_record177     virtual void deallocate() noexcept {
178     }
179 };
180 
181 struct BOOST_CONTEXT_DECL fiber_activation_record_initializer {
182     fiber_activation_record_initializer() noexcept;
183     ~fiber_activation_record_initializer();
184 };
185 
186 struct forced_unwind {
187     fiber_activation_record  *  from{ nullptr };
188 #ifndef BOOST_ASSERT_IS_VOID
189     bool                        caught{ false };
190 #endif
191 
forced_unwindboost::context::detail::forced_unwind192     explicit forced_unwind( fiber_activation_record * from_) :
193         from{ from_ } {
194     }
195 
196 #ifndef BOOST_ASSERT_IS_VOID
~forced_unwindboost::context::detail::forced_unwind197     ~forced_unwind() {
198         BOOST_ASSERT( caught);
199     }
200 #endif
201 };
202 
203 template< typename Ctx, typename StackAlloc, typename Fn >
204 class fiber_capture_record : public fiber_activation_record {
205 private:
206     typename std::decay< StackAlloc >::type             salloc_;
207     typename std::decay< Fn >::type                     fn_;
208 
destroy(fiber_capture_record * p)209     static void destroy( fiber_capture_record * p) noexcept {
210         typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_);
211         stack_context sctx = p->sctx;
212         // deallocate activation record
213         p->~fiber_capture_record();
214         // destroy stack with stack allocator
215         salloc.deallocate( sctx);
216     }
217 
218 public:
fiber_capture_record(stack_context sctx,StackAlloc && salloc,Fn && fn)219     fiber_capture_record( stack_context sctx, StackAlloc && salloc, Fn && fn) noexcept :
220         fiber_activation_record( sctx),
221         salloc_( std::forward< StackAlloc >( salloc)),
222         fn_( std::forward< Fn >( fn) ) {
223     }
224 
deallocate()225     void deallocate() noexcept override final {
226         BOOST_ASSERT( main_ctx || ( ! main_ctx && terminated) );
227         destroy( this);
228     }
229 
run()230     void run() {
231         Ctx c{ from };
232         try {
233             // invoke context-function
234 #if defined(BOOST_NO_CXX17_STD_INVOKE)
235             c = boost::context::detail::invoke( fn_, std::move( c) );
236 #else
237             c = std::invoke( fn_, std::move( c) );
238 #endif
239         } catch ( forced_unwind const& ex) {
240             c = Ctx{ ex.from };
241 #ifndef BOOST_ASSERT_IS_VOID
242             const_cast< forced_unwind & >( ex).caught = true;
243 #endif
244         }
245         // this context has finished its task
246         from = nullptr;
247         ontop = nullptr;
248         terminated = true;
249         force_unwind = false;
250         std::move( c).resume();
251         BOOST_ASSERT_MSG( false, "fiber already terminated");
252     }
253 };
254 
255 template< typename Ctx, typename StackAlloc, typename Fn >
create_fiber1(StackAlloc && salloc,Fn && fn)256 static fiber_activation_record * create_fiber1( StackAlloc && salloc, Fn && fn) {
257     typedef fiber_capture_record< Ctx, StackAlloc, Fn >  capture_t;
258 
259     auto sctx = salloc.allocate();
260     BOOST_ASSERT( ( sizeof( capture_t) ) < sctx.size);
261     // reserve space for control structure
262     void * storage = reinterpret_cast< void * >(
263             ( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( capture_t) ) )
264             & ~ static_cast< uintptr_t >( 0xff) );
265     // placment new for control structure on context stack
266     capture_t * record = new ( storage) capture_t{
267             sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
268     // create user-context
269     record->fiber = ::CreateFiber( sctx.size, & detail::fiber_entry_func< capture_t >, record);
270     return record;
271 }
272 
273 template< typename Ctx, typename StackAlloc, typename Fn >
create_fiber2(preallocated palloc,StackAlloc && salloc,Fn && fn)274 static fiber_activation_record * create_fiber2( preallocated palloc, StackAlloc && salloc, Fn && fn) {
275     typedef fiber_capture_record< Ctx, StackAlloc, Fn >  capture_t;
276 
277     BOOST_ASSERT( ( sizeof( capture_t) ) < palloc.size);
278     // reserve space for control structure
279     void * storage = reinterpret_cast< void * >(
280             ( reinterpret_cast< uintptr_t >( palloc.sp) - static_cast< uintptr_t >( sizeof( capture_t) ) )
281             & ~ static_cast< uintptr_t >( 0xff) );
282     // placment new for control structure on context stack
283     capture_t * record = new ( storage) capture_t{
284             palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
285     // create user-context
286     record->fiber = ::CreateFiber( palloc.sctx.size, & detail::fiber_entry_func< capture_t >, record);
287     return record;
288 }
289 
290 }
291 
292 class BOOST_CONTEXT_DECL fiber {
293 private:
294     friend struct detail::fiber_activation_record;
295 
296     template< typename Ctx, typename StackAlloc, typename Fn >
297     friend class detail::fiber_capture_record;
298 
299     template< typename Ctx, typename StackAlloc, typename Fn >
300     friend detail::fiber_activation_record * detail::create_fiber1( StackAlloc &&, Fn &&);
301 
302     template< typename Ctx, typename StackAlloc, typename Fn >
303     friend detail::fiber_activation_record * detail::create_fiber2( preallocated, StackAlloc &&, Fn &&);
304 
305     template< typename StackAlloc, typename Fn >
306     friend fiber
307     callcc( std::allocator_arg_t, StackAlloc &&, Fn &&);
308 
309     template< typename StackAlloc, typename Fn >
310     friend fiber
311     callcc( std::allocator_arg_t, preallocated, StackAlloc &&, Fn &&);
312 
313     detail::fiber_activation_record   *   ptr_{ nullptr };
314 
fiber(detail::fiber_activation_record * ptr)315     fiber( detail::fiber_activation_record * ptr) noexcept :
316         ptr_{ ptr } {
317     }
318 
319 public:
320     fiber() = default;
321 
322     template< typename Fn, typename = detail::disable_overload< fiber, Fn > >
fiber(Fn && fn)323     fiber( Fn && fn) :
324         fiber{ std::allocator_arg,
325                fixedsize_stack(),
326                std::forward< Fn >( fn) } {
327     }
328 
329     template< typename StackAlloc, typename Fn >
fiber(std::allocator_arg_t,StackAlloc && salloc,Fn && fn)330     fiber( std::allocator_arg_t, StackAlloc && salloc, Fn && fn) :
331         ptr_{ detail::create_fiber1< fiber >(
332                 std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) } {;
333     }
334 
335     template< typename StackAlloc, typename Fn >
fiber(std::allocator_arg_t,preallocated palloc,StackAlloc && salloc,Fn && fn)336     fiber( std::allocator_arg_t, preallocated palloc, StackAlloc && salloc, Fn && fn) :
337         ptr_{ detail::create_fiber2< fiber >(
338                 palloc, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) } {
339     }
340 
~fiber()341     ~fiber() {
342         if ( BOOST_UNLIKELY( nullptr != ptr_) && ! ptr_->main_ctx) {
343             if ( BOOST_LIKELY( ! ptr_->terminated) ) {
344                 ptr_->force_unwind = true;
345                 ptr_->resume();
346                 BOOST_ASSERT( ptr_->terminated);
347             }
348             ptr_->deallocate();
349         }
350     }
351 
352     fiber( fiber const&) = delete;
353     fiber & operator=( fiber const&) = delete;
354 
fiber(fiber && other)355     fiber( fiber && other) noexcept {
356         swap( other);
357     }
358 
operator =(fiber && other)359     fiber & operator=( fiber && other) noexcept {
360         if ( BOOST_LIKELY( this != & other) ) {
361             fiber tmp = std::move( other);
362             swap( tmp);
363         }
364         return * this;
365     }
366 
resume()367     fiber resume() && {
368         BOOST_ASSERT( nullptr != ptr_);
369 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
370         detail::fiber_activation_record * ptr = detail::exchange( ptr_, nullptr)->resume();
371 #else
372         detail::fiber_activation_record * ptr = std::exchange( ptr_, nullptr)->resume();
373 #endif
374         if ( BOOST_UNLIKELY( detail::fiber_activation_record::current()->force_unwind) ) {
375             throw detail::forced_unwind{ ptr};
376         } else if ( BOOST_UNLIKELY( nullptr != detail::fiber_activation_record::current()->ontop) ) {
377             ptr = detail::fiber_activation_record::current()->ontop( ptr);
378             detail::fiber_activation_record::current()->ontop = nullptr;
379         }
380         return { ptr };
381     }
382 
383     template< typename Fn >
resume_with(Fn && fn)384     fiber resume_with( Fn && fn) && {
385         BOOST_ASSERT( nullptr != ptr_);
386 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
387         detail::fiber_activation_record * ptr =
388             detail::exchange( ptr_, nullptr)->resume_with< fiber >( std::forward< Fn >( fn) );
389 #else
390         detail::fiber_activation_record * ptr =
391             std::exchange( ptr_, nullptr)->resume_with< fiber >( std::forward< Fn >( fn) );
392 #endif
393         if ( BOOST_UNLIKELY( detail::fiber_activation_record::current()->force_unwind) ) {
394             throw detail::forced_unwind{ ptr};
395         } else if ( BOOST_UNLIKELY( nullptr != detail::fiber_activation_record::current()->ontop) ) {
396             ptr = detail::fiber_activation_record::current()->ontop( ptr);
397             detail::fiber_activation_record::current()->ontop = nullptr;
398         }
399         return { ptr };
400     }
401 
operator bool() const402     explicit operator bool() const noexcept {
403         return nullptr != ptr_ && ! ptr_->terminated;
404     }
405 
operator !() const406     bool operator!() const noexcept {
407         return nullptr == ptr_ || ptr_->terminated;
408     }
409 
operator <(fiber const & other) const410     bool operator<( fiber const& other) const noexcept {
411         return ptr_ < other.ptr_;
412     }
413 
414     #if !defined(BOOST_EMBTC)
415 
416     template< typename charT, class traitsT >
417     friend std::basic_ostream< charT, traitsT > &
operator <<(std::basic_ostream<charT,traitsT> & os,fiber const & other)418     operator<<( std::basic_ostream< charT, traitsT > & os, fiber const& other) {
419         if ( nullptr != other.ptr_) {
420             return os << other.ptr_;
421         } else {
422             return os << "{not-a-context}";
423         }
424     }
425 
426     #else
427 
428     template< typename charT, class traitsT >
429     friend std::basic_ostream< charT, traitsT > &
430     operator<<( std::basic_ostream< charT, traitsT > & os, fiber const& other);
431 
432     #endif
433 
swap(fiber & other)434     void swap( fiber & other) noexcept {
435         std::swap( ptr_, other.ptr_);
436     }
437 };
438 
439 #if defined(BOOST_EMBTC)
440 
441     template< typename charT, class traitsT >
442     inline std::basic_ostream< charT, traitsT > &
operator <<(std::basic_ostream<charT,traitsT> & os,fiber const & other)443     operator<<( std::basic_ostream< charT, traitsT > & os, fiber const& other) {
444         if ( nullptr != other.ptr_) {
445             return os << other.ptr_;
446         } else {
447             return os << "{not-a-context}";
448         }
449     }
450 
451 #endif
452 
453 inline
swap(fiber & l,fiber & r)454 void swap( fiber & l, fiber & r) noexcept {
455     l.swap( r);
456 }
457 
458 typedef fiber fiber_context;
459 
460 }}
461 
462 #if defined(BOOST_MSVC)
463 # pragma warning(pop)
464 #endif
465 
466 #ifdef BOOST_HAS_ABI_HEADERS
467 # include BOOST_ABI_SUFFIX
468 #endif
469 
470 #endif // BOOST_CONTEXT_FIBER_H
471