]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/fiber/examples/asio/detail/yield.hpp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / libs / fiber / examples / asio / detail / yield.hpp
CommitLineData
7c673cae
FG
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>
b32b8144
FG
12#include <boost/assert.hpp>
13#include <boost/atomic.hpp>
14#include <boost/intrusive_ptr.hpp>
7c673cae
FG
15#include <boost/system/error_code.hpp>
16#include <boost/system/system_error.hpp>
17#include <boost/throw_exception.hpp>
7c673cae
FG
18
19#include <boost/fiber/all.hpp>
20
21#include <mutex> // std::unique_lock
22
23#ifdef BOOST_HAS_ABI_HEADERS
24# include BOOST_ABI_PREFIX
25#endif
26
27namespace boost {
28namespace fibers {
29namespace asio {
30namespace detail {
31
32//[fibers_asio_yield_completion
33// Bundle a completion bool flag with a spinlock to protect it.
34struct yield_completion {
b32b8144
FG
35 enum state_t {
36 init,
37 waiting,
38 complete
39 };
7c673cae 40
b32b8144
FG
41 typedef fibers::detail::spinlock mutex_t;
42 typedef std::unique_lock< mutex_t > lock_t;
43 typedef boost::intrusive_ptr< yield_completion > ptr_t;
44
45 std::atomic< std::size_t > use_count_{ 0 };
46 mutex_t mtx_{};
47 state_t state_{ init };
7c673cae
FG
48
49 void wait() {
b32b8144 50 // yield_handler_base::operator()() will set state_ `complete` and
7c673cae 51 // attempt to wake a suspended fiber. It would be Bad if that call
b32b8144 52 // happened between our detecting (complete != state_) and suspending.
7c673cae 53 lock_t lk{ mtx_ };
b32b8144
FG
54 // If state_ is already set, we're done here: don't suspend.
55 if ( complete != state_) {
56 state_ = waiting;
7c673cae
FG
57 // suspend(unique_lock<spinlock>) unlocks the lock in the act of
58 // resuming another fiber
59 fibers::context::active()->suspend( lk);
60 }
61 }
b32b8144
FG
62
63 friend void intrusive_ptr_add_ref( yield_completion * yc) noexcept {
64 BOOST_ASSERT( nullptr != yc);
65 yc->use_count_.fetch_add( 1, std::memory_order_relaxed);
66 }
67
68 friend void intrusive_ptr_release( yield_completion * yc) noexcept {
69 BOOST_ASSERT( nullptr != yc);
70 if ( 1 == yc->use_count_.fetch_sub( 1, std::memory_order_release) ) {
71 std::atomic_thread_fence( std::memory_order_acquire);
72 delete yc;
73 }
74 }
7c673cae
FG
75};
76//]
77
78//[fibers_asio_yield_handler_base
79// This class encapsulates common elements between yield_handler<T> (capturing
80// a value to return from asio async function) and yield_handler<void> (no
81// such value). See yield_handler<T> and its <void> specialization below. Both
82// yield_handler<T> and yield_handler<void> are passed by value through
83// various layers of asio functions. In other words, they're potentially
84// copied multiple times. So key data such as the yield_completion instance
85// must be stored in our async_result<yield_handler<>> specialization, which
86// should be instantiated only once.
87class yield_handler_base {
88public:
89 yield_handler_base( yield_t const& y) :
90 // capture the context* associated with the running fiber
91 ctx_{ boost::fibers::context::active() },
92 // capture the passed yield_t
93 yt_( y ) {
94 }
95
96 // completion callback passing only (error_code)
97 void operator()( boost::system::error_code const& ec) {
98 BOOST_ASSERT_MSG( ycomp_,
99 "Must inject yield_completion* "
100 "before calling yield_handler_base::operator()()");
101 BOOST_ASSERT_MSG( yt_.ec_,
102 "Must inject boost::system::error_code* "
103 "before calling yield_handler_base::operator()()");
b32b8144
FG
104 // If originating fiber is busy testing state_ flag, wait until it
105 // has observed (completed != state_).
7c673cae 106 yield_completion::lock_t lk{ ycomp_->mtx_ };
b32b8144 107 yield_completion::state_t state = ycomp_->state_;
7c673cae
FG
108 // Notify a subsequent yield_completion::wait() call that it need not
109 // suspend.
b32b8144 110 ycomp_->state_ = yield_completion::complete;
7c673cae
FG
111 // set the error_code bound by yield_t
112 * yt_.ec_ = ec;
b32b8144
FG
113 // unlock the lock that protects state_
114 lk.unlock();
7c673cae
FG
115 // If ctx_ is still active, e.g. because the async operation
116 // immediately called its callback (this method!) before the asio
117 // async function called async_result_base::get(), we must not set it
118 // ready.
b32b8144 119 if ( yield_completion::waiting == state) {
7c673cae 120 // wake the fiber
b32b8144 121 fibers::context::active()->schedule( ctx_);
7c673cae
FG
122 }
123 }
124
125//private:
126 boost::fibers::context * ctx_;
127 yield_t yt_;
128 // We depend on this pointer to yield_completion, which will be injected
129 // by async_result.
b32b8144 130 yield_completion::ptr_t ycomp_{};
7c673cae
FG
131};
132//]
133
134//[fibers_asio_yield_handler_T
135// asio uses handler_type<completion token type, signature>::type to decide
136// what to instantiate as the actual handler. Below, we specialize
137// handler_type< yield_t, ... > to indicate yield_handler<>. So when you pass
138// an instance of yield_t as an asio completion token, asio selects
139// yield_handler<> as the actual handler class.
140template< typename T >
141class yield_handler: public yield_handler_base {
142public:
143 // asio passes the completion token to the handler constructor
144 explicit yield_handler( yield_t const& y) :
145 yield_handler_base{ y } {
146 }
147
148 // completion callback passing only value (T)
149 void operator()( T t) {
150 // just like callback passing success error_code
151 (*this)( boost::system::error_code(), std::move(t) );
152 }
153
154 // completion callback passing (error_code, T)
155 void operator()( boost::system::error_code const& ec, T t) {
156 BOOST_ASSERT_MSG( value_,
157 "Must inject value ptr "
158 "before caling yield_handler<T>::operator()()");
159 // move the value to async_result<> instance BEFORE waking up a
160 // suspended fiber
161 * value_ = std::move( t);
162 // forward the call to base-class completion handler
163 yield_handler_base::operator()( ec);
164 }
165
166//private:
167 // pointer to destination for eventual value
168 // this must be injected by async_result before operator()() is called
b32b8144 169 T * value_{ nullptr };
7c673cae
FG
170};
171//]
172
173//[fibers_asio_yield_handler_void
174// yield_handler<void> is like yield_handler<T> without value_. In fact it's
175// just like yield_handler_base.
176template<>
177class yield_handler< void >: public yield_handler_base {
178public:
179 explicit yield_handler( yield_t const& y) :
180 yield_handler_base{ y } {
181 }
182
183 // nullary completion callback
184 void operator()() {
185 ( * this)( boost::system::error_code() );
186 }
187
188 // inherit operator()(error_code) overload from base class
189 using yield_handler_base::operator();
190};
191//]
192
193// Specialize asio_handler_invoke hook to ensure that any exceptions thrown
194// from the handler are propagated back to the caller
195template< typename Fn, typename T >
196void asio_handler_invoke( Fn fn, yield_handler< T > * h) {
197 fn();
198}
199
200//[fibers_asio_async_result_base
201// Factor out commonality between async_result<yield_handler<T>> and
202// async_result<yield_handler<void>>
203class async_result_base {
204public:
b32b8144
FG
205 explicit async_result_base( yield_handler_base & h) :
206 ycomp_{ new yield_completion{} } {
7c673cae
FG
207 // Inject ptr to our yield_completion instance into this
208 // yield_handler<>.
b32b8144 209 h.ycomp_ = this->ycomp_;
7c673cae
FG
210 // if yield_t didn't bind an error_code, make yield_handler_base's
211 // error_code* point to an error_code local to this object so
212 // yield_handler_base::operator() can unconditionally store through
213 // its error_code*
214 if ( ! h.yt_.ec_) {
215 h.yt_.ec_ = & ec_;
216 }
217 }
218
219 void get() {
220 // Unless yield_handler_base::operator() has already been called,
221 // suspend the calling fiber until that call.
b32b8144 222 ycomp_->wait();
7c673cae
FG
223 // The only way our own ec_ member could have a non-default value is
224 // if our yield_handler did not have a bound error_code AND the
225 // completion callback passed a non-default error_code.
226 if ( ec_) {
227 throw_exception( boost::system::system_error{ ec_ } );
228 }
229 }
230
231private:
232 // If yield_t does not bind an error_code instance, store into here.
233 boost::system::error_code ec_{};
b32b8144 234 yield_completion::ptr_t ycomp_;
7c673cae
FG
235};
236//]
237
238}}}}
239
240namespace boost {
241namespace asio {
242
243//[fibers_asio_async_result_T
244// asio constructs an async_result<> instance from the yield_handler specified
245// by handler_type<>::type. A particular asio async method constructs the
246// yield_handler, constructs this async_result specialization from it, then
247// returns the result of calling its get() method.
248template< typename T >
249class async_result< boost::fibers::asio::detail::yield_handler< T > > :
250 public boost::fibers::asio::detail::async_result_base {
251public:
252 // type returned by get()
253 typedef T type;
254
255 explicit async_result( boost::fibers::asio::detail::yield_handler< T > & h) :
256 boost::fibers::asio::detail::async_result_base{ h } {
257 // Inject ptr to our value_ member into yield_handler<>: result will
258 // be stored here.
259 h.value_ = & value_;
260 }
261
262 // asio async method returns result of calling get()
263 type get() {
264 boost::fibers::asio::detail::async_result_base::get();
265 return std::move( value_);
266 }
267
268private:
269 type value_{};
270};
271//]
272
273//[fibers_asio_async_result_void
274// Without the need to handle a passed value, our yield_handler<void>
275// specialization is just like async_result_base.
276template<>
277class async_result< boost::fibers::asio::detail::yield_handler< void > > :
278 public boost::fibers::asio::detail::async_result_base {
279public:
280 typedef void type;
281
282 explicit async_result( boost::fibers::asio::detail::yield_handler< void > & h):
283 boost::fibers::asio::detail::async_result_base{ h } {
284 }
285};
286//]
287
288// Handler type specialisation for fibers::asio::yield.
289// When 'yield' is passed as a completion handler which accepts no parameters,
290// use yield_handler<void>.
291template< typename ReturnType >
292struct handler_type< fibers::asio::yield_t, ReturnType() >
293{ typedef fibers::asio::detail::yield_handler< void > type; };
294
295// Handler type specialisation for fibers::asio::yield.
296// When 'yield' is passed as a completion handler which accepts a data
297// parameter, use yield_handler<parameter type> to return that parameter to
298// the caller.
299template< typename ReturnType, typename Arg1 >
300struct handler_type< fibers::asio::yield_t, ReturnType( Arg1) >
301{ typedef fibers::asio::detail::yield_handler< Arg1 > type; };
302
303//[asio_handler_type
304// Handler type specialisation for fibers::asio::yield.
305// When 'yield' is passed as a completion handler which accepts only
306// error_code, use yield_handler<void>. yield_handler will take care of the
307// error_code one way or another.
308template< typename ReturnType >
309struct handler_type< fibers::asio::yield_t, ReturnType( boost::system::error_code) >
310{ typedef fibers::asio::detail::yield_handler< void > type; };
311//]
312
313// Handler type specialisation for fibers::asio::yield.
314// When 'yield' is passed as a completion handler which accepts a data
315// parameter and an error_code, use yield_handler<parameter type> to return
316// just the parameter to the caller. yield_handler will take care of the
317// error_code one way or another.
318template< typename ReturnType, typename Arg2 >
319struct handler_type< fibers::asio::yield_t, ReturnType( boost::system::error_code, Arg2) >
320{ typedef fibers::asio::detail::yield_handler< Arg2 > type; };
321
322}}
323
324#ifdef BOOST_HAS_ABI_HEADERS
325# include BOOST_ABI_SUFFIX
326#endif
327
328#endif // BOOST_FIBERS_ASIO_DETAIL_YIELD_HPP