]>
Commit | Line | Data |
---|---|---|
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 | ||
35 | namespace boost { | |
36 | namespace asio { | |
37 | namespace detail { | |
38 | ||
1e59de90 TL |
39 | struct 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 | ||
77 | template <typename Executor> | |
78 | class awaitable_frame_base | |
79 | { | |
80 | public: | |
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 | ||
463 | protected: | |
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 | ||
470 | template <typename T, typename Executor> | |
471 | class awaitable_frame | |
472 | : public awaitable_frame_base<Executor> | |
473 | { | |
474 | public: | |
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 | ||
516 | private: | |
517 | alignas(T) unsigned char result_[sizeof(T)]; | |
518 | bool has_result_ = false; | |
519 | }; | |
520 | ||
521 | template <typename Executor> | |
522 | class awaitable_frame<void, Executor> | |
523 | : public awaitable_frame_base<Executor> | |
524 | { | |
525 | public: | |
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 |
543 | struct awaitable_thread_entry_point {}; |
544 | ||
545 | template <typename Executor> | |
546 | class awaitable_frame<awaitable_thread_entry_point, Executor> | |
547 | : public awaitable_frame_base<Executor> | |
548 | { | |
549 | public: | |
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 | ||
580 | private: | |
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 |
601 | template <typename Executor> |
602 | class awaitable_thread | |
603 | { | |
604 | public: | |
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 | ||
705 | protected: | |
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 | ||
734 | namespace std { | |
735 | ||
736 | template <typename T, typename Executor, typename... Args> | |
737 | struct 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 | |
746 | namespace std { namespace experimental { | |
747 | ||
748 | template <typename T, typename Executor, typename... Args> | |
749 | struct 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 |