]>
Commit | Line | Data |
---|---|---|
92f5a8d4 TL |
1 | // |
2 | // impl/co_spawn.hpp | |
3 | // ~~~~~~~~~~~~~~~~~ | |
4 | // | |
1e59de90 | 5 | // Copyright (c) 2003-2022 Christopher M. Kohlhoff (chris at kohlhoff dot com) |
92f5a8d4 TL |
6 | // |
7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
9 | // | |
10 | ||
11 | #ifndef BOOST_ASIO_IMPL_CO_SPAWN_HPP | |
12 | #define BOOST_ASIO_IMPL_CO_SPAWN_HPP | |
13 | ||
14 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) | |
15 | # pragma once | |
16 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) | |
17 | ||
18 | #include <boost/asio/detail/config.hpp> | |
1e59de90 | 19 | #include <boost/asio/associated_cancellation_slot.hpp> |
92f5a8d4 TL |
20 | #include <boost/asio/awaitable.hpp> |
21 | #include <boost/asio/dispatch.hpp> | |
20effc67 | 22 | #include <boost/asio/execution/outstanding_work.hpp> |
92f5a8d4 | 23 | #include <boost/asio/post.hpp> |
20effc67 | 24 | #include <boost/asio/prefer.hpp> |
92f5a8d4 TL |
25 | #include <boost/asio/use_awaitable.hpp> |
26 | ||
27 | #include <boost/asio/detail/push_options.hpp> | |
28 | ||
29 | namespace boost { | |
30 | namespace asio { | |
31 | namespace detail { | |
32 | ||
20effc67 TL |
33 | template <typename Executor, typename = void> |
34 | class co_spawn_work_guard | |
35 | { | |
36 | public: | |
37 | typedef typename decay< | |
38 | typename prefer_result<Executor, | |
39 | execution::outstanding_work_t::tracked_t | |
40 | >::type | |
41 | >::type executor_type; | |
42 | ||
43 | co_spawn_work_guard(const Executor& ex) | |
44 | : executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked)) | |
45 | { | |
46 | } | |
47 | ||
48 | executor_type get_executor() const BOOST_ASIO_NOEXCEPT | |
49 | { | |
50 | return executor_; | |
51 | } | |
52 | ||
53 | private: | |
54 | executor_type executor_; | |
55 | }; | |
56 | ||
57 | #if !defined(BOOST_ASIO_NO_TS_EXECUTORS) | |
58 | ||
59 | template <typename Executor> | |
60 | struct co_spawn_work_guard<Executor, | |
61 | typename enable_if< | |
62 | !execution::is_executor<Executor>::value | |
63 | >::type> : executor_work_guard<Executor> | |
64 | { | |
65 | co_spawn_work_guard(const Executor& ex) | |
66 | : executor_work_guard<Executor>(ex) | |
67 | { | |
68 | } | |
69 | }; | |
70 | ||
71 | #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS) | |
72 | ||
73 | template <typename Executor> | |
74 | inline co_spawn_work_guard<Executor> | |
75 | make_co_spawn_work_guard(const Executor& ex) | |
76 | { | |
77 | return co_spawn_work_guard<Executor>(ex); | |
78 | } | |
79 | ||
92f5a8d4 | 80 | template <typename T, typename Executor, typename F, typename Handler> |
1e59de90 | 81 | awaitable<awaitable_thread_entry_point, Executor> co_spawn_entry_point( |
92f5a8d4 TL |
82 | awaitable<T, Executor>*, Executor ex, F f, Handler handler) |
83 | { | |
20effc67 TL |
84 | auto spawn_work = make_co_spawn_work_guard(ex); |
85 | auto handler_work = make_co_spawn_work_guard( | |
86 | boost::asio::get_associated_executor(handler, ex)); | |
92f5a8d4 | 87 | |
1e59de90 TL |
88 | (void) co_await (dispatch)( |
89 | use_awaitable_t<Executor>{__FILE__, __LINE__, "co_spawn_entry_point"}); | |
92f5a8d4 | 90 | |
1e59de90 TL |
91 | (co_await awaitable_thread_has_context_switched{}) = false; |
92 | std::exception_ptr e = nullptr; | |
92f5a8d4 TL |
93 | bool done = false; |
94 | try | |
95 | { | |
96 | T t = co_await f(); | |
97 | ||
98 | done = true; | |
99 | ||
1e59de90 TL |
100 | if (co_await awaitable_thread_has_context_switched{}) |
101 | { | |
102 | (dispatch)(handler_work.get_executor(), | |
103 | [handler = std::move(handler), t = std::move(t)]() mutable | |
104 | { | |
105 | std::move(handler)(std::exception_ptr(), std::move(t)); | |
106 | }); | |
107 | } | |
108 | else | |
109 | { | |
110 | (post)(handler_work.get_executor(), | |
111 | [handler = std::move(handler), t = std::move(t)]() mutable | |
112 | { | |
113 | std::move(handler)(std::exception_ptr(), std::move(t)); | |
114 | }); | |
115 | } | |
116 | ||
117 | co_return; | |
92f5a8d4 TL |
118 | } |
119 | catch (...) | |
120 | { | |
121 | if (done) | |
122 | throw; | |
123 | ||
1e59de90 TL |
124 | e = std::current_exception(); |
125 | } | |
126 | ||
127 | if (co_await awaitable_thread_has_context_switched{}) | |
128 | { | |
92f5a8d4 | 129 | (dispatch)(handler_work.get_executor(), |
1e59de90 TL |
130 | [handler = std::move(handler), e]() mutable |
131 | { | |
132 | std::move(handler)(e, T()); | |
133 | }); | |
134 | } | |
135 | else | |
136 | { | |
137 | (post)(handler_work.get_executor(), | |
138 | [handler = std::move(handler), e]() mutable | |
92f5a8d4 | 139 | { |
1e59de90 | 140 | std::move(handler)(e, T()); |
92f5a8d4 TL |
141 | }); |
142 | } | |
143 | } | |
144 | ||
145 | template <typename Executor, typename F, typename Handler> | |
1e59de90 | 146 | awaitable<awaitable_thread_entry_point, Executor> co_spawn_entry_point( |
92f5a8d4 TL |
147 | awaitable<void, Executor>*, Executor ex, F f, Handler handler) |
148 | { | |
20effc67 TL |
149 | auto spawn_work = make_co_spawn_work_guard(ex); |
150 | auto handler_work = make_co_spawn_work_guard( | |
151 | boost::asio::get_associated_executor(handler, ex)); | |
92f5a8d4 | 152 | |
1e59de90 | 153 | (void) co_await (dispatch)( |
20effc67 | 154 | use_awaitable_t<Executor>{__FILE__, __LINE__, "co_spawn_entry_point"}); |
92f5a8d4 | 155 | |
1e59de90 | 156 | (co_await awaitable_thread_has_context_switched{}) = false; |
92f5a8d4 TL |
157 | std::exception_ptr e = nullptr; |
158 | try | |
159 | { | |
160 | co_await f(); | |
161 | } | |
162 | catch (...) | |
163 | { | |
164 | e = std::current_exception(); | |
165 | } | |
166 | ||
1e59de90 TL |
167 | if (co_await awaitable_thread_has_context_switched{}) |
168 | { | |
169 | (dispatch)(handler_work.get_executor(), | |
170 | [handler = std::move(handler), e]() mutable | |
171 | { | |
172 | std::move(handler)(e); | |
173 | }); | |
174 | } | |
175 | else | |
176 | { | |
177 | (post)(handler_work.get_executor(), | |
178 | [handler = std::move(handler), e]() mutable | |
179 | { | |
180 | std::move(handler)(e); | |
181 | }); | |
182 | } | |
92f5a8d4 TL |
183 | } |
184 | ||
20effc67 TL |
185 | template <typename T, typename Executor> |
186 | class awaitable_as_function | |
187 | { | |
188 | public: | |
189 | explicit awaitable_as_function(awaitable<T, Executor>&& a) | |
190 | : awaitable_(std::move(a)) | |
191 | { | |
192 | } | |
193 | ||
194 | awaitable<T, Executor> operator()() | |
195 | { | |
196 | return std::move(awaitable_); | |
197 | } | |
198 | ||
199 | private: | |
200 | awaitable<T, Executor> awaitable_; | |
201 | }; | |
202 | ||
1e59de90 TL |
203 | template <typename Handler, typename Executor, typename = void> |
204 | class co_spawn_cancellation_handler | |
205 | { | |
206 | public: | |
207 | co_spawn_cancellation_handler(const Handler& handler, const Executor& ex) | |
208 | : ex_(boost::asio::get_associated_executor(handler, ex)) | |
209 | { | |
210 | } | |
211 | ||
212 | cancellation_slot slot() | |
213 | { | |
214 | return signal_.slot(); | |
215 | } | |
216 | ||
217 | void operator()(cancellation_type_t type) | |
218 | { | |
219 | cancellation_signal* sig = &signal_; | |
220 | boost::asio::dispatch(ex_, [sig, type]{ sig->emit(type); }); | |
221 | } | |
222 | ||
223 | private: | |
224 | cancellation_signal signal_; | |
225 | typename associated_executor<Handler, Executor>::type ex_; | |
226 | }; | |
227 | ||
228 | ||
229 | template <typename Handler, typename Executor> | |
230 | class co_spawn_cancellation_handler<Handler, Executor, | |
231 | typename enable_if< | |
232 | is_same< | |
233 | typename associated_executor<Handler, | |
234 | Executor>::asio_associated_executor_is_unspecialised, | |
235 | void | |
236 | >::value | |
237 | >::type> | |
238 | { | |
239 | public: | |
240 | co_spawn_cancellation_handler(const Handler&, const Executor&) | |
241 | { | |
242 | } | |
243 | ||
244 | cancellation_slot slot() | |
245 | { | |
246 | return signal_.slot(); | |
247 | } | |
248 | ||
249 | void operator()(cancellation_type_t type) | |
250 | { | |
251 | signal_.emit(type); | |
252 | } | |
253 | ||
254 | private: | |
255 | cancellation_signal signal_; | |
256 | }; | |
257 | ||
92f5a8d4 TL |
258 | template <typename Executor> |
259 | class initiate_co_spawn | |
260 | { | |
261 | public: | |
262 | typedef Executor executor_type; | |
263 | ||
264 | template <typename OtherExecutor> | |
265 | explicit initiate_co_spawn(const OtherExecutor& ex) | |
266 | : ex_(ex) | |
267 | { | |
268 | } | |
269 | ||
270 | executor_type get_executor() const BOOST_ASIO_NOEXCEPT | |
271 | { | |
272 | return ex_; | |
273 | } | |
274 | ||
275 | template <typename Handler, typename F> | |
276 | void operator()(Handler&& handler, F&& f) const | |
277 | { | |
278 | typedef typename result_of<F()>::type awaitable_type; | |
1e59de90 TL |
279 | typedef typename decay<Handler>::type handler_type; |
280 | typedef co_spawn_cancellation_handler< | |
281 | handler_type, Executor> cancel_handler_type; | |
282 | ||
283 | auto slot = boost::asio::get_associated_cancellation_slot(handler); | |
284 | cancel_handler_type* cancel_handler = slot.is_connected() | |
285 | ? &slot.template emplace<cancel_handler_type>(handler, ex_) | |
286 | : nullptr; | |
287 | ||
288 | cancellation_slot proxy_slot( | |
289 | cancel_handler | |
290 | ? cancel_handler->slot() | |
291 | : cancellation_slot()); | |
292 | ||
293 | cancellation_state cancel_state(proxy_slot); | |
92f5a8d4 TL |
294 | |
295 | auto a = (co_spawn_entry_point)(static_cast<awaitable_type*>(nullptr), | |
296 | ex_, std::forward<F>(f), std::forward<Handler>(handler)); | |
1e59de90 TL |
297 | awaitable_handler<executor_type, void>(std::move(a), |
298 | ex_, proxy_slot, cancel_state).launch(); | |
92f5a8d4 TL |
299 | } |
300 | ||
301 | private: | |
302 | Executor ex_; | |
303 | }; | |
304 | ||
305 | } // namespace detail | |
306 | ||
20effc67 TL |
307 | template <typename Executor, typename T, typename AwaitableExecutor, |
308 | BOOST_ASIO_COMPLETION_TOKEN_FOR( | |
309 | void(std::exception_ptr, T)) CompletionToken> | |
310 | inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( | |
311 | CompletionToken, void(std::exception_ptr, T)) | |
312 | co_spawn(const Executor& ex, | |
313 | awaitable<T, AwaitableExecutor> a, CompletionToken&& token, | |
1e59de90 | 314 | typename constraint< |
20effc67 TL |
315 | (is_executor<Executor>::value || execution::is_executor<Executor>::value) |
316 | && is_convertible<Executor, AwaitableExecutor>::value | |
1e59de90 | 317 | >::type) |
20effc67 TL |
318 | { |
319 | return async_initiate<CompletionToken, void(std::exception_ptr, T)>( | |
320 | detail::initiate_co_spawn<AwaitableExecutor>(AwaitableExecutor(ex)), | |
321 | token, detail::awaitable_as_function<T, AwaitableExecutor>(std::move(a))); | |
322 | } | |
323 | ||
324 | template <typename Executor, typename AwaitableExecutor, | |
325 | BOOST_ASIO_COMPLETION_TOKEN_FOR( | |
326 | void(std::exception_ptr)) CompletionToken> | |
327 | inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( | |
328 | CompletionToken, void(std::exception_ptr)) | |
329 | co_spawn(const Executor& ex, | |
330 | awaitable<void, AwaitableExecutor> a, CompletionToken&& token, | |
1e59de90 | 331 | typename constraint< |
20effc67 TL |
332 | (is_executor<Executor>::value || execution::is_executor<Executor>::value) |
333 | && is_convertible<Executor, AwaitableExecutor>::value | |
1e59de90 | 334 | >::type) |
20effc67 TL |
335 | { |
336 | return async_initiate<CompletionToken, void(std::exception_ptr)>( | |
337 | detail::initiate_co_spawn<AwaitableExecutor>(AwaitableExecutor(ex)), | |
338 | token, detail::awaitable_as_function< | |
339 | void, AwaitableExecutor>(std::move(a))); | |
340 | } | |
341 | ||
342 | template <typename ExecutionContext, typename T, typename AwaitableExecutor, | |
343 | BOOST_ASIO_COMPLETION_TOKEN_FOR( | |
344 | void(std::exception_ptr, T)) CompletionToken> | |
345 | inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( | |
346 | CompletionToken, void(std::exception_ptr, T)) | |
347 | co_spawn(ExecutionContext& ctx, | |
348 | awaitable<T, AwaitableExecutor> a, CompletionToken&& token, | |
1e59de90 | 349 | typename constraint< |
20effc67 TL |
350 | is_convertible<ExecutionContext&, execution_context&>::value |
351 | && is_convertible<typename ExecutionContext::executor_type, | |
352 | AwaitableExecutor>::value | |
1e59de90 | 353 | >::type) |
20effc67 TL |
354 | { |
355 | return (co_spawn)(ctx.get_executor(), std::move(a), | |
356 | std::forward<CompletionToken>(token)); | |
357 | } | |
358 | ||
359 | template <typename ExecutionContext, typename AwaitableExecutor, | |
360 | BOOST_ASIO_COMPLETION_TOKEN_FOR( | |
361 | void(std::exception_ptr)) CompletionToken> | |
362 | inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( | |
363 | CompletionToken, void(std::exception_ptr)) | |
364 | co_spawn(ExecutionContext& ctx, | |
365 | awaitable<void, AwaitableExecutor> a, CompletionToken&& token, | |
1e59de90 | 366 | typename constraint< |
20effc67 TL |
367 | is_convertible<ExecutionContext&, execution_context&>::value |
368 | && is_convertible<typename ExecutionContext::executor_type, | |
369 | AwaitableExecutor>::value | |
1e59de90 | 370 | >::type) |
20effc67 TL |
371 | { |
372 | return (co_spawn)(ctx.get_executor(), std::move(a), | |
373 | std::forward<CompletionToken>(token)); | |
374 | } | |
375 | ||
92f5a8d4 TL |
376 | template <typename Executor, typename F, |
377 | BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::awaitable_signature< | |
378 | typename result_of<F()>::type>::type) CompletionToken> | |
379 | inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, | |
380 | typename detail::awaitable_signature<typename result_of<F()>::type>::type) | |
381 | co_spawn(const Executor& ex, F&& f, CompletionToken&& token, | |
1e59de90 | 382 | typename constraint< |
20effc67 | 383 | is_executor<Executor>::value || execution::is_executor<Executor>::value |
1e59de90 | 384 | >::type) |
92f5a8d4 TL |
385 | { |
386 | return async_initiate<CompletionToken, | |
20effc67 | 387 | typename detail::awaitable_signature<typename result_of<F()>::type>::type>( |
92f5a8d4 TL |
388 | detail::initiate_co_spawn< |
389 | typename result_of<F()>::type::executor_type>(ex), | |
390 | token, std::forward<F>(f)); | |
391 | } | |
392 | ||
393 | template <typename ExecutionContext, typename F, | |
394 | BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::awaitable_signature< | |
395 | typename result_of<F()>::type>::type) CompletionToken> | |
396 | inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, | |
397 | typename detail::awaitable_signature<typename result_of<F()>::type>::type) | |
398 | co_spawn(ExecutionContext& ctx, F&& f, CompletionToken&& token, | |
1e59de90 | 399 | typename constraint< |
92f5a8d4 | 400 | is_convertible<ExecutionContext&, execution_context&>::value |
1e59de90 | 401 | >::type) |
92f5a8d4 TL |
402 | { |
403 | return (co_spawn)(ctx.get_executor(), std::forward<F>(f), | |
404 | std::forward<CompletionToken>(token)); | |
405 | } | |
406 | ||
407 | } // namespace asio | |
408 | } // namespace boost | |
409 | ||
410 | #include <boost/asio/detail/pop_options.hpp> | |
411 | ||
412 | #endif // BOOST_ASIO_IMPL_CO_SPAWN_HPP |