]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/asio/impl/awaitable.hpp
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / boost / asio / impl / awaitable.hpp
CommitLineData
92f5a8d4
TL
1//
2// impl/awaitable.hpp
3// ~~~~~~~~~~~~~~~~~~
4//
1e59de90 5// Copyright (c) 2003-2022 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>
1e59de90
TL
22#include <boost/asio/cancellation_signal.hpp>
23#include <boost/asio/cancellation_state.hpp>
92f5a8d4
TL
24#include <boost/asio/detail/thread_context.hpp>
25#include <boost/asio/detail/thread_info_base.hpp>
1e59de90 26#include <boost/asio/detail/throw_error.hpp>
92f5a8d4 27#include <boost/asio/detail/type_traits.hpp>
1e59de90 28#include <boost/asio/error.hpp>
92f5a8d4
TL
29#include <boost/asio/post.hpp>
30#include <boost/system/system_error.hpp>
31#include <boost/asio/this_coro.hpp>
32
33#include <boost/asio/detail/push_options.hpp>
34
35namespace boost {
36namespace asio {
37namespace detail {
38
1e59de90
TL
39struct awaitable_thread_has_context_switched {};
40
92f5a8d4
TL
41// An awaitable_thread represents a thread-of-execution that is composed of one
42// or more "stack frames", with each frame represented by an awaitable_frame.
43// All execution occurs in the context of the awaitable_thread's executor. An
44// awaitable_thread continues to "pump" the stack frames by repeatedly resuming
45// the top stack frame until the stack is empty, or until ownership of the
46// stack is transferred to another awaitable_thread object.
47//
48// +------------------------------------+
49// | top_of_stack_ |
50// | V
51// +--------------+---+ +-----------------+
52// | | | |
53// | awaitable_thread |<---------------------------+ awaitable_frame |
54// | | attached_thread_ | |
55// +--------------+---+ (Set only when +---+-------------+
56// | frames are being |
57// | actively pumped | caller_
58// | by a thread, and |
59// | then only for V
60// | the top frame.) +-----------------+
61// | | |
62// | | awaitable_frame |
63// | | |
64// | +---+-------------+
65// | |
66// | | caller_
67// | :
68// | :
69// | |
70// | V
71// | +-----------------+
72// | bottom_of_stack_ | |
73// +------------------------------->| awaitable_frame |
74// | |
75// +-----------------+
76
77template <typename Executor>
78class awaitable_frame_base
79{
80public:
81#if !defined(BOOST_ASIO_DISABLE_AWAITABLE_FRAME_RECYCLING)
82 void* operator new(std::size_t size)
83 {
84 return boost::asio::detail::thread_info_base::allocate(
85 boost::asio::detail::thread_info_base::awaitable_frame_tag(),
1e59de90 86 boost::asio::detail::thread_context::top_of_thread_call_stack(),
92f5a8d4
TL
87 size);
88 }
89
90 void operator delete(void* pointer, std::size_t size)
91 {
92 boost::asio::detail::thread_info_base::deallocate(
93 boost::asio::detail::thread_info_base::awaitable_frame_tag(),
1e59de90 94 boost::asio::detail::thread_context::top_of_thread_call_stack(),
92f5a8d4
TL
95 pointer, size);
96 }
97#endif // !defined(BOOST_ASIO_DISABLE_AWAITABLE_FRAME_RECYCLING)
98
99 // The frame starts in a suspended state until the awaitable_thread object
100 // pumps the stack.
101 auto initial_suspend() noexcept
102 {
103 return suspend_always();
104 }
105
106 // On final suspension the frame is popped from the top of the stack.
107 auto final_suspend() noexcept
108 {
109 struct result
110 {
111 awaitable_frame_base* this_;
112
113 bool await_ready() const noexcept
114 {
115 return false;
116 }
117
118 void await_suspend(coroutine_handle<void>) noexcept
119 {
20effc67 120 this->this_->pop_frame();
92f5a8d4
TL
121 }
122
123 void await_resume() const noexcept
124 {
125 }
126 };
127
128 return result{this};
129 }
130
131 void set_except(std::exception_ptr e) noexcept
132 {
133 pending_exception_ = e;
134 }
135
136 void set_error(const boost::system::error_code& ec)
137 {
138 this->set_except(std::make_exception_ptr(boost::system::system_error(ec)));
139 }
140
141 void unhandled_exception()
142 {
143 set_except(std::current_exception());
144 }
145
146 void rethrow_exception()
147 {
148 if (pending_exception_)
149 {
150 std::exception_ptr ex = std::exchange(pending_exception_, nullptr);
151 std::rethrow_exception(ex);
152 }
153 }
154
1e59de90
TL
155 void clear_cancellation_slot()
156 {
157 this->attached_thread_->entry_point()->cancellation_state_.slot().clear();
158 }
159
92f5a8d4
TL
160 template <typename T>
161 auto await_transform(awaitable<T, Executor> a) const
162 {
1e59de90
TL
163 if (attached_thread_->entry_point()->throw_if_cancelled_)
164 if (!!attached_thread_->get_cancellation_state().cancelled())
165 do_throw_error(boost::asio::error::operation_aborted, "co_await");
92f5a8d4
TL
166 return a;
167 }
168
169 // This await transformation obtains the associated executor of the thread of
170 // execution.
171 auto await_transform(this_coro::executor_t) noexcept
172 {
173 struct result
174 {
175 awaitable_frame_base* this_;
176
177 bool await_ready() const noexcept
178 {
179 return true;
180 }
181
182 void await_suspend(coroutine_handle<void>) noexcept
183 {
184 }
185
186 auto await_resume() const noexcept
187 {
188 return this_->attached_thread_->get_executor();
189 }
190 };
191
192 return result{this};
193 }
194
1e59de90
TL
195 // This await transformation obtains the associated cancellation state of the
196 // thread of execution.
197 auto await_transform(this_coro::cancellation_state_t) noexcept
198 {
199 struct result
200 {
201 awaitable_frame_base* this_;
202
203 bool await_ready() const noexcept
204 {
205 return true;
206 }
207
208 void await_suspend(coroutine_handle<void>) noexcept
209 {
210 }
211
212 auto await_resume() const noexcept
213 {
214 return this_->attached_thread_->get_cancellation_state();
215 }
216 };
217
218 return result{this};
219 }
220
221 // This await transformation resets the associated cancellation state.
222 auto await_transform(this_coro::reset_cancellation_state_0_t) noexcept
223 {
224 struct result
225 {
226 awaitable_frame_base* this_;
227
228 bool await_ready() const noexcept
229 {
230 return true;
231 }
232
233 void await_suspend(coroutine_handle<void>) noexcept
234 {
235 }
236
237 auto await_resume() const
238 {
239 return this_->attached_thread_->reset_cancellation_state();
240 }
241 };
242
243 return result{this};
244 }
245
246 // This await transformation resets the associated cancellation state.
247 template <typename Filter>
248 auto await_transform(
249 this_coro::reset_cancellation_state_1_t<Filter> reset) noexcept
250 {
251 struct result
252 {
253 awaitable_frame_base* this_;
254 Filter filter_;
255
256 bool await_ready() const noexcept
257 {
258 return true;
259 }
260
261 void await_suspend(coroutine_handle<void>) noexcept
262 {
263 }
264
265 auto await_resume()
266 {
267 return this_->attached_thread_->reset_cancellation_state(
268 BOOST_ASIO_MOVE_CAST(Filter)(filter_));
269 }
270 };
271
272 return result{this, BOOST_ASIO_MOVE_CAST(Filter)(reset.filter)};
273 }
274
275 // This await transformation resets the associated cancellation state.
276 template <typename InFilter, typename OutFilter>
277 auto await_transform(
278 this_coro::reset_cancellation_state_2_t<InFilter, OutFilter> reset)
279 noexcept
280 {
281 struct result
282 {
283 awaitable_frame_base* this_;
284 InFilter in_filter_;
285 OutFilter out_filter_;
286
287 bool await_ready() const noexcept
288 {
289 return true;
290 }
291
292 void await_suspend(coroutine_handle<void>) noexcept
293 {
294 }
295
296 auto await_resume()
297 {
298 return this_->attached_thread_->reset_cancellation_state(
299 BOOST_ASIO_MOVE_CAST(InFilter)(in_filter_),
300 BOOST_ASIO_MOVE_CAST(OutFilter)(out_filter_));
301 }
302 };
303
304 return result{this,
305 BOOST_ASIO_MOVE_CAST(InFilter)(reset.in_filter),
306 BOOST_ASIO_MOVE_CAST(OutFilter)(reset.out_filter)};
307 }
308
309 // This await transformation determines whether cancellation is propagated as
310 // an exception.
311 auto await_transform(this_coro::throw_if_cancelled_0_t)
312 noexcept
313 {
314 struct result
315 {
316 awaitable_frame_base* this_;
317
318 bool await_ready() const noexcept
319 {
320 return true;
321 }
322
323 void await_suspend(coroutine_handle<void>) noexcept
324 {
325 }
326
327 auto await_resume()
328 {
329 return this_->attached_thread_->throw_if_cancelled();
330 }
331 };
332
333 return result{this};
334 }
335
336 // This await transformation sets whether cancellation is propagated as an
337 // exception.
338 auto await_transform(this_coro::throw_if_cancelled_1_t throw_if_cancelled)
339 noexcept
340 {
341 struct result
342 {
343 awaitable_frame_base* this_;
344 bool value_;
345
346 bool await_ready() const noexcept
347 {
348 return true;
349 }
350
351 void await_suspend(coroutine_handle<void>) noexcept
352 {
353 }
354
355 auto await_resume()
356 {
357 this_->attached_thread_->throw_if_cancelled(value_);
358 }
359 };
360
361 return result{this, throw_if_cancelled.value};
362 }
363
92f5a8d4
TL
364 // This await transformation is used to run an async operation's initiation
365 // function object after the coroutine has been suspended. This ensures that
366 // immediate resumption of the coroutine in another thread does not cause a
367 // race condition.
368 template <typename Function>
369 auto await_transform(Function f,
370 typename enable_if<
371 is_convertible<
372 typename result_of<Function(awaitable_frame_base*)>::type,
373 awaitable_thread<Executor>*
374 >::value
1e59de90 375 >::type* = nullptr)
92f5a8d4
TL
376 {
377 struct result
378 {
379 Function function_;
380 awaitable_frame_base* this_;
381
382 bool await_ready() const noexcept
383 {
384 return false;
385 }
386
387 void await_suspend(coroutine_handle<void>) noexcept
388 {
389 function_(this_);
390 }
391
392 void await_resume() const noexcept
393 {
394 }
395 };
396
397 return result{std::move(f), this};
398 }
399
1e59de90
TL
400 // Access the awaitable thread's has_context_switched_ flag.
401 auto await_transform(detail::awaitable_thread_has_context_switched) noexcept
402 {
403 struct result
404 {
405 awaitable_frame_base* this_;
406
407 bool await_ready() const noexcept
408 {
409 return true;
410 }
411
412 void await_suspend(coroutine_handle<void>) noexcept
413 {
414 }
415
416 bool& await_resume() const noexcept
417 {
418 return this_->attached_thread_->entry_point()->has_context_switched_;
419 }
420 };
421
422 return result{this};
423 }
424
92f5a8d4
TL
425 void attach_thread(awaitable_thread<Executor>* handler) noexcept
426 {
427 attached_thread_ = handler;
428 }
429
430 awaitable_thread<Executor>* detach_thread() noexcept
431 {
1e59de90 432 attached_thread_->entry_point()->has_context_switched_ = true;
92f5a8d4
TL
433 return std::exchange(attached_thread_, nullptr);
434 }
435
436 void push_frame(awaitable_frame_base<Executor>* caller) noexcept
437 {
438 caller_ = caller;
439 attached_thread_ = caller_->attached_thread_;
1e59de90 440 attached_thread_->entry_point()->top_of_stack_ = this;
92f5a8d4
TL
441 caller_->attached_thread_ = nullptr;
442 }
443
444 void pop_frame() noexcept
445 {
446 if (caller_)
447 caller_->attached_thread_ = attached_thread_;
1e59de90 448 attached_thread_->entry_point()->top_of_stack_ = caller_;
92f5a8d4
TL
449 attached_thread_ = nullptr;
450 caller_ = nullptr;
451 }
452
453 void resume()
454 {
455 coro_.resume();
456 }
457
458 void destroy()
459 {
460 coro_.destroy();
461 }
462
463protected:
464 coroutine_handle<void> coro_ = nullptr;
465 awaitable_thread<Executor>* attached_thread_ = nullptr;
466 awaitable_frame_base<Executor>* caller_ = nullptr;
467 std::exception_ptr pending_exception_ = nullptr;
468};
469
470template <typename T, typename Executor>
471class awaitable_frame
472 : public awaitable_frame_base<Executor>
473{
474public:
475 awaitable_frame() noexcept
476 {
477 }
478
479 awaitable_frame(awaitable_frame&& other) noexcept
480 : awaitable_frame_base<Executor>(std::move(other))
481 {
482 }
483
484 ~awaitable_frame()
485 {
486 if (has_result_)
487 static_cast<T*>(static_cast<void*>(result_))->~T();
488 }
489
490 awaitable<T, Executor> get_return_object() noexcept
491 {
492 this->coro_ = coroutine_handle<awaitable_frame>::from_promise(*this);
493 return awaitable<T, Executor>(this);
494 };
495
496 template <typename U>
497 void return_value(U&& u)
498 {
499 new (&result_) T(std::forward<U>(u));
500 has_result_ = true;
501 }
502
503 template <typename... Us>
504 void return_values(Us&&... us)
505 {
506 this->return_value(std::forward_as_tuple(std::forward<Us>(us)...));
507 }
508
509 T get()
510 {
511 this->caller_ = nullptr;
512 this->rethrow_exception();
513 return std::move(*static_cast<T*>(static_cast<void*>(result_)));
514 }
515
516private:
517 alignas(T) unsigned char result_[sizeof(T)];
518 bool has_result_ = false;
519};
520
521template <typename Executor>
522class awaitable_frame<void, Executor>
523 : public awaitable_frame_base<Executor>
524{
525public:
526 awaitable<void, Executor> get_return_object()
527 {
528 this->coro_ = coroutine_handle<awaitable_frame>::from_promise(*this);
529 return awaitable<void, Executor>(this);
530 };
531
532 void return_void()
533 {
534 }
535
536 void get()
537 {
538 this->caller_ = nullptr;
539 this->rethrow_exception();
540 }
541};
542
1e59de90
TL
543struct awaitable_thread_entry_point {};
544
545template <typename Executor>
546class awaitable_frame<awaitable_thread_entry_point, Executor>
547 : public awaitable_frame_base<Executor>
548{
549public:
550 awaitable_frame()
551 : top_of_stack_(0),
552 has_executor_(false),
553 has_context_switched_(false),
554 throw_if_cancelled_(true)
555 {
556 }
557
558 ~awaitable_frame()
559 {
560 if (has_executor_)
561 u_.executor_.~Executor();
562 }
563
564 awaitable<awaitable_thread_entry_point, Executor> get_return_object()
565 {
566 this->coro_ = coroutine_handle<awaitable_frame>::from_promise(*this);
567 return awaitable<awaitable_thread_entry_point, Executor>(this);
568 };
569
570 void return_void()
571 {
572 }
573
574 void get()
575 {
576 this->caller_ = nullptr;
577 this->rethrow_exception();
578 }
579
580private:
581 template <typename> friend class awaitable_frame_base;
582 template <typename, typename> friend class awaitable_handler_base;
583 template <typename> friend class awaitable_thread;
584
585 union u
586 {
587 u() {}
588 ~u() {}
589 char c_;
590 Executor executor_;
591 } u_;
592
593 awaitable_frame_base<Executor>* top_of_stack_;
594 boost::asio::cancellation_slot parent_cancellation_slot_;
595 boost::asio::cancellation_state cancellation_state_;
596 bool has_executor_;
597 bool has_context_switched_;
598 bool throw_if_cancelled_;
599};
600
92f5a8d4
TL
601template <typename Executor>
602class awaitable_thread
603{
604public:
605 typedef Executor executor_type;
1e59de90 606 typedef cancellation_slot cancellation_slot_type;
92f5a8d4
TL
607
608 // Construct from the entry point of a new thread of execution.
1e59de90
TL
609 awaitable_thread(awaitable<awaitable_thread_entry_point, Executor> p,
610 const Executor& ex, cancellation_slot parent_cancel_slot,
611 cancellation_state cancel_state)
612 : bottom_of_stack_(std::move(p))
613 {
614 bottom_of_stack_.frame_->top_of_stack_ = bottom_of_stack_.frame_;
615 new (&bottom_of_stack_.frame_->u_.executor_) Executor(ex);
616 bottom_of_stack_.frame_->has_executor_ = true;
617 bottom_of_stack_.frame_->parent_cancellation_slot_ = parent_cancel_slot;
618 bottom_of_stack_.frame_->cancellation_state_ = cancel_state;
92f5a8d4
TL
619 }
620
621 // Transfer ownership from another awaitable_thread.
622 awaitable_thread(awaitable_thread&& other) noexcept
1e59de90 623 : bottom_of_stack_(std::move(other.bottom_of_stack_))
92f5a8d4
TL
624 {
625 }
626
627 // Clean up with a last ditch effort to ensure the thread is unwound within
628 // the context of the executor.
629 ~awaitable_thread()
630 {
631 if (bottom_of_stack_.valid())
632 {
633 // Coroutine "stack unwinding" must be performed through the executor.
1e59de90
TL
634 auto* bottom_frame = bottom_of_stack_.frame_;
635 (post)(bottom_frame->u_.executor_,
92f5a8d4
TL
636 [a = std::move(bottom_of_stack_)]() mutable
637 {
1e59de90
TL
638 (void)awaitable<awaitable_thread_entry_point, Executor>(
639 std::move(a));
92f5a8d4
TL
640 });
641 }
642 }
643
1e59de90
TL
644 awaitable_frame<awaitable_thread_entry_point, Executor>* entry_point()
645 {
646 return bottom_of_stack_.frame_;
647 }
648
92f5a8d4
TL
649 executor_type get_executor() const noexcept
650 {
1e59de90
TL
651 return bottom_of_stack_.frame_->u_.executor_;
652 }
653
654 cancellation_state get_cancellation_state() const noexcept
655 {
656 return bottom_of_stack_.frame_->cancellation_state_;
657 }
658
659 void reset_cancellation_state()
660 {
661 bottom_of_stack_.frame_->cancellation_state_ =
662 cancellation_state(bottom_of_stack_.frame_->parent_cancellation_slot_);
663 }
664
665 template <typename Filter>
666 void reset_cancellation_state(BOOST_ASIO_MOVE_ARG(Filter) filter)
667 {
668 bottom_of_stack_.frame_->cancellation_state_ =
669 cancellation_state(bottom_of_stack_.frame_->parent_cancellation_slot_,
670 BOOST_ASIO_MOVE_CAST(Filter)(filter));
671 }
672
673 template <typename InFilter, typename OutFilter>
674 void reset_cancellation_state(BOOST_ASIO_MOVE_ARG(InFilter) in_filter,
675 BOOST_ASIO_MOVE_ARG(OutFilter) out_filter)
676 {
677 bottom_of_stack_.frame_->cancellation_state_ =
678 cancellation_state(bottom_of_stack_.frame_->parent_cancellation_slot_,
679 BOOST_ASIO_MOVE_CAST(InFilter)(in_filter),
680 BOOST_ASIO_MOVE_CAST(OutFilter)(out_filter));
681 }
682
683 bool throw_if_cancelled() const
684 {
685 return bottom_of_stack_.frame_->throw_if_cancelled_;
686 }
687
688 void throw_if_cancelled(bool value)
689 {
690 bottom_of_stack_.frame_->throw_if_cancelled_ = value;
691 }
692
693 cancellation_slot_type get_cancellation_slot() const noexcept
694 {
695 return bottom_of_stack_.frame_->cancellation_state_.slot();
92f5a8d4
TL
696 }
697
698 // Launch a new thread of execution.
699 void launch()
700 {
1e59de90 701 bottom_of_stack_.frame_->top_of_stack_->attach_thread(this);
92f5a8d4
TL
702 pump();
703 }
704
705protected:
706 template <typename> friend class awaitable_frame_base;
707
708 // Repeatedly resume the top stack frame until the stack is empty or until it
709 // has been transferred to another resumable_thread object.
710 void pump()
711 {
1e59de90
TL
712 do
713 bottom_of_stack_.frame_->top_of_stack_->resume();
714 while (bottom_of_stack_.frame_ && bottom_of_stack_.frame_->top_of_stack_);
715
716 if (bottom_of_stack_.frame_)
92f5a8d4 717 {
1e59de90
TL
718 awaitable<awaitable_thread_entry_point, Executor> a(
719 std::move(bottom_of_stack_));
92f5a8d4
TL
720 a.frame_->rethrow_exception();
721 }
722 }
723
1e59de90 724 awaitable<awaitable_thread_entry_point, Executor> bottom_of_stack_;
92f5a8d4
TL
725};
726
727} // namespace detail
728} // namespace asio
729} // namespace boost
730
731#if !defined(GENERATING_DOCUMENTATION)
20effc67
TL
732# if defined(BOOST_ASIO_HAS_STD_COROUTINE)
733
734namespace std {
735
736template <typename T, typename Executor, typename... Args>
737struct coroutine_traits<boost::asio::awaitable<T, Executor>, Args...>
738{
739 typedef boost::asio::detail::awaitable_frame<T, Executor> promise_type;
740};
741
742} // namespace std
743
744# else // defined(BOOST_ASIO_HAS_STD_COROUTINE)
92f5a8d4
TL
745
746namespace std { namespace experimental {
747
748template <typename T, typename Executor, typename... Args>
749struct coroutine_traits<boost::asio::awaitable<T, Executor>, Args...>
750{
751 typedef boost::asio::detail::awaitable_frame<T, Executor> promise_type;
752};
753
754}} // namespace std::experimental
755
20effc67 756# endif // defined(BOOST_ASIO_HAS_STD_COROUTINE)
92f5a8d4
TL
757#endif // !defined(GENERATING_DOCUMENTATION)
758
759#include <boost/asio/detail/pop_options.hpp>
760
761#endif // BOOST_ASIO_IMPL_AWAITABLE_HPP