1//
2// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
3//
4// Distributed under the Boost Software License, Version 1.0. (See accompanying
5// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6//
7
8#ifndef BOOST_COBALT_DETAIL_PROMISE_HPP
9#define BOOST_COBALT_DETAIL_PROMISE_HPP
10
11#include <boost/cobalt/detail/exception.hpp>
12#include <boost/cobalt/detail/forward_cancellation.hpp>
13#include <boost/cobalt/detail/wrapper.hpp>
14#include <boost/cobalt/detail/this_thread.hpp>
15#include <boost/cobalt/unique_handle.hpp>
16
17#include <boost/asio/cancellation_signal.hpp>
18
19
20
21
22#include <boost/core/exchange.hpp>
23
24#include <coroutine>
25#include <optional>
26#include <utility>
27#include <boost/asio/bind_allocator.hpp>
28
29namespace boost::cobalt
30{
31
32struct as_tuple_tag;
33struct as_result_tag;
34
35template<typename Return>
36struct promise;
37
38namespace detail
39{
40
41template<typename T>
42struct promise_receiver;
43
44template<typename T>
45struct promise_value_holder
46{
47 std::optional<T> result;
48 bool result_taken = false;
49
50 system::result<T, std::exception_ptr> get_result_value()
51 {
52 result_taken = true;
53 BOOST_ASSERT(result);
54 return {system::in_place_value, std::move(*result)};
55 }
56
57 void return_value(T && ret)
58 {
59 result.emplace(std::move(ret));
60 static_cast<promise_receiver<T>*>(this)->set_done();
61 }
62
63 void return_value(const T & ret)
64 {
65 result.emplace(ret);
66 static_cast<promise_receiver<T>*>(this)->set_done();
67 }
68
69};
70
71template<>
72struct promise_value_holder<void>
73{
74 bool result_taken = false;
75 system::result<void, std::exception_ptr> get_result_value()
76 {
77 result_taken = true;
78 return {system::in_place_value};
79 }
80
81 inline void return_void();
82};
83
84
85
86template<typename T>
87struct promise_receiver : promise_value_holder<T>
88{
89 std::exception_ptr exception;
90 system::result<T, std::exception_ptr> get_result()
91 {
92 if (exception && !done) // detached error
93 return {system::in_place_error, std::exchange(obj&: exception, new_val: nullptr)};
94 else if (exception)
95 {
96 this->result_taken = true;
97 return {system::in_place_error, exception};
98 }
99 return this->get_result_value();
100 }
101 void unhandled_exception()
102 {
103 exception = std::current_exception();
104 set_done();
105 }
106
107 bool done = false;
108 unique_handle<void> awaited_from{nullptr};
109
110 void set_done()
111 {
112 done = true;
113 }
114
115 promise_receiver() = default;
116 promise_receiver(promise_receiver && lhs) noexcept
117 : promise_value_holder<T>(std::move(lhs)),
118 exception(std::move(lhs.exception)), done(lhs.done), awaited_from(std::move(lhs.awaited_from)),
119 reference(lhs.reference), cancel_signal(lhs.cancel_signal)
120 {
121 if (!done && !exception)
122 {
123 reference = this;
124 lhs.exception = moved_from_exception();
125 }
126
127 lhs.done = true;
128
129 }
130
131 ~promise_receiver()
132 {
133 if (!done && reference == this)
134 reference = nullptr;
135 }
136
137 promise_receiver(promise_receiver * &reference, asio::cancellation_signal & cancel_signal)
138 : reference(reference), cancel_signal(cancel_signal)
139 {
140 reference = this;
141 }
142
143 struct awaitable
144 {
145 promise_receiver * self;
146 std::exception_ptr ex;
147 asio::cancellation_slot cl;
148
149 awaitable(promise_receiver * self) : self(self)
150 {
151 }
152
153 awaitable(awaitable && aw) : self(aw.self)
154 {
155 }
156
157 ~awaitable ()
158 {
159 }
160 bool await_ready() const { return self->done; }
161
162 template<typename Promise>
163 bool await_suspend(std::coroutine_handle<Promise> h)
164 {
165 if (self->done) // ok, so we're actually done already, so noop
166 return false;
167
168 if (ex)
169 return false;
170
171 if (self->awaited_from != nullptr) // we're already being awaited, that's an error!
172 {
173 ex = already_awaited();
174 return false;
175 }
176
177 if constexpr (requires (Promise p) {p.get_cancellation_slot();})
178 if ((cl = h.promise().get_cancellation_slot()).is_connected())
179 cl.emplace<forward_cancellation>(args&: self->cancel_signal);
180
181 self->awaited_from.reset(handle: h.address());
182 return true;
183 }
184
185 T await_resume(const boost::source_location & loc = BOOST_CURRENT_LOCATION)
186 {
187 if (cl.is_connected())
188 cl.clear();
189 if (ex)
190 std::rethrow_exception(ex);
191 return self->get_result().value(loc);
192 }
193
194 system::result<T, std::exception_ptr> await_resume(const as_result_tag &)
195 {
196 if (cl.is_connected())
197 cl.clear();
198 if (ex)
199 return {system::in_place_error, std::move(ex)};
200 return self->get_result();
201 }
202
203 auto await_resume(const as_tuple_tag &)
204 {
205 if (cl.is_connected())
206 cl.clear();
207
208 if constexpr (std::is_void_v<T>)
209 {
210 if (ex)
211 return std::move(ex);
212 return self->get_result().error();
213 }
214 else
215 {
216 if (ex)
217 return std::make_tuple(std::move(ex), T{});
218 auto res = self->get_result();
219 if (res.has_error())
220 return std::make_tuple(res.error(), T{});
221 else
222 return std::make_tuple(std::exception_ptr(), std::move(*res));
223 }
224 }
225 void interrupt_await() &
226 {
227 if (!self)
228 return ;
229 ex = detached_exception();
230 if (self->awaited_from)
231 self->awaited_from.release().resume();
232 }
233 };
234
235 promise_receiver * &reference;
236 asio::cancellation_signal & cancel_signal;
237
238 awaitable get_awaitable() {return awaitable{this};}
239
240
241 void interrupt_await() &
242 {
243 exception = detached_exception();
244 awaited_from.release().resume();
245 }
246};
247
248inline void promise_value_holder<void>::return_void()
249{
250 static_cast<promise_receiver<void>*>(this)->set_done();
251}
252
253template<typename Return>
254struct cobalt_promise_result
255{
256 promise_receiver<Return>* receiver{nullptr};
257 void return_value(Return && ret)
258 {
259 if(receiver)
260 receiver->return_value(std::move(ret));
261 }
262
263 void return_value(const Return & ret)
264 {
265 if(receiver)
266 receiver->return_value(ret);
267 }
268
269};
270
271template<>
272struct cobalt_promise_result<void>
273{
274 promise_receiver<void>* receiver{nullptr};
275 void return_void()
276 {
277 if(receiver)
278 receiver->return_void();
279 }
280};
281
282template<typename Return>
283struct cobalt_promise
284 : promise_memory_resource_base,
285 promise_cancellation_base<asio::cancellation_slot, asio::enable_total_cancellation>,
286 promise_throw_if_cancelled_base,
287 enable_awaitables<cobalt_promise<Return>>,
288 enable_await_allocator<cobalt_promise<Return>>,
289 enable_await_executor<cobalt_promise<Return>>,
290 cobalt_promise_result<Return>
291{
292 using promise_cancellation_base<asio::cancellation_slot, asio::enable_total_cancellation>::await_transform;
293 using promise_throw_if_cancelled_base::await_transform;
294 using enable_awaitables<cobalt_promise<Return>>::await_transform;
295 using enable_await_allocator<cobalt_promise<Return>>::await_transform;
296 using enable_await_executor<cobalt_promise<Return>>::await_transform;
297
298 [[nodiscard]] promise<Return> get_return_object()
299 {
300 return promise<Return>{this};
301 }
302
303 mutable asio::cancellation_signal signal;
304
305 using executor_type = executor;
306 executor_type exec;
307 const executor_type & get_executor() const {return exec;}
308
309 template<typename ... Args>
310 cobalt_promise(Args & ...args)
311 :
312#if !defined(BOOST_COBALT_NO_PMR)
313 promise_memory_resource_base(detail::get_memory_resource_from_args(args...)),
314#endif
315 exec{detail::get_executor_from_args(args...)}
316 {
317 this->reset_cancellation_source(signal.slot());
318 }
319
320 std::suspend_never initial_suspend() {return {};}
321 auto final_suspend() noexcept
322 {
323 return final_awaitable{this};
324 }
325
326 void unhandled_exception()
327 {
328 if (this->receiver)
329 this->receiver->unhandled_exception();
330 else
331 throw ;
332 }
333
334 ~cobalt_promise()
335 {
336 if (this->receiver)
337 {
338 if (!this->receiver->done && !this->receiver->exception)
339 this->receiver->exception = completed_unexpected();
340 this->receiver->set_done();
341 this->receiver->awaited_from.reset(nullptr);
342 }
343
344 }
345 private:
346 struct final_awaitable
347 {
348 cobalt_promise * promise;
349 bool await_ready() const noexcept
350 {
351 return promise->receiver && promise->receiver->awaited_from.get() == nullptr;
352 }
353
354 std::coroutine_handle<void> await_suspend(std::coroutine_handle<cobalt_promise> h) noexcept
355 {
356 std::coroutine_handle<void> res = std::noop_coroutine();
357 if (promise->receiver && promise->receiver->awaited_from.get() != nullptr)
358 res = promise->receiver->awaited_from.release();
359
360
361 if (auto &rec = h.promise().receiver; rec != nullptr)
362 {
363 if (!rec->done && !rec->exception)
364 rec->exception = completed_unexpected();
365 rec->set_done();
366 rec->awaited_from.reset(nullptr);
367 rec = nullptr;
368 }
369 detail::self_destroy(h);
370 return res;
371 }
372
373 void await_resume() noexcept
374 {
375 }
376 };
377
378
379};
380
381}
382
383}
384
385#endif //BOOST_COBALT_DETAIL_PROMISE_HPP
386

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