]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | #include <algorithm> | |
13 | #include <cstddef> | |
14 | #include <cstdint> | |
15 | #include <cstdlib> | |
16 | #include <functional> | |
17 | #include <memory> | |
18 | #include <ostream> | |
19 | #include <tuple> | |
20 | #include <utility> | |
21 | ||
22 | #include <boost/assert.hpp> | |
23 | #include <boost/config.hpp> | |
24 | #include <boost/intrusive_ptr.hpp> | |
25 | ||
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> | |
37 | ||
38 | #ifdef BOOST_HAS_ABI_HEADERS | |
39 | # include BOOST_ABI_PREFIX | |
40 | #endif | |
41 | ||
42 | namespace boost { | |
43 | namespace context { | |
44 | namespace detail { | |
45 | ||
46 | inline | |
47 | transfer_t context_unwind( transfer_t t) { | |
48 | throw forced_unwind( t.fctx); | |
49 | return { nullptr, nullptr }; | |
50 | } | |
51 | ||
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 | |
56 | rec->deallocate(); | |
57 | return { nullptr, nullptr }; | |
58 | } | |
59 | ||
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 }; | |
66 | try { | |
67 | // jump back to `context_create()` | |
68 | t = jump_fcontext( t_.fctx, nullptr); | |
69 | // start executing | |
70 | t = rec->run( t); | |
71 | } catch ( forced_unwind const& e) { | |
72 | t = { e.fctx, nullptr }; | |
73 | } | |
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"); | |
78 | } | |
79 | ||
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) ); | |
86 | Ctx ctx{ t.fctx }; | |
87 | // execute function | |
88 | auto result = apply( | |
89 | fn, | |
90 | std::tuple_cat( | |
91 | std::forward_as_tuple( std::move( ctx) ), | |
92 | std::move( args) ) ); | |
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) }; | |
98 | } | |
99 | ||
100 | template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > | |
101 | class record { | |
102 | private: | |
103 | StackAlloc salloc_; | |
104 | stack_context sctx_; | |
105 | typename std::decay< Fn >::type fn_; | |
106 | std::tuple< typename std::decay< Params >::type ... > params_; | |
107 | ||
108 | static void destroy( record * p) noexcept { | |
109 | StackAlloc salloc = p->salloc_; | |
110 | stack_context sctx = p->sctx_; | |
111 | // deallocate record | |
112 | p->~record(); | |
113 | // destroy stack with stack allocator | |
114 | salloc.deallocate( sctx); | |
115 | } | |
116 | ||
117 | public: | |
118 | record( stack_context sctx, StackAlloc const& salloc, | |
119 | Fn && fn, Params && ... params) noexcept : | |
120 | salloc_( salloc), | |
121 | sctx_( sctx), | |
122 | fn_( std::forward< Fn >( fn) ), | |
123 | params_( std::forward< Params >( params) ... ) { | |
124 | } | |
125 | ||
126 | record( record const&) = delete; | |
127 | record & operator=( record const&) = delete; | |
128 | ||
129 | void deallocate() noexcept { | |
130 | destroy( this); | |
131 | } | |
132 | ||
133 | transfer_t run( transfer_t t) { | |
134 | Ctx from{ t.fctx }; | |
135 | typename Ctx::args_tpl_t args = std::move( * static_cast< typename Ctx::args_tpl_t * >( t.data) ); | |
136 | auto tpl = std::tuple_cat( | |
137 | params_, | |
138 | std::forward_as_tuple( std::move( from) ), | |
139 | std::move( args) ); | |
140 | // invoke context-function | |
141 | Ctx cc = apply( std::move( fn_), std::move( tpl) ); | |
142 | return { exchange( cc.fctx_, nullptr), nullptr }; | |
143 | } | |
144 | }; | |
145 | ||
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; | |
149 | ||
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); | |
155 | #else | |
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; | |
160 | // align sp pointer | |
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) ); | |
166 | #endif | |
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; | |
175 | } | |
176 | ||
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; | |
180 | ||
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); | |
185 | #else | |
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; | |
190 | // align sp pointer | |
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) ); | |
196 | #endif | |
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; | |
205 | } | |
206 | ||
207 | } | |
208 | ||
209 | template< typename ... Args > | |
210 | class execution_context { | |
211 | private: | |
212 | typedef std::tuple< Args ... > args_tpl_t; | |
213 | typedef std::tuple< execution_context, typename std::decay< Args >::type ... > ret_tpl_t; | |
214 | ||
215 | template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > | |
216 | friend class detail::record; | |
217 | ||
218 | template< typename Ctx, typename Fn, typename ... ArgsT > | |
219 | friend detail::transfer_t detail::context_ontop( detail::transfer_t); | |
220 | ||
221 | detail::fcontext_t fctx_{ nullptr }; | |
222 | ||
223 | execution_context( detail::fcontext_t fctx) noexcept : | |
224 | fctx_( fctx) { | |
225 | } | |
226 | ||
227 | public: | |
228 | constexpr execution_context() noexcept = default; | |
229 | ||
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; | |
235 | ||
236 | template< typename Fn, typename ... Params > | |
237 | execution_context( std::allocator_arg_t, preallocated, segmented_stack, Fn &&, Params && ...) = delete; | |
238 | #else | |
239 | template< typename Fn, | |
240 | typename ... Params, | |
241 | typename = detail::disable_overload< execution_context, Fn > | |
242 | > | |
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 >( | |
250 | fixedsize_stack(), | |
251 | std::forward< Fn >( fn), | |
252 | std::forward< Params >( params) ... ) ) { | |
253 | } | |
254 | ||
255 | template< typename StackAlloc, | |
256 | typename Fn, | |
257 | typename ... Params | |
258 | > | |
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 >( | |
266 | salloc, | |
267 | std::forward< Fn >( fn), | |
268 | std::forward< Params >( params) ... ) ) { | |
269 | } | |
270 | ||
271 | template< typename StackAlloc, | |
272 | typename Fn, | |
273 | typename ... Params | |
274 | > | |
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 >( | |
282 | palloc, salloc, | |
283 | std::forward< Fn >( fn), | |
284 | std::forward< Params >( params) ... ) ) { | |
285 | } | |
286 | #endif | |
287 | ||
288 | ~execution_context() { | |
289 | if ( nullptr != fctx_) { | |
290 | detail::ontop_fcontext( detail::exchange( fctx_, nullptr), nullptr, detail::context_unwind); | |
291 | } | |
292 | } | |
293 | ||
294 | execution_context( execution_context && other) noexcept : | |
295 | fctx_( other.fctx_) { | |
296 | other.fctx_ = nullptr; | |
297 | } | |
298 | ||
299 | execution_context & operator=( execution_context && other) noexcept { | |
300 | if ( this != & other) { | |
301 | execution_context tmp = std::move( other); | |
302 | swap( tmp); | |
303 | } | |
304 | return * this; | |
305 | } | |
306 | ||
307 | execution_context( execution_context const& other) noexcept = delete; | |
308 | execution_context & operator=( execution_context const& other) noexcept = delete; | |
309 | ||
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) ); | |
316 | } | |
317 | return std::tuple_cat( std::forward_as_tuple( execution_context( t.fctx) ), std::move( data) ); | |
318 | } | |
319 | ||
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), | |
327 | & p, | |
328 | detail::context_ontop< execution_context, Fn, Args ... >); | |
329 | if ( nullptr != t.data) { | |
330 | data = std::move( * static_cast< args_tpl_t * >( t.data) ); | |
331 | } | |
332 | return std::tuple_cat( std::forward_as_tuple( execution_context( t.fctx) ), std::move( data) ); | |
333 | } | |
334 | ||
335 | explicit operator bool() const noexcept { | |
336 | return nullptr != fctx_; | |
337 | } | |
338 | ||
339 | bool operator!() const noexcept { | |
340 | return nullptr == fctx_; | |
341 | } | |
342 | ||
343 | bool operator==( execution_context const& other) const noexcept { | |
344 | return fctx_ == other.fctx_; | |
345 | } | |
346 | ||
347 | bool operator!=( execution_context const& other) const noexcept { | |
348 | return fctx_ != other.fctx_; | |
349 | } | |
350 | ||
351 | bool operator<( execution_context const& other) const noexcept { | |
352 | return fctx_ < other.fctx_; | |
353 | } | |
354 | ||
355 | bool operator>( execution_context const& other) const noexcept { | |
356 | return other.fctx_ < fctx_; | |
357 | } | |
358 | ||
359 | bool operator<=( execution_context const& other) const noexcept { | |
360 | return ! ( * this > other); | |
361 | } | |
362 | ||
363 | bool operator>=( execution_context const& other) const noexcept { | |
364 | return ! ( * this < other); | |
365 | } | |
366 | ||
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_; | |
372 | } else { | |
373 | return os << "{not-a-context}"; | |
374 | } | |
375 | } | |
376 | ||
377 | void swap( execution_context & other) noexcept { | |
378 | std::swap( fctx_, other.fctx_); | |
379 | } | |
380 | }; | |
381 | ||
382 | #include <boost/context/execution_context_v2_void.ipp> | |
383 | ||
384 | template< typename ... Args > | |
385 | void swap( execution_context< Args ... > & l, execution_context< Args ... > & r) noexcept { | |
386 | l.swap( r); | |
387 | } | |
388 | ||
389 | }} | |
390 | ||
391 | #ifdef BOOST_HAS_ABI_HEADERS | |
392 | # include BOOST_ABI_SUFFIX | |
393 | #endif | |
394 | ||
395 | #endif // BOOST_CONTEXT_EXECUTION_CONTEXT_H |