]>
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_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_IPP | |
8 | #define BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_IPP | |
9 | ||
10 | #include <exception> | |
11 | #include <functional> | |
12 | #include <memory> | |
13 | ||
14 | #include <boost/assert.hpp> | |
15 | #include <boost/config.hpp> | |
16 | ||
17 | #include <boost/context/execution_context.hpp> | |
18 | ||
19 | #include <boost/coroutine2/detail/config.hpp> | |
20 | #include <boost/coroutine2/detail/decay_copy.hpp> | |
21 | #include <boost/coroutine2/detail/forced_unwind.hpp> | |
22 | #include <boost/coroutine2/detail/state.hpp> | |
23 | ||
24 | #ifdef BOOST_HAS_ABI_HEADERS | |
25 | # include BOOST_ABI_PREFIX | |
26 | #endif | |
27 | ||
28 | namespace boost { | |
29 | namespace coroutines2 { | |
30 | namespace detail { | |
31 | ||
32 | // pull_coroutine< T > | |
33 | ||
34 | template< typename T > | |
35 | void | |
36 | pull_coroutine< T >::control_block::destroy( control_block * cb) noexcept { | |
37 | boost::context::execution_context ctx = cb->ctx; | |
38 | // destroy control structure | |
39 | cb->state |= state_t::destroy; | |
40 | cb->~control_block(); | |
41 | } | |
42 | ||
43 | template< typename T > | |
44 | template< typename StackAllocator, typename Fn > | |
45 | pull_coroutine< T >::control_block::control_block( context::preallocated palloc, StackAllocator salloc, | |
46 | Fn && fn) : | |
47 | #if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) | |
48 | ctx{ std::allocator_arg, palloc, salloc, | |
49 | std::move( | |
50 | std::bind( | |
51 | [this]( typename std::decay< Fn >::type & fn_, boost::context::execution_context & ctx, void *) mutable noexcept { | |
52 | // create synthesized push_coroutine< T > | |
53 | typename push_coroutine< T >::control_block synthesized_cb{ this, ctx }; | |
54 | push_coroutine< T > synthesized{ & synthesized_cb }; | |
55 | other = & synthesized_cb; | |
56 | if ( state_t::none == ( state & state_t::destroy) ) { | |
57 | try { | |
58 | auto fn = std::move( fn_); | |
59 | // call coroutine-fn with synthesized push_coroutine as argument | |
60 | fn( synthesized); | |
61 | } catch ( forced_unwind const&) { | |
62 | // do nothing for unwinding exception | |
63 | } catch (...) { | |
64 | // store other exceptions in exception-pointer | |
65 | except = std::current_exception(); | |
66 | } | |
67 | } | |
68 | // set termination flags | |
69 | state |= state_t::complete; | |
70 | // jump back to ctx | |
71 | other->ctx(); | |
72 | BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); | |
73 | }, | |
74 | std::forward< Fn >( fn), | |
75 | boost::context::execution_context::current(), | |
76 | std::placeholders::_1))}, | |
77 | #else | |
78 | ctx{ std::allocator_arg, palloc, salloc, | |
79 | [this,fn_=decay_copy( std::forward< Fn >( fn) ),ctx=boost::context::execution_context::current()] (void *) mutable noexcept { | |
80 | // create synthesized push_coroutine< T > | |
81 | typename push_coroutine< T >::control_block synthesized_cb{ this, ctx }; | |
82 | push_coroutine< T > synthesized{ & synthesized_cb }; | |
83 | other = & synthesized_cb; | |
84 | if ( state_t::none == ( state & state_t::destroy) ) { | |
85 | try { | |
86 | auto fn = std::move( fn_); | |
87 | // call coroutine-fn with synthesized push_coroutine as argument | |
88 | fn( synthesized); | |
89 | } catch ( forced_unwind const&) { | |
90 | // do nothing for unwinding exception | |
91 | } catch (...) { | |
92 | // store other exceptions in exception-pointer | |
93 | except = std::current_exception(); | |
94 | } | |
95 | } | |
96 | // set termination flags | |
97 | state |= state_t::complete; | |
98 | // jump back to ctx | |
99 | other->ctx(); | |
100 | BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); | |
101 | }}, | |
102 | #endif | |
103 | other{ nullptr }, | |
104 | state{ state_t::unwind }, | |
105 | except{}, | |
106 | bvalid{ false }, | |
107 | storage{} { | |
108 | // enter coroutine-fn in order to have first value available after ctor (of `*this`) returns | |
109 | set( static_cast< T * >( ctx() ) ); | |
110 | } | |
111 | ||
112 | template< typename T > | |
113 | pull_coroutine< T >::control_block::control_block( typename push_coroutine< T >::control_block * cb, | |
114 | boost::context::execution_context const& ctx_) noexcept : | |
115 | ctx{ ctx_ }, | |
116 | other{ cb }, | |
117 | state{ state_t::none }, | |
118 | except{}, | |
119 | bvalid{ false }, | |
120 | storage{} { | |
121 | } | |
122 | ||
123 | template< typename T > | |
124 | pull_coroutine< T >::control_block::~control_block() { | |
125 | if ( state_t::none == ( state & state_t::complete) && | |
126 | state_t::none != ( state & state_t::unwind) ) { | |
127 | // unwind coroutine stack | |
128 | other->ctx = boost::context::execution_context::current(); | |
129 | ctx( context::exec_ontop_arg, unwind_coroutine); | |
130 | } | |
131 | // destroy data if it set | |
132 | if ( bvalid) { | |
133 | reinterpret_cast< T * >( std::addressof( storage) )->~T(); | |
134 | } | |
135 | } | |
136 | ||
137 | template< typename T > | |
138 | void | |
139 | pull_coroutine< T >::control_block::deallocate() noexcept { | |
140 | if ( state_t::none != ( state & state_t::unwind) ) { | |
141 | destroy( this); | |
142 | } | |
143 | } | |
144 | ||
145 | template< typename T > | |
146 | void | |
147 | pull_coroutine< T >::control_block::resume() { | |
148 | other->ctx = boost::context::execution_context::current(); | |
149 | set( static_cast< T * >( ctx() ) ); | |
150 | if ( except) { | |
151 | std::rethrow_exception( except); | |
152 | } | |
153 | } | |
154 | ||
155 | template< typename T > | |
156 | void | |
157 | pull_coroutine< T >::control_block::set( T * t) { | |
158 | // destroy data if it set | |
159 | if ( bvalid) { | |
160 | reinterpret_cast< T * >( std::addressof( storage) )->~T(); | |
161 | } | |
162 | if ( nullptr != t) { | |
163 | ::new ( static_cast< void * >( std::addressof( storage) ) ) T( std::move( * t) ); | |
164 | bvalid = true; | |
165 | } else { | |
166 | bvalid = false; | |
167 | } | |
168 | } | |
169 | ||
170 | template< typename T > | |
171 | T & | |
172 | pull_coroutine< T >::control_block::get() noexcept { | |
173 | return * reinterpret_cast< T * >( std::addressof( storage) ); | |
174 | } | |
175 | ||
176 | template< typename T > | |
177 | bool | |
178 | pull_coroutine< T >::control_block::valid() const noexcept { | |
179 | return nullptr != other && state_t::none == ( state & state_t::complete) && bvalid; | |
180 | } | |
181 | ||
182 | ||
183 | // pull_coroutine< T & > | |
184 | ||
185 | template< typename T > | |
186 | void | |
187 | pull_coroutine< T & >::control_block::destroy( control_block * cb) noexcept { | |
188 | boost::context::execution_context ctx = cb->ctx; | |
189 | // destroy control structure | |
190 | cb->state |= state_t::destroy; | |
191 | cb->~control_block(); | |
192 | } | |
193 | ||
194 | template< typename T > | |
195 | template< typename StackAllocator, typename Fn > | |
196 | pull_coroutine< T & >::control_block::control_block( context::preallocated palloc, StackAllocator salloc, | |
197 | Fn && fn) : | |
198 | #if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) | |
199 | ctx{ std::allocator_arg, palloc, salloc, | |
200 | std::move( | |
201 | std::bind( | |
202 | [this]( typename std::decay< Fn >::type & fn_, boost::context::execution_context & ctx, void *) mutable noexcept { | |
203 | // create synthesized push_coroutine< T > | |
204 | typename push_coroutine< T & >::control_block synthesized_cb{ this, ctx }; | |
205 | push_coroutine< T & > synthesized{ & synthesized_cb }; | |
206 | other = & synthesized_cb; | |
207 | if ( state_t::none == ( state & state_t::destroy) ) { | |
208 | try { | |
209 | auto fn = std::move( fn_); | |
210 | // call coroutine-fn with synthesized push_coroutine as argument | |
211 | fn( synthesized); | |
212 | } catch ( forced_unwind const&) { | |
213 | // do nothing for unwinding exception | |
214 | } catch (...) { | |
215 | // store other exceptions in exception-pointer | |
216 | except = std::current_exception(); | |
217 | } | |
218 | } | |
219 | // set termination flags | |
220 | state |= state_t::complete; | |
221 | // jump back to ctx | |
222 | other->ctx(); | |
223 | BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); | |
224 | }, | |
225 | std::forward< Fn >( fn), | |
226 | boost::context::execution_context::current(), | |
227 | std::placeholders::_1))}, | |
228 | #else | |
229 | ctx{ std::allocator_arg, palloc, salloc, | |
230 | [this,fn_=decay_copy( std::forward< Fn >( fn) ),ctx=boost::context::execution_context::current()] (void *) mutable noexcept { | |
231 | // create synthesized push_coroutine< T > | |
232 | typename push_coroutine< T & >::control_block synthesized_cb{ this, ctx }; | |
233 | push_coroutine< T & > synthesized{ & synthesized_cb }; | |
234 | other = & synthesized_cb; | |
235 | if ( state_t::none == ( state & state_t::destroy) ) { | |
236 | try { | |
237 | auto fn = std::move( fn_); | |
238 | // call coroutine-fn with synthesized push_coroutine as argument | |
239 | fn( synthesized); | |
240 | } catch ( forced_unwind const&) { | |
241 | // do nothing for unwinding exception | |
242 | } catch (...) { | |
243 | // store other exceptions in exception-pointer | |
244 | except = std::current_exception(); | |
245 | } | |
246 | } | |
247 | // set termination flags | |
248 | state |= state_t::complete; | |
249 | // jump back to ctx | |
250 | other->ctx(); | |
251 | BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); | |
252 | }}, | |
253 | #endif | |
254 | other{ nullptr }, | |
255 | state{ state_t::unwind }, | |
256 | except{}, | |
257 | t{ nullptr } { | |
258 | // enter coroutine-fn in order to have first value available after ctor (of `*this`) returns | |
259 | t = static_cast< T * >( ctx() ); | |
260 | } | |
261 | ||
262 | template< typename T > | |
263 | pull_coroutine< T & >::control_block::control_block( typename push_coroutine< T & >::control_block * cb, | |
264 | boost::context::execution_context const& ctx_) noexcept : | |
265 | ctx{ ctx_ }, | |
266 | other{ cb }, | |
267 | state{ state_t::none }, | |
268 | except{}, | |
269 | t( nullptr) { | |
270 | } | |
271 | ||
272 | template< typename T > | |
273 | pull_coroutine< T & >::control_block::~control_block() { | |
274 | if ( state_t::none == ( state & state_t::complete) && | |
275 | state_t::none != ( state & state_t::unwind) ) { | |
276 | // unwind coroutine stack | |
277 | other->ctx = boost::context::execution_context::current(); | |
278 | ctx( context::exec_ontop_arg, unwind_coroutine); | |
279 | } | |
280 | } | |
281 | ||
282 | template< typename T > | |
283 | void | |
284 | pull_coroutine< T & >::control_block::deallocate() noexcept { | |
285 | if ( state_t::none != ( state & state_t::unwind) ) { | |
286 | destroy( this); | |
287 | } | |
288 | } | |
289 | ||
290 | template< typename T > | |
291 | void | |
292 | pull_coroutine< T & >::control_block::resume() { | |
293 | other->ctx = boost::context::execution_context::current(); | |
294 | t = static_cast< T * >( ctx() ); | |
295 | if ( except) { | |
296 | std::rethrow_exception( except); | |
297 | } | |
298 | } | |
299 | ||
300 | template< typename T > | |
301 | T & | |
302 | pull_coroutine< T & >::control_block::get() noexcept { | |
303 | return * static_cast< T * >( t); | |
304 | } | |
305 | ||
306 | template< typename T > | |
307 | bool | |
308 | pull_coroutine< T & >::control_block::valid() const noexcept { | |
309 | return nullptr != other && state_t::none == ( state & state_t::complete) && nullptr != t; | |
310 | } | |
311 | ||
312 | ||
313 | // pull_coroutine< void > | |
314 | ||
315 | inline | |
316 | void | |
317 | pull_coroutine< void >::control_block::destroy( control_block * cb) noexcept { | |
318 | boost::context::execution_context ctx = cb->ctx; | |
319 | // destroy control structure | |
320 | cb->state |= state_t::destroy; | |
321 | cb->~control_block(); | |
322 | } | |
323 | ||
324 | template< typename StackAllocator, typename Fn > | |
325 | pull_coroutine< void >::control_block::control_block( context::preallocated palloc, StackAllocator salloc, | |
326 | Fn && fn) : | |
327 | #if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) | |
328 | ctx{ std::allocator_arg, palloc, salloc, | |
329 | std::move( | |
330 | std::bind( | |
331 | [this]( typename std::decay< Fn >::type & fn_, boost::context::execution_context & ctx, void *) mutable noexcept { | |
332 | // create synthesized push_coroutine< T > | |
333 | typename push_coroutine< void >::control_block synthesized_cb{ this, ctx }; | |
334 | push_coroutine< void > synthesized{ & synthesized_cb }; | |
335 | other = & synthesized_cb; | |
336 | if ( state_t::none == ( state & state_t::destroy) ) { | |
337 | try { | |
338 | auto fn = std::move( fn_); | |
339 | // call coroutine-fn with synthesized push_coroutine as argument | |
340 | fn( synthesized); | |
341 | } catch ( forced_unwind const&) { | |
342 | // do nothing for unwinding exception | |
343 | } catch (...) { | |
344 | // store other exceptions in exception-pointer | |
345 | except = std::current_exception(); | |
346 | } | |
347 | } | |
348 | // set termination flags | |
349 | state |= state_t::complete; | |
350 | // jump back to ctx | |
351 | other->ctx(); | |
352 | BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); | |
353 | }, | |
354 | std::forward< Fn >( fn), | |
355 | boost::context::execution_context::current(), | |
356 | std::placeholders::_1))}, | |
357 | #else | |
358 | ctx{ std::allocator_arg, palloc, salloc, | |
359 | [this,fn_=decay_copy( std::forward< Fn >( fn) ),ctx=boost::context::execution_context::current()] (void *) mutable noexcept { | |
360 | // create synthesized push_coroutine< T > | |
361 | typename push_coroutine< void >::control_block synthesized_cb{ this, ctx }; | |
362 | push_coroutine< void > synthesized{ & synthesized_cb }; | |
363 | other = & synthesized_cb; | |
364 | if ( state_t::none == ( state & state_t::destroy) ) { | |
365 | try { | |
366 | auto fn = std::move( fn_); | |
367 | // call coroutine-fn with synthesized push_coroutine as argument | |
368 | fn( synthesized); | |
369 | } catch ( forced_unwind const&) { | |
370 | // do nothing for unwinding exception | |
371 | } catch (...) { | |
372 | // store other exceptions in exception-pointer | |
373 | except = std::current_exception(); | |
374 | } | |
375 | } | |
376 | // set termination flags | |
377 | state |= state_t::complete; | |
378 | // jump back to ctx | |
379 | other->ctx(); | |
380 | BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); | |
381 | }}, | |
382 | #endif | |
383 | other{ nullptr }, | |
384 | state{ state_t::unwind }, | |
385 | except{} { | |
386 | // enter coroutine-fn in order to have first value available after ctor returns | |
387 | ctx(); | |
388 | } | |
389 | ||
390 | inline | |
391 | pull_coroutine< void >::control_block::control_block( push_coroutine< void >::control_block * cb, | |
392 | boost::context::execution_context const& ctx_) noexcept : | |
393 | ctx{ ctx_ }, | |
394 | other{ cb }, | |
395 | state{ state_t::none }, | |
396 | except{} { | |
397 | } | |
398 | ||
399 | inline | |
400 | pull_coroutine< void >::control_block::~control_block() { | |
401 | if ( state_t::none == ( state & state_t::complete) && | |
402 | state_t::none != ( state & state_t::unwind) ) { | |
403 | // unwind coroutine stack | |
404 | other->ctx = boost::context::execution_context::current(); | |
405 | ctx( context::exec_ontop_arg, unwind_coroutine); | |
406 | } | |
407 | } | |
408 | ||
409 | inline | |
410 | void | |
411 | pull_coroutine< void >::control_block::deallocate() noexcept { | |
412 | if ( state_t::none != ( state & state_t::unwind) ) { | |
413 | destroy( this); | |
414 | } | |
415 | } | |
416 | ||
417 | inline | |
418 | void | |
419 | pull_coroutine< void >::control_block::resume() { | |
420 | other->ctx = boost::context::execution_context::current(); | |
421 | ctx(); | |
422 | if ( except) { | |
423 | std::rethrow_exception( except); | |
424 | } | |
425 | } | |
426 | ||
427 | inline | |
428 | bool | |
429 | pull_coroutine< void >::control_block::valid() const noexcept { | |
430 | return nullptr != other && state_t::none == ( state & state_t::complete); | |
431 | } | |
432 | ||
433 | }}} | |
434 | ||
435 | #ifdef BOOST_HAS_ABI_HEADERS | |
436 | # include BOOST_ABI_SUFFIX | |
437 | #endif | |
438 | ||
439 | #endif // BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_IPP |