]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/asio/impl/awaitable.hpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / boost / asio / impl / awaitable.hpp
CommitLineData
92f5a8d4
TL
1//
2// impl/awaitable.hpp
3// ~~~~~~~~~~~~~~~~~~
4//
f67539c2 5// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com)
92f5a8d4
TL
6//
7// Distributed under the Boost Software License, Version 1.0. (See accompanying
8// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9//
10
11#ifndef BOOST_ASIO_IMPL_AWAITABLE_HPP
12#define BOOST_ASIO_IMPL_AWAITABLE_HPP
13
14#if defined(_MSC_VER) && (_MSC_VER >= 1200)
15# pragma once
16#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
17
18#include <boost/asio/detail/config.hpp>
19#include <exception>
20#include <new>
21#include <tuple>
22#include <utility>
23#include <boost/asio/detail/thread_context.hpp>
24#include <boost/asio/detail/thread_info_base.hpp>
25#include <boost/asio/detail/type_traits.hpp>
26#include <boost/asio/post.hpp>
27#include <boost/system/system_error.hpp>
28#include <boost/asio/this_coro.hpp>
29
30#include <boost/asio/detail/push_options.hpp>
31
32namespace boost {
33namespace asio {
34namespace detail {
35
36// An awaitable_thread represents a thread-of-execution that is composed of one
37// or more "stack frames", with each frame represented by an awaitable_frame.
38// All execution occurs in the context of the awaitable_thread's executor. An
39// awaitable_thread continues to "pump" the stack frames by repeatedly resuming
40// the top stack frame until the stack is empty, or until ownership of the
41// stack is transferred to another awaitable_thread object.
42//
43// +------------------------------------+
44// | top_of_stack_ |
45// | V
46// +--------------+---+ +-----------------+
47// | | | |
48// | awaitable_thread |<---------------------------+ awaitable_frame |
49// | | attached_thread_ | |
50// +--------------+---+ (Set only when +---+-------------+
51// | frames are being |
52// | actively pumped | caller_
53// | by a thread, and |
54// | then only for V
55// | the top frame.) +-----------------+
56// | | |
57// | | awaitable_frame |
58// | | |
59// | +---+-------------+
60// | |
61// | | caller_
62// | :
63// | :
64// | |
65// | V
66// | +-----------------+
67// | bottom_of_stack_ | |
68// +------------------------------->| awaitable_frame |
69// | |
70// +-----------------+
71
72template <typename Executor>
73class awaitable_frame_base
74{
75public:
76#if !defined(BOOST_ASIO_DISABLE_AWAITABLE_FRAME_RECYCLING)
77 void* operator new(std::size_t size)
78 {
79 return boost::asio::detail::thread_info_base::allocate(
80 boost::asio::detail::thread_info_base::awaitable_frame_tag(),
81 boost::asio::detail::thread_context::thread_call_stack::top(),
82 size);
83 }
84
85 void operator delete(void* pointer, std::size_t size)
86 {
87 boost::asio::detail::thread_info_base::deallocate(
88 boost::asio::detail::thread_info_base::awaitable_frame_tag(),
89 boost::asio::detail::thread_context::thread_call_stack::top(),
90 pointer, size);
91 }
92#endif // !defined(BOOST_ASIO_DISABLE_AWAITABLE_FRAME_RECYCLING)
93
94 // The frame starts in a suspended state until the awaitable_thread object
95 // pumps the stack.
96 auto initial_suspend() noexcept
97 {
98 return suspend_always();
99 }
100
101 // On final suspension the frame is popped from the top of the stack.
102 auto final_suspend() noexcept
103 {
104 struct result
105 {
106 awaitable_frame_base* this_;
107
108 bool await_ready() const noexcept
109 {
110 return false;
111 }
112
113 void await_suspend(coroutine_handle<void>) noexcept
114 {
20effc67 115 this->this_->pop_frame();
92f5a8d4
TL
116 }
117
118 void await_resume() const noexcept
119 {
120 }
121 };
122
123 return result{this};
124 }
125
126 void set_except(std::exception_ptr e) noexcept
127 {
128 pending_exception_ = e;
129 }
130
131 void set_error(const boost::system::error_code& ec)
132 {
133 this->set_except(std::make_exception_ptr(boost::system::system_error(ec)));
134 }
135
136 void unhandled_exception()
137 {
138 set_except(std::current_exception());
139 }
140
141 void rethrow_exception()
142 {
143 if (pending_exception_)
144 {
145 std::exception_ptr ex = std::exchange(pending_exception_, nullptr);
146 std::rethrow_exception(ex);
147 }
148 }
149
150 template <typename T>
151 auto await_transform(awaitable<T, Executor> a) const
152 {
153 return a;
154 }
155
156 // This await transformation obtains the associated executor of the thread of
157 // execution.
158 auto await_transform(this_coro::executor_t) noexcept
159 {
160 struct result
161 {
162 awaitable_frame_base* this_;
163
164 bool await_ready() const noexcept
165 {
166 return true;
167 }
168
169 void await_suspend(coroutine_handle<void>) noexcept
170 {
171 }
172
173 auto await_resume() const noexcept
174 {
175 return this_->attached_thread_->get_executor();
176 }
177 };
178
179 return result{this};
180 }
181
182 // This await transformation is used to run an async operation's initiation
183 // function object after the coroutine has been suspended. This ensures that
184 // immediate resumption of the coroutine in another thread does not cause a
185 // race condition.
186 template <typename Function>
187 auto await_transform(Function f,
188 typename enable_if<
189 is_convertible<
190 typename result_of<Function(awaitable_frame_base*)>::type,
191 awaitable_thread<Executor>*
192 >::value
193 >::type* = 0)
194 {
195 struct result
196 {
197 Function function_;
198 awaitable_frame_base* this_;
199
200 bool await_ready() const noexcept
201 {
202 return false;
203 }
204
205 void await_suspend(coroutine_handle<void>) noexcept
206 {
207 function_(this_);
208 }
209
210 void await_resume() const noexcept
211 {
212 }
213 };
214
215 return result{std::move(f), this};
216 }
217
218 void attach_thread(awaitable_thread<Executor>* handler) noexcept
219 {
220 attached_thread_ = handler;
221 }
222
223 awaitable_thread<Executor>* detach_thread() noexcept
224 {
225 return std::exchange(attached_thread_, nullptr);
226 }
227
228 void push_frame(awaitable_frame_base<Executor>* caller) noexcept
229 {
230 caller_ = caller;
231 attached_thread_ = caller_->attached_thread_;
232 attached_thread_->top_of_stack_ = this;
233 caller_->attached_thread_ = nullptr;
234 }
235
236 void pop_frame() noexcept
237 {
238 if (caller_)
239 caller_->attached_thread_ = attached_thread_;
240 attached_thread_->top_of_stack_ = caller_;
241 attached_thread_ = nullptr;
242 caller_ = nullptr;
243 }
244
245 void resume()
246 {
247 coro_.resume();
248 }
249
250 void destroy()
251 {
252 coro_.destroy();
253 }
254
255protected:
256 coroutine_handle<void> coro_ = nullptr;
257 awaitable_thread<Executor>* attached_thread_ = nullptr;
258 awaitable_frame_base<Executor>* caller_ = nullptr;
259 std::exception_ptr pending_exception_ = nullptr;
260};
261
262template <typename T, typename Executor>
263class awaitable_frame
264 : public awaitable_frame_base<Executor>
265{
266public:
267 awaitable_frame() noexcept
268 {
269 }
270
271 awaitable_frame(awaitable_frame&& other) noexcept
272 : awaitable_frame_base<Executor>(std::move(other))
273 {
274 }
275
276 ~awaitable_frame()
277 {
278 if (has_result_)
279 static_cast<T*>(static_cast<void*>(result_))->~T();
280 }
281
282 awaitable<T, Executor> get_return_object() noexcept
283 {
284 this->coro_ = coroutine_handle<awaitable_frame>::from_promise(*this);
285 return awaitable<T, Executor>(this);
286 };
287
288 template <typename U>
289 void return_value(U&& u)
290 {
291 new (&result_) T(std::forward<U>(u));
292 has_result_ = true;
293 }
294
295 template <typename... Us>
296 void return_values(Us&&... us)
297 {
298 this->return_value(std::forward_as_tuple(std::forward<Us>(us)...));
299 }
300
301 T get()
302 {
303 this->caller_ = nullptr;
304 this->rethrow_exception();
305 return std::move(*static_cast<T*>(static_cast<void*>(result_)));
306 }
307
308private:
309 alignas(T) unsigned char result_[sizeof(T)];
310 bool has_result_ = false;
311};
312
313template <typename Executor>
314class awaitable_frame<void, Executor>
315 : public awaitable_frame_base<Executor>
316{
317public:
318 awaitable<void, Executor> get_return_object()
319 {
320 this->coro_ = coroutine_handle<awaitable_frame>::from_promise(*this);
321 return awaitable<void, Executor>(this);
322 };
323
324 void return_void()
325 {
326 }
327
328 void get()
329 {
330 this->caller_ = nullptr;
331 this->rethrow_exception();
332 }
333};
334
335template <typename Executor>
336class awaitable_thread
337{
338public:
339 typedef Executor executor_type;
340
341 // Construct from the entry point of a new thread of execution.
342 awaitable_thread(awaitable<void, Executor> p, const Executor& ex)
343 : bottom_of_stack_(std::move(p)),
344 top_of_stack_(bottom_of_stack_.frame_),
345 executor_(ex)
346 {
347 }
348
349 // Transfer ownership from another awaitable_thread.
350 awaitable_thread(awaitable_thread&& other) noexcept
351 : bottom_of_stack_(std::move(other.bottom_of_stack_)),
352 top_of_stack_(std::exchange(other.top_of_stack_, nullptr)),
353 executor_(std::move(other.executor_))
354 {
355 }
356
357 // Clean up with a last ditch effort to ensure the thread is unwound within
358 // the context of the executor.
359 ~awaitable_thread()
360 {
361 if (bottom_of_stack_.valid())
362 {
363 // Coroutine "stack unwinding" must be performed through the executor.
364 (post)(executor_,
365 [a = std::move(bottom_of_stack_)]() mutable
366 {
367 awaitable<void, Executor>(std::move(a));
368 });
369 }
370 }
371
372 executor_type get_executor() const noexcept
373 {
374 return executor_;
375 }
376
377 // Launch a new thread of execution.
378 void launch()
379 {
380 top_of_stack_->attach_thread(this);
381 pump();
382 }
383
384protected:
385 template <typename> friend class awaitable_frame_base;
386
387 // Repeatedly resume the top stack frame until the stack is empty or until it
388 // has been transferred to another resumable_thread object.
389 void pump()
390 {
391 do top_of_stack_->resume(); while (top_of_stack_);
392 if (bottom_of_stack_.valid())
393 {
394 awaitable<void, Executor> a(std::move(bottom_of_stack_));
395 a.frame_->rethrow_exception();
396 }
397 }
398
399 awaitable<void, Executor> bottom_of_stack_;
400 awaitable_frame_base<Executor>* top_of_stack_;
401 executor_type executor_;
402};
403
404} // namespace detail
405} // namespace asio
406} // namespace boost
407
408#if !defined(GENERATING_DOCUMENTATION)
20effc67
TL
409# if defined(BOOST_ASIO_HAS_STD_COROUTINE)
410
411namespace std {
412
413template <typename T, typename Executor, typename... Args>
414struct coroutine_traits<boost::asio::awaitable<T, Executor>, Args...>
415{
416 typedef boost::asio::detail::awaitable_frame<T, Executor> promise_type;
417};
418
419} // namespace std
420
421# else // defined(BOOST_ASIO_HAS_STD_COROUTINE)
92f5a8d4
TL
422
423namespace std { namespace experimental {
424
425template <typename T, typename Executor, typename... Args>
426struct coroutine_traits<boost::asio::awaitable<T, Executor>, Args...>
427{
428 typedef boost::asio::detail::awaitable_frame<T, Executor> promise_type;
429};
430
431}} // namespace std::experimental
432
20effc67 433# endif // defined(BOOST_ASIO_HAS_STD_COROUTINE)
92f5a8d4
TL
434#endif // !defined(GENERATING_DOCUMENTATION)
435
436#include <boost/asio/detail/pop_options.hpp>
437
438#endif // BOOST_ASIO_IMPL_AWAITABLE_HPP