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_HANDLER_HPP
6#define BOOST_COBALT_HANDLER_HPP
7
8#include <boost/cobalt/this_coro.hpp>
9#include <boost/cobalt/unique_handle.hpp>
10#include <boost/cobalt/detail/util.hpp>
11
12#include <boost/cobalt/detail/sbo_resource.hpp>
13#include <boost/asio/bind_allocator.hpp>
14#include <boost/asio/post.hpp>
15#include <boost/system/result.hpp>
16
17#include <memory>
18#include <optional>
19
20namespace boost::cobalt
21{
22
23namespace detail
24{
25
26enum class completed_immediately_t
27{
28 no, maybe, yes, initiating
29};
30
31struct completion_handler_noop_executor
32{
33 executor exec;
34 completed_immediately_t * completed_immediately = nullptr;
35
36 template<typename Fn>
37 void execute(Fn && fn) const
38 {
39 // only allow it when we're still initializing
40 if (completed_immediately &&
41 ((*completed_immediately == completed_immediately_t::initiating)
42 || (*completed_immediately == completed_immediately_t::maybe)))
43 {
44 // only use this indicator if the fn will actually call our completion-handler
45 // otherwise this was a single op in a composed operation
46 *completed_immediately = completed_immediately_t::maybe;
47 fn();
48 // yes means completion_handler::operator() was called, so we're good.
49 if (*completed_immediately != completed_immediately_t::yes)
50 *completed_immediately = completed_immediately_t::initiating;
51 }
52 else
53 {
54 asio::post(exec, std::forward<Fn>(fn));
55 }
56 }
57
58 friend bool operator==(const completion_handler_noop_executor&, const completion_handler_noop_executor&) noexcept
59 {
60 return true;
61 }
62
63 friend bool operator!=(const completion_handler_noop_executor&, const completion_handler_noop_executor&) noexcept
64 {
65 return false;
66 }
67
68 completion_handler_noop_executor(const completion_handler_noop_executor & rhs) noexcept = default;
69 completion_handler_noop_executor(cobalt::executor inner, completed_immediately_t * completed_immediately)
70 : exec(std::move(inner)), completed_immediately(completed_immediately)
71 {
72 }
73
74};
75
76
77struct completion_handler_base
78{
79 using cancellation_slot_type = asio::cancellation_slot;
80 cancellation_slot_type cancellation_slot ;
81 cancellation_slot_type get_cancellation_slot() const noexcept
82 {
83 return cancellation_slot ;
84 }
85
86 using executor_type = executor;
87 const executor_type & executor_ ;
88 const executor_type & get_executor() const noexcept
89 {
90 return executor_ ;
91 }
92
93#if !defined(BOOST_COBALT_NO_PMR)
94 using allocator_type = pmr::polymorphic_allocator<void>;
95 pmr::polymorphic_allocator<void> allocator ;
96 allocator_type get_allocator() const noexcept
97 {
98 return allocator ;
99 }
100#else
101 using allocator_type = detail::sbo_allocator<void>;
102 detail::sbo_allocator<void> allocator ;
103 allocator_type get_allocator() const noexcept
104 {
105 return allocator ;
106 }
107#endif
108 using immediate_executor_type = completion_handler_noop_executor;
109 completed_immediately_t * completed_immediately = nullptr;
110 immediate_executor_type get_immediate_executor() const noexcept
111 {
112 return {get_executor(), completed_immediately};
113 }
114
115 template<typename Promise>
116 requires (requires (Promise p) {{p.get_executor()} -> std::same_as<const executor&>;})
117 completion_handler_base(std::coroutine_handle<Promise> h,
118 completed_immediately_t * completed_immediately = nullptr)
119 : cancellation_slot(asio::get_associated_cancellation_slot(h.promise())),
120 executor_(h.promise().get_executor()),
121#if !defined(BOOST_COBALT_NO_PMR)
122 allocator(asio::get_associated_allocator(h.promise(), this_thread::get_allocator())),
123#else
124 allocator(detail::get_null_sbo_resource()),
125#endif
126 completed_immediately(completed_immediately)
127 {
128 }
129#if !defined(BOOST_COBALT_NO_PMR)
130 template<typename Promise>
131 requires (requires (Promise p) {{p.get_executor()} -> std::same_as<const executor&>;})
132 completion_handler_base(std::coroutine_handle<Promise> h,
133 pmr::memory_resource * resource,
134 completed_immediately_t * completed_immediately = nullptr)
135 : cancellation_slot(asio::get_associated_cancellation_slot(h.promise())),
136 executor_(h.promise().get_executor()),
137 allocator(resource),
138 completed_immediately(completed_immediately)
139 {
140 }
141#else
142 template<typename Promise>
143 requires (requires (Promise p) {{p.get_executor()} -> std::same_as<const executor&>;})
144 completion_handler_base(std::coroutine_handle<Promise> h,
145 detail::sbo_resource * resource,
146 completed_immediately_t * completed_immediately = nullptr)
147 : cancellation_slot(asio::get_associated_cancellation_slot(h.promise())),
148 executor_(h.promise().get_executor()),
149 allocator(resource),
150 completed_immediately(completed_immediately)
151 {
152 }
153
154#endif
155};
156
157
158template<typename Handler>
159void assign_cancellation(std::coroutine_handle<void>, Handler &&) {}
160
161template<typename Promise, typename Handler>
162void assign_cancellation(std::coroutine_handle<Promise> h, Handler && func)
163{
164 if constexpr (requires {h.promise().get_cancellation_slot();})
165 if (h.promise().get_cancellation_slot().is_connected())
166 h.promise().get_cancellation_slot().assign(std::forward<Handler>(func));
167}
168
169template<typename Promise>
170const executor &
171get_executor(std::coroutine_handle<Promise> h)
172{
173 if constexpr (requires {h.promise().get_executor();})
174 {
175 static_assert(std::same_as<decltype(h.promise().get_executor()),
176 const executor &>,
177 "for performance reasons, the get_executor function on a promise must return a const reference");
178 return h.promise().get_executor();
179 }
180 else
181 return this_thread::get_executor();
182}
183
184inline const executor &
185get_executor(std::coroutine_handle<>)
186{
187 return this_thread::get_executor();
188}
189
190}
191
192template<typename ... Args>
193struct handler
194{
195 void operator()(Args ... args)
196 {
197 result.emplace(static_cast<Args>(args)...);
198 }
199 handler(std::optional<std::tuple<Args...>> &result) : result(result) {}
200 private:
201 std::optional<std::tuple<Args...>> &result;
202};
203
204template<typename ... Args>
205handler(std::optional<std::tuple<Args...>> &result) -> handler<Args...>;
206
207template<typename ... Args>
208struct completion_handler : detail::completion_handler_base
209{
210 completion_handler(completion_handler && ) = default;
211
212 template<typename Promise>
213 completion_handler(std::coroutine_handle<Promise> h,
214 std::optional<std::tuple<Args...>> &result,
215 detail::completed_immediately_t * completed_immediately = nullptr
216#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
217 , const boost::source_location & loc = BOOST_CURRENT_LOCATION
218#endif
219 ) : completion_handler_base(h, completed_immediately),
220 self(h.address()), result(result)
221#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
222 , loc_(loc)
223#endif
224 {
225 }
226
227#if !defined(BOOST_COBALT_NO_PMR)
228 template<typename Promise>
229 completion_handler(std::coroutine_handle<Promise> h,
230 std::optional<std::tuple<Args...>> &result,
231 pmr::memory_resource * resource,
232 detail::completed_immediately_t * completed_immediately = nullptr
233#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
234 , const boost::source_location & loc = BOOST_CURRENT_LOCATION
235#endif
236 ) : completion_handler_base(h, resource, completed_immediately),
237 self(h.address()), result(result)
238#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
239 , loc_(loc)
240#endif
241 {
242 }
243#else
244 template<typename Promise>
245 completion_handler(std::coroutine_handle<Promise> h,
246 std::optional<std::tuple<Args...>> &result,
247 detail::sbo_resource * resource,
248 detail::completed_immediately_t * completed_immediately = nullptr)
249 : completion_handler_base(h, resource, completed_immediately),
250 self(h.address()), result(result)
251 {
252 }
253#endif
254
255
256 void operator()(Args ... args)
257 {
258#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
259 BOOST_ASIO_HANDLER_LOCATION((loc_.file_name(), loc_.line(), loc_.function_name()));
260#endif
261 result.emplace(std::move(args)...);
262 BOOST_ASSERT(this->self != nullptr);
263 auto p = this->self.release();
264 if (completed_immediately != nullptr
265 && *completed_immediately == detail::completed_immediately_t::maybe)
266 {
267 *completed_immediately = detail::completed_immediately_t::yes;
268 return;
269 }
270
271 std::move(p)();
272 }
273 using result_type = std::optional<std::tuple<Args...>>;
274
275 ~completion_handler()
276 {
277 if (self && completed_immediately
278 && *completed_immediately == detail::completed_immediately_t::initiating
279 && std::uncaught_exceptions() > 0)
280 self.release();
281 }
282 private:
283 unique_handle<void> self;
284 std::optional<std::tuple<Args...>> &result;
285#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
286 boost::source_location loc_;
287#endif
288};
289
290};
291
292#endif //BOOST_COBALT_HANDLER_HPP
293

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