1// Copyright (c) 2022 Klemens D. Morgenstern
2//
3// Distributed under the Boost Software License, Version 1.0. (See accompanying
4// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5#ifndef BOOST_COBALT_DETAIL_WITH_HPP
6#define BOOST_COBALT_DETAIL_WITH_HPP
7
8#include <boost/cobalt/concepts.hpp>
9#include <boost/cobalt/this_coro.hpp>
10
11#include <boost/asio/cancellation_signal.hpp>
12
13namespace boost::cobalt::detail
14{
15
16template<typename T>
17struct [[nodiscard]] with_impl
18{
19 struct promise_type;
20
21 bool await_ready() { return false;}
22
23 template<typename Promise>
24 BOOST_NOINLINE auto await_suspend(std::coroutine_handle<Promise> h) -> std::coroutine_handle<promise_type>;
25 inline T await_resume();
26
27 private:
28 with_impl(promise_type & promise) : promise(promise) {}
29 promise_type & promise;
30};
31
32template<typename T>
33struct with_promise_value
34{
35 std::optional<T> result;
36
37 void return_value(std::optional<T> && value)
38 {
39 if (value) // so non-move-assign types work
40 result.emplace(std::move(*value));
41 }
42
43 T get_result()
44 {
45 return std::move(result).value();
46 }
47};
48
49template<>
50struct with_promise_value<void>
51{
52 void return_void() {}
53 void get_result() {}
54};
55
56
57template<typename T>
58struct with_impl<T>::promise_type
59 : with_promise_value<T>,
60 enable_awaitables<promise_type>,
61 enable_await_allocator<promise_type>
62{
63 using enable_awaitables<promise_type>::await_transform;
64 using enable_await_allocator<promise_type>::await_transform;
65
66
67 using executor_type = executor;
68 const executor_type & get_executor() const {return *exec;}
69 std::optional<executor_type> exec;
70
71 with_impl get_return_object()
72 {
73 return with_impl{*this};
74 }
75
76 std::exception_ptr e;
77 void unhandled_exception()
78 {
79 e = std::current_exception();
80 }
81
82 std::suspend_always initial_suspend() {return {};}
83
84 struct final_awaitable
85 {
86 promise_type *promise;
87
88 bool await_ready() const noexcept
89 {
90 return false;
91 }
92 BOOST_NOINLINE
93 auto await_suspend(std::coroutine_handle<promise_type> h) noexcept -> std::coroutine_handle<void>
94 {
95 return std::coroutine_handle<void>::from_address(a: h.promise().awaited_from.address());
96 }
97
98 void await_resume() noexcept
99 {
100 }
101 };
102
103 auto final_suspend() noexcept
104 {
105 return final_awaitable{this};
106 }
107 using cancellation_slot_type = asio::cancellation_slot;
108 cancellation_slot_type get_cancellation_slot() const {return slot_;}
109 asio::cancellation_slot slot_;
110
111 std::coroutine_handle<void> awaited_from{nullptr};
112
113};
114
115template<typename T>
116T with_impl<T>::await_resume()
117{
118 auto e = promise.e;
119 auto res = std::move(promise.get_result());
120 std::coroutine_handle<promise_type>::from_promise(promise).destroy();
121 if (e)
122 std::rethrow_exception(e);
123
124 return std::move(res);
125}
126
127template<>
128inline void with_impl<void>::await_resume()
129{
130 auto e = promise.e;
131 std::coroutine_handle<promise_type>::from_promise(p&: promise).destroy();
132 if (e)
133 std::rethrow_exception(e);
134}
135
136template<typename T>
137template<typename Promise>
138auto with_impl<T>::await_suspend(std::coroutine_handle<Promise> h) -> std::coroutine_handle<promise_type>
139{
140 if constexpr (requires (Promise p) {p.get_executor();})
141 promise.exec.emplace(h.promise().get_executor());
142 else
143 promise.exec.emplace(this_thread::get_executor());
144
145 if constexpr (requires (Promise p) {p.get_cancellation_slot();})
146 promise.slot_ = h.promise().get_cancellation_slot();
147
148 promise.awaited_from = h;
149 return std::coroutine_handle<promise_type>::from_promise(promise);
150}
151
152}
153
154#endif //BOOST_COBALT_DETAIL_WITH_HPP
155

source code of boost/libs/cobalt/include/boost/cobalt/detail/with.hpp