TLA Line data Source code
1 : //
2 : // Copyright (c) 2026 Vinnie Falco (vinnie.falco@gmail.com)
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 : // Official repository: https://github.com/cppalliance/capy
8 : //
9 :
10 : #ifndef BOOST_CAPY_ASIO_DETAIL_COMPLETION_HANDLER_HPP
11 : #define BOOST_CAPY_ASIO_DETAIL_COMPLETION_HANDLER_HPP
12 :
13 : #include <boost/capy/asio/detail/asio_coroutine_unique_handle.hpp>
14 : #include <boost/capy/asio/executor_adapter.hpp>
15 : #include <boost/capy/ex/executor_ref.hpp>
16 : #include <boost/capy/ex/io_env.hpp>
17 : #include <boost/capy/io_result.hpp>
18 :
19 :
20 : #include <memory_resource>
21 : #include <optional>
22 : #include <system_error>
23 : #include <tuple>
24 : #include <type_traits>
25 :
26 : namespace boost::capy::detail
27 : {
28 :
29 : struct asio_immediate_executor_helper
30 : {
31 : enum completed_immediately_t
32 : {
33 : no, maybe, yes, initiating
34 : };
35 :
36 : executor_ref exec;
37 : completed_immediately_t * completed_immediately = nullptr;
38 :
39 : template<typename Fn>
40 HIT 2 : void execute(Fn && fn) const
41 : {
42 : // only allow it when we're still initializing
43 2 : if (completed_immediately &&
44 2 : ((*completed_immediately == initiating)
45 MIS 0 : || (*completed_immediately == maybe)))
46 : {
47 : // only use this indicator if the fn will actually call our handler
48 : // otherwise this was a single op in a composed operation
49 HIT 2 : *completed_immediately = maybe;
50 2 : fn();
51 :
52 2 : if (*completed_immediately != yes)
53 MIS 0 : *completed_immediately = initiating;
54 : }
55 : else
56 : {
57 0 : exec.post(
58 : make_continuation(
59 : std::forward<Fn>(fn),
60 0 : std::pmr::polymorphic_allocator<void>(
61 0 : exec.context().get_frame_allocator())));
62 : }
63 HIT 2 : }
64 :
65 : friend bool operator==(const asio_immediate_executor_helper& lhs,
66 : const asio_immediate_executor_helper& rhs) noexcept
67 : {
68 : return lhs.exec == rhs.exec;
69 : }
70 :
71 : friend bool operator!=(const asio_immediate_executor_helper& lhs,
72 : const asio_immediate_executor_helper& rhs) noexcept
73 : {
74 : return lhs.exec != rhs.exec;
75 : }
76 :
77 : asio_immediate_executor_helper(
78 : const asio_immediate_executor_helper & rhs) noexcept = default;
79 :
80 2 : asio_immediate_executor_helper(
81 : executor_ref inner,
82 : completed_immediately_t * completed_immediately
83 2 : ) : exec(std::move(inner)), completed_immediately(completed_immediately)
84 : {
85 2 : }
86 : };
87 :
88 :
89 : template<typename CancellationSlot, typename ... Args>
90 : struct asio_coroutine_completion_handler
91 : {
92 : asio_coroutine_unique_handle handle;
93 : std::optional<std::tuple<Args...>> & result;
94 : const capy::io_env * env;
95 : CancellationSlot slot;
96 : using completed_immediately_t = asio_immediate_executor_helper::completed_immediately_t;
97 :
98 : completed_immediately_t * completed_immediately = nullptr;
99 :
100 : using allocator_type = std::pmr::polymorphic_allocator<void>;
101 10 : allocator_type get_allocator() const {return env->frame_allocator;}
102 :
103 : using executor_type = asio_executor_adapter<executor_ref>;
104 5 : executor_type get_executor() const {return env->executor;}
105 :
106 : using cancellation_slot_type = CancellationSlot;
107 2 : cancellation_slot_type get_cancellation_slot() const {return slot;}
108 :
109 : using immediate_executor_type = asio_immediate_executor_helper;
110 2 : immediate_executor_type get_immediate_executor() const
111 : {
112 2 : return immediate_executor_type{env->executor, completed_immediately };
113 : };
114 :
115 3 : asio_coroutine_completion_handler(
116 : std::coroutine_handle<void> h,
117 : std::optional<std::tuple<Args...>> & result,
118 : const capy::io_env * env,
119 : CancellationSlot slot = {},
120 : completed_immediately_t * ci = nullptr)
121 3 : : handle(h)
122 3 : , result(result)
123 3 : , env(env)
124 3 : , slot(slot), completed_immediately(ci)
125 3 : {}
126 :
127 26 : asio_coroutine_completion_handler(
128 : asio_coroutine_completion_handler &&
129 : ) noexcept = default;
130 :
131 3 : void operator()(Args ... args)
132 : {
133 3 : result.emplace(std::forward<Args>(args)...);
134 :
135 3 : auto h = handle.release();
136 :
137 3 : if (completed_immediately != nullptr
138 3 : && *completed_immediately == completed_immediately_t::maybe)
139 : {
140 2 : *completed_immediately = completed_immediately_t::yes;
141 : }
142 : else
143 1 : h();
144 3 : }
145 : };
146 :
147 : template<typename ... Ts>
148 : struct async_result_impl_result_tuple
149 : {
150 : using type = std::tuple<Ts...>;
151 : };
152 :
153 :
154 : template<typename T0, typename ... Ts>
155 : requires std::constructible_from<io_result<Ts...>, T0, Ts...>
156 : struct async_result_impl_result_tuple<T0, Ts...>
157 : {
158 : using type = io_result<Ts...>;
159 : };
160 :
161 1 : inline std::tuple<> make_async_result(const std::tuple<> &) { return {}; }
162 :
163 : template<typename E, typename ... Ts>
164 2 : inline auto make_async_result(std::tuple<E, Ts...> && tup)
165 : {
166 : if constexpr (std::convertible_to<E, std::error_code>)
167 : return std::apply(
168 2 : [](auto &&e, auto &&... args)
169 : {
170 2 : return io_result(std::move(e), std::move(args)...);
171 : },
172 4 : std::move(tup));
173 :
174 : else
175 : return std::move(tup);
176 : }
177 :
178 :
179 : template<typename CancellationSignal, typename CancellationType, typename ... Ts>
180 : struct async_result_impl
181 : {
182 : template<typename Initiation, typename... Args>
183 : struct awaitable_t
184 : {
185 : using completed_immediately_t
186 : = asio_immediate_executor_helper::completed_immediately_t;
187 :
188 : CancellationSignal signal;
189 : completed_immediately_t completed_immediately;
190 :
191 : struct cb
192 : {
193 : CancellationSignal &signal;
194 3 : cb(CancellationSignal &signal) : signal(signal) {}
195 MIS 0 : void operator()() {signal.emit(CancellationType::terminal); }
196 : };
197 : std::optional<std::stop_callback<cb>> stopper;
198 :
199 HIT 3 : bool await_ready() const {return false;}
200 :
201 3 : bool await_suspend(std::coroutine_handle<> h, const capy::io_env * env)
202 : {
203 3 : completed_immediately = completed_immediately_t::initiating;
204 3 : stopper.emplace(env->stop_token, signal);
205 : using slot_t = std::decay_t<decltype(CancellationSignal().slot())>;
206 3 : capy::detail::asio_coroutine_completion_handler<slot_t, Ts...> ch(
207 3 : h, result_, env,
208 : signal.slot(),
209 : &completed_immediately);
210 :
211 3 : std::apply(
212 9 : [&](auto ... args)
213 : {
214 6 : std::move(init_)(
215 3 : std::move(ch),
216 3 : std::move(args)...);
217 : },
218 3 : std::move(args_));
219 :
220 3 : if (completed_immediately == completed_immediately_t::initiating)
221 1 : completed_immediately = completed_immediately_t::no;
222 3 : return completed_immediately != completed_immediately_t::yes;
223 3 : }
224 :
225 3 : auto await_resume()
226 : {
227 3 : return make_async_result(std::move(*result_));
228 : }
229 :
230 :
231 3 : awaitable_t(Initiation init, std::tuple<Args...> args)
232 3 : : init_(std::move(init)), args_(std::move(args))
233 : {
234 3 : }
235 :
236 1 : awaitable_t(awaitable_t && rhs) noexcept
237 1 : : init_(std::move(rhs.init_))
238 1 : , args_(std::move(rhs.args_))
239 2 : , result_(std::move(rhs.result_)) {}
240 : private:
241 : Initiation init_;
242 : // if Args<0> == error_code this needs to be an io_result.
243 : typename async_result_impl_result_tuple<Args...>::type args_;
244 : std::optional<std::tuple<Ts...>> result_;
245 : };
246 :
247 : template <typename Initiation, typename RawToken, typename... Args>
248 3 : static auto initiate(Initiation&& initiation,
249 : RawToken&&, Args&&... args)
250 : {
251 : return awaitable_t<
252 : std::decay_t<Initiation>,
253 : std::decay_t<Args>...>(
254 2 : std::forward<Initiation>(initiation),
255 5 : std::make_tuple(std::forward<Args>(args)...));
256 : }
257 : };
258 :
259 :
260 : }
261 :
262 : #endif //BOOST_CAPY_ASIO_DETAIL_COMPLETION_HANDLER
263 :
|