xref: /openbmc/sdbusplus/include/sdbusplus/async/stdexec/__detail/__with_awaitable_senders.hpp (revision 36137e09614746b13603b5fbae79e6f70819c46b)
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 "__as_awaitable.hpp"
19 #include "__concepts.hpp"
20 #include "__execution_fwd.hpp"
21 
22 #include <exception>
23 
24 namespace stdexec
25 {
26 #if !STDEXEC_STD_NO_COROUTINES()
27 namespace __was
28 {
29 template <class _Promise = void>
30 class __continuation_handle;
31 
32 template <>
33 class __continuation_handle<void>
34 {
35   public:
36     __continuation_handle() = default;
37 
38     template <class _Promise>
__continuation_handle(__coro::coroutine_handle<_Promise> __coro)39     __continuation_handle(__coro::coroutine_handle<_Promise> __coro) noexcept :
40         __coro_(__coro)
41     {
42         if constexpr (requires(_Promise& __promise) {
43                           __promise.unhandled_stopped();
44                       })
45         {
46             __stopped_callback_ =
47                 [](void* __address) noexcept -> __coro::coroutine_handle<> {
48                 // This causes the rest of the coroutine (the part after the
49                 // co_await of the sender) to be skipped and invokes the calling
50                 // coroutine's stopped handler.
51                 return __coro::coroutine_handle<_Promise>::from_address(
52                            __address)
53                     .promise()
54                     .unhandled_stopped();
55             };
56         }
57         // If _Promise doesn't implement unhandled_stopped(), then if a
58         // "stopped" unwind reaches this point, it's considered an unhandled
59         // exception and terminate() is called.
60     }
61 
handle() const62     [[nodiscard]] auto handle() const noexcept -> __coro::coroutine_handle<>
63     {
64         return __coro_;
65     }
66 
unhandled_stopped() const67     [[nodiscard]] auto unhandled_stopped() const noexcept
68         -> __coro::coroutine_handle<>
69     {
70         return __stopped_callback_(__coro_.address());
71     }
72 
73   private:
74     using __stopped_callback_t = __coro::coroutine_handle<> (*)(void*) noexcept;
75 
76     __coro::coroutine_handle<> __coro_{};
77     __stopped_callback_t __stopped_callback_ =
__anon7e953c250202(void*) 78         [](void*) noexcept -> __coro::coroutine_handle<> { std::terminate(); };
79 };
80 
81 template <class _Promise>
82 class __continuation_handle
83 {
84   public:
85     __continuation_handle() = default;
86 
__continuation_handle(__coro::coroutine_handle<_Promise> __coro)87     __continuation_handle(__coro::coroutine_handle<_Promise> __coro) noexcept :
88         __continuation_{__coro}
89     {}
90 
handle() const91     auto handle() const noexcept -> __coro::coroutine_handle<_Promise>
92     {
93         return __coro::coroutine_handle<_Promise>::from_address(
94             __continuation_.handle().address());
95     }
96 
unhandled_stopped() const97     [[nodiscard]] auto unhandled_stopped() const noexcept
98         -> __coro::coroutine_handle<>
99     {
100         return __continuation_.unhandled_stopped();
101     }
102 
103   private:
104     __continuation_handle<> __continuation_{};
105 };
106 
107 struct __with_awaitable_senders_base
108 {
109     template <class _OtherPromise>
set_continuationstdexec::__was::__with_awaitable_senders_base110     void set_continuation(
111         __coro::coroutine_handle<_OtherPromise> __hcoro) noexcept
112     {
113         static_assert(!__same_as<_OtherPromise, void>);
114         __continuation_ = __hcoro;
115     }
116 
set_continuationstdexec::__was::__with_awaitable_senders_base117     void set_continuation(__continuation_handle<> __continuation) noexcept
118     {
119         __continuation_ = __continuation;
120     }
121 
continuationstdexec::__was::__with_awaitable_senders_base122     [[nodiscard]] auto continuation() const noexcept -> __continuation_handle<>
123     {
124         return __continuation_;
125     }
126 
unhandled_stoppedstdexec::__was::__with_awaitable_senders_base127     auto unhandled_stopped() noexcept -> __coro::coroutine_handle<>
128     {
129         return __continuation_.unhandled_stopped();
130     }
131 
132   private:
133     __continuation_handle<> __continuation_{};
134 };
135 
136 template <class _Promise>
137 struct with_awaitable_senders : __with_awaitable_senders_base
138 {
139     template <class _Value>
await_transformstdexec::__was::with_awaitable_senders140     auto await_transform(_Value&& __val)
141         -> __call_result_t<as_awaitable_t, _Value, _Promise&>
142     {
143         static_assert(derived_from<_Promise, with_awaitable_senders>);
144         return as_awaitable(static_cast<_Value&&>(__val),
145                             static_cast<_Promise&>(*this));
146     }
147 };
148 } // namespace __was
149 
150 using __was::__continuation_handle;
151 using __was::with_awaitable_senders;
152 #endif
153 } // namespace stdexec
154