1//
2// impl/spawn.hpp
3// ~~~~~~~~~~~~~~
4//
5// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
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_SPAWN_HPP
12#define BOOST_ASIO_IMPL_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>
19#include <tuple>
20#include <boost/asio/associated_allocator.hpp>
21#include <boost/asio/associated_cancellation_slot.hpp>
22#include <boost/asio/associated_executor.hpp>
23#include <boost/asio/async_result.hpp>
24#include <boost/asio/bind_executor.hpp>
25#include <boost/asio/detail/atomic_count.hpp>
26#include <boost/asio/detail/bind_handler.hpp>
27#include <boost/asio/detail/handler_cont_helpers.hpp>
28#include <boost/asio/detail/memory.hpp>
29#include <boost/asio/detail/noncopyable.hpp>
30#include <boost/asio/detail/type_traits.hpp>
31#include <boost/asio/detail/utility.hpp>
32#include <boost/system/system_error.hpp>
33
34#if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
35# include <boost/context/fiber.hpp>
36#endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
37
38#include <boost/asio/detail/push_options.hpp>
39
40namespace boost {
41namespace asio {
42namespace detail {
43
44#if !defined(BOOST_ASIO_NO_EXCEPTIONS)
45inline void spawned_thread_rethrow(void* ex)
46{
47 if (*static_cast<exception_ptr*>(ex))
48 rethrow_exception(*static_cast<exception_ptr*>(ex));
49}
50#endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
51
52#if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
53
54// Spawned thread implementation using Boost.Coroutine.
55class spawned_coroutine_thread : public spawned_thread_base
56{
57public:
58#if defined(BOOST_COROUTINES_UNIDIRECT) || defined(BOOST_COROUTINES_V2)
59 typedef boost::coroutines::pull_coroutine<void> callee_type;
60 typedef boost::coroutines::push_coroutine<void> caller_type;
61#else
62 typedef boost::coroutines::coroutine<void()> callee_type;
63 typedef boost::coroutines::coroutine<void()> caller_type;
64#endif
65
66 spawned_coroutine_thread(caller_type& caller)
67 : caller_(caller),
68 on_suspend_fn_(0),
69 on_suspend_arg_(0)
70 {
71 }
72
73 template <typename F>
74 static spawned_thread_base* spawn(F&& f,
75 const boost::coroutines::attributes& attributes,
76 cancellation_slot parent_cancel_slot = cancellation_slot(),
77 cancellation_state cancel_state = cancellation_state())
78 {
79 spawned_coroutine_thread* spawned_thread = 0;
80 callee_type callee(entry_point<decay_t<F>>(
81 static_cast<F&&>(f), &spawned_thread), attributes);
82 spawned_thread->callee_.swap(other&: callee);
83 spawned_thread->parent_cancellation_slot_ = parent_cancel_slot;
84 spawned_thread->cancellation_state_ = cancel_state;
85 return spawned_thread;
86 }
87
88 template <typename F>
89 static spawned_thread_base* spawn(F&& f,
90 cancellation_slot parent_cancel_slot = cancellation_slot(),
91 cancellation_state cancel_state = cancellation_state())
92 {
93 return spawn(static_cast<F&&>(f), boost::coroutines::attributes(),
94 parent_cancel_slot, cancel_state);
95 }
96
97 void resume()
98 {
99 callee_();
100 if (on_suspend_fn_)
101 {
102 void (*fn)(void*) = on_suspend_fn_;
103 void* arg = on_suspend_arg_;
104 on_suspend_fn_ = 0;
105 fn(arg);
106 }
107 }
108
109 void suspend_with(void (*fn)(void*), void* arg)
110 {
111 if (throw_if_cancelled_)
112 if (!!cancellation_state_.cancelled())
113 throw_error(err: boost::asio::error::operation_aborted, location: "yield");
114 has_context_switched_ = true;
115 on_suspend_fn_ = fn;
116 on_suspend_arg_ = arg;
117 caller_();
118 }
119
120 void destroy()
121 {
122 callee_type callee;
123 callee.swap(other&: callee_);
124 if (terminal_)
125 callee();
126 }
127
128private:
129 template <typename Function>
130 class entry_point
131 {
132 public:
133 template <typename F>
134 entry_point(F&& f,
135 spawned_coroutine_thread** spawned_thread_out)
136 : function_(static_cast<F&&>(f)),
137 spawned_thread_out_(spawned_thread_out)
138 {
139 }
140
141 void operator()(caller_type& caller)
142 {
143 Function function(static_cast<Function&&>(function_));
144 spawned_coroutine_thread spawned_thread(caller);
145 *spawned_thread_out_ = &spawned_thread;
146 spawned_thread_out_ = 0;
147 spawned_thread.suspend();
148#if !defined(BOOST_ASIO_NO_EXCEPTIONS)
149 try
150#endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
151 {
152 function(&spawned_thread);
153 spawned_thread.terminal_ = true;
154 spawned_thread.suspend();
155 }
156#if !defined(BOOST_ASIO_NO_EXCEPTIONS)
157 catch (const boost::coroutines::detail::forced_unwind&)
158 {
159 throw;
160 }
161 catch (...)
162 {
163 exception_ptr ex = current_exception();
164 spawned_thread.terminal_ = true;
165 spawned_thread.suspend_with(fn: spawned_thread_rethrow, arg: &ex);
166 }
167#endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
168 }
169
170 private:
171 Function function_;
172 spawned_coroutine_thread** spawned_thread_out_;
173 };
174
175 caller_type& caller_;
176 callee_type callee_;
177 void (*on_suspend_fn_)(void*);
178 void* on_suspend_arg_;
179};
180
181#endif // defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
182
183#if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
184
185// Spawned thread implementation using Boost.Context's fiber.
186class spawned_fiber_thread : public spawned_thread_base
187{
188public:
189 typedef boost::context::fiber fiber_type;
190
191 spawned_fiber_thread(fiber_type&& caller)
192 : caller_(static_cast<fiber_type&&>(caller)),
193 on_suspend_fn_(0),
194 on_suspend_arg_(0)
195 {
196 }
197
198 template <typename StackAllocator, typename F>
199 static spawned_thread_base* spawn(allocator_arg_t,
200 StackAllocator&& stack_allocator,
201 F&& f,
202 cancellation_slot parent_cancel_slot = cancellation_slot(),
203 cancellation_state cancel_state = cancellation_state())
204 {
205 spawned_fiber_thread* spawned_thread = 0;
206 fiber_type callee(allocator_arg_t(),
207 static_cast<StackAllocator&&>(stack_allocator),
208 entry_point<decay_t<F>>(
209 static_cast<F&&>(f), &spawned_thread));
210 callee = fiber_type(static_cast<fiber_type&&>(callee)).resume();
211 spawned_thread->callee_ = static_cast<fiber_type&&>(callee);
212 spawned_thread->parent_cancellation_slot_ = parent_cancel_slot;
213 spawned_thread->cancellation_state_ = cancel_state;
214 return spawned_thread;
215 }
216
217 template <typename F>
218 static spawned_thread_base* spawn(F&& f,
219 cancellation_slot parent_cancel_slot = cancellation_slot(),
220 cancellation_state cancel_state = cancellation_state())
221 {
222 return spawn(allocator_arg_t(), boost::context::fixedsize_stack(),
223 static_cast<F&&>(f), parent_cancel_slot, cancel_state);
224 }
225
226 void resume()
227 {
228 callee_ = fiber_type(static_cast<fiber_type&&>(callee_)).resume();
229 if (on_suspend_fn_)
230 {
231 void (*fn)(void*) = on_suspend_fn_;
232 void* arg = on_suspend_arg_;
233 on_suspend_fn_ = 0;
234 fn(arg);
235 }
236 }
237
238 void suspend_with(void (*fn)(void*), void* arg)
239 {
240 if (throw_if_cancelled_)
241 if (!!cancellation_state_.cancelled())
242 throw_error(err: boost::asio::error::operation_aborted, location: "yield");
243 has_context_switched_ = true;
244 on_suspend_fn_ = fn;
245 on_suspend_arg_ = arg;
246 caller_ = fiber_type(static_cast<fiber_type&&>(caller_)).resume();
247 }
248
249 void destroy()
250 {
251 fiber_type callee = static_cast<fiber_type&&>(callee_);
252 if (terminal_)
253 fiber_type(static_cast<fiber_type&&>(callee)).resume();
254 }
255
256private:
257 template <typename Function>
258 class entry_point
259 {
260 public:
261 template <typename F>
262 entry_point(F&& f,
263 spawned_fiber_thread** spawned_thread_out)
264 : function_(static_cast<F&&>(f)),
265 spawned_thread_out_(spawned_thread_out)
266 {
267 }
268
269 fiber_type operator()(fiber_type&& caller)
270 {
271 Function function(static_cast<Function&&>(function_));
272 spawned_fiber_thread spawned_thread(
273 static_cast<fiber_type&&>(caller));
274 *spawned_thread_out_ = &spawned_thread;
275 spawned_thread_out_ = 0;
276 spawned_thread.suspend();
277#if !defined(BOOST_ASIO_NO_EXCEPTIONS)
278 try
279#endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
280 {
281 function(&spawned_thread);
282 spawned_thread.terminal_ = true;
283 spawned_thread.suspend();
284 }
285#if !defined(BOOST_ASIO_NO_EXCEPTIONS)
286 catch (const boost::context::detail::forced_unwind&)
287 {
288 throw;
289 }
290 catch (...)
291 {
292 exception_ptr ex = current_exception();
293 spawned_thread.terminal_ = true;
294 spawned_thread.suspend_with(fn: spawned_thread_rethrow, arg: &ex);
295 }
296#endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
297 return static_cast<fiber_type&&>(spawned_thread.caller_);
298 }
299
300 private:
301 Function function_;
302 spawned_fiber_thread** spawned_thread_out_;
303 };
304
305 fiber_type caller_;
306 fiber_type callee_;
307 void (*on_suspend_fn_)(void*);
308 void* on_suspend_arg_;
309};
310
311#endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
312
313#if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
314typedef spawned_fiber_thread default_spawned_thread_type;
315#elif defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
316typedef spawned_coroutine_thread default_spawned_thread_type;
317#else
318# error No spawn() implementation available
319#endif
320
321// Helper class to perform the initial resume on the correct executor.
322class spawned_thread_resumer
323{
324public:
325 explicit spawned_thread_resumer(spawned_thread_base* spawned_thread)
326 : spawned_thread_(spawned_thread)
327 {
328 }
329
330 spawned_thread_resumer(spawned_thread_resumer&& other) noexcept
331 : spawned_thread_(other.spawned_thread_)
332 {
333 other.spawned_thread_ = 0;
334 }
335
336 ~spawned_thread_resumer()
337 {
338 if (spawned_thread_)
339 spawned_thread_->destroy();
340 }
341
342 void operator()()
343 {
344 spawned_thread_->attach(owner: &spawned_thread_);
345 spawned_thread_->resume();
346 }
347
348private:
349 spawned_thread_base* spawned_thread_;
350};
351
352// Helper class to ensure spawned threads are destroyed on the correct executor.
353class spawned_thread_destroyer
354{
355public:
356 explicit spawned_thread_destroyer(spawned_thread_base* spawned_thread)
357 : spawned_thread_(spawned_thread)
358 {
359 spawned_thread->detach();
360 }
361
362 spawned_thread_destroyer(spawned_thread_destroyer&& other) noexcept
363 : spawned_thread_(other.spawned_thread_)
364 {
365 other.spawned_thread_ = 0;
366 }
367
368 ~spawned_thread_destroyer()
369 {
370 if (spawned_thread_)
371 spawned_thread_->destroy();
372 }
373
374 void operator()()
375 {
376 if (spawned_thread_)
377 {
378 spawned_thread_->destroy();
379 spawned_thread_ = 0;
380 }
381 }
382
383private:
384 spawned_thread_base* spawned_thread_;
385};
386
387// Base class for all completion handlers associated with a spawned thread.
388template <typename Executor>
389class spawn_handler_base
390{
391public:
392 typedef Executor executor_type;
393 typedef cancellation_slot cancellation_slot_type;
394
395 spawn_handler_base(const basic_yield_context<Executor>& yield)
396 : yield_(yield),
397 spawned_thread_(yield.spawned_thread_)
398 {
399 spawned_thread_->detach();
400 }
401
402 spawn_handler_base(spawn_handler_base&& other) noexcept
403 : yield_(other.yield_),
404 spawned_thread_(other.spawned_thread_)
405
406 {
407 other.spawned_thread_ = 0;
408 }
409
410 ~spawn_handler_base()
411 {
412 if (spawned_thread_)
413 (post)(yield_.executor_, spawned_thread_destroyer(spawned_thread_));
414 }
415
416 executor_type get_executor() const noexcept
417 {
418 return yield_.executor_;
419 }
420
421 cancellation_slot_type get_cancellation_slot() const noexcept
422 {
423 return spawned_thread_->get_cancellation_slot();
424 }
425
426 void resume()
427 {
428 spawned_thread_resumer resumer(spawned_thread_);
429 spawned_thread_ = 0;
430 resumer();
431 }
432
433protected:
434 const basic_yield_context<Executor>& yield_;
435 spawned_thread_base* spawned_thread_;
436};
437
438// Completion handlers for when basic_yield_context is used as a token.
439template <typename Executor, typename Signature>
440class spawn_handler;
441
442template <typename Executor, typename R>
443class spawn_handler<Executor, R()>
444 : public spawn_handler_base<Executor>
445{
446public:
447 typedef void return_type;
448
449 struct result_type {};
450
451 spawn_handler(const basic_yield_context<Executor>& yield, result_type&)
452 : spawn_handler_base<Executor>(yield)
453 {
454 }
455
456 void operator()()
457 {
458 this->resume();
459 }
460
461 static return_type on_resume(result_type&)
462 {
463 }
464};
465
466template <typename Executor, typename R>
467class spawn_handler<Executor, R(boost::system::error_code)>
468 : public spawn_handler_base<Executor>
469{
470public:
471 typedef void return_type;
472 typedef boost::system::error_code* result_type;
473
474 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
475 : spawn_handler_base<Executor>(yield),
476 result_(result)
477 {
478 }
479
480 void operator()(boost::system::error_code ec)
481 {
482 if (this->yield_.ec_)
483 {
484 *this->yield_.ec_ = ec;
485 result_ = 0;
486 }
487 else
488 result_ = &ec;
489 this->resume();
490 }
491
492 static return_type on_resume(result_type& result)
493 {
494 if (result)
495 throw_error(err: *result);
496 }
497
498private:
499 result_type& result_;
500};
501
502template <typename Executor, typename R>
503class spawn_handler<Executor, R(exception_ptr)>
504 : public spawn_handler_base<Executor>
505{
506public:
507 typedef void return_type;
508 typedef exception_ptr* result_type;
509
510 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
511 : spawn_handler_base<Executor>(yield),
512 result_(result)
513 {
514 }
515
516 void operator()(exception_ptr ex)
517 {
518 result_ = &ex;
519 this->resume();
520 }
521
522 static return_type on_resume(result_type& result)
523 {
524 if (*result)
525 rethrow_exception(*result);
526 }
527
528private:
529 result_type& result_;
530};
531
532template <typename Executor, typename R, typename T>
533class spawn_handler<Executor, R(T)>
534 : public spawn_handler_base<Executor>
535{
536public:
537 typedef T return_type;
538 typedef return_type* result_type;
539
540 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
541 : spawn_handler_base<Executor>(yield),
542 result_(result)
543 {
544 }
545
546 void operator()(T value)
547 {
548 result_ = &value;
549 this->resume();
550 }
551
552 static return_type on_resume(result_type& result)
553 {
554 return static_cast<return_type&&>(*result);
555 }
556
557private:
558 result_type& result_;
559};
560
561template <typename Executor, typename R, typename T>
562class spawn_handler<Executor, R(boost::system::error_code, T)>
563 : public spawn_handler_base<Executor>
564{
565public:
566 typedef T return_type;
567
568 struct result_type
569 {
570 boost::system::error_code* ec_;
571 return_type* value_;
572 };
573
574 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
575 : spawn_handler_base<Executor>(yield),
576 result_(result)
577 {
578 }
579
580 void operator()(boost::system::error_code ec, T value)
581 {
582 if (this->yield_.ec_)
583 {
584 *this->yield_.ec_ = ec;
585 result_.ec_ = 0;
586 }
587 else
588 result_.ec_ = &ec;
589 result_.value_ = &value;
590 this->resume();
591 }
592
593 static return_type on_resume(result_type& result)
594 {
595 if (result.ec_)
596 throw_error(*result.ec_);
597 return static_cast<return_type&&>(*result.value_);
598 }
599
600private:
601 result_type& result_;
602};
603
604template <typename Executor, typename R, typename T>
605class spawn_handler<Executor, R(exception_ptr, T)>
606 : public spawn_handler_base<Executor>
607{
608public:
609 typedef T return_type;
610
611 struct result_type
612 {
613 exception_ptr* ex_;
614 return_type* value_;
615 };
616
617 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
618 : spawn_handler_base<Executor>(yield),
619 result_(result)
620 {
621 }
622
623 void operator()(exception_ptr ex, T value)
624 {
625 result_.ex_ = &ex;
626 result_.value_ = &value;
627 this->resume();
628 }
629
630 static return_type on_resume(result_type& result)
631 {
632 if (*result.ex_)
633 rethrow_exception(*result.ex_);
634 return static_cast<return_type&&>(*result.value_);
635 }
636
637private:
638 result_type& result_;
639};
640
641template <typename Executor, typename R, typename... Ts>
642class spawn_handler<Executor, R(Ts...)>
643 : public spawn_handler_base<Executor>
644{
645public:
646 typedef std::tuple<Ts...> return_type;
647
648 typedef return_type* result_type;
649
650 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
651 : spawn_handler_base<Executor>(yield),
652 result_(result)
653 {
654 }
655
656 template <typename... Args>
657 void operator()(Args&&... args)
658 {
659 return_type value(static_cast<Args&&>(args)...);
660 result_ = &value;
661 this->resume();
662 }
663
664 static return_type on_resume(result_type& result)
665 {
666 return static_cast<return_type&&>(*result);
667 }
668
669private:
670 result_type& result_;
671};
672
673template <typename Executor, typename R, typename... Ts>
674class spawn_handler<Executor, R(boost::system::error_code, Ts...)>
675 : public spawn_handler_base<Executor>
676{
677public:
678 typedef std::tuple<Ts...> return_type;
679
680 struct result_type
681 {
682 boost::system::error_code* ec_;
683 return_type* value_;
684 };
685
686 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
687 : spawn_handler_base<Executor>(yield),
688 result_(result)
689 {
690 }
691
692 template <typename... Args>
693 void operator()(boost::system::error_code ec,
694 Args&&... args)
695 {
696 return_type value(static_cast<Args&&>(args)...);
697 if (this->yield_.ec_)
698 {
699 *this->yield_.ec_ = ec;
700 result_.ec_ = 0;
701 }
702 else
703 result_.ec_ = &ec;
704 result_.value_ = &value;
705 this->resume();
706 }
707
708 static return_type on_resume(result_type& result)
709 {
710 if (result.ec_)
711 throw_error(*result.ec_);
712 return static_cast<return_type&&>(*result.value_);
713 }
714
715private:
716 result_type& result_;
717};
718
719template <typename Executor, typename R, typename... Ts>
720class spawn_handler<Executor, R(exception_ptr, Ts...)>
721 : public spawn_handler_base<Executor>
722{
723public:
724 typedef std::tuple<Ts...> return_type;
725
726 struct result_type
727 {
728 exception_ptr* ex_;
729 return_type* value_;
730 };
731
732 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
733 : spawn_handler_base<Executor>(yield),
734 result_(result)
735 {
736 }
737
738 template <typename... Args>
739 void operator()(exception_ptr ex, Args&&... args)
740 {
741 return_type value(static_cast<Args&&>(args)...);
742 result_.ex_ = &ex;
743 result_.value_ = &value;
744 this->resume();
745 }
746
747 static return_type on_resume(result_type& result)
748 {
749 if (*result.ex_)
750 rethrow_exception(*result.ex_);
751 return static_cast<return_type&&>(*result.value_);
752 }
753
754private:
755 result_type& result_;
756};
757
758template <typename Executor, typename Signature>
759inline bool asio_handler_is_continuation(spawn_handler<Executor, Signature>*)
760{
761 return true;
762}
763
764} // namespace detail
765
766template <typename Executor, typename Signature>
767class async_result<basic_yield_context<Executor>, Signature>
768{
769public:
770 typedef typename detail::spawn_handler<Executor, Signature> handler_type;
771 typedef typename handler_type::return_type return_type;
772
773#if defined(BOOST_ASIO_HAS_VARIADIC_LAMBDA_CAPTURES)
774
775 template <typename Initiation, typename... InitArgs>
776 static return_type initiate(Initiation&& init,
777 const basic_yield_context<Executor>& yield,
778 InitArgs&&... init_args)
779 {
780 typename handler_type::result_type result
781 = typename handler_type::result_type();
782
783 yield.spawned_thread_->suspend_with(
784 [&]()
785 {
786 static_cast<Initiation&&>(init)(
787 handler_type(yield, result),
788 static_cast<InitArgs&&>(init_args)...);
789 });
790
791 return handler_type::on_resume(result);
792 }
793
794#else // defined(BOOST_ASIO_HAS_VARIADIC_LAMBDA_CAPTURES)
795
796 template <typename Initiation, typename... InitArgs>
797 struct suspend_with_helper
798 {
799 typename handler_type::result_type& result_;
800 Initiation&& init_;
801 const basic_yield_context<Executor>& yield_;
802 std::tuple<InitArgs&&...> init_args_;
803
804 template <std::size_t... I>
805 void do_invoke(detail::index_sequence<I...>)
806 {
807 static_cast<Initiation&&>(init_)(
808 handler_type(yield_, result_),
809 static_cast<InitArgs&&>(std::get<I>(init_args_))...);
810 }
811
812 void operator()()
813 {
814 this->do_invoke(detail::make_index_sequence<sizeof...(InitArgs)>());
815 }
816 };
817
818 template <typename Initiation, typename... InitArgs>
819 static return_type initiate(Initiation&& init,
820 const basic_yield_context<Executor>& yield,
821 InitArgs&&... init_args)
822 {
823 typename handler_type::result_type result
824 = typename handler_type::result_type();
825
826 yield.spawned_thread_->suspend_with(
827 suspend_with_helper<Initiation, InitArgs...>{
828 result, static_cast<Initiation&&>(init), yield,
829 std::tuple<InitArgs&&...>(
830 static_cast<InitArgs&&>(init_args)...)});
831
832 return handler_type::on_resume(result);
833 }
834
835#endif // defined(BOOST_ASIO_HAS_VARIADIC_LAMBDA_CAPTURES)
836};
837
838namespace detail {
839
840template <typename Executor, typename Function, typename Handler>
841class spawn_entry_point
842{
843public:
844 template <typename F, typename H>
845 spawn_entry_point(const Executor& ex,
846 F&& f, H&& h)
847 : executor_(ex),
848 function_(static_cast<F&&>(f)),
849 handler_(static_cast<H&&>(h)),
850 work_(handler_, executor_)
851 {
852 }
853
854 void operator()(spawned_thread_base* spawned_thread)
855 {
856 const basic_yield_context<Executor> yield(spawned_thread, executor_);
857 this->call(yield,
858 void_type<result_of_t<Function(basic_yield_context<Executor>)>>());
859 }
860
861private:
862 void call(const basic_yield_context<Executor>& yield, void_type<void>)
863 {
864#if !defined(BOOST_ASIO_NO_EXCEPTIONS)
865 try
866#endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
867 {
868 function_(yield);
869 if (!yield.spawned_thread_->has_context_switched())
870 (post)(yield);
871 detail::binder1<Handler, exception_ptr>
872 handler(handler_, exception_ptr());
873 work_.complete(handler, handler.handler_);
874 }
875#if !defined(BOOST_ASIO_NO_EXCEPTIONS)
876# if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
877 catch (const boost::context::detail::forced_unwind&)
878 {
879 throw;
880 }
881# endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
882# if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
883 catch (const boost::coroutines::detail::forced_unwind&)
884 {
885 throw;
886 }
887# endif // defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
888 catch (...)
889 {
890 exception_ptr ex = current_exception();
891 if (!yield.spawned_thread_->has_context_switched())
892 (post)(yield);
893 detail::binder1<Handler, exception_ptr> handler(handler_, ex);
894 work_.complete(handler, handler.handler_);
895 }
896#endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
897 }
898
899 template <typename T>
900 void call(const basic_yield_context<Executor>& yield, void_type<T>)
901 {
902#if !defined(BOOST_ASIO_NO_EXCEPTIONS)
903 try
904#endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
905 {
906 T result(function_(yield));
907 if (!yield.spawned_thread_->has_context_switched())
908 (post)(yield);
909 detail::binder2<Handler, exception_ptr, T>
910 handler(handler_, exception_ptr(), static_cast<T&&>(result));
911 work_.complete(handler, handler.handler_);
912 }
913#if !defined(BOOST_ASIO_NO_EXCEPTIONS)
914# if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
915 catch (const boost::context::detail::forced_unwind&)
916 {
917 throw;
918 }
919# endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
920# if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
921 catch (const boost::coroutines::detail::forced_unwind&)
922 {
923 throw;
924 }
925# endif // defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
926 catch (...)
927 {
928 exception_ptr ex = current_exception();
929 if (!yield.spawned_thread_->has_context_switched())
930 (post)(yield);
931 detail::binder2<Handler, exception_ptr, T> handler(handler_, ex, T());
932 work_.complete(handler, handler.handler_);
933 }
934#endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
935 }
936
937 Executor executor_;
938 Function function_;
939 Handler handler_;
940 handler_work<Handler, Executor> work_;
941};
942
943struct spawn_cancellation_signal_emitter
944{
945 cancellation_signal* signal_;
946 cancellation_type_t type_;
947
948 void operator()()
949 {
950 signal_->emit(type: type_);
951 }
952};
953
954template <typename Handler, typename Executor, typename = void>
955class spawn_cancellation_handler
956{
957public:
958 spawn_cancellation_handler(const Handler&, const Executor& ex)
959 : ex_(ex)
960 {
961 }
962
963 cancellation_slot slot()
964 {
965 return signal_.slot();
966 }
967
968 void operator()(cancellation_type_t type)
969 {
970 spawn_cancellation_signal_emitter emitter = { .signal_: &signal_, .type_: type };
971 (dispatch)(ex_, emitter);
972 }
973
974private:
975 cancellation_signal signal_;
976 Executor ex_;
977};
978
979
980template <typename Handler, typename Executor>
981class spawn_cancellation_handler<Handler, Executor,
982 enable_if_t<
983 is_same<
984 typename associated_executor<Handler,
985 Executor>::asio_associated_executor_is_unspecialised,
986 void
987 >::value
988 >>
989{
990public:
991 spawn_cancellation_handler(const Handler&, const Executor&)
992 {
993 }
994
995 cancellation_slot slot()
996 {
997 return signal_.slot();
998 }
999
1000 void operator()(cancellation_type_t type)
1001 {
1002 signal_.emit(type);
1003 }
1004
1005private:
1006 cancellation_signal signal_;
1007};
1008
1009template <typename Executor>
1010class initiate_spawn
1011{
1012public:
1013 typedef Executor executor_type;
1014
1015 explicit initiate_spawn(const executor_type& ex)
1016 : executor_(ex)
1017 {
1018 }
1019
1020 executor_type get_executor() const noexcept
1021 {
1022 return executor_;
1023 }
1024
1025 template <typename Handler, typename F>
1026 void operator()(Handler&& handler,
1027 F&& f) const
1028 {
1029 typedef decay_t<Handler> handler_type;
1030 typedef decay_t<F> function_type;
1031 typedef spawn_cancellation_handler<
1032 handler_type, Executor> cancel_handler_type;
1033
1034 associated_cancellation_slot_t<handler_type> slot
1035 = boost::asio::get_associated_cancellation_slot(handler);
1036
1037 cancel_handler_type* cancel_handler = slot.is_connected()
1038 ? &slot.template emplace<cancel_handler_type>(handler, executor_)
1039 : 0;
1040
1041 cancellation_slot proxy_slot(
1042 cancel_handler
1043 ? cancel_handler->slot()
1044 : cancellation_slot());
1045
1046 cancellation_state cancel_state(proxy_slot);
1047
1048 (dispatch)(executor_,
1049 spawned_thread_resumer(
1050 default_spawned_thread_type::spawn(
1051 spawn_entry_point<Executor, function_type, handler_type>(
1052 executor_, static_cast<F&&>(f),
1053 static_cast<Handler&&>(handler)),
1054 proxy_slot, cancel_state)));
1055 }
1056
1057#if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
1058
1059 template <typename Handler, typename StackAllocator, typename F>
1060 void operator()(Handler&& handler, allocator_arg_t,
1061 StackAllocator&& stack_allocator,
1062 F&& f) const
1063 {
1064 typedef decay_t<Handler> handler_type;
1065 typedef decay_t<F> function_type;
1066 typedef spawn_cancellation_handler<
1067 handler_type, Executor> cancel_handler_type;
1068
1069 associated_cancellation_slot_t<handler_type> slot
1070 = boost::asio::get_associated_cancellation_slot(handler);
1071
1072 cancel_handler_type* cancel_handler = slot.is_connected()
1073 ? &slot.template emplace<cancel_handler_type>(handler, executor_)
1074 : 0;
1075
1076 cancellation_slot proxy_slot(
1077 cancel_handler
1078 ? cancel_handler->slot()
1079 : cancellation_slot());
1080
1081 cancellation_state cancel_state(proxy_slot);
1082
1083 (dispatch)(executor_,
1084 spawned_thread_resumer(
1085 spawned_fiber_thread::spawn(allocator_arg_t(),
1086 static_cast<StackAllocator&&>(stack_allocator),
1087 spawn_entry_point<Executor, function_type, handler_type>(
1088 executor_, static_cast<F&&>(f),
1089 static_cast<Handler&&>(handler)),
1090 proxy_slot, cancel_state)));
1091 }
1092
1093#endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
1094
1095private:
1096 executor_type executor_;
1097};
1098
1099} // namespace detail
1100
1101template <typename Executor, typename F,
1102 BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
1103 result_of_t<F(basic_yield_context<Executor>)>>::type) CompletionToken>
1104inline auto spawn(const Executor& ex, F&& function, CompletionToken&& token,
1105#if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
1106 constraint_t<
1107 !is_same<
1108 decay_t<CompletionToken>,
1109 boost::coroutines::attributes
1110 >::value
1111 >,
1112#endif // defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
1113 constraint_t<
1114 is_executor<Executor>::value || execution::is_executor<Executor>::value
1115 >)
1116 -> decltype(
1117 async_initiate<CompletionToken,
1118 typename detail::spawn_signature<
1119 result_of_t<F(basic_yield_context<Executor>)>>::type>(
1120 declval<detail::initiate_spawn<Executor>>(),
1121 token, static_cast<F&&>(function)))
1122{
1123 return async_initiate<CompletionToken,
1124 typename detail::spawn_signature<
1125 result_of_t<F(basic_yield_context<Executor>)>>::type>(
1126 detail::initiate_spawn<Executor>(ex),
1127 token, static_cast<F&&>(function));
1128}
1129
1130template <typename ExecutionContext, typename F,
1131 BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
1132 result_of_t<F(basic_yield_context<
1133 typename ExecutionContext::executor_type>)>>::type) CompletionToken>
1134inline auto spawn(ExecutionContext& ctx, F&& function, CompletionToken&& token,
1135#if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
1136 constraint_t<
1137 !is_same<
1138 decay_t<CompletionToken>,
1139 boost::coroutines::attributes
1140 >::value
1141 >,
1142#endif // defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
1143 constraint_t<
1144 is_convertible<ExecutionContext&, execution_context&>::value
1145 >)
1146 -> decltype(
1147 async_initiate<CompletionToken,
1148 typename detail::spawn_signature<
1149 result_of_t<F(basic_yield_context<
1150 typename ExecutionContext::executor_type>)>>::type>(
1151 declval<detail::initiate_spawn<
1152 typename ExecutionContext::executor_type>>(),
1153 token, static_cast<F&&>(function)))
1154{
1155 return (spawn)(ctx.get_executor(), static_cast<F&&>(function),
1156 static_cast<CompletionToken&&>(token));
1157}
1158
1159template <typename Executor, typename F,
1160 BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
1161 result_of_t<F(basic_yield_context<Executor>)>>::type)
1162 CompletionToken>
1163inline auto spawn(const basic_yield_context<Executor>& ctx,
1164 F&& function, CompletionToken&& token,
1165#if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
1166 constraint_t<
1167 !is_same<
1168 decay_t<CompletionToken>,
1169 boost::coroutines::attributes
1170 >::value
1171 >,
1172#endif // defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
1173 constraint_t<
1174 is_executor<Executor>::value || execution::is_executor<Executor>::value
1175 >)
1176 -> decltype(
1177 async_initiate<CompletionToken,
1178 typename detail::spawn_signature<
1179 result_of_t<F(basic_yield_context<Executor>)>>::type>(
1180 declval<detail::initiate_spawn<Executor>>(),
1181 token, static_cast<F&&>(function)))
1182{
1183 return (spawn)(ctx.get_executor(), static_cast<F&&>(function),
1184 static_cast<CompletionToken&&>(token));
1185}
1186
1187#if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
1188
1189template <typename Executor, typename StackAllocator, typename F,
1190 BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
1191 result_of_t<F(basic_yield_context<Executor>)>>::type)
1192 CompletionToken>
1193inline auto spawn(const Executor& ex, allocator_arg_t,
1194 StackAllocator&& stack_allocator, F&& function, CompletionToken&& token,
1195 constraint_t<
1196 is_executor<Executor>::value || execution::is_executor<Executor>::value
1197 >)
1198 -> decltype(
1199 async_initiate<CompletionToken,
1200 typename detail::spawn_signature<
1201 result_of_t<F(basic_yield_context<Executor>)>>::type>(
1202 declval<detail::initiate_spawn<Executor>>(),
1203 token, allocator_arg_t(),
1204 static_cast<StackAllocator&&>(stack_allocator),
1205 static_cast<F&&>(function)))
1206{
1207 return async_initiate<CompletionToken,
1208 typename detail::spawn_signature<
1209 result_of_t<F(basic_yield_context<Executor>)>>::type>(
1210 detail::initiate_spawn<Executor>(ex), token, allocator_arg_t(),
1211 static_cast<StackAllocator&&>(stack_allocator),
1212 static_cast<F&&>(function));
1213}
1214
1215template <typename ExecutionContext, typename StackAllocator, typename F,
1216 BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
1217 result_of_t<F(basic_yield_context<
1218 typename ExecutionContext::executor_type>)>>::type) CompletionToken>
1219inline auto spawn(ExecutionContext& ctx, allocator_arg_t,
1220 StackAllocator&& stack_allocator, F&& function, CompletionToken&& token,
1221 constraint_t<
1222 is_convertible<ExecutionContext&, execution_context&>::value
1223 >)
1224 -> decltype(
1225 async_initiate<CompletionToken,
1226 typename detail::spawn_signature<
1227 result_of_t<F(basic_yield_context<
1228 typename ExecutionContext::executor_type>)>>::type>(
1229 declval<detail::initiate_spawn<
1230 typename ExecutionContext::executor_type>>(),
1231 token, allocator_arg_t(),
1232 static_cast<StackAllocator&&>(stack_allocator),
1233 static_cast<F&&>(function)))
1234{
1235 return (spawn)(ctx.get_executor(), allocator_arg_t(),
1236 static_cast<StackAllocator&&>(stack_allocator),
1237 static_cast<F&&>(function), static_cast<CompletionToken&&>(token));
1238}
1239
1240template <typename Executor, typename StackAllocator, typename F,
1241 BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
1242 result_of_t<F(basic_yield_context<Executor>)>>::type) CompletionToken>
1243inline auto spawn(const basic_yield_context<Executor>& ctx, allocator_arg_t,
1244 StackAllocator&& stack_allocator, F&& function, CompletionToken&& token,
1245 constraint_t<
1246 is_executor<Executor>::value || execution::is_executor<Executor>::value
1247 >)
1248 -> decltype(
1249 async_initiate<CompletionToken,
1250 typename detail::spawn_signature<
1251 result_of_t<F(basic_yield_context<Executor>)>>::type>(
1252 declval<detail::initiate_spawn<Executor>>(), token,
1253 allocator_arg_t(), static_cast<StackAllocator&&>(stack_allocator),
1254 static_cast<F&&>(function)))
1255{
1256 return (spawn)(ctx.get_executor(), allocator_arg_t(),
1257 static_cast<StackAllocator&&>(stack_allocator),
1258 static_cast<F&&>(function), static_cast<CompletionToken&&>(token));
1259}
1260
1261#endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
1262
1263#if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
1264
1265namespace detail {
1266
1267template <typename Executor, typename Function, typename Handler>
1268class old_spawn_entry_point
1269{
1270public:
1271 template <typename F, typename H>
1272 old_spawn_entry_point(const Executor& ex, F&& f, H&& h)
1273 : executor_(ex),
1274 function_(static_cast<F&&>(f)),
1275 handler_(static_cast<H&&>(h))
1276 {
1277 }
1278
1279 void operator()(spawned_thread_base* spawned_thread)
1280 {
1281 const basic_yield_context<Executor> yield(spawned_thread, executor_);
1282 this->call(yield,
1283 void_type<result_of_t<Function(basic_yield_context<Executor>)>>());
1284 }
1285
1286private:
1287 void call(const basic_yield_context<Executor>& yield, void_type<void>)
1288 {
1289 function_(yield);
1290 static_cast<Handler&&>(handler_)();
1291 }
1292
1293 template <typename T>
1294 void call(const basic_yield_context<Executor>& yield, void_type<T>)
1295 {
1296 static_cast<Handler&&>(handler_)(function_(yield));
1297 }
1298
1299 Executor executor_;
1300 Function function_;
1301 Handler handler_;
1302};
1303
1304inline void default_spawn_handler() {}
1305
1306} // namespace detail
1307
1308template <typename Function>
1309inline void spawn(Function&& function,
1310 const boost::coroutines::attributes& attributes)
1311{
1312 associated_executor_t<decay_t<Function>> ex(
1313 (get_associated_executor)(function));
1314
1315 boost::asio::spawn(ex, static_cast<Function&&>(function), attributes);
1316}
1317
1318template <typename Handler, typename Function>
1319void spawn(Handler&& handler, Function&& function,
1320 const boost::coroutines::attributes& attributes,
1321 constraint_t<
1322 !is_executor<decay_t<Handler>>::value &&
1323 !execution::is_executor<decay_t<Handler>>::value &&
1324 !is_convertible<Handler&, execution_context&>::value>)
1325{
1326 typedef associated_executor_t<decay_t<Handler>> executor_type;
1327 executor_type ex((get_associated_executor)(handler));
1328
1329 (dispatch)(ex,
1330 detail::spawned_thread_resumer(
1331 detail::spawned_coroutine_thread::spawn(
1332 detail::old_spawn_entry_point<executor_type,
1333 decay_t<Function>, void (*)()>(
1334 ex, static_cast<Function&&>(function),
1335 &detail::default_spawn_handler), attributes)));
1336}
1337
1338template <typename Executor, typename Function>
1339void spawn(basic_yield_context<Executor> ctx, Function&& function,
1340 const boost::coroutines::attributes& attributes)
1341{
1342 (dispatch)(ctx.get_executor(),
1343 detail::spawned_thread_resumer(
1344 detail::spawned_coroutine_thread::spawn(
1345 detail::old_spawn_entry_point<Executor,
1346 decay_t<Function>, void (*)()>(
1347 ctx.get_executor(), static_cast<Function&&>(function),
1348 &detail::default_spawn_handler), attributes)));
1349}
1350
1351template <typename Function, typename Executor>
1352inline void spawn(const Executor& ex, Function&& function,
1353 const boost::coroutines::attributes& attributes,
1354 constraint_t<
1355 is_executor<Executor>::value || execution::is_executor<Executor>::value
1356 >)
1357{
1358 boost::asio::spawn(boost::asio::strand<Executor>(ex),
1359 static_cast<Function&&>(function), attributes);
1360}
1361
1362template <typename Function, typename Executor>
1363inline void spawn(const strand<Executor>& ex, Function&& function,
1364 const boost::coroutines::attributes& attributes)
1365{
1366 boost::asio::spawn(boost::asio::bind_executor(
1367 ex, &detail::default_spawn_handler),
1368 static_cast<Function&&>(function), attributes);
1369}
1370
1371#if !defined(BOOST_ASIO_NO_TS_EXECUTORS)
1372
1373template <typename Function>
1374inline void spawn(const boost::asio::io_context::strand& s, Function&& function,
1375 const boost::coroutines::attributes& attributes)
1376{
1377 boost::asio::spawn(boost::asio::bind_executor(
1378 ex: s, t: &detail::default_spawn_handler),
1379 static_cast<Function&&>(function), attributes);
1380}
1381
1382#endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS)
1383
1384template <typename Function, typename ExecutionContext>
1385inline void spawn(ExecutionContext& ctx, Function&& function,
1386 const boost::coroutines::attributes& attributes,
1387 constraint_t<
1388 is_convertible<ExecutionContext&, execution_context&>::value
1389 >)
1390{
1391 boost::asio::spawn(ctx.get_executor(),
1392 static_cast<Function&&>(function), attributes);
1393}
1394
1395#endif // defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
1396
1397} // namespace asio
1398} // namespace boost
1399
1400#include <boost/asio/detail/pop_options.hpp>
1401
1402#endif // BOOST_ASIO_IMPL_SPAWN_HPP
1403

source code of boost/libs/asio/include/boost/asio/impl/spawn.hpp