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