Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
yield.hpp
Go to the documentation of this file.
1// Copyright Oliver Kowalke, Nat Goodspeed 2015.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at
4// http://www.boost.org/LICENSE_1_0.txt)
5
6#ifndef BOOST_FIBERS_ASIO_DETAIL_YIELD_HPP
7#define BOOST_FIBERS_ASIO_DETAIL_YIELD_HPP
8
9#include <boost/asio/async_result.hpp>
10#include <boost/asio/detail/config.hpp>
11#include <boost/asio/handler_type.hpp>
12#include <boost/system/error_code.hpp>
13#include <boost/system/system_error.hpp>
14#include <boost/throw_exception.hpp>
15#include <boost/assert.hpp>
16
17#include <boost/fiber/all.hpp>
18
19#include <mutex> // std::unique_lock
20
21#ifdef BOOST_HAS_ABI_HEADERS
22# include BOOST_ABI_PREFIX
23#endif
24
25namespace boost {
26namespace fibers {
27namespace asio {
28namespace detail {
29
30//[fibers_asio_yield_completion
31// Bundle a completion bool flag with a spinlock to protect it.
33 typedef fibers::detail::spinlock mutex_t;
34 typedef std::unique_lock< mutex_t > lock_t;
35
37 bool completed_{ false };
38
39 void wait() {
40 // yield_handler_base::operator()() will set completed_ true and
41 // attempt to wake a suspended fiber. It would be Bad if that call
42 // happened between our detecting (! completed_) and suspending.
43 lock_t lk{ mtx_ };
44 // If completed_ is already set, we're done here: don't suspend.
45 if ( ! completed_) {
46 // suspend(unique_lock<spinlock>) unlocks the lock in the act of
47 // resuming another fiber
48 fibers::context::active()->suspend( lk);
49 }
50 }
51};
52//]
53
54//[fibers_asio_yield_handler_base
55// This class encapsulates common elements between yield_handler<T> (capturing
56// a value to return from asio async function) and yield_handler<void> (no
57// such value). See yield_handler<T> and its <void> specialization below. Both
58// yield_handler<T> and yield_handler<void> are passed by value through
59// various layers of asio functions. In other words, they're potentially
60// copied multiple times. So key data such as the yield_completion instance
61// must be stored in our async_result<yield_handler<>> specialization, which
62// should be instantiated only once.
64public:
66 // capture the context* associated with the running fiber
67 ctx_{ boost::fibers::context::active() },
68 // capture the passed yield_t
69 yt_( y ) {
70 }
71
72 // completion callback passing only (error_code)
73 void operator()( boost::system::error_code const& ec) {
74 BOOST_ASSERT_MSG( ycomp_,
75 "Must inject yield_completion* "
76 "before calling yield_handler_base::operator()()");
77 BOOST_ASSERT_MSG( yt_.ec_,
78 "Must inject boost::system::error_code* "
79 "before calling yield_handler_base::operator()()");
80 // If originating fiber is busy testing completed_ flag, wait until it
81 // has observed (! completed_).
83 // Notify a subsequent yield_completion::wait() call that it need not
84 // suspend.
85 ycomp_->completed_ = true;
86 // set the error_code bound by yield_t
87 * yt_.ec_ = ec;
88 // If ctx_ is still active, e.g. because the async operation
89 // immediately called its callback (this method!) before the asio
90 // async function called async_result_base::get(), we must not set it
91 // ready.
92 if ( fibers::context::active() != ctx_ ) {
93 // wake the fiber
94 fibers::context::active()->set_ready( ctx_);
95 }
96 }
97
98//private:
99 boost::fibers::context * ctx_;
101 // We depend on this pointer to yield_completion, which will be injected
102 // by async_result.
104};
105//]
106
107//[fibers_asio_yield_handler_T
108// asio uses handler_type<completion token type, signature>::type to decide
109// what to instantiate as the actual handler. Below, we specialize
110// handler_type< yield_t, ... > to indicate yield_handler<>. So when you pass
111// an instance of yield_t as an asio completion token, asio selects
112// yield_handler<> as the actual handler class.
113template< typename T >
115public:
116 // asio passes the completion token to the handler constructor
117 explicit yield_handler( yield_t const& y) :
118 yield_handler_base{ y } {
119 }
120
121 // completion callback passing only value (T)
122 void operator()( T t) {
123 // just like callback passing success error_code
124 (*this)( boost::system::error_code(), std::move(t) );
125 }
126
127 // completion callback passing (error_code, T)
128 void operator()( boost::system::error_code const& ec, T t) {
129 BOOST_ASSERT_MSG( value_,
130 "Must inject value ptr "
131 "before caling yield_handler<T>::operator()()");
132 // move the value to async_result<> instance BEFORE waking up a
133 // suspended fiber
134 * value_ = std::move( t);
135 // forward the call to base-class completion handler
137 }
138
139//private:
140 // pointer to destination for eventual value
141 // this must be injected by async_result before operator()() is called
142 T * value_{ nullptr };
143};
144//]
145
146//[fibers_asio_yield_handler_void
147// yield_handler<void> is like yield_handler<T> without value_. In fact it's
148// just like yield_handler_base.
149template<>
150class yield_handler< void >: public yield_handler_base {
151public:
152 explicit yield_handler( yield_t const& y) :
153 yield_handler_base{ y } {
154 }
155
156 // nullary completion callback
157 void operator()() {
158 ( * this)( boost::system::error_code() );
159 }
160
161 // inherit operator()(error_code) overload from base class
162 using yield_handler_base::operator();
163};
164//]
165
166// Specialize asio_handler_invoke hook to ensure that any exceptions thrown
167// from the handler are propagated back to the caller
168template< typename Fn, typename T >
170 fn();
171}
172
173//[fibers_asio_async_result_base
174// Factor out commonality between async_result<yield_handler<T>> and
175// async_result<yield_handler<void>>
177public:
179 // Inject ptr to our yield_completion instance into this
180 // yield_handler<>.
181 h.ycomp_ = & this->ycomp_;
182 // if yield_t didn't bind an error_code, make yield_handler_base's
183 // error_code* point to an error_code local to this object so
184 // yield_handler_base::operator() can unconditionally store through
185 // its error_code*
186 if ( ! h.yt_.ec_) {
187 h.yt_.ec_ = & ec_;
188 }
189 }
190
191 void get() {
192 // Unless yield_handler_base::operator() has already been called,
193 // suspend the calling fiber until that call.
194 ycomp_.wait();
195 // The only way our own ec_ member could have a non-default value is
196 // if our yield_handler did not have a bound error_code AND the
197 // completion callback passed a non-default error_code.
198 if ( ec_) {
199 throw_exception( boost::system::system_error{ ec_ } );
200 }
201 }
202
203private:
204 // If yield_t does not bind an error_code instance, store into here.
205 boost::system::error_code ec_{};
206 // async_result_base owns the yield_completion because, unlike
207 // yield_handler<>, async_result<> is only instantiated once.
208 yield_completion ycomp_{};
209};
210//]
211
212}}}}
213
214namespace boost {
215namespace asio {
216
217//[fibers_asio_async_result_T
218// asio constructs an async_result<> instance from the yield_handler specified
219// by handler_type<>::type. A particular asio async method constructs the
220// yield_handler, constructs this async_result specialization from it, then
221// returns the result of calling its get() method.
222template< typename T >
223class async_result< boost::fibers::asio::detail::yield_handler< T > > :
225public:
226 // type returned by get()
227 typedef T type;
228
230 boost::fibers::asio::detail::async_result_base{ h } {
231 // Inject ptr to our value_ member into yield_handler<>: result will
232 // be stored here.
233 h.value_ = & value_;
234 }
235
236 // asio async method returns result of calling get()
239 return std::move( value_);
240 }
241
242private:
243 type value_{};
244};
245//]
246
247//[fibers_asio_async_result_void
248// Without the need to handle a passed value, our yield_handler<void>
249// specialization is just like async_result_base.
250template<>
251class async_result< boost::fibers::asio::detail::yield_handler< void > > :
253public:
254 typedef void type;
255
257 boost::fibers::asio::detail::async_result_base{ h } {
258 }
259};
260//]
261
262// Handler type specialisation for fibers::asio::yield.
263// When 'yield' is passed as a completion handler which accepts no parameters,
264// use yield_handler<void>.
265template< typename ReturnType >
266struct handler_type< fibers::asio::yield_t, ReturnType() >
268
269// Handler type specialisation for fibers::asio::yield.
270// When 'yield' is passed as a completion handler which accepts a data
271// parameter, use yield_handler<parameter type> to return that parameter to
272// the caller.
273template< typename ReturnType, typename Arg1 >
274struct handler_type< fibers::asio::yield_t, ReturnType( Arg1) >
276
277//[asio_handler_type
278// Handler type specialisation for fibers::asio::yield.
279// When 'yield' is passed as a completion handler which accepts only
280// error_code, use yield_handler<void>. yield_handler will take care of the
281// error_code one way or another.
282template< typename ReturnType >
283struct handler_type< fibers::asio::yield_t, ReturnType( boost::system::error_code) >
285//]
286
287// Handler type specialisation for fibers::asio::yield.
288// When 'yield' is passed as a completion handler which accepts a data
289// parameter and an error_code, use yield_handler<parameter type> to return
290// just the parameter to the caller. yield_handler will take care of the
291// error_code one way or another.
292template< typename ReturnType, typename Arg2 >
293struct handler_type< fibers::asio::yield_t, ReturnType( boost::system::error_code, Arg2) >
295
296}}
297
298#ifdef BOOST_HAS_ABI_HEADERS
299# include BOOST_ABI_SUFFIX
300#endif
301
302#endif // BOOST_FIBERS_ASIO_DETAIL_YIELD_HPP
async_result(boost::fibers::asio::detail::yield_handler< T > &h)
Definition yield.hpp:229
async_result(boost::fibers::asio::detail::yield_handler< void > &h)
Definition yield.hpp:256
void operator()(boost::system::error_code const &ec)
Definition yield.hpp:73
void operator()(boost::system::error_code const &ec, T t)
Definition yield.hpp:128
boost::system::error_code * ec_
Definition yield.hpp:46
void asio_handler_invoke(Fn fn, yield_handler< T > *h)
Definition yield.hpp:169
#define T(meth, val, expected)
fibers::asio::detail::yield_handler< void > type
Definition yield.hpp:267
fibers::asio::detail::yield_handler< Arg1 > type
Definition yield.hpp:275
std::unique_lock< mutex_t > lock_t
Definition yield.hpp:34