Imported Upstream version 1.71.0
[platform/upstream/boost.git] / boost / context / execution_context_v1.hpp
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_V1_H
8 #define BOOST_CONTEXT_EXECUTION_CONTEXT_V1_H
9
10 #include <boost/context/detail/config.hpp>
11
12 #include <algorithm>
13 #include <atomic>
14 #include <cstddef>
15 #include <cstdint>
16 #include <cstdlib>
17 #include <functional>
18 #include <memory>
19 #include <ostream>
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 #if defined(BOOST_NO_CXX17_STD_APPLY)
28 #include <boost/context/detail/apply.hpp>
29 #endif
30 #include <boost/context/detail/disable_overload.hpp>
31 #include <boost/context/detail/externc.hpp>
32 #include <boost/context/detail/fcontext.hpp>
33 #include <boost/context/fixedsize_stack.hpp>
34 #include <boost/context/flags.hpp>
35 #include <boost/context/preallocated.hpp>
36 #include <boost/context/segmented_stack.hpp>
37 #include <boost/context/stack_context.hpp>
38
39 #ifdef BOOST_HAS_ABI_HEADERS
40 # include BOOST_ABI_PREFIX
41 #endif
42
43 namespace boost {
44 namespace context {
45 namespace detail {
46
47 template< typename Fn >
48 transfer_t ecv1_context_ontop( transfer_t);
49
50 struct ecv1_activation_record;
51
52 struct ecv1_data_t {
53     ecv1_activation_record  *   from;
54     void                    *   data;
55 };
56
57 struct BOOST_CONTEXT_DECL ecv1_activation_record {
58     typedef boost::intrusive_ptr< ecv1_activation_record >    ptr_t;
59
60     static ptr_t & current() noexcept;
61
62     std::atomic< std::size_t >  use_count{ 0 };
63     fcontext_t                  fctx{ nullptr };
64     stack_context               sctx{};
65     bool                        main_ctx{ true };
66
67     // used for toplevel-context
68     // (e.g. main context, thread-entry context)
69     ecv1_activation_record() = default;
70
71     ecv1_activation_record( fcontext_t fctx_, stack_context sctx_) noexcept :
72         fctx{ fctx_ },
73         sctx( sctx_ ), // sctx{ sctx_ } - clang-3.6: no viable conversion from 'boost::context::stack_context' to 'std::size_t'
74         main_ctx{ false } {
75     } 
76
77     virtual ~ecv1_activation_record() = default;
78
79     bool is_main_context() const noexcept {
80         return main_ctx;
81     }
82
83     void * resume( void * vp) {
84         // store current activation record in local variable
85         auto from = current().get();
86         // store `this` in static, thread local pointer
87         // `this` will become the active (running) context
88         // returned by execution_context::current()
89         current() = this;
90 #if defined(BOOST_USE_SEGMENTED_STACKS)
91         // adjust segmented stack properties
92         __splitstack_getcontext( from->sctx.segments_ctx);
93         __splitstack_setcontext( sctx.segments_ctx);
94 #endif
95         ecv1_data_t d = { from, vp };
96         // context switch from parent context to `this`-context
97         transfer_t t = jump_fcontext( fctx, & d);
98         ecv1_data_t * dp = reinterpret_cast< ecv1_data_t * >( t.data);
99         dp->from->fctx = t.fctx;
100         // parent context resumed
101         return dp->data;
102     }
103
104     template< typename Fn >
105     void * resume_ontop( void *  data, Fn && fn) {
106         // store current activation record in local variable
107         ecv1_activation_record * from = current().get();
108         // store `this` in static, thread local pointer
109         // `this` will become the active (running) context
110         // returned by execution_context::current()
111         current() = this;
112 #if defined(BOOST_USE_SEGMENTED_STACKS)
113         // adjust segmented stack properties
114         __splitstack_getcontext( from->sctx.segments_ctx);
115         __splitstack_setcontext( sctx.segments_ctx);
116 #endif
117         std::tuple< void *, Fn > p = std::forward_as_tuple( data, fn);
118         ecv1_data_t d = { from, & p };
119         // context switch from parent context to `this`-context
120         // execute Fn( Tpl) on top of `this`
121         transfer_t t = ontop_fcontext( fctx, & d, ecv1_context_ontop< Fn >);
122         ecv1_data_t * dp = reinterpret_cast< ecv1_data_t * >( t.data);
123         dp->from->fctx = t.fctx;
124         // parent context resumed
125         return dp->data;
126     }
127
128     virtual void deallocate() noexcept {
129     }
130
131     friend void intrusive_ptr_add_ref( ecv1_activation_record * ar) noexcept {
132         ++ar->use_count;
133     }
134
135     friend void intrusive_ptr_release( ecv1_activation_record * ar) noexcept {
136         BOOST_ASSERT( nullptr != ar);
137         if ( 0 == --ar->use_count) {
138             ar->deallocate();
139         }
140     }
141 };
142
143 struct BOOST_CONTEXT_DECL ecv1_activation_record_initializer {
144     ecv1_activation_record_initializer() noexcept;
145     ~ecv1_activation_record_initializer();
146 };
147
148 template< typename Fn >
149 transfer_t ecv1_context_ontop( transfer_t t) {
150     ecv1_data_t * dp = reinterpret_cast< ecv1_data_t * >( t.data);
151     dp->from->fctx = t.fctx;
152     auto tpl = reinterpret_cast< std::tuple< void *, Fn > * >( dp->data);
153     BOOST_ASSERT( nullptr != tpl);
154     auto data = std::get< 0 >( * tpl);
155     typename std::decay< Fn >::type fn = std::forward< Fn >( std::get< 1 >( * tpl) );
156 #if defined(BOOST_NO_CXX17_STD_APPLY)
157     dp->data = boost::context::detail::apply( fn, std::tie( data) );
158 #else
159     dp->data = std::apply( fn, std::tie( data) );
160 #endif
161     return { t.fctx, dp };
162 }
163
164 template< typename StackAlloc, typename Fn, typename ... Args >
165 class ecv1_capture_record : public ecv1_activation_record {
166 private:
167     typename std::decay< StackAlloc >::type             salloc_;
168     typename std::decay< Fn >::type                     fn_;
169     std::tuple< typename std::decay< Args >::type ... > args_;
170     ecv1_activation_record                               *   caller_;
171
172     static void destroy( ecv1_capture_record * p) noexcept {
173         typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_);
174         stack_context sctx = p->sctx;
175         // deallocate activation record
176         p->~ecv1_capture_record();
177         // destroy stack with stack allocator
178         salloc.deallocate( sctx);
179     }
180
181 public:
182     ecv1_capture_record( stack_context sctx, StackAlloc && salloc,
183                     fcontext_t fctx,
184                     ecv1_activation_record * caller,
185                     Fn && fn, Args && ... args) noexcept :
186         ecv1_activation_record{ fctx, sctx },
187         salloc_{ std::forward< StackAlloc >( salloc) },
188         fn_( std::forward< Fn >( fn) ),
189         args_( std::forward< Args >( args) ... ),
190         caller_{ caller } {
191     }
192
193     void deallocate() noexcept override final {
194         destroy( this);
195     }
196
197     void run() {
198         auto data = caller_->resume( nullptr);
199 #if defined(BOOST_NO_CXX17_STD_APPLY)
200         boost::context::detail::apply( fn_, std::tuple_cat( args_, std::tie( data) ) );
201 #else
202         std::apply( fn_, std::tuple_cat( args_, std::tie( data) ) );
203 #endif
204         BOOST_ASSERT_MSG( ! main_ctx, "main-context does not execute activation-record::run()");
205     }
206 };
207
208 }
209
210 namespace v1 {
211
212 class BOOST_CONTEXT_DECL execution_context {
213 private:
214     // tampoline function
215     // entered if the execution context
216     // is resumed for the first time
217     template< typename AR >
218     static void entry_func( detail::transfer_t t) noexcept {
219         detail::ecv1_data_t * dp = reinterpret_cast< detail::ecv1_data_t * >( t.data);
220         AR * ar = static_cast< AR * >( dp->data);
221         BOOST_ASSERT( nullptr != ar);
222         dp->from->fctx = t.fctx;
223         // start execution of toplevel context-function
224         ar->run();
225     }
226
227     typedef boost::intrusive_ptr< detail::ecv1_activation_record >    ptr_t;
228
229     ptr_t   ptr_;
230
231     template< typename StackAlloc, typename Fn, typename ... Args >
232     static detail::ecv1_activation_record * create_context( StackAlloc && salloc,
233                                                        Fn && fn, Args && ... args) {
234         typedef detail::ecv1_capture_record<
235             StackAlloc, Fn, Args ...
236         >                                           capture_t;
237
238         auto sctx = salloc.allocate();
239         // reserve space for control structure
240 #if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
241         const std::size_t size = sctx.size - sizeof( capture_t);
242         void * sp = static_cast< char * >( sctx.sp) - sizeof( capture_t);
243 #else
244         constexpr std::size_t func_alignment = 64; // alignof( capture_t);
245         constexpr std::size_t func_size = sizeof( capture_t);
246         // reserve space on stack
247         void * sp = static_cast< char * >( sctx.sp) - func_size - func_alignment;
248         // align sp pointer
249         std::size_t space = func_size + func_alignment;
250         sp = std::align( func_alignment, func_size, sp, space);
251         BOOST_ASSERT( nullptr != sp);
252         // calculate remaining size
253         const std::size_t size = sctx.size - ( static_cast< char * >( sctx.sp) - static_cast< char * >( sp) );
254 #endif
255         // create fast-context
256         const detail::fcontext_t fctx = detail::make_fcontext( sp, size, & execution_context::entry_func< capture_t >);
257         BOOST_ASSERT( nullptr != fctx);
258         // get current activation record
259         auto curr = execution_context::current().ptr_;
260         // placment new for control structure on fast-context stack
261         return ::new ( sp) capture_t{
262                 sctx, std::forward< StackAlloc >( salloc), fctx, curr.get(), std::forward< Fn >( fn), std::forward< Args >( args) ... };
263     }
264
265     template< typename StackAlloc, typename Fn, typename ... Args >
266     static detail::ecv1_activation_record * create_context( preallocated palloc, StackAlloc && salloc,
267                                                        Fn && fn, Args && ... args) {
268         typedef detail::ecv1_capture_record<
269             StackAlloc, Fn, Args ...
270         >                                           capture_t;
271
272         // reserve space for control structure
273 #if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
274         const std::size_t size = palloc.size - sizeof( capture_t);
275         void * sp = static_cast< char * >( palloc.sp) - sizeof( capture_t);
276 #else
277         constexpr std::size_t func_alignment = 64; // alignof( capture_t);
278         constexpr std::size_t func_size = sizeof( capture_t);
279         // reserve space on stack
280         void * sp = static_cast< char * >( palloc.sp) - func_size - func_alignment;
281         // align sp pointer
282         std::size_t space = func_size + func_alignment;
283         sp = std::align( func_alignment, func_size, sp, space);
284         BOOST_ASSERT( nullptr != sp);
285         // calculate remaining size
286         const std::size_t size = palloc.size - ( static_cast< char * >( palloc.sp) - static_cast< char * >( sp) );
287 #endif
288         // create fast-context
289         const detail::fcontext_t fctx = detail::make_fcontext( sp, size, & execution_context::entry_func< capture_t >);
290         BOOST_ASSERT( nullptr != fctx);
291         // get current activation record
292         auto curr = execution_context::current().ptr_;
293         // placment new for control structure on fast-context stack
294         return ::new ( sp) capture_t{
295                 palloc.sctx, std::forward< StackAlloc >( salloc), fctx, curr.get(), std::forward< Fn >( fn), std::forward< Args >( args) ... };
296     }
297
298     execution_context() noexcept :
299         // default constructed with current ecv1_activation_record
300         ptr_{ detail::ecv1_activation_record::current() } {
301     }
302
303 public:
304     static execution_context current() noexcept;
305
306 #if defined(BOOST_USE_SEGMENTED_STACKS)
307     template< typename Fn,
308               typename ... Args,
309               typename = detail::disable_overload< execution_context, Fn >
310     >
311     execution_context( Fn && fn, Args && ... args) :
312         // deferred execution of fn and its arguments
313         // arguments are stored in std::tuple<>
314         // non-type template parameter pack via std::index_sequence_for<>
315         // preserves the number of arguments
316         // used to extract the function arguments from std::tuple<>
317         ptr_{ create_context( segmented_stack(),
318                               std::forward< Fn >( fn),
319                               std::forward< Args >( args) ...) } {
320         ptr_->resume( ptr_.get() );
321     }
322
323     template< typename Fn,
324               typename ... Args
325     >
326     execution_context( std::allocator_arg_t, segmented_stack salloc, Fn && fn, Args && ... args) :
327         // deferred execution of fn and its arguments
328         // arguments are stored in std::tuple<>
329         // non-type template parameter pack via std::index_sequence_for<>
330         // preserves the number of arguments
331         // used to extract the function arguments from std::tuple<>
332         ptr_{ create_context( salloc,
333                               std::forward< Fn >( fn),
334                               std::forward< Args >( args) ...) } {
335         ptr_->resume( ptr_.get() );
336     }
337
338     template< typename Fn,
339               typename ... Args
340     >
341     execution_context( std::allocator_arg_t, preallocated palloc, segmented_stack salloc, Fn && fn, Args && ... args) :
342         // deferred execution of fn and its arguments
343         // arguments are stored in std::tuple<>
344         // non-type template parameter pack via std::index_sequence_for<>
345         // preserves the number of arguments
346         // used to extract the function arguments from std::tuple<>
347         ptr_{ create_context( palloc, salloc,
348                               std::forward< Fn >( fn),
349                               std::forward< Args >( args) ...) } {
350         ptr_->resume( ptr_.get() );
351     }
352 #else
353     template< typename Fn,
354               typename ... Args,
355               typename = detail::disable_overload< execution_context, Fn >
356     >
357     execution_context( Fn && fn, Args && ... args) :
358         // deferred execution of fn and its arguments
359         // arguments are stored in std::tuple<>
360         // non-type template parameter pack via std::index_sequence_for<>
361         // preserves the number of arguments
362         // used to extract the function arguments from std::tuple<>
363         ptr_{ create_context( fixedsize_stack(),
364                               std::forward< Fn >( fn),
365                               std::forward< Args >( args) ...) } {
366         ptr_->resume( ptr_.get() );
367     }
368
369     template< typename StackAlloc,
370               typename Fn,
371               typename ... Args
372     >
373     execution_context( std::allocator_arg_t, StackAlloc && salloc, Fn && fn, Args && ... args) :
374         // deferred execution of fn and its arguments
375         // arguments are stored in std::tuple<>
376         // non-type template parameter pack via std::index_sequence_for<>
377         // preserves the number of arguments
378         // used to extract the function arguments from std::tuple<>
379         ptr_{ create_context( std::forward< StackAlloc >( salloc),
380                               std::forward< Fn >( fn),
381                               std::forward< Args >( args) ...) } {
382         ptr_->resume( ptr_.get() );
383     }
384
385     template< typename StackAlloc,
386               typename Fn,
387               typename ... Args
388     >
389     execution_context( std::allocator_arg_t, preallocated palloc, StackAlloc && salloc, Fn && fn, Args && ... args) :
390         // deferred execution of fn and its arguments
391         // arguments are stored in std::tuple<>
392         // non-type template parameter pack via std::index_sequence_for<>
393         // preserves the number of arguments
394         // used to extract the function arguments from std::tuple<>
395         ptr_{ create_context( palloc, std::forward< StackAlloc >( salloc),
396                               std::forward< Fn >( fn),
397                               std::forward< Args >( args) ...) } {
398         ptr_->resume( ptr_.get() );
399     }
400 #endif
401
402     execution_context( execution_context const& other) noexcept :
403         ptr_{ other.ptr_ } {
404     }
405
406     execution_context( execution_context && other) noexcept :
407         ptr_{ other.ptr_ } {
408         other.ptr_.reset();
409     }
410
411     execution_context & operator=( execution_context const& other) noexcept {
412         // intrusive_ptr<> does not test for self-assignment
413         if ( this == & other) return * this;
414         ptr_ = other.ptr_;
415         return * this;
416     }
417
418     execution_context & operator=( execution_context && other) noexcept {
419         if ( this == & other) return * this;
420         execution_context tmp{ std::move( other) };
421         swap( tmp);
422         return * this;
423     }
424
425     void * operator()( void * vp = nullptr) {
426         return ptr_->resume( vp);
427     }
428
429     template< typename Fn >
430     void * operator()( exec_ontop_arg_t, Fn && fn, void * vp = nullptr) {
431         return ptr_->resume_ontop( vp,
432                                    std::forward< Fn >( fn) );
433     }
434
435     explicit operator bool() const noexcept {
436         return nullptr != ptr_.get();
437     }
438
439     bool operator!() const noexcept {
440         return nullptr == ptr_.get();
441     }
442
443     bool operator<( execution_context const& other) const noexcept {
444         return ptr_ < other.ptr_;
445     }
446
447     template< typename charT, class traitsT >
448     friend std::basic_ostream< charT, traitsT > &
449     operator<<( std::basic_ostream< charT, traitsT > & os, execution_context const& other) {
450         if ( nullptr != other.ptr_) {
451             return os << other.ptr_;
452         } else {
453             return os << "{not-a-context}";
454         }
455     }
456
457     void swap( execution_context & other) noexcept {
458         ptr_.swap( other.ptr_);
459     }
460 };
461
462 inline
463 void swap( execution_context & l, execution_context & r) noexcept {
464     l.swap( r);
465 }
466
467 }}}
468
469 #ifdef BOOST_HAS_ABI_HEADERS
470 # include BOOST_ABI_SUFFIX
471 #endif
472
473 #endif // BOOST_CONTEXT_EXECUTION_CONTEXT_V1_H