]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/fiber/examples/asio/detail/yield.hpp
39e5695aae3b499527538fc6e0a09f440721f9e7
[ceph.git] / ceph / src / boost / libs / fiber / examples / asio / detail / yield.hpp
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
25 namespace boost {
26 namespace fibers {
27 namespace asio {
28 namespace detail {
29
30 //[fibers_asio_yield_completion
31 // Bundle a completion bool flag with a spinlock to protect it.
32 struct yield_completion {
33 typedef fibers::detail::spinlock mutex_t;
34 typedef std::unique_lock< mutex_t > lock_t;
35
36 mutex_t mtx_{};
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.
63 class yield_handler_base {
64 public:
65 yield_handler_base( yield_t const& y) :
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_).
82 yield_completion::lock_t lk{ ycomp_->mtx_ };
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_;
100 yield_t yt_;
101 // We depend on this pointer to yield_completion, which will be injected
102 // by async_result.
103 yield_completion * ycomp_{ nullptr };
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.
113 template< typename T >
114 class yield_handler: public yield_handler_base {
115 public:
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
136 yield_handler_base::operator()( ec);
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.
149 template<>
150 class yield_handler< void >: public yield_handler_base {
151 public:
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
168 template< typename Fn, typename T >
169 void asio_handler_invoke( Fn fn, yield_handler< T > * h) {
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>>
176 class async_result_base {
177 public:
178 explicit async_result_base( yield_handler_base & h) {
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
203 private:
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
214 namespace boost {
215 namespace 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.
222 template< typename T >
223 class async_result< boost::fibers::asio::detail::yield_handler< T > > :
224 public boost::fibers::asio::detail::async_result_base {
225 public:
226 // type returned by get()
227 typedef T type;
228
229 explicit async_result( boost::fibers::asio::detail::yield_handler< T > & h) :
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()
237 type get() {
238 boost::fibers::asio::detail::async_result_base::get();
239 return std::move( value_);
240 }
241
242 private:
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.
250 template<>
251 class async_result< boost::fibers::asio::detail::yield_handler< void > > :
252 public boost::fibers::asio::detail::async_result_base {
253 public:
254 typedef void type;
255
256 explicit async_result( boost::fibers::asio::detail::yield_handler< void > & h):
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>.
265 template< typename ReturnType >
266 struct handler_type< fibers::asio::yield_t, ReturnType() >
267 { typedef fibers::asio::detail::yield_handler< void > type; };
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.
273 template< typename ReturnType, typename Arg1 >
274 struct handler_type< fibers::asio::yield_t, ReturnType( Arg1) >
275 { typedef fibers::asio::detail::yield_handler< Arg1 > type; };
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.
282 template< typename ReturnType >
283 struct handler_type< fibers::asio::yield_t, ReturnType( boost::system::error_code) >
284 { typedef fibers::asio::detail::yield_handler< void > type; };
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.
292 template< typename ReturnType, typename Arg2 >
293 struct handler_type< fibers::asio::yield_t, ReturnType( boost::system::error_code, Arg2) >
294 { typedef fibers::asio::detail::yield_handler< Arg2 > type; };
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