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)
7 #ifndef BOOST_CONTEXT_EXECUTION_CONTEXT_H
8 #define BOOST_CONTEXT_EXECUTION_CONTEXT_H
10 #include <boost/context/detail/config.hpp>
22 #include <boost/assert.hpp>
23 #include <boost/config.hpp>
24 #include <boost/intrusive_ptr.hpp>
26 #include <boost/context/detail/apply.hpp>
27 #include <boost/context/detail/disable_overload.hpp>
28 #include <boost/context/detail/exception.hpp>
29 #include <boost/context/detail/exchange.hpp>
30 #include <boost/context/detail/fcontext.hpp>
31 #include <boost/context/detail/tuple.hpp>
32 #include <boost/context/fixedsize_stack.hpp>
33 #include <boost/context/flags.hpp>
34 #include <boost/context/preallocated.hpp>
35 #include <boost/context/segmented_stack.hpp>
36 #include <boost/context/stack_context.hpp>
38 #ifdef BOOST_HAS_ABI_HEADERS
39 # include BOOST_ABI_PREFIX
47 transfer_t context_unwind( transfer_t t) {
48 throw forced_unwind( t.fctx);
49 return { nullptr, nullptr };
52 template< typename Rec >
53 transfer_t context_exit( transfer_t t) noexcept {
54 Rec * rec = static_cast< Rec * >( t.data);
55 // destroy context stack
57 return { nullptr, nullptr };
60 template< typename Rec >
61 void context_entry( transfer_t t_) noexcept {
62 // transfer control structure to the context-stack
63 Rec * rec = static_cast< Rec * >( t_.data);
64 BOOST_ASSERT( nullptr != rec);
65 transfer_t t = { nullptr, nullptr };
67 // jump back to `context_create()`
68 t = jump_fcontext( t_.fctx, nullptr);
71 } catch ( forced_unwind const& e) {
72 t = { e.fctx, nullptr };
74 BOOST_ASSERT( nullptr != t.fctx);
75 // destroy context-stack of `this`context on next context
76 ontop_fcontext( t.fctx, rec, context_exit< Rec >);
77 BOOST_ASSERT_MSG( false, "context already terminated");
80 template< typename Ctx, typename Fn, typename ... Args >
81 transfer_t context_ontop( transfer_t t) {
82 auto tpl = static_cast< std::tuple< Fn, std::tuple< Args ... > > * >( t.data);
83 BOOST_ASSERT( nullptr != tpl);
84 typename std::decay< Fn >::type fn = std::forward< Fn >( std::get< 0 >( * tpl) );
85 auto args = std::move( std::get< 1 >( * tpl) );
91 std::forward_as_tuple( std::move( ctx) ),
93 ctx = std::move( std::get< 0 >( result) );
94 // apply returned data
95 detail::tail( args) = std::move( result);
96 std::get< 1 >( * tpl) = std::move( args);
97 return { exchange( ctx.fctx_, nullptr), & std::get< 1 >( * tpl) };
100 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
105 typename std::decay< Fn >::type fn_;
106 std::tuple< typename std::decay< Params >::type ... > params_;
108 static void destroy( record * p) noexcept {
109 StackAlloc salloc = p->salloc_;
110 stack_context sctx = p->sctx_;
113 // destroy stack with stack allocator
114 salloc.deallocate( sctx);
118 record( stack_context sctx, StackAlloc const& salloc,
119 Fn && fn, Params && ... params) noexcept :
122 fn_( std::forward< Fn >( fn) ),
123 params_( std::forward< Params >( params) ... ) {
126 record( record const&) = delete;
127 record & operator=( record const&) = delete;
129 void deallocate() noexcept {
133 transfer_t run( transfer_t t) {
135 typename Ctx::args_tpl_t args = std::move( * static_cast< typename Ctx::args_tpl_t * >( t.data) );
136 auto tpl = std::tuple_cat(
138 std::forward_as_tuple( std::move( from) ),
140 // invoke context-function
141 Ctx cc = apply( std::move( fn_), std::move( tpl) );
142 return { exchange( cc.fctx_, nullptr), nullptr };
146 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
147 fcontext_t context_create( StackAlloc salloc, Fn && fn, Params && ... params) {
148 typedef record< Ctx, StackAlloc, Fn, Params ... > record_t;
150 auto sctx = salloc.allocate();
151 // reserve space for control structure
152 #if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
153 const std::size_t size = sctx.size - sizeof( record_t);
154 void * sp = static_cast< char * >( sctx.sp) - sizeof( record_t);
156 constexpr std::size_t func_alignment = 64; // alignof( record_t);
157 constexpr std::size_t func_size = sizeof( record_t);
158 // reserve space on stack
159 void * sp = static_cast< char * >( sctx.sp) - func_size - func_alignment;
161 std::size_t space = func_size + func_alignment;
162 sp = std::align( func_alignment, func_size, sp, space);
163 BOOST_ASSERT( nullptr != sp);
164 // calculate remaining size
165 const std::size_t size = sctx.size - ( static_cast< char * >( sctx.sp) - static_cast< char * >( sp) );
167 // create fast-context
168 const fcontext_t fctx = make_fcontext( sp, size, & context_entry< record_t >);
169 BOOST_ASSERT( nullptr != fctx);
170 // placment new for control structure on context-stack
171 auto rec = ::new ( sp) record_t{
172 sctx, salloc, std::forward< Fn >( fn), std::forward< Params >( params) ... };
173 // transfer control structure to context-stack
174 return jump_fcontext( fctx, rec).fctx;
177 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
178 fcontext_t context_create( preallocated palloc, StackAlloc salloc, Fn && fn, Params && ... params) {
179 typedef record< Ctx, StackAlloc, Fn, Params ... > record_t;
181 // reserve space for control structure
182 #if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
183 const std::size_t size = palloc.size - sizeof( record_t);
184 void * sp = static_cast< char * >( palloc.sp) - sizeof( record_t);
186 constexpr std::size_t func_alignment = 64; // alignof( record_t);
187 constexpr std::size_t func_size = sizeof( record_t);
188 // reserve space on stack
189 void * sp = static_cast< char * >( palloc.sp) - func_size - func_alignment;
191 std::size_t space = func_size + func_alignment;
192 sp = std::align( func_alignment, func_size, sp, space);
193 BOOST_ASSERT( nullptr != sp);
194 // calculate remaining size
195 const std::size_t size = palloc.size - ( static_cast< char * >( palloc.sp) - static_cast< char * >( sp) );
197 // create fast-context
198 const fcontext_t fctx = make_fcontext( sp, size, & context_entry< record_t >);
199 BOOST_ASSERT( nullptr != fctx);
200 // placment new for control structure on context-stack
201 auto rec = ::new ( sp) record_t{
202 palloc.sctx, salloc, std::forward< Fn >( fn), std::forward< Params >( params) ... };
203 // transfer control structure to context-stack
204 return jump_fcontext( fctx, rec).fctx;
209 template< typename ... Args >
210 class execution_context {
212 typedef std::tuple< Args ... > args_tpl_t;
213 typedef std::tuple< execution_context, typename std::decay< Args >::type ... > ret_tpl_t;
215 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
216 friend class detail::record;
218 template< typename Ctx, typename Fn, typename ... ArgsT >
219 friend detail::transfer_t detail::context_ontop( detail::transfer_t);
221 detail::fcontext_t fctx_{ nullptr };
223 execution_context( detail::fcontext_t fctx) noexcept :
228 constexpr execution_context() noexcept = default;
230 #if defined(BOOST_USE_SEGMENTED_STACKS)
231 // segmented-stack requires to preserve the segments of the `current` context
232 // which is not possible (no global pointer to current context)
233 template< typename Fn, typename ... Params >
234 execution_context( std::allocator_arg_t, segmented_stack, Fn &&, Params && ...) = delete;
236 template< typename Fn, typename ... Params >
237 execution_context( std::allocator_arg_t, preallocated, segmented_stack, Fn &&, Params && ...) = delete;
239 template< typename Fn,
241 typename = detail::disable_overload< execution_context, Fn >
243 execution_context( Fn && fn, Params && ... params) :
244 // deferred execution of fn and its arguments
245 // arguments are stored in std::tuple<>
246 // non-type template parameter pack via std::index_sequence_for<>
247 // preserves the number of arguments
248 // used to extract the function arguments from std::tuple<>
249 fctx_( detail::context_create< execution_context >(
251 std::forward< Fn >( fn),
252 std::forward< Params >( params) ... ) ) {
255 template< typename StackAlloc,
259 execution_context( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Params && ... params) :
260 // deferred execution of fn and its arguments
261 // arguments are stored in std::tuple<>
262 // non-type template parameter pack via std::index_sequence_for<>
263 // preserves the number of arguments
264 // used to extract the function arguments from std::tuple<>
265 fctx_( detail::context_create< execution_context >(
267 std::forward< Fn >( fn),
268 std::forward< Params >( params) ... ) ) {
271 template< typename StackAlloc,
275 execution_context( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn, Params && ... params) :
276 // deferred execution of fn and its arguments
277 // arguments are stored in std::tuple<>
278 // non-type template parameter pack via std::index_sequence_for<>
279 // preserves the number of arguments
280 // used to extract the function arguments from std::tuple<>
281 fctx_( detail::context_create< execution_context >(
283 std::forward< Fn >( fn),
284 std::forward< Params >( params) ... ) ) {
288 ~execution_context() {
289 if ( nullptr != fctx_) {
290 detail::ontop_fcontext( detail::exchange( fctx_, nullptr), nullptr, detail::context_unwind);
294 execution_context( execution_context && other) noexcept :
295 fctx_( other.fctx_) {
296 other.fctx_ = nullptr;
299 execution_context & operator=( execution_context && other) noexcept {
300 if ( this != & other) {
301 execution_context tmp = std::move( other);
307 execution_context( execution_context const& other) noexcept = delete;
308 execution_context & operator=( execution_context const& other) noexcept = delete;
310 ret_tpl_t operator()( Args ... args) {
311 BOOST_ASSERT( nullptr != fctx_);
312 args_tpl_t data( std::forward< Args >( args) ... );
313 detail::transfer_t t = detail::jump_fcontext( detail::exchange( fctx_, nullptr), & data);
314 if ( nullptr != t.data) {
315 data = std::move( * static_cast< args_tpl_t * >( t.data) );
317 return std::tuple_cat( std::forward_as_tuple( execution_context( t.fctx) ), std::move( data) );
320 template< typename Fn >
321 ret_tpl_t operator()( exec_ontop_arg_t, Fn && fn, Args ... args) {
322 BOOST_ASSERT( nullptr != fctx_);
323 args_tpl_t data{ std::forward< Args >( args) ... };
324 auto p = std::make_tuple( fn, std::move( data) );
325 detail::transfer_t t = detail::ontop_fcontext(
326 detail::exchange( fctx_, nullptr),
328 detail::context_ontop< execution_context, Fn, Args ... >);
329 if ( nullptr != t.data) {
330 data = std::move( * static_cast< args_tpl_t * >( t.data) );
332 return std::tuple_cat( std::forward_as_tuple( execution_context( t.fctx) ), std::move( data) );
335 explicit operator bool() const noexcept {
336 return nullptr != fctx_;
339 bool operator!() const noexcept {
340 return nullptr == fctx_;
343 bool operator==( execution_context const& other) const noexcept {
344 return fctx_ == other.fctx_;
347 bool operator!=( execution_context const& other) const noexcept {
348 return fctx_ != other.fctx_;
351 bool operator<( execution_context const& other) const noexcept {
352 return fctx_ < other.fctx_;
355 bool operator>( execution_context const& other) const noexcept {
356 return other.fctx_ < fctx_;
359 bool operator<=( execution_context const& other) const noexcept {
360 return ! ( * this > other);
363 bool operator>=( execution_context const& other) const noexcept {
364 return ! ( * this < other);
367 template< typename charT, class traitsT >
368 friend std::basic_ostream< charT, traitsT > &
369 operator<<( std::basic_ostream< charT, traitsT > & os, execution_context const& other) {
370 if ( nullptr != other.fctx_) {
371 return os << other.fctx_;
373 return os << "{not-a-context}";
377 void swap( execution_context & other) noexcept {
378 std::swap( fctx_, other.fctx_);
382 #include <boost/context/execution_context_v2_void.ipp>
384 template< typename ... Args >
385 void swap( execution_context< Args ... > & l, execution_context< Args ... > & r) noexcept {
391 #ifdef BOOST_HAS_ABI_HEADERS
392 # include BOOST_ABI_SUFFIX
395 #endif // BOOST_CONTEXT_EXECUTION_CONTEXT_H