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