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
12#include <boost/assert.hpp>
13#include <boost/config.hpp>
14
15#include <boost/context/execution_context.hpp>
16
17#include <boost/coroutine2/detail/config.hpp>
18#include <boost/coroutine2/detail/forced_unwind.hpp>
19#include <boost/coroutine2/detail/state.hpp>
20
21#ifdef BOOST_HAS_ABI_HEADERS
22#  include BOOST_ABI_PREFIX
23#endif
24
25namespace boost {
26namespace coroutines2 {
27namespace detail {
28
29// pull_coroutine< T >
30
31template< typename T >
32template< typename StackAllocator, typename Fn >
33pull_coroutine< T >::control_block::control_block( context::preallocated palloc, StackAllocator salloc,
34                                                   Fn && fn_, bool preserve_fpu_) :
35    other( nullptr),
36    caller( boost::context::execution_context::current() ),
37    callee( palloc, salloc,
38            [=,fn=std::forward< Fn >( fn_)] () mutable -> decltype( auto) {
39               // create synthesized push_coroutine< T >
40               typename push_coroutine< T >::control_block synthesized_cb( this);
41               push_coroutine< T > synthesized( & synthesized_cb);
42               other = & synthesized_cb;
43               try {
44                   // call coroutine-fn with synthesized push_coroutine as argument
45                   fn( synthesized);
46               } catch ( forced_unwind const&) {
47                   // do nothing for unwinding exception
48               } catch (...) {
49                   // store other exceptions in exception-pointer
50                   except = std::current_exception();
51               }
52               // set termination flags
53               state |= static_cast< int >( state_t::complete);
54               // jump back to caller
55               caller( preserve_fpu);
56               BOOST_ASSERT_MSG( false, "pull_coroutine is complete");
57            }),
58    preserve_fpu( preserve_fpu_),
59    state( static_cast< int >( state_t::unwind) ),
60    except() {
61    callee( preserve_fpu);
62}
63
64template< typename T >
65pull_coroutine< T >::control_block::control_block( typename push_coroutine< T >::control_block * cb) :
66    other( cb),
67    caller( other->callee),
68    callee( other->caller),
69    preserve_fpu( other->preserve_fpu),
70    state( 0),
71    except() {
72}
73
74template< typename T >
75pull_coroutine< T >::control_block::~control_block() {
76    if ( 0 == ( state & static_cast< int >( state_t::complete ) ) &&
77         0 != ( state & static_cast< int >( state_t::unwind) ) ) {
78        // set early-exit flag
79        state |= static_cast< int >( state_t::early_exit);
80        callee( preserve_fpu);
81    }
82}
83
84template< typename T >
85void
86pull_coroutine< T >::control_block::resume() {
87    callee( preserve_fpu);
88    if ( except) {
89        std::rethrow_exception( except);
90    }
91    // test early-exit-flag
92    if ( 0 != ( ( other->state) & static_cast< int >( state_t::early_exit) ) ) {
93        throw forced_unwind();
94    }
95}
96
97template< typename T >
98bool
99pull_coroutine< T >::control_block::valid() const noexcept {
100    return nullptr != other && nullptr != other->t && 0 == ( state & static_cast< int >( state_t::complete) );
101}
102
103
104// pull_coroutine< T & >
105
106template< typename T >
107template< typename StackAllocator, typename Fn >
108pull_coroutine< T & >::control_block::control_block( context::preallocated palloc, StackAllocator salloc,
109                                                     Fn && fn_, bool preserve_fpu_) :
110    other( nullptr),
111    caller( boost::context::execution_context::current() ),
112    callee( palloc, salloc,
113            [=,fn=std::forward< Fn >( fn_)] () mutable -> decltype( auto) {
114               // create synthesized push_coroutine< T >
115               typename push_coroutine< T & >::control_block synthesized_cb( this);
116               push_coroutine< T & > synthesized( & synthesized_cb);
117               other = & synthesized_cb;
118               try {
119                   // call coroutine-fn with synthesized push_coroutine as argument
120                   fn( synthesized);
121               } catch ( forced_unwind const&) {
122                   // do nothing for unwinding exception
123               } catch (...) {
124                   // store other exceptions in exception-pointer
125                   except = std::current_exception();
126               }
127               // set termination flags
128               state |= static_cast< int >( state_t::complete);
129               // jump back to caller
130               caller( preserve_fpu);
131               BOOST_ASSERT_MSG( false, "pull_coroutine is complete");
132            }),
133    preserve_fpu( preserve_fpu_),
134    state( static_cast< int >( state_t::unwind) ),
135    except() {
136    callee( preserve_fpu);
137}
138
139template< typename T >
140pull_coroutine< T & >::control_block::control_block( typename push_coroutine< T & >::control_block * cb) :
141    other( cb),
142    caller( other->callee),
143    callee( other->caller),
144    preserve_fpu( other->preserve_fpu),
145    state( 0),
146    except() {
147}
148
149template< typename T >
150pull_coroutine< T & >::control_block::~control_block() {
151    if ( 0 == ( state & static_cast< int >( state_t::complete ) ) &&
152         0 != ( state & static_cast< int >( state_t::unwind) ) ) {
153        // set early-exit flag
154        state |= static_cast< int >( state_t::early_exit);
155        callee( preserve_fpu);
156    }
157}
158
159template< typename T >
160void
161pull_coroutine< T & >::control_block::resume() {
162    callee( preserve_fpu);
163    if ( except) {
164        std::rethrow_exception( except);
165    }
166    // test early-exit-flag
167    if ( 0 != ( ( other->state) & static_cast< int >( state_t::early_exit) ) ) {
168        throw forced_unwind();
169    }
170}
171
172template< typename T >
173bool
174pull_coroutine< T & >::control_block::valid() const noexcept {
175    return nullptr != other && nullptr != other->t && 0 == ( state & static_cast< int >( state_t::complete) );
176}
177
178
179// pull_coroutine< void >
180
181template< typename StackAllocator, typename Fn >
182pull_coroutine< void >::control_block::control_block( context::preallocated palloc, StackAllocator salloc,
183                                                      Fn && fn_, bool preserve_fpu_) :
184    other( nullptr),
185    caller( boost::context::execution_context::current() ),
186    callee( palloc, salloc,
187            [=,fn=std::forward< Fn >( fn_)] () mutable -> decltype( auto) {
188               // create synthesized push_coroutine< T >
189               typename push_coroutine< void >::control_block synthesized_cb( this);
190               push_coroutine< void > synthesized( & synthesized_cb);
191               other = & synthesized_cb;
192               try {
193                   // call coroutine-fn with synthesized push_coroutine as argument
194                   fn( synthesized);
195               } catch ( forced_unwind const&) {
196                   // do nothing for unwinding exception
197               } catch (...) {
198                   // store other exceptions in exception-pointer
199                   except = std::current_exception();
200               }
201               // set termination flags
202               state |= static_cast< int >( state_t::complete);
203               // jump back to caller
204               caller( preserve_fpu);
205               BOOST_ASSERT_MSG( false, "pull_coroutine is complete");
206            }),
207    preserve_fpu( preserve_fpu_),
208    state( static_cast< int >( state_t::unwind) ),
209    except() {
210    callee( preserve_fpu);
211}
212
213inline
214pull_coroutine< void >::control_block::control_block( push_coroutine< void >::control_block * cb) :
215    other( cb),
216    caller( other->callee),
217    callee( other->caller),
218    preserve_fpu( other->preserve_fpu),
219    state( 0),
220    except() {
221}
222
223inline
224pull_coroutine< void >::control_block::~control_block() {
225    if ( 0 == ( state & static_cast< int >( state_t::complete ) ) &&
226         0 != ( state & static_cast< int >( state_t::unwind) ) ) {
227        // set early-exit flag
228        state |= static_cast< int >( state_t::early_exit);
229        callee( preserve_fpu);
230    }
231}
232
233inline
234void
235pull_coroutine< void >::control_block::resume() {
236    callee( preserve_fpu);
237    if ( except) {
238        std::rethrow_exception( except);
239    }
240    // test early-exit-flag
241    if ( 0 != ( ( other->state) & static_cast< int >( state_t::early_exit) ) ) {
242        throw forced_unwind();
243    }
244}
245
246inline
247bool
248pull_coroutine< void >::control_block::valid() const noexcept {
249    return nullptr != other && 0 == ( state & static_cast< int >( state_t::complete) );
250}
251
252}}}
253
254#ifdef BOOST_HAS_ABI_HEADERS
255#  include BOOST_ABI_SUFFIX
256#endif
257
258#endif // BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_IPP
259