1 
2 //          Copyright Oliver Kowalke 2009.
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_COROUTINES_DETAIL_SYMMETRIC_COROUTINE_IMPL_H
8 #define BOOST_COROUTINES_DETAIL_SYMMETRIC_COROUTINE_IMPL_H
9 
10 #include <boost/assert.hpp>
11 #include <boost/config.hpp>
12 #include <boost/cstdint.hpp>
13 #include <boost/utility.hpp>
14 
15 #include <boost/coroutine/detail/config.hpp>
16 #include <boost/coroutine/detail/coroutine_context.hpp>
17 #include <boost/coroutine/detail/flags.hpp>
18 #include <boost/coroutine/detail/parameters.hpp>
19 #include <boost/coroutine/detail/preallocated.hpp>
20 #include <boost/coroutine/detail/trampoline.hpp>
21 #include <boost/coroutine/exceptions.hpp>
22 #include <boost/coroutine/stack_context.hpp>
23 
24 #ifdef BOOST_HAS_ABI_HEADERS
25 #  include BOOST_ABI_PREFIX
26 #endif
27 
28 namespace boost {
29 namespace coroutines {
30 namespace detail {
31 
32 template< typename R >
33 class symmetric_coroutine_impl : private noncopyable
34 {
35 public:
36     typedef parameters< R >                           param_type;
37 
symmetric_coroutine_impl(preallocated const & palloc,bool unwind)38     symmetric_coroutine_impl( preallocated const& palloc,
39                               bool unwind) BOOST_NOEXCEPT :
40         flags_( 0),
41         caller_(),
42         callee_( trampoline< symmetric_coroutine_impl< R > >, palloc)
43     {
44         if ( unwind) flags_ |= flag_force_unwind;
45     }
46 
~symmetric_coroutine_impl()47     virtual ~symmetric_coroutine_impl() {}
48 
force_unwind() const49     bool force_unwind() const BOOST_NOEXCEPT
50     { return 0 != ( flags_ & flag_force_unwind); }
51 
unwind_requested() const52     bool unwind_requested() const BOOST_NOEXCEPT
53     { return 0 != ( flags_ & flag_unwind_stack); }
54 
is_started() const55     bool is_started() const BOOST_NOEXCEPT
56     { return 0 != ( flags_ & flag_started); }
57 
is_running() const58     bool is_running() const BOOST_NOEXCEPT
59     { return 0 != ( flags_ & flag_running); }
60 
is_complete() const61     bool is_complete() const BOOST_NOEXCEPT
62     { return 0 != ( flags_ & flag_complete); }
63 
unwind_stack()64     void unwind_stack() BOOST_NOEXCEPT
65     {
66         if ( is_started() && ! is_complete() && force_unwind() )
67         {
68             flags_ |= flag_unwind_stack;
69             flags_ |= flag_running;
70             param_type to( unwind_t::force_unwind);
71             caller_.jump(
72                 callee_,
73                 & to);
74             flags_ &= ~flag_running;
75             flags_ &= ~flag_unwind_stack;
76 
77             BOOST_ASSERT( is_complete() );
78         }
79     }
80 
resume(R r)81     void resume( R r) BOOST_NOEXCEPT
82     {
83         param_type to( const_cast< R * >( & r), this);
84         resume_( & to);
85     }
86 
yield()87     R * yield()
88     {
89         BOOST_ASSERT( is_running() );
90         BOOST_ASSERT( ! is_complete() );
91 
92         flags_ &= ~flag_running;
93         param_type to;
94         param_type * from(
95             static_cast< param_type * >(
96                 callee_.jump(
97                     caller_,
98                     & to) ) );
99         flags_ |= flag_running;
100         if ( from->do_unwind) throw forced_unwind();
101         BOOST_ASSERT( from->data);
102         return from->data;
103     }
104 
105     template< typename X >
yield_to(symmetric_coroutine_impl<X> * other,X x)106     R * yield_to( symmetric_coroutine_impl< X > * other, X x)
107     {
108         typename symmetric_coroutine_impl< X >::param_type to( & x, other);
109         return yield_to_( other, & to);
110     }
111 
112     template< typename X >
yield_to(symmetric_coroutine_impl<X &> * other,X & x)113     R * yield_to( symmetric_coroutine_impl< X & > * other, X & x)
114     {
115         typename symmetric_coroutine_impl< X & >::param_type to( & x, other);
116         return yield_to_( other, & to);
117     }
118 
119     template< typename X >
yield_to(symmetric_coroutine_impl<X> * other)120     R * yield_to( symmetric_coroutine_impl< X > * other)
121     {
122         typename symmetric_coroutine_impl< X >::param_type to( other);
123         return yield_to_( other, & to);
124     }
125 
126     virtual void run( R *) BOOST_NOEXCEPT = 0;
127 
128     virtual void destroy() = 0;
129 
130 protected:
131     template< typename X >
132     friend class symmetric_coroutine_impl;
133 
134     int                 flags_;
135     coroutine_context   caller_;
136     coroutine_context   callee_;
137 
resume_(param_type * to)138     void resume_( param_type * to) BOOST_NOEXCEPT
139     {
140         BOOST_ASSERT( ! is_running() );
141         BOOST_ASSERT( ! is_complete() );
142 
143         flags_ |= flag_running;
144         caller_.jump(
145             callee_,
146             to);
147         flags_ &= ~flag_running;
148     }
149 
150     template< typename Other >
yield_to_(Other * other,typename Other::param_type * to)151     R * yield_to_( Other * other, typename Other::param_type * to)
152     {
153         BOOST_ASSERT( is_running() );
154         BOOST_ASSERT( ! is_complete() );
155         BOOST_ASSERT( ! other->is_running() );
156         BOOST_ASSERT( ! other->is_complete() );
157 
158         other->caller_ = caller_;
159         flags_ &= ~flag_running;
160         param_type * from(
161             static_cast< param_type * >(
162                 callee_.jump(
163                     other->callee_,
164                     to) ) );
165         flags_ |= flag_running;
166         if ( from->do_unwind) throw forced_unwind();
167         BOOST_ASSERT( from->data);
168         return from->data;
169     }
170 };
171 
172 template< typename R >
173 class symmetric_coroutine_impl< R & > : private noncopyable
174 {
175 public:
176     typedef parameters< R & >                         param_type;
177 
symmetric_coroutine_impl(preallocated const & palloc,bool unwind)178     symmetric_coroutine_impl( preallocated const& palloc,
179                               bool unwind) BOOST_NOEXCEPT :
180         flags_( 0),
181         caller_(),
182         callee_( trampoline< symmetric_coroutine_impl< R > >, palloc)
183     {
184         if ( unwind) flags_ |= flag_force_unwind;
185     }
186 
~symmetric_coroutine_impl()187     virtual ~symmetric_coroutine_impl() {}
188 
force_unwind() const189     bool force_unwind() const BOOST_NOEXCEPT
190     { return 0 != ( flags_ & flag_force_unwind); }
191 
unwind_requested() const192     bool unwind_requested() const BOOST_NOEXCEPT
193     { return 0 != ( flags_ & flag_unwind_stack); }
194 
is_started() const195     bool is_started() const BOOST_NOEXCEPT
196     { return 0 != ( flags_ & flag_started); }
197 
is_running() const198     bool is_running() const BOOST_NOEXCEPT
199     { return 0 != ( flags_ & flag_running); }
200 
is_complete() const201     bool is_complete() const BOOST_NOEXCEPT
202     { return 0 != ( flags_ & flag_complete); }
203 
unwind_stack()204     void unwind_stack() BOOST_NOEXCEPT
205     {
206         if ( is_started() && ! is_complete() && force_unwind() )
207         {
208             flags_ |= flag_unwind_stack;
209             flags_ |= flag_running;
210             param_type to( unwind_t::force_unwind);
211             caller_.jump(
212                 callee_,
213                 & to);
214             flags_ &= ~flag_running;
215             flags_ &= ~flag_unwind_stack;
216 
217             BOOST_ASSERT( is_complete() );
218         }
219     }
220 
resume(R & arg)221     void resume( R & arg) BOOST_NOEXCEPT
222     {
223         param_type to( & arg, this);
224         resume_( & to);
225     }
226 
yield()227     R * yield()
228     {
229         BOOST_ASSERT( is_running() );
230         BOOST_ASSERT( ! is_complete() );
231 
232         flags_ &= ~flag_running;
233         param_type to;
234         param_type * from(
235             static_cast< param_type * >(
236                 callee_.jump(
237                     caller_,
238                     & to) ) );
239         flags_ |= flag_running;
240         if ( from->do_unwind) throw forced_unwind();
241         BOOST_ASSERT( from->data);
242         return from->data;
243     }
244 
245     template< typename X >
yield_to(symmetric_coroutine_impl<X> * other,X x)246     R * yield_to( symmetric_coroutine_impl< X > * other, X x)
247     {
248         typename symmetric_coroutine_impl< X >::param_type to( & x, other);
249         return yield_to_( other, & to);
250     }
251 
252     template< typename X >
yield_to(symmetric_coroutine_impl<X &> * other,X & x)253     R * yield_to( symmetric_coroutine_impl< X & > * other, X & x)
254     {
255         typename symmetric_coroutine_impl< X & >::param_type to( & x, other);
256         return yield_to_( other, & to);
257     }
258 
259     template< typename X >
yield_to(symmetric_coroutine_impl<X> * other)260     R * yield_to( symmetric_coroutine_impl< X > * other)
261     {
262         typename symmetric_coroutine_impl< X >::param_type to( other);
263         return yield_to_( other, & to);
264     }
265 
266     virtual void run( R *) BOOST_NOEXCEPT = 0;
267 
268     virtual void destroy() = 0;
269 
270 protected:
271     template< typename X >
272     friend class symmetric_coroutine_impl;
273 
274     int                 flags_;
275     coroutine_context   caller_;
276     coroutine_context   callee_;
277 
resume_(param_type * to)278     void resume_( param_type * to) BOOST_NOEXCEPT
279     {
280         BOOST_ASSERT( ! is_running() );
281         BOOST_ASSERT( ! is_complete() );
282 
283         flags_ |= flag_running;
284         caller_.jump(
285             callee_,
286             to);
287         flags_ &= ~flag_running;
288     }
289 
290     template< typename Other >
yield_to_(Other * other,typename Other::param_type * to)291     R * yield_to_( Other * other, typename Other::param_type * to)
292     {
293         BOOST_ASSERT( is_running() );
294         BOOST_ASSERT( ! is_complete() );
295         BOOST_ASSERT( ! other->is_running() );
296         BOOST_ASSERT( ! other->is_complete() );
297 
298         other->caller_ = caller_;
299         flags_ &= ~flag_running;
300         param_type * from(
301             static_cast< param_type * >(
302                 callee_.jump(
303                     other->callee_,
304                     to) ) );
305         flags_ |= flag_running;
306         if ( from->do_unwind) throw forced_unwind();
307         BOOST_ASSERT( from->data);
308         return from->data;
309     }
310 };
311 
312 template<>
313 class symmetric_coroutine_impl< void > : private noncopyable
314 {
315 public:
316     typedef parameters< void >                          param_type;
317 
symmetric_coroutine_impl(preallocated const & palloc,bool unwind)318     symmetric_coroutine_impl( preallocated const& palloc,
319                               bool unwind) BOOST_NOEXCEPT :
320         flags_( 0),
321         caller_(),
322         callee_( trampoline_void< symmetric_coroutine_impl< void > >, palloc)
323     {
324         if ( unwind) flags_ |= flag_force_unwind;
325     }
326 
~symmetric_coroutine_impl()327     virtual ~symmetric_coroutine_impl() {}
328 
force_unwind() const329     inline bool force_unwind() const BOOST_NOEXCEPT
330     { return 0 != ( flags_ & flag_force_unwind); }
331 
unwind_requested() const332     inline bool unwind_requested() const BOOST_NOEXCEPT
333     { return 0 != ( flags_ & flag_unwind_stack); }
334 
is_started() const335     inline bool is_started() const BOOST_NOEXCEPT
336     { return 0 != ( flags_ & flag_started); }
337 
is_running() const338     inline bool is_running() const BOOST_NOEXCEPT
339     { return 0 != ( flags_ & flag_running); }
340 
is_complete() const341     inline bool is_complete() const BOOST_NOEXCEPT
342     { return 0 != ( flags_ & flag_complete); }
343 
unwind_stack()344     inline void unwind_stack() BOOST_NOEXCEPT
345     {
346         if ( is_started() && ! is_complete() && force_unwind() )
347         {
348             flags_ |= flag_unwind_stack;
349             flags_ |= flag_running;
350             param_type to( unwind_t::force_unwind);
351             caller_.jump(
352                 callee_,
353                 & to);
354             flags_ &= ~flag_running;
355             flags_ &= ~flag_unwind_stack;
356 
357             BOOST_ASSERT( is_complete() );
358         }
359     }
360 
resume()361     inline void resume() BOOST_NOEXCEPT
362     {
363         BOOST_ASSERT( ! is_running() );
364         BOOST_ASSERT( ! is_complete() );
365 
366         param_type to( this);
367         flags_ |= flag_running;
368         caller_.jump(
369             callee_,
370             & to);
371         flags_ &= ~flag_running;
372     }
373 
yield()374     inline void yield()
375     {
376         BOOST_ASSERT( is_running() );
377         BOOST_ASSERT( ! is_complete() );
378 
379         flags_ &= ~flag_running;
380         param_type to;
381         param_type * from(
382             static_cast< param_type * >(
383                 callee_.jump(
384                      caller_,
385                     & to) ) );
386         flags_ |= flag_running;
387         if ( from->do_unwind) throw forced_unwind();
388     }
389 
390     template< typename X >
yield_to(symmetric_coroutine_impl<X> * other,X x)391     void yield_to( symmetric_coroutine_impl< X > * other, X x)
392     {
393         typename symmetric_coroutine_impl< X >::param_type to( & x, other);
394         yield_to_( other, & to);
395     }
396 
397     template< typename X >
yield_to(symmetric_coroutine_impl<X &> * other,X & x)398     void yield_to( symmetric_coroutine_impl< X & > * other, X & x)
399     {
400         typename symmetric_coroutine_impl< X & >::param_type to( & x, other);
401         yield_to_( other, & to);
402     }
403 
404     template< typename X >
yield_to(symmetric_coroutine_impl<X> * other)405     void yield_to( symmetric_coroutine_impl< X > * other)
406     {
407         typename symmetric_coroutine_impl< X >::param_type to( other);
408         yield_to_( other, & to);
409     }
410 
411     virtual void run() BOOST_NOEXCEPT = 0;
412 
413     virtual void destroy() = 0;
414 
415 protected:
416     template< typename X >
417     friend class symmetric_coroutine_impl;
418 
419     int                 flags_;
420     coroutine_context   caller_;
421     coroutine_context   callee_;
422 
423     template< typename Other >
yield_to_(Other * other,typename Other::param_type * to)424     void yield_to_( Other * other, typename Other::param_type * to)
425     {
426         BOOST_ASSERT( is_running() );
427         BOOST_ASSERT( ! is_complete() );
428         BOOST_ASSERT( ! other->is_running() );
429         BOOST_ASSERT( ! other->is_complete() );
430 
431         other->caller_ = caller_;
432         flags_ &= ~flag_running;
433         param_type * from(
434             static_cast< param_type * >(
435                 callee_.jump(
436                     other->callee_,
437                     to) ) );
438         flags_ |= flag_running;
439         if ( from->do_unwind) throw forced_unwind();
440     }
441 };
442 
443 }}}
444 
445 #ifdef BOOST_HAS_ABI_HEADERS
446 #  include BOOST_ABI_SUFFIX
447 #endif
448 
449 #endif // BOOST_COROUTINES_DETAIL_SYMMETRIC_COROUTINE_IMPL_H
450