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_PUSH_COROUTINE_OBJECT_H
8 #define BOOST_COROUTINES_DETAIL_PUSH_COROUTINE_OBJECT_H
9 
10 #include <boost/assert.hpp>
11 #include <boost/config.hpp>
12 #include <boost/context/detail/config.hpp>
13 #include <boost/cstdint.hpp>
14 #include <boost/exception_ptr.hpp>
15 #include <boost/move/move.hpp>
16 
17 #include <boost/coroutine/detail/config.hpp>
18 #include <boost/coroutine/detail/coroutine_context.hpp>
19 #include <boost/coroutine/detail/flags.hpp>
20 #include <boost/coroutine/detail/preallocated.hpp>
21 #include <boost/coroutine/detail/push_coroutine_impl.hpp>
22 #include <boost/coroutine/detail/trampoline_push.hpp>
23 #include <boost/coroutine/exceptions.hpp>
24 #include <boost/coroutine/flags.hpp>
25 #include <boost/coroutine/stack_context.hpp>
26 
27 #ifdef BOOST_HAS_ABI_HEADERS
28 #  include BOOST_ABI_PREFIX
29 #endif
30 
31 #if defined(BOOST_MSVC)
32 # pragma warning(push)
33 # pragma warning(disable:4355)
34 #endif
35 
36 namespace boost {
37 namespace coroutines {
38 namespace detail {
39 
40 struct push_coroutine_context
41 {
42     coroutine_context   caller;
43     coroutine_context   callee;
44 
45     template< typename Coro >
push_coroutine_contextboost::coroutines::detail::push_coroutine_context46     push_coroutine_context( preallocated const& palloc, Coro *) :
47         caller(),
48         callee( trampoline_push< Coro >, palloc)
49     {}
50 };
51 
52 struct push_coroutine_context_void
53 {
54     coroutine_context   caller;
55     coroutine_context   callee;
56 
57     template< typename Coro >
push_coroutine_context_voidboost::coroutines::detail::push_coroutine_context_void58     push_coroutine_context_void( preallocated const& palloc, Coro *) :
59         caller(),
60         callee( trampoline_push_void< Coro >, palloc)
61     {}
62 };
63 
64 template< typename PullCoro, typename R, typename Fn, typename StackAllocator >
65 class push_coroutine_object : private push_coroutine_context,
66                               public push_coroutine_impl< R >
67 {
68 private:
69     typedef push_coroutine_context                                      ctx_t;
70     typedef push_coroutine_impl< R >                                    base_t;
71     typedef push_coroutine_object< PullCoro, R, Fn, StackAllocator >    obj_t;
72 
73     Fn                  fn_;
74     stack_context       stack_ctx_;
75     StackAllocator      stack_alloc_;
76 
deallocate_(obj_t * obj)77     static void deallocate_( obj_t * obj)
78     {
79         stack_context stack_ctx( obj->stack_ctx_);
80         StackAllocator stack_alloc( obj->stack_alloc_);
81         obj->unwind_stack();
82         obj->~obj_t();
83         stack_alloc.deallocate( stack_ctx);
84     }
85 
86 public:
87 #ifdef BOOST_NO_CXX11_RVALUE_REFERENCES
push_coroutine_object(Fn fn,attributes const & attrs,preallocated const & palloc,StackAllocator const & stack_alloc)88     push_coroutine_object( Fn fn, attributes const& attrs,
89                            preallocated const& palloc,
90                            StackAllocator const& stack_alloc) BOOST_NOEXCEPT :
91         ctx_t( palloc, this),
92         base_t( & this->caller,
93                 & this->callee,
94                 stack_unwind == attrs.do_unwind),
95         fn_( fn),
96         stack_ctx_( palloc.sctx),
97         stack_alloc_( stack_alloc)
98     {}
99 #endif
100 
push_coroutine_object(BOOST_RV_REF (Fn)fn,attributes const & attrs,preallocated const & palloc,StackAllocator const & stack_alloc)101     push_coroutine_object( BOOST_RV_REF( Fn) fn, attributes const& attrs,
102                            preallocated const& palloc,
103                            StackAllocator const& stack_alloc) BOOST_NOEXCEPT :
104         ctx_t( palloc, this),
105         base_t( & this->caller,
106                 & this->callee,
107                 stack_unwind == attrs.do_unwind),
108 #ifdef BOOST_NO_CXX11_RVALUE_REFERENCES
109         fn_( fn),
110 #else
111         fn_( boost::forward< Fn >( fn) ),
112 #endif
113         stack_ctx_( palloc.sctx),
114         stack_alloc_( stack_alloc)
115     {}
116 
run(R * result)117     void run( R * result)
118     {
119         BOOST_ASSERT( ! base_t::unwind_requested() );
120 
121         base_t::flags_ |= flag_started;
122         base_t::flags_ |= flag_running;
123 
124         // create push_coroutine
125         typename PullCoro::synth_type b( & this->callee, & this->caller, false, result);
126         PullCoro pull_coro( synthesized_t::syntesized, b);
127         try
128         { fn_( pull_coro); }
129         catch ( forced_unwind const&)
130         {}
131 #if defined( BOOST_CONTEXT_HAS_CXXABI_H )
132         catch ( abi::__forced_unwind const&)
133         { throw; }
134 #endif
135         catch (...)
136         { base_t::except_ = current_exception(); }
137 
138         base_t::flags_ |= flag_complete;
139         base_t::flags_ &= ~flag_running;
140         typename base_t::param_type to;
141         this->callee.jump(
142             this->caller,
143             & to);
144         BOOST_ASSERT_MSG( false, "pull_coroutine is complete");
145     }
146 
destroy()147     void destroy()
148     { deallocate_( this); }
149 };
150 
151 template< typename PullCoro, typename R, typename Fn, typename StackAllocator >
152 class push_coroutine_object< PullCoro, R &, Fn, StackAllocator > : private push_coroutine_context,
153                                                                    public push_coroutine_impl< R & >
154 {
155 private:
156     typedef push_coroutine_context                                          ctx_t;
157     typedef push_coroutine_impl< R & >                                      base_t;
158     typedef push_coroutine_object< PullCoro, R &, Fn, StackAllocator >      obj_t;
159 
160     Fn                  fn_;
161     stack_context       stack_ctx_;
162     StackAllocator      stack_alloc_;
163 
deallocate_(obj_t * obj)164     static void deallocate_( obj_t * obj)
165     {
166         stack_context stack_ctx( obj->stack_ctx_);
167         StackAllocator stack_alloc( obj->stack_alloc_);
168         obj->unwind_stack();
169         obj->~obj_t();
170         stack_alloc.deallocate( stack_ctx);
171     }
172 
173 public:
174 #ifdef BOOST_NO_CXX11_RVALUE_REFERENCES
push_coroutine_object(Fn fn,attributes const & attrs,preallocated const & palloc,StackAllocator const & stack_alloc)175     push_coroutine_object( Fn fn, attributes const& attrs,
176                            preallocated const& palloc,
177                            StackAllocator const& stack_alloc) BOOST_NOEXCEPT :
178         ctx_t( palloc, this),
179         base_t( & this->caller,
180                 & this->callee,
181                 stack_unwind == attrs.do_unwind),
182         fn_( fn),
183         stack_ctx_( palloc.sctx),
184         stack_alloc_( stack_alloc)
185     {}
186 #endif
187 
push_coroutine_object(BOOST_RV_REF (Fn)fn,attributes const & attrs,preallocated const & palloc,StackAllocator const & stack_alloc)188     push_coroutine_object( BOOST_RV_REF( Fn) fn, attributes const& attrs,
189                            preallocated const& palloc,
190                            StackAllocator const& stack_alloc) BOOST_NOEXCEPT :
191         ctx_t( palloc, this),
192         base_t( & this->caller,
193                 & this->callee,
194                 stack_unwind == attrs.do_unwind),
195 #ifdef BOOST_NO_CXX11_RVALUE_REFERENCES
196         fn_( fn),
197 #else
198         fn_( boost::forward< Fn >( fn) ),
199 #endif
200         stack_ctx_( palloc.sctx),
201         stack_alloc_( stack_alloc)
202     {}
203 
run(R * result)204     void run( R * result)
205     {
206         BOOST_ASSERT( ! base_t::unwind_requested() );
207 
208         base_t::flags_ |= flag_started;
209         base_t::flags_ |= flag_running;
210 
211         // create push_coroutine
212         typename PullCoro::synth_type b( & this->callee, & this->caller, false, result);
213         PullCoro push_coro( synthesized_t::syntesized, b);
214         try
215         { fn_( push_coro); }
216         catch ( forced_unwind const&)
217         {}
218 #if defined( BOOST_CONTEXT_HAS_CXXABI_H )
219         catch ( abi::__forced_unwind const&)
220         { throw; }
221 #endif
222         catch (...)
223         { base_t::except_ = current_exception(); }
224 
225         base_t::flags_ |= flag_complete;
226         base_t::flags_ &= ~flag_running;
227         typename base_t::param_type to;
228         this->callee.jump(
229             this->caller,
230             & to);
231         BOOST_ASSERT_MSG( false, "pull_coroutine is complete");
232     }
233 
destroy()234     void destroy()
235     { deallocate_( this); }
236 };
237 
238 template< typename PullCoro, typename Fn, typename StackAllocator >
239 class push_coroutine_object< PullCoro, void, Fn, StackAllocator > : private push_coroutine_context_void,
240                                                                     public push_coroutine_impl< void >
241 {
242 private:
243     typedef push_coroutine_context_void                                     ctx_t;
244     typedef push_coroutine_impl< void >                                     base_t;
245     typedef push_coroutine_object< PullCoro, void, Fn, StackAllocator >     obj_t;
246 
247     Fn                  fn_;
248     stack_context       stack_ctx_;
249     StackAllocator      stack_alloc_;
250 
deallocate_(obj_t * obj)251     static void deallocate_( obj_t * obj)
252     {
253         stack_context stack_ctx( obj->stack_ctx_);
254         StackAllocator stack_alloc( obj->stack_alloc_);
255         obj->unwind_stack();
256         obj->~obj_t();
257         stack_alloc.deallocate( stack_ctx);
258     }
259 
260 public:
261 #ifdef BOOST_NO_CXX11_RVALUE_REFERENCES
push_coroutine_object(Fn fn,attributes const & attrs,preallocated const & palloc,StackAllocator const & stack_alloc)262     push_coroutine_object( Fn fn, attributes const& attrs,
263                            preallocated const& palloc,
264                            StackAllocator const& stack_alloc) BOOST_NOEXCEPT :
265         ctx_t( palloc, this),
266         base_t( & this->caller,
267                 & this->callee,
268                 stack_unwind == attrs.do_unwind),
269         fn_( fn),
270         stack_ctx_( palloc.sctx),
271         stack_alloc_( stack_alloc)
272     {}
273 #endif
274 
push_coroutine_object(BOOST_RV_REF (Fn)fn,attributes const & attrs,preallocated const & palloc,StackAllocator const & stack_alloc)275     push_coroutine_object( BOOST_RV_REF( Fn) fn, attributes const& attrs,
276                            preallocated const& palloc,
277                            StackAllocator const& stack_alloc) BOOST_NOEXCEPT :
278         ctx_t( palloc, this),
279         base_t( & this->caller,
280                 & this->callee,
281                 stack_unwind == attrs.do_unwind),
282 #ifdef BOOST_NO_CXX11_RVALUE_REFERENCES
283         fn_( fn),
284 #else
285         fn_( boost::forward< Fn >( fn) ),
286 #endif
287         stack_ctx_( palloc.sctx),
288         stack_alloc_( stack_alloc)
289     {}
290 
run()291     void run()
292     {
293         BOOST_ASSERT( ! base_t::unwind_requested() );
294 
295         base_t::flags_ |= flag_started;
296         base_t::flags_ |= flag_running;
297 
298         // create push_coroutine
299         typename PullCoro::synth_type b( & this->callee, & this->caller, false);
300         PullCoro push_coro( synthesized_t::syntesized, b);
301         try
302         { fn_( push_coro); }
303         catch ( forced_unwind const&)
304         {}
305 #if defined( BOOST_CONTEXT_HAS_CXXABI_H )
306         catch ( abi::__forced_unwind const&)
307         { throw; }
308 #endif
309         catch (...)
310         { base_t::except_ = current_exception(); }
311 
312         base_t::flags_ |= flag_complete;
313         base_t::flags_ &= ~flag_running;
314         typename base_t::param_type to;
315         this->callee.jump(
316             this->caller,
317             & to);
318         BOOST_ASSERT_MSG( false, "pull_coroutine is complete");
319     }
320 
destroy()321     void destroy()
322     { deallocate_( this); }
323 };
324 
325 }}}
326 
327 #if defined(BOOST_MSVC)
328 # pragma warning(pop)
329 #endif
330 
331 #ifdef BOOST_HAS_ABI_HEADERS
332 #  include BOOST_ABI_SUFFIX
333 #endif
334 
335 #endif // BOOST_COROUTINES_DETAIL_PUSH_COROUTINE_OBJECT_H
336