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_PULL_COROUTINE_IMPL_H
8 #define BOOST_COROUTINES_DETAIL_PULL_COROUTINE_IMPL_H
9 
10 #include <boost/assert.hpp>
11 #include <boost/config.hpp>
12 #include <boost/exception_ptr.hpp>
13 #include <boost/throw_exception.hpp>
14 #include <boost/utility.hpp>
15 
16 #include <boost/coroutine/detail/config.hpp>
17 #include <boost/coroutine/detail/coroutine_context.hpp>
18 #include <boost/coroutine/detail/flags.hpp>
19 #include <boost/coroutine/detail/parameters.hpp>
20 #include <boost/coroutine/detail/trampoline_pull.hpp>
21 #include <boost/coroutine/exceptions.hpp>
22 
23 #ifdef BOOST_HAS_ABI_HEADERS
24 #  include BOOST_ABI_PREFIX
25 #endif
26 
27 namespace boost {
28 namespace coroutines {
29 
30 struct stack_context;
31 
32 namespace detail {
33 
34 template< typename R >
35 class pull_coroutine_impl : private noncopyable
36 {
37 protected:
38     int                     flags_;
39     exception_ptr           except_;
40     coroutine_context   *   caller_;
41     coroutine_context   *   callee_;
42     R                   *   result_;
43 
44 public:
45     typedef parameters< R >                           param_type;
46 
pull_coroutine_impl(coroutine_context * caller,coroutine_context * callee,bool unwind)47     pull_coroutine_impl( coroutine_context * caller,
48                          coroutine_context * callee,
49                          bool unwind) :
50         flags_( 0),
51         except_(),
52         caller_( caller),
53         callee_( callee),
54         result_( 0)
55     {
56         if ( unwind) flags_ |= flag_force_unwind;
57     }
58 
pull_coroutine_impl(coroutine_context * caller,coroutine_context * callee,bool unwind,R * result)59     pull_coroutine_impl( coroutine_context * caller,
60                          coroutine_context * callee,
61                          bool unwind,
62                          R * result) :
63         flags_( 0),
64         except_(),
65         caller_( caller),
66         callee_( callee),
67         result_( result)
68     {
69         if ( unwind) flags_ |= flag_force_unwind;
70     }
71 
~pull_coroutine_impl()72     virtual ~pull_coroutine_impl() {}
73 
force_unwind() const74     bool force_unwind() const BOOST_NOEXCEPT
75     { return 0 != ( flags_ & flag_force_unwind); }
76 
unwind_requested() const77     bool unwind_requested() const BOOST_NOEXCEPT
78     { return 0 != ( flags_ & flag_unwind_stack); }
79 
is_started() const80     bool is_started() const BOOST_NOEXCEPT
81     { return 0 != ( flags_ & flag_started); }
82 
is_running() const83     bool is_running() const BOOST_NOEXCEPT
84     { return 0 != ( flags_ & flag_running); }
85 
is_complete() const86     bool is_complete() const BOOST_NOEXCEPT
87     { return 0 != ( flags_ & flag_complete); }
88 
unwind_stack()89     void unwind_stack() BOOST_NOEXCEPT
90     {
91         if ( is_started() && ! is_complete() && force_unwind() )
92         {
93             flags_ |= flag_unwind_stack;
94             param_type to( unwind_t::force_unwind);
95             caller_->jump(
96                 * callee_,
97                 & to);
98             flags_ &= ~flag_unwind_stack;
99 
100             BOOST_ASSERT( is_complete() );
101         }
102     }
103 
pull()104     void pull()
105     {
106         BOOST_ASSERT( ! is_running() );
107         BOOST_ASSERT( ! is_complete() );
108 
109         flags_ |= flag_running;
110         param_type to( this);
111         param_type * from(
112             static_cast< param_type * >(
113                 caller_->jump(
114                     * callee_,
115                     & to) ) );
116         flags_ &= ~flag_running;
117         result_ = from->data;
118         if ( from->do_unwind) throw forced_unwind();
119         if ( except_) rethrow_exception( except_);
120     }
121 
has_result() const122     bool has_result() const
123     { return 0 != result_; }
124 
get() const125     R get() const
126     {
127         if ( ! has_result() )
128             boost::throw_exception(
129                 invalid_result() );
130         return * result_;
131     }
132 
get_pointer() const133     R * get_pointer() const
134     {
135         if ( ! has_result() )
136             boost::throw_exception(
137                 invalid_result() );
138         return result_;
139     }
140 
141     virtual void destroy() = 0;
142 };
143 
144 template< typename R >
145 class pull_coroutine_impl< R & > : private noncopyable
146 {
147 protected:
148     int                     flags_;
149     exception_ptr           except_;
150     coroutine_context   *   caller_;
151     coroutine_context   *   callee_;
152     R                   *   result_;
153 
154 public:
155     typedef parameters< R & >                           param_type;
156 
pull_coroutine_impl(coroutine_context * caller,coroutine_context * callee,bool unwind)157     pull_coroutine_impl( coroutine_context * caller,
158                          coroutine_context * callee,
159                          bool unwind) :
160         flags_( 0),
161         except_(),
162         caller_( caller),
163         callee_( callee),
164         result_( 0)
165     {
166         if ( unwind) flags_ |= flag_force_unwind;
167     }
168 
pull_coroutine_impl(coroutine_context * caller,coroutine_context * callee,bool unwind,R * result)169     pull_coroutine_impl( coroutine_context * caller,
170                          coroutine_context * callee,
171                          bool unwind,
172                          R * result) :
173         flags_( 0),
174         except_(),
175         caller_( caller),
176         callee_( callee),
177         result_( result)
178     {
179         if ( unwind) flags_ |= flag_force_unwind;
180     }
181 
~pull_coroutine_impl()182     virtual ~pull_coroutine_impl() {}
183 
force_unwind() const184     bool force_unwind() const BOOST_NOEXCEPT
185     { return 0 != ( flags_ & flag_force_unwind); }
186 
unwind_requested() const187     bool unwind_requested() const BOOST_NOEXCEPT
188     { return 0 != ( flags_ & flag_unwind_stack); }
189 
is_started() const190     bool is_started() const BOOST_NOEXCEPT
191     { return 0 != ( flags_ & flag_started); }
192 
is_running() const193     bool is_running() const BOOST_NOEXCEPT
194     { return 0 != ( flags_ & flag_running); }
195 
is_complete() const196     bool is_complete() const BOOST_NOEXCEPT
197     { return 0 != ( flags_ & flag_complete); }
198 
unwind_stack()199     void unwind_stack() BOOST_NOEXCEPT
200     {
201         if ( is_started() && ! is_complete() && force_unwind() )
202         {
203             flags_ |= flag_unwind_stack;
204             param_type to( unwind_t::force_unwind);
205             caller_->jump(
206                 * callee_,
207                 & to);
208             flags_ &= ~flag_unwind_stack;
209 
210             BOOST_ASSERT( is_complete() );
211         }
212     }
213 
pull()214     void pull()
215     {
216         BOOST_ASSERT( ! is_running() );
217         BOOST_ASSERT( ! is_complete() );
218 
219         flags_ |= flag_running;
220         param_type to( this);
221         param_type * from(
222             static_cast< param_type * >(
223                 caller_->jump(
224                     * callee_,
225                     & to) ) );
226         flags_ &= ~flag_running;
227         result_ = from->data;
228         if ( from->do_unwind) throw forced_unwind();
229         if ( except_) rethrow_exception( except_);
230     }
231 
has_result() const232     bool has_result() const
233     { return 0 != result_; }
234 
get() const235     R & get() const
236     {
237         if ( ! has_result() )
238             boost::throw_exception(
239                 invalid_result() );
240         return * result_;
241     }
242 
get_pointer() const243     R * get_pointer() const
244     {
245         if ( ! has_result() )
246             boost::throw_exception(
247                 invalid_result() );
248         return result_;
249     }
250 
251     virtual void destroy() = 0;
252 };
253 
254 template<>
255 class pull_coroutine_impl< void > : private noncopyable
256 {
257 protected:
258     int                     flags_;
259     exception_ptr           except_;
260     coroutine_context   *   caller_;
261     coroutine_context   *   callee_;
262 
263 public:
264     typedef parameters< void >      param_type;
265 
pull_coroutine_impl(coroutine_context * caller,coroutine_context * callee,bool unwind)266     pull_coroutine_impl( coroutine_context * caller,
267                          coroutine_context * callee,
268                          bool unwind) :
269         flags_( 0),
270         except_(),
271         caller_( caller),
272         callee_( callee)
273     {
274         if ( unwind) flags_ |= flag_force_unwind;
275     }
276 
~pull_coroutine_impl()277     virtual ~pull_coroutine_impl() {}
278 
force_unwind() const279     inline bool force_unwind() const BOOST_NOEXCEPT
280     { return 0 != ( flags_ & flag_force_unwind); }
281 
unwind_requested() const282     inline bool unwind_requested() const BOOST_NOEXCEPT
283     { return 0 != ( flags_ & flag_unwind_stack); }
284 
is_started() const285     inline bool is_started() const BOOST_NOEXCEPT
286     { return 0 != ( flags_ & flag_started); }
287 
is_running() const288     inline bool is_running() const BOOST_NOEXCEPT
289     { return 0 != ( flags_ & flag_running); }
290 
is_complete() const291     inline bool is_complete() const BOOST_NOEXCEPT
292     { return 0 != ( flags_ & flag_complete); }
293 
unwind_stack()294     inline void unwind_stack() BOOST_NOEXCEPT
295     {
296         if ( is_started() && ! is_complete() && force_unwind() )
297         {
298             flags_ |= flag_unwind_stack;
299             param_type to( unwind_t::force_unwind);
300             caller_->jump(
301                 * callee_,
302                 & to);
303             flags_ &= ~flag_unwind_stack;
304 
305             BOOST_ASSERT( is_complete() );
306         }
307     }
308 
pull()309     inline void pull()
310     {
311         BOOST_ASSERT( ! is_running() );
312         BOOST_ASSERT( ! is_complete() );
313 
314         flags_ |= flag_running;
315         param_type to( this);
316         param_type * from(
317             static_cast< param_type * >(
318                 caller_->jump(
319                     * callee_,
320                     & to) ) );
321         flags_ &= ~flag_running;
322         if ( from->do_unwind) throw forced_unwind();
323         if ( except_) rethrow_exception( except_);
324     }
325 
326     virtual void destroy() = 0;
327 };
328 
329 }}}
330 
331 #ifdef BOOST_HAS_ABI_HEADERS
332 #  include BOOST_ABI_SUFFIX
333 #endif
334 
335 #endif // BOOST_COROUTINES_DETAIL_PULL_COROUTINE_IMPL_H
336