xref: /openbmc/sdbusplus/include/sdbusplus/async/stdexec/__detail/__with_awaitable_senders.hpp (revision 10d0b4b7d1498cfd5c3d37edea271a54d1984e41)
1 /*
2  * Copyright (c) 2021-2024 NVIDIA Corporation
3  *
4  * Licensed under the Apache License Version 2.0 with LLVM Exceptions
5  * (the "License"); you may not use this file except in compliance with
6  * the License. You may obtain a copy of the License at
7  *
8  *   https://llvm.org/LICENSE.txt
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #pragma once
17 
18 #include "__execution_fwd.hpp"
19 
20 #include "__as_awaitable.hpp"
21 #include "__concepts.hpp"
22 #include "../coroutine.hpp" // IWYU pragma: keep for __coro::coroutine_handle
23 
24 #include <exception>
25 
26 namespace stdexec {
27 #if !STDEXEC_STD_NO_COROUTINES()
28   namespace __was {
29     template <class _Promise = void>
30     class __coroutine_handle;
31 
32     template <>
33     class __coroutine_handle<void> : __coro::coroutine_handle<> {
34      public:
35       __coroutine_handle() = default;
36 
37       template <class _Promise>
__coroutine_handle(__coro::coroutine_handle<_Promise> __coro)38       __coroutine_handle(__coro::coroutine_handle<_Promise> __coro) noexcept
39         : __coro::coroutine_handle<>(__coro) {
40         if constexpr (requires(_Promise& __promise) { __promise.unhandled_stopped(); }) {
41           __stopped_callback_ = [](void* __address) noexcept -> __coro::coroutine_handle<> {
42             // This causes the rest of the coroutine (the part after the co_await
43             // of the sender) to be skipped and invokes the calling coroutine's
44             // stopped handler.
45             return __coro::coroutine_handle<_Promise>::from_address(__address)
46               .promise()
47               .unhandled_stopped();
48           };
49         }
50         // If _Promise doesn't implement unhandled_stopped(), then if a "stopped" unwind
51         // reaches this point, it's considered an unhandled exception and terminate()
52         // is called.
53       }
54 
55       [[nodiscard]]
handle() const56       auto handle() const noexcept -> __coro::coroutine_handle<> {
57         return *this;
58       }
59 
60       [[nodiscard]]
unhandled_stopped() const61       auto unhandled_stopped() const noexcept -> __coro::coroutine_handle<> {
62         return __stopped_callback_(address());
63       }
64 
65      private:
66       using __stopped_callback_t = __coro::coroutine_handle<> (*)(void*) noexcept;
67 
__anon7e953c250202(void*) 68       __stopped_callback_t __stopped_callback_ = [](void*) noexcept -> __coro::coroutine_handle<> {
69         std::terminate();
70       };
71     };
72 
73     template <class _Promise>
74     class __coroutine_handle : public __coroutine_handle<> {
75      public:
76       __coroutine_handle() = default;
77 
__coroutine_handle(__coro::coroutine_handle<_Promise> __coro)78       __coroutine_handle(__coro::coroutine_handle<_Promise> __coro) noexcept
79         : __coroutine_handle<>{__coro} {
80       }
81 
from_promise(_Promise & __promise)82       static auto from_promise(_Promise& __promise) noexcept -> __coroutine_handle {
83         return __coroutine_handle(__coro::coroutine_handle<_Promise>::from_promise(__promise));
84       }
85 
promise() const86       auto promise() const noexcept -> _Promise& {
87         return __coro::coroutine_handle<_Promise>::from_address(address()).promise();
88       }
89 
handle() const90       auto handle() const noexcept -> __coro::coroutine_handle<_Promise> {
91         return __coro::coroutine_handle<_Promise>::from_address(address());
92       }
93 
operator __coro::coroutine_handle<_Promise>() const94       operator __coro::coroutine_handle<_Promise>() const noexcept {
95         return handle();
96       }
97     };
98 
99     struct __with_awaitable_senders_base {
100       template <class _OtherPromise>
set_continuationstdexec::__was::__with_awaitable_senders_base101       void set_continuation(__coro::coroutine_handle<_OtherPromise> __hcoro) noexcept {
102         static_assert(!__same_as<_OtherPromise, void>);
103         __continuation_ = __hcoro;
104       }
105 
set_continuationstdexec::__was::__with_awaitable_senders_base106       void set_continuation(__coroutine_handle<> __continuation) noexcept {
107         __continuation_ = __continuation;
108       }
109 
110       [[nodiscard]]
continuationstdexec::__was::__with_awaitable_senders_base111       auto continuation() const noexcept -> __coroutine_handle<> {
112         return __continuation_;
113       }
114 
unhandled_stoppedstdexec::__was::__with_awaitable_senders_base115       auto unhandled_stopped() noexcept -> __coro::coroutine_handle<> {
116         return __continuation_.unhandled_stopped();
117       }
118 
119      private:
120       __coroutine_handle<> __continuation_{};
121     };
122 
123     template <class _Promise>
124     struct with_awaitable_senders : __with_awaitable_senders_base {
125       template <class _Value>
await_transformstdexec::__was::with_awaitable_senders126       auto await_transform(_Value&& __val) -> __call_result_t<as_awaitable_t, _Value, _Promise&> {
127         static_assert(derived_from<_Promise, with_awaitable_senders>);
128         return as_awaitable(static_cast<_Value&&>(__val), static_cast<_Promise&>(*this));
129       }
130     };
131   } // namespace __was
132 
133   using __was::with_awaitable_senders;
134   using __was::__coroutine_handle;
135 #endif
136 } // namespace stdexec
137