1401 lines
37 KiB
C++
1401 lines
37 KiB
C++
|
//
|
||
|
// impl/spawn.hpp
|
||
|
// ~~~~~~~~~~~~~~
|
||
|
//
|
||
|
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||
|
//
|
||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||
|
//
|
||
|
|
||
|
#ifndef ASIO_IMPL_SPAWN_HPP
|
||
|
#define ASIO_IMPL_SPAWN_HPP
|
||
|
|
||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||
|
# pragma once
|
||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||
|
|
||
|
#include "asio/detail/config.hpp"
|
||
|
#include <tuple>
|
||
|
#include "asio/associated_allocator.hpp"
|
||
|
#include "asio/associated_cancellation_slot.hpp"
|
||
|
#include "asio/associated_executor.hpp"
|
||
|
#include "asio/async_result.hpp"
|
||
|
#include "asio/bind_executor.hpp"
|
||
|
#include "asio/detail/atomic_count.hpp"
|
||
|
#include "asio/detail/bind_handler.hpp"
|
||
|
#include "asio/detail/handler_cont_helpers.hpp"
|
||
|
#include "asio/detail/memory.hpp"
|
||
|
#include "asio/detail/noncopyable.hpp"
|
||
|
#include "asio/detail/type_traits.hpp"
|
||
|
#include "asio/detail/utility.hpp"
|
||
|
#include "asio/system_error.hpp"
|
||
|
|
||
|
#if defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
|
||
|
# include <boost/context/fiber.hpp>
|
||
|
#endif // defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
|
||
|
|
||
|
#include "asio/detail/push_options.hpp"
|
||
|
|
||
|
namespace asio {
|
||
|
namespace detail {
|
||
|
|
||
|
#if !defined(ASIO_NO_EXCEPTIONS)
|
||
|
inline void spawned_thread_rethrow(void* ex)
|
||
|
{
|
||
|
if (*static_cast<exception_ptr*>(ex))
|
||
|
rethrow_exception(*static_cast<exception_ptr*>(ex));
|
||
|
}
|
||
|
#endif // !defined(ASIO_NO_EXCEPTIONS)
|
||
|
|
||
|
#if defined(ASIO_HAS_BOOST_COROUTINE)
|
||
|
|
||
|
// Spawned thread implementation using Boost.Coroutine.
|
||
|
class spawned_coroutine_thread : public spawned_thread_base
|
||
|
{
|
||
|
public:
|
||
|
#if defined(BOOST_COROUTINES_UNIDIRECT) || defined(BOOST_COROUTINES_V2)
|
||
|
typedef boost::coroutines::pull_coroutine<void> callee_type;
|
||
|
typedef boost::coroutines::push_coroutine<void> caller_type;
|
||
|
#else
|
||
|
typedef boost::coroutines::coroutine<void()> callee_type;
|
||
|
typedef boost::coroutines::coroutine<void()> caller_type;
|
||
|
#endif
|
||
|
|
||
|
spawned_coroutine_thread(caller_type& caller)
|
||
|
: caller_(caller),
|
||
|
on_suspend_fn_(0),
|
||
|
on_suspend_arg_(0)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
template <typename F>
|
||
|
static spawned_thread_base* spawn(F&& f,
|
||
|
const boost::coroutines::attributes& attributes,
|
||
|
cancellation_slot parent_cancel_slot = cancellation_slot(),
|
||
|
cancellation_state cancel_state = cancellation_state())
|
||
|
{
|
||
|
spawned_coroutine_thread* spawned_thread = 0;
|
||
|
callee_type callee(entry_point<decay_t<F>>(
|
||
|
static_cast<F&&>(f), &spawned_thread), attributes);
|
||
|
spawned_thread->callee_.swap(callee);
|
||
|
spawned_thread->parent_cancellation_slot_ = parent_cancel_slot;
|
||
|
spawned_thread->cancellation_state_ = cancel_state;
|
||
|
return spawned_thread;
|
||
|
}
|
||
|
|
||
|
template <typename F>
|
||
|
static spawned_thread_base* spawn(F&& f,
|
||
|
cancellation_slot parent_cancel_slot = cancellation_slot(),
|
||
|
cancellation_state cancel_state = cancellation_state())
|
||
|
{
|
||
|
return spawn(static_cast<F&&>(f), boost::coroutines::attributes(),
|
||
|
parent_cancel_slot, cancel_state);
|
||
|
}
|
||
|
|
||
|
void resume()
|
||
|
{
|
||
|
callee_();
|
||
|
if (on_suspend_fn_)
|
||
|
{
|
||
|
void (*fn)(void*) = on_suspend_fn_;
|
||
|
void* arg = on_suspend_arg_;
|
||
|
on_suspend_fn_ = 0;
|
||
|
fn(arg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void suspend_with(void (*fn)(void*), void* arg)
|
||
|
{
|
||
|
if (throw_if_cancelled_)
|
||
|
if (!!cancellation_state_.cancelled())
|
||
|
throw_error(asio::error::operation_aborted, "yield");
|
||
|
has_context_switched_ = true;
|
||
|
on_suspend_fn_ = fn;
|
||
|
on_suspend_arg_ = arg;
|
||
|
caller_();
|
||
|
}
|
||
|
|
||
|
void destroy()
|
||
|
{
|
||
|
callee_type callee;
|
||
|
callee.swap(callee_);
|
||
|
if (terminal_)
|
||
|
callee();
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
template <typename Function>
|
||
|
class entry_point
|
||
|
{
|
||
|
public:
|
||
|
template <typename F>
|
||
|
entry_point(F&& f,
|
||
|
spawned_coroutine_thread** spawned_thread_out)
|
||
|
: function_(static_cast<F&&>(f)),
|
||
|
spawned_thread_out_(spawned_thread_out)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void operator()(caller_type& caller)
|
||
|
{
|
||
|
Function function(static_cast<Function&&>(function_));
|
||
|
spawned_coroutine_thread spawned_thread(caller);
|
||
|
*spawned_thread_out_ = &spawned_thread;
|
||
|
spawned_thread_out_ = 0;
|
||
|
spawned_thread.suspend();
|
||
|
#if !defined(ASIO_NO_EXCEPTIONS)
|
||
|
try
|
||
|
#endif // !defined(ASIO_NO_EXCEPTIONS)
|
||
|
{
|
||
|
function(&spawned_thread);
|
||
|
spawned_thread.terminal_ = true;
|
||
|
spawned_thread.suspend();
|
||
|
}
|
||
|
#if !defined(ASIO_NO_EXCEPTIONS)
|
||
|
catch (const boost::coroutines::detail::forced_unwind&)
|
||
|
{
|
||
|
throw;
|
||
|
}
|
||
|
catch (...)
|
||
|
{
|
||
|
exception_ptr ex = current_exception();
|
||
|
spawned_thread.terminal_ = true;
|
||
|
spawned_thread.suspend_with(spawned_thread_rethrow, &ex);
|
||
|
}
|
||
|
#endif // !defined(ASIO_NO_EXCEPTIONS)
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
Function function_;
|
||
|
spawned_coroutine_thread** spawned_thread_out_;
|
||
|
};
|
||
|
|
||
|
caller_type& caller_;
|
||
|
callee_type callee_;
|
||
|
void (*on_suspend_fn_)(void*);
|
||
|
void* on_suspend_arg_;
|
||
|
};
|
||
|
|
||
|
#endif // defined(ASIO_HAS_BOOST_COROUTINE)
|
||
|
|
||
|
#if defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
|
||
|
|
||
|
// Spawned thread implementation using Boost.Context's fiber.
|
||
|
class spawned_fiber_thread : public spawned_thread_base
|
||
|
{
|
||
|
public:
|
||
|
typedef boost::context::fiber fiber_type;
|
||
|
|
||
|
spawned_fiber_thread(fiber_type&& caller)
|
||
|
: caller_(static_cast<fiber_type&&>(caller)),
|
||
|
on_suspend_fn_(0),
|
||
|
on_suspend_arg_(0)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
template <typename StackAllocator, typename F>
|
||
|
static spawned_thread_base* spawn(allocator_arg_t,
|
||
|
StackAllocator&& stack_allocator,
|
||
|
F&& f,
|
||
|
cancellation_slot parent_cancel_slot = cancellation_slot(),
|
||
|
cancellation_state cancel_state = cancellation_state())
|
||
|
{
|
||
|
spawned_fiber_thread* spawned_thread = 0;
|
||
|
fiber_type callee(allocator_arg_t(),
|
||
|
static_cast<StackAllocator&&>(stack_allocator),
|
||
|
entry_point<decay_t<F>>(
|
||
|
static_cast<F&&>(f), &spawned_thread));
|
||
|
callee = fiber_type(static_cast<fiber_type&&>(callee)).resume();
|
||
|
spawned_thread->callee_ = static_cast<fiber_type&&>(callee);
|
||
|
spawned_thread->parent_cancellation_slot_ = parent_cancel_slot;
|
||
|
spawned_thread->cancellation_state_ = cancel_state;
|
||
|
return spawned_thread;
|
||
|
}
|
||
|
|
||
|
template <typename F>
|
||
|
static spawned_thread_base* spawn(F&& f,
|
||
|
cancellation_slot parent_cancel_slot = cancellation_slot(),
|
||
|
cancellation_state cancel_state = cancellation_state())
|
||
|
{
|
||
|
return spawn(allocator_arg_t(), boost::context::fixedsize_stack(),
|
||
|
static_cast<F&&>(f), parent_cancel_slot, cancel_state);
|
||
|
}
|
||
|
|
||
|
void resume()
|
||
|
{
|
||
|
callee_ = fiber_type(static_cast<fiber_type&&>(callee_)).resume();
|
||
|
if (on_suspend_fn_)
|
||
|
{
|
||
|
void (*fn)(void*) = on_suspend_fn_;
|
||
|
void* arg = on_suspend_arg_;
|
||
|
on_suspend_fn_ = 0;
|
||
|
fn(arg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void suspend_with(void (*fn)(void*), void* arg)
|
||
|
{
|
||
|
if (throw_if_cancelled_)
|
||
|
if (!!cancellation_state_.cancelled())
|
||
|
throw_error(asio::error::operation_aborted, "yield");
|
||
|
has_context_switched_ = true;
|
||
|
on_suspend_fn_ = fn;
|
||
|
on_suspend_arg_ = arg;
|
||
|
caller_ = fiber_type(static_cast<fiber_type&&>(caller_)).resume();
|
||
|
}
|
||
|
|
||
|
void destroy()
|
||
|
{
|
||
|
fiber_type callee = static_cast<fiber_type&&>(callee_);
|
||
|
if (terminal_)
|
||
|
fiber_type(static_cast<fiber_type&&>(callee)).resume();
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
template <typename Function>
|
||
|
class entry_point
|
||
|
{
|
||
|
public:
|
||
|
template <typename F>
|
||
|
entry_point(F&& f,
|
||
|
spawned_fiber_thread** spawned_thread_out)
|
||
|
: function_(static_cast<F&&>(f)),
|
||
|
spawned_thread_out_(spawned_thread_out)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
fiber_type operator()(fiber_type&& caller)
|
||
|
{
|
||
|
Function function(static_cast<Function&&>(function_));
|
||
|
spawned_fiber_thread spawned_thread(
|
||
|
static_cast<fiber_type&&>(caller));
|
||
|
*spawned_thread_out_ = &spawned_thread;
|
||
|
spawned_thread_out_ = 0;
|
||
|
spawned_thread.suspend();
|
||
|
#if !defined(ASIO_NO_EXCEPTIONS)
|
||
|
try
|
||
|
#endif // !defined(ASIO_NO_EXCEPTIONS)
|
||
|
{
|
||
|
function(&spawned_thread);
|
||
|
spawned_thread.terminal_ = true;
|
||
|
spawned_thread.suspend();
|
||
|
}
|
||
|
#if !defined(ASIO_NO_EXCEPTIONS)
|
||
|
catch (const boost::context::detail::forced_unwind&)
|
||
|
{
|
||
|
throw;
|
||
|
}
|
||
|
catch (...)
|
||
|
{
|
||
|
exception_ptr ex = current_exception();
|
||
|
spawned_thread.terminal_ = true;
|
||
|
spawned_thread.suspend_with(spawned_thread_rethrow, &ex);
|
||
|
}
|
||
|
#endif // !defined(ASIO_NO_EXCEPTIONS)
|
||
|
return static_cast<fiber_type&&>(spawned_thread.caller_);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
Function function_;
|
||
|
spawned_fiber_thread** spawned_thread_out_;
|
||
|
};
|
||
|
|
||
|
fiber_type caller_;
|
||
|
fiber_type callee_;
|
||
|
void (*on_suspend_fn_)(void*);
|
||
|
void* on_suspend_arg_;
|
||
|
};
|
||
|
|
||
|
#endif // defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
|
||
|
|
||
|
#if defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
|
||
|
typedef spawned_fiber_thread default_spawned_thread_type;
|
||
|
#elif defined(ASIO_HAS_BOOST_COROUTINE)
|
||
|
typedef spawned_coroutine_thread default_spawned_thread_type;
|
||
|
#else
|
||
|
# error No spawn() implementation available
|
||
|
#endif
|
||
|
|
||
|
// Helper class to perform the initial resume on the correct executor.
|
||
|
class spawned_thread_resumer
|
||
|
{
|
||
|
public:
|
||
|
explicit spawned_thread_resumer(spawned_thread_base* spawned_thread)
|
||
|
: spawned_thread_(spawned_thread)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
spawned_thread_resumer(spawned_thread_resumer&& other) noexcept
|
||
|
: spawned_thread_(other.spawned_thread_)
|
||
|
{
|
||
|
other.spawned_thread_ = 0;
|
||
|
}
|
||
|
|
||
|
~spawned_thread_resumer()
|
||
|
{
|
||
|
if (spawned_thread_)
|
||
|
spawned_thread_->destroy();
|
||
|
}
|
||
|
|
||
|
void operator()()
|
||
|
{
|
||
|
spawned_thread_->attach(&spawned_thread_);
|
||
|
spawned_thread_->resume();
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
spawned_thread_base* spawned_thread_;
|
||
|
};
|
||
|
|
||
|
// Helper class to ensure spawned threads are destroyed on the correct executor.
|
||
|
class spawned_thread_destroyer
|
||
|
{
|
||
|
public:
|
||
|
explicit spawned_thread_destroyer(spawned_thread_base* spawned_thread)
|
||
|
: spawned_thread_(spawned_thread)
|
||
|
{
|
||
|
spawned_thread->detach();
|
||
|
}
|
||
|
|
||
|
spawned_thread_destroyer(spawned_thread_destroyer&& other) noexcept
|
||
|
: spawned_thread_(other.spawned_thread_)
|
||
|
{
|
||
|
other.spawned_thread_ = 0;
|
||
|
}
|
||
|
|
||
|
~spawned_thread_destroyer()
|
||
|
{
|
||
|
if (spawned_thread_)
|
||
|
spawned_thread_->destroy();
|
||
|
}
|
||
|
|
||
|
void operator()()
|
||
|
{
|
||
|
if (spawned_thread_)
|
||
|
{
|
||
|
spawned_thread_->destroy();
|
||
|
spawned_thread_ = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
spawned_thread_base* spawned_thread_;
|
||
|
};
|
||
|
|
||
|
// Base class for all completion handlers associated with a spawned thread.
|
||
|
template <typename Executor>
|
||
|
class spawn_handler_base
|
||
|
{
|
||
|
public:
|
||
|
typedef Executor executor_type;
|
||
|
typedef cancellation_slot cancellation_slot_type;
|
||
|
|
||
|
spawn_handler_base(const basic_yield_context<Executor>& yield)
|
||
|
: yield_(yield),
|
||
|
spawned_thread_(yield.spawned_thread_)
|
||
|
{
|
||
|
spawned_thread_->detach();
|
||
|
}
|
||
|
|
||
|
spawn_handler_base(spawn_handler_base&& other) noexcept
|
||
|
: yield_(other.yield_),
|
||
|
spawned_thread_(other.spawned_thread_)
|
||
|
|
||
|
{
|
||
|
other.spawned_thread_ = 0;
|
||
|
}
|
||
|
|
||
|
~spawn_handler_base()
|
||
|
{
|
||
|
if (spawned_thread_)
|
||
|
(post)(yield_.executor_, spawned_thread_destroyer(spawned_thread_));
|
||
|
}
|
||
|
|
||
|
executor_type get_executor() const noexcept
|
||
|
{
|
||
|
return yield_.executor_;
|
||
|
}
|
||
|
|
||
|
cancellation_slot_type get_cancellation_slot() const noexcept
|
||
|
{
|
||
|
return spawned_thread_->get_cancellation_slot();
|
||
|
}
|
||
|
|
||
|
void resume()
|
||
|
{
|
||
|
spawned_thread_resumer resumer(spawned_thread_);
|
||
|
spawned_thread_ = 0;
|
||
|
resumer();
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
const basic_yield_context<Executor>& yield_;
|
||
|
spawned_thread_base* spawned_thread_;
|
||
|
};
|
||
|
|
||
|
// Completion handlers for when basic_yield_context is used as a token.
|
||
|
template <typename Executor, typename Signature>
|
||
|
class spawn_handler;
|
||
|
|
||
|
template <typename Executor, typename R>
|
||
|
class spawn_handler<Executor, R()>
|
||
|
: public spawn_handler_base<Executor>
|
||
|
{
|
||
|
public:
|
||
|
typedef void return_type;
|
||
|
|
||
|
struct result_type {};
|
||
|
|
||
|
spawn_handler(const basic_yield_context<Executor>& yield, result_type&)
|
||
|
: spawn_handler_base<Executor>(yield)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void operator()()
|
||
|
{
|
||
|
this->resume();
|
||
|
}
|
||
|
|
||
|
static return_type on_resume(result_type&)
|
||
|
{
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <typename Executor, typename R>
|
||
|
class spawn_handler<Executor, R(asio::error_code)>
|
||
|
: public spawn_handler_base<Executor>
|
||
|
{
|
||
|
public:
|
||
|
typedef void return_type;
|
||
|
typedef asio::error_code* result_type;
|
||
|
|
||
|
spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
|
||
|
: spawn_handler_base<Executor>(yield),
|
||
|
result_(result)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void operator()(asio::error_code ec)
|
||
|
{
|
||
|
if (this->yield_.ec_)
|
||
|
{
|
||
|
*this->yield_.ec_ = ec;
|
||
|
result_ = 0;
|
||
|
}
|
||
|
else
|
||
|
result_ = &ec;
|
||
|
this->resume();
|
||
|
}
|
||
|
|
||
|
static return_type on_resume(result_type& result)
|
||
|
{
|
||
|
if (result)
|
||
|
throw_error(*result);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
result_type& result_;
|
||
|
};
|
||
|
|
||
|
template <typename Executor, typename R>
|
||
|
class spawn_handler<Executor, R(exception_ptr)>
|
||
|
: public spawn_handler_base<Executor>
|
||
|
{
|
||
|
public:
|
||
|
typedef void return_type;
|
||
|
typedef exception_ptr* result_type;
|
||
|
|
||
|
spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
|
||
|
: spawn_handler_base<Executor>(yield),
|
||
|
result_(result)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void operator()(exception_ptr ex)
|
||
|
{
|
||
|
result_ = &ex;
|
||
|
this->resume();
|
||
|
}
|
||
|
|
||
|
static return_type on_resume(result_type& result)
|
||
|
{
|
||
|
if (*result)
|
||
|
rethrow_exception(*result);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
result_type& result_;
|
||
|
};
|
||
|
|
||
|
template <typename Executor, typename R, typename T>
|
||
|
class spawn_handler<Executor, R(T)>
|
||
|
: public spawn_handler_base<Executor>
|
||
|
{
|
||
|
public:
|
||
|
typedef T return_type;
|
||
|
typedef return_type* result_type;
|
||
|
|
||
|
spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
|
||
|
: spawn_handler_base<Executor>(yield),
|
||
|
result_(result)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void operator()(T value)
|
||
|
{
|
||
|
result_ = &value;
|
||
|
this->resume();
|
||
|
}
|
||
|
|
||
|
static return_type on_resume(result_type& result)
|
||
|
{
|
||
|
return static_cast<return_type&&>(*result);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
result_type& result_;
|
||
|
};
|
||
|
|
||
|
template <typename Executor, typename R, typename T>
|
||
|
class spawn_handler<Executor, R(asio::error_code, T)>
|
||
|
: public spawn_handler_base<Executor>
|
||
|
{
|
||
|
public:
|
||
|
typedef T return_type;
|
||
|
|
||
|
struct result_type
|
||
|
{
|
||
|
asio::error_code* ec_;
|
||
|
return_type* value_;
|
||
|
};
|
||
|
|
||
|
spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
|
||
|
: spawn_handler_base<Executor>(yield),
|
||
|
result_(result)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void operator()(asio::error_code ec, T value)
|
||
|
{
|
||
|
if (this->yield_.ec_)
|
||
|
{
|
||
|
*this->yield_.ec_ = ec;
|
||
|
result_.ec_ = 0;
|
||
|
}
|
||
|
else
|
||
|
result_.ec_ = &ec;
|
||
|
result_.value_ = &value;
|
||
|
this->resume();
|
||
|
}
|
||
|
|
||
|
static return_type on_resume(result_type& result)
|
||
|
{
|
||
|
if (result.ec_)
|
||
|
throw_error(*result.ec_);
|
||
|
return static_cast<return_type&&>(*result.value_);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
result_type& result_;
|
||
|
};
|
||
|
|
||
|
template <typename Executor, typename R, typename T>
|
||
|
class spawn_handler<Executor, R(exception_ptr, T)>
|
||
|
: public spawn_handler_base<Executor>
|
||
|
{
|
||
|
public:
|
||
|
typedef T return_type;
|
||
|
|
||
|
struct result_type
|
||
|
{
|
||
|
exception_ptr* ex_;
|
||
|
return_type* value_;
|
||
|
};
|
||
|
|
||
|
spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
|
||
|
: spawn_handler_base<Executor>(yield),
|
||
|
result_(result)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void operator()(exception_ptr ex, T value)
|
||
|
{
|
||
|
result_.ex_ = &ex;
|
||
|
result_.value_ = &value;
|
||
|
this->resume();
|
||
|
}
|
||
|
|
||
|
static return_type on_resume(result_type& result)
|
||
|
{
|
||
|
if (*result.ex_)
|
||
|
rethrow_exception(*result.ex_);
|
||
|
return static_cast<return_type&&>(*result.value_);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
result_type& result_;
|
||
|
};
|
||
|
|
||
|
template <typename Executor, typename R, typename... Ts>
|
||
|
class spawn_handler<Executor, R(Ts...)>
|
||
|
: public spawn_handler_base<Executor>
|
||
|
{
|
||
|
public:
|
||
|
typedef std::tuple<Ts...> return_type;
|
||
|
|
||
|
typedef return_type* result_type;
|
||
|
|
||
|
spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
|
||
|
: spawn_handler_base<Executor>(yield),
|
||
|
result_(result)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
template <typename... Args>
|
||
|
void operator()(Args&&... args)
|
||
|
{
|
||
|
return_type value(static_cast<Args&&>(args)...);
|
||
|
result_ = &value;
|
||
|
this->resume();
|
||
|
}
|
||
|
|
||
|
static return_type on_resume(result_type& result)
|
||
|
{
|
||
|
return static_cast<return_type&&>(*result);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
result_type& result_;
|
||
|
};
|
||
|
|
||
|
template <typename Executor, typename R, typename... Ts>
|
||
|
class spawn_handler<Executor, R(asio::error_code, Ts...)>
|
||
|
: public spawn_handler_base<Executor>
|
||
|
{
|
||
|
public:
|
||
|
typedef std::tuple<Ts...> return_type;
|
||
|
|
||
|
struct result_type
|
||
|
{
|
||
|
asio::error_code* ec_;
|
||
|
return_type* value_;
|
||
|
};
|
||
|
|
||
|
spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
|
||
|
: spawn_handler_base<Executor>(yield),
|
||
|
result_(result)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
template <typename... Args>
|
||
|
void operator()(asio::error_code ec,
|
||
|
Args&&... args)
|
||
|
{
|
||
|
return_type value(static_cast<Args&&>(args)...);
|
||
|
if (this->yield_.ec_)
|
||
|
{
|
||
|
*this->yield_.ec_ = ec;
|
||
|
result_.ec_ = 0;
|
||
|
}
|
||
|
else
|
||
|
result_.ec_ = &ec;
|
||
|
result_.value_ = &value;
|
||
|
this->resume();
|
||
|
}
|
||
|
|
||
|
static return_type on_resume(result_type& result)
|
||
|
{
|
||
|
if (result.ec_)
|
||
|
throw_error(*result.ec_);
|
||
|
return static_cast<return_type&&>(*result.value_);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
result_type& result_;
|
||
|
};
|
||
|
|
||
|
template <typename Executor, typename R, typename... Ts>
|
||
|
class spawn_handler<Executor, R(exception_ptr, Ts...)>
|
||
|
: public spawn_handler_base<Executor>
|
||
|
{
|
||
|
public:
|
||
|
typedef std::tuple<Ts...> return_type;
|
||
|
|
||
|
struct result_type
|
||
|
{
|
||
|
exception_ptr* ex_;
|
||
|
return_type* value_;
|
||
|
};
|
||
|
|
||
|
spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
|
||
|
: spawn_handler_base<Executor>(yield),
|
||
|
result_(result)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
template <typename... Args>
|
||
|
void operator()(exception_ptr ex, Args&&... args)
|
||
|
{
|
||
|
return_type value(static_cast<Args&&>(args)...);
|
||
|
result_.ex_ = &ex;
|
||
|
result_.value_ = &value;
|
||
|
this->resume();
|
||
|
}
|
||
|
|
||
|
static return_type on_resume(result_type& result)
|
||
|
{
|
||
|
if (*result.ex_)
|
||
|
rethrow_exception(*result.ex_);
|
||
|
return static_cast<return_type&&>(*result.value_);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
result_type& result_;
|
||
|
};
|
||
|
|
||
|
template <typename Executor, typename Signature>
|
||
|
inline bool asio_handler_is_continuation(spawn_handler<Executor, Signature>*)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
} // namespace detail
|
||
|
|
||
|
template <typename Executor, typename Signature>
|
||
|
class async_result<basic_yield_context<Executor>, Signature>
|
||
|
{
|
||
|
public:
|
||
|
typedef typename detail::spawn_handler<Executor, Signature> handler_type;
|
||
|
typedef typename handler_type::return_type return_type;
|
||
|
|
||
|
#if defined(ASIO_HAS_VARIADIC_LAMBDA_CAPTURES)
|
||
|
|
||
|
template <typename Initiation, typename... InitArgs>
|
||
|
static return_type initiate(Initiation&& init,
|
||
|
const basic_yield_context<Executor>& yield,
|
||
|
InitArgs&&... init_args)
|
||
|
{
|
||
|
typename handler_type::result_type result
|
||
|
= typename handler_type::result_type();
|
||
|
|
||
|
yield.spawned_thread_->suspend_with(
|
||
|
[&]()
|
||
|
{
|
||
|
static_cast<Initiation&&>(init)(
|
||
|
handler_type(yield, result),
|
||
|
static_cast<InitArgs&&>(init_args)...);
|
||
|
});
|
||
|
|
||
|
return handler_type::on_resume(result);
|
||
|
}
|
||
|
|
||
|
#else // defined(ASIO_HAS_VARIADIC_LAMBDA_CAPTURES)
|
||
|
|
||
|
template <typename Initiation, typename... InitArgs>
|
||
|
struct suspend_with_helper
|
||
|
{
|
||
|
typename handler_type::result_type& result_;
|
||
|
Initiation&& init_;
|
||
|
const basic_yield_context<Executor>& yield_;
|
||
|
std::tuple<InitArgs&&...> init_args_;
|
||
|
|
||
|
template <std::size_t... I>
|
||
|
void do_invoke(detail::index_sequence<I...>)
|
||
|
{
|
||
|
static_cast<Initiation&&>(init_)(
|
||
|
handler_type(yield_, result_),
|
||
|
static_cast<InitArgs&&>(std::get<I>(init_args_))...);
|
||
|
}
|
||
|
|
||
|
void operator()()
|
||
|
{
|
||
|
this->do_invoke(detail::make_index_sequence<sizeof...(InitArgs)>());
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <typename Initiation, typename... InitArgs>
|
||
|
static return_type initiate(Initiation&& init,
|
||
|
const basic_yield_context<Executor>& yield,
|
||
|
InitArgs&&... init_args)
|
||
|
{
|
||
|
typename handler_type::result_type result
|
||
|
= typename handler_type::result_type();
|
||
|
|
||
|
yield.spawned_thread_->suspend_with(
|
||
|
suspend_with_helper<Initiation, InitArgs...>{
|
||
|
result, static_cast<Initiation&&>(init), yield,
|
||
|
std::tuple<InitArgs&&...>(
|
||
|
static_cast<InitArgs&&>(init_args)...)});
|
||
|
|
||
|
return handler_type::on_resume(result);
|
||
|
}
|
||
|
|
||
|
#endif // defined(ASIO_HAS_VARIADIC_LAMBDA_CAPTURES)
|
||
|
};
|
||
|
|
||
|
namespace detail {
|
||
|
|
||
|
template <typename Executor, typename Function, typename Handler>
|
||
|
class spawn_entry_point
|
||
|
{
|
||
|
public:
|
||
|
template <typename F, typename H>
|
||
|
spawn_entry_point(const Executor& ex,
|
||
|
F&& f, H&& h)
|
||
|
: executor_(ex),
|
||
|
function_(static_cast<F&&>(f)),
|
||
|
handler_(static_cast<H&&>(h)),
|
||
|
work_(handler_, executor_)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void operator()(spawned_thread_base* spawned_thread)
|
||
|
{
|
||
|
const basic_yield_context<Executor> yield(spawned_thread, executor_);
|
||
|
this->call(yield,
|
||
|
void_type<result_of_t<Function(basic_yield_context<Executor>)>>());
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
void call(const basic_yield_context<Executor>& yield, void_type<void>)
|
||
|
{
|
||
|
#if !defined(ASIO_NO_EXCEPTIONS)
|
||
|
try
|
||
|
#endif // !defined(ASIO_NO_EXCEPTIONS)
|
||
|
{
|
||
|
function_(yield);
|
||
|
if (!yield.spawned_thread_->has_context_switched())
|
||
|
(post)(yield);
|
||
|
detail::binder1<Handler, exception_ptr>
|
||
|
handler(handler_, exception_ptr());
|
||
|
work_.complete(handler, handler.handler_);
|
||
|
}
|
||
|
#if !defined(ASIO_NO_EXCEPTIONS)
|
||
|
# if defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
|
||
|
catch (const boost::context::detail::forced_unwind&)
|
||
|
{
|
||
|
throw;
|
||
|
}
|
||
|
# endif // defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
|
||
|
# if defined(ASIO_HAS_BOOST_COROUTINE)
|
||
|
catch (const boost::coroutines::detail::forced_unwind&)
|
||
|
{
|
||
|
throw;
|
||
|
}
|
||
|
# endif // defined(ASIO_HAS_BOOST_COROUTINE)
|
||
|
catch (...)
|
||
|
{
|
||
|
exception_ptr ex = current_exception();
|
||
|
if (!yield.spawned_thread_->has_context_switched())
|
||
|
(post)(yield);
|
||
|
detail::binder1<Handler, exception_ptr> handler(handler_, ex);
|
||
|
work_.complete(handler, handler.handler_);
|
||
|
}
|
||
|
#endif // !defined(ASIO_NO_EXCEPTIONS)
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
void call(const basic_yield_context<Executor>& yield, void_type<T>)
|
||
|
{
|
||
|
#if !defined(ASIO_NO_EXCEPTIONS)
|
||
|
try
|
||
|
#endif // !defined(ASIO_NO_EXCEPTIONS)
|
||
|
{
|
||
|
T result(function_(yield));
|
||
|
if (!yield.spawned_thread_->has_context_switched())
|
||
|
(post)(yield);
|
||
|
detail::binder2<Handler, exception_ptr, T>
|
||
|
handler(handler_, exception_ptr(), static_cast<T&&>(result));
|
||
|
work_.complete(handler, handler.handler_);
|
||
|
}
|
||
|
#if !defined(ASIO_NO_EXCEPTIONS)
|
||
|
# if defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
|
||
|
catch (const boost::context::detail::forced_unwind&)
|
||
|
{
|
||
|
throw;
|
||
|
}
|
||
|
# endif // defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
|
||
|
# if defined(ASIO_HAS_BOOST_COROUTINE)
|
||
|
catch (const boost::coroutines::detail::forced_unwind&)
|
||
|
{
|
||
|
throw;
|
||
|
}
|
||
|
# endif // defined(ASIO_HAS_BOOST_COROUTINE)
|
||
|
catch (...)
|
||
|
{
|
||
|
exception_ptr ex = current_exception();
|
||
|
if (!yield.spawned_thread_->has_context_switched())
|
||
|
(post)(yield);
|
||
|
detail::binder2<Handler, exception_ptr, T> handler(handler_, ex, T());
|
||
|
work_.complete(handler, handler.handler_);
|
||
|
}
|
||
|
#endif // !defined(ASIO_NO_EXCEPTIONS)
|
||
|
}
|
||
|
|
||
|
Executor executor_;
|
||
|
Function function_;
|
||
|
Handler handler_;
|
||
|
handler_work<Handler, Executor> work_;
|
||
|
};
|
||
|
|
||
|
struct spawn_cancellation_signal_emitter
|
||
|
{
|
||
|
cancellation_signal* signal_;
|
||
|
cancellation_type_t type_;
|
||
|
|
||
|
void operator()()
|
||
|
{
|
||
|
signal_->emit(type_);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <typename Handler, typename Executor, typename = void>
|
||
|
class spawn_cancellation_handler
|
||
|
{
|
||
|
public:
|
||
|
spawn_cancellation_handler(const Handler&, const Executor& ex)
|
||
|
: ex_(ex)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
cancellation_slot slot()
|
||
|
{
|
||
|
return signal_.slot();
|
||
|
}
|
||
|
|
||
|
void operator()(cancellation_type_t type)
|
||
|
{
|
||
|
spawn_cancellation_signal_emitter emitter = { &signal_, type };
|
||
|
(dispatch)(ex_, emitter);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
cancellation_signal signal_;
|
||
|
Executor ex_;
|
||
|
};
|
||
|
|
||
|
|
||
|
template <typename Handler, typename Executor>
|
||
|
class spawn_cancellation_handler<Handler, Executor,
|
||
|
enable_if_t<
|
||
|
is_same<
|
||
|
typename associated_executor<Handler,
|
||
|
Executor>::asio_associated_executor_is_unspecialised,
|
||
|
void
|
||
|
>::value
|
||
|
>>
|
||
|
{
|
||
|
public:
|
||
|
spawn_cancellation_handler(const Handler&, const Executor&)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
cancellation_slot slot()
|
||
|
{
|
||
|
return signal_.slot();
|
||
|
}
|
||
|
|
||
|
void operator()(cancellation_type_t type)
|
||
|
{
|
||
|
signal_.emit(type);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
cancellation_signal signal_;
|
||
|
};
|
||
|
|
||
|
template <typename Executor>
|
||
|
class initiate_spawn
|
||
|
{
|
||
|
public:
|
||
|
typedef Executor executor_type;
|
||
|
|
||
|
explicit initiate_spawn(const executor_type& ex)
|
||
|
: executor_(ex)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
executor_type get_executor() const noexcept
|
||
|
{
|
||
|
return executor_;
|
||
|
}
|
||
|
|
||
|
template <typename Handler, typename F>
|
||
|
void operator()(Handler&& handler,
|
||
|
F&& f) const
|
||
|
{
|
||
|
typedef decay_t<Handler> handler_type;
|
||
|
typedef decay_t<F> function_type;
|
||
|
typedef spawn_cancellation_handler<
|
||
|
handler_type, Executor> cancel_handler_type;
|
||
|
|
||
|
associated_cancellation_slot_t<handler_type> slot
|
||
|
= asio::get_associated_cancellation_slot(handler);
|
||
|
|
||
|
cancel_handler_type* cancel_handler = slot.is_connected()
|
||
|
? &slot.template emplace<cancel_handler_type>(handler, executor_)
|
||
|
: 0;
|
||
|
|
||
|
cancellation_slot proxy_slot(
|
||
|
cancel_handler
|
||
|
? cancel_handler->slot()
|
||
|
: cancellation_slot());
|
||
|
|
||
|
cancellation_state cancel_state(proxy_slot);
|
||
|
|
||
|
(dispatch)(executor_,
|
||
|
spawned_thread_resumer(
|
||
|
default_spawned_thread_type::spawn(
|
||
|
spawn_entry_point<Executor, function_type, handler_type>(
|
||
|
executor_, static_cast<F&&>(f),
|
||
|
static_cast<Handler&&>(handler)),
|
||
|
proxy_slot, cancel_state)));
|
||
|
}
|
||
|
|
||
|
#if defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
|
||
|
|
||
|
template <typename Handler, typename StackAllocator, typename F>
|
||
|
void operator()(Handler&& handler, allocator_arg_t,
|
||
|
StackAllocator&& stack_allocator,
|
||
|
F&& f) const
|
||
|
{
|
||
|
typedef decay_t<Handler> handler_type;
|
||
|
typedef decay_t<F> function_type;
|
||
|
typedef spawn_cancellation_handler<
|
||
|
handler_type, Executor> cancel_handler_type;
|
||
|
|
||
|
associated_cancellation_slot_t<handler_type> slot
|
||
|
= asio::get_associated_cancellation_slot(handler);
|
||
|
|
||
|
cancel_handler_type* cancel_handler = slot.is_connected()
|
||
|
? &slot.template emplace<cancel_handler_type>(handler, executor_)
|
||
|
: 0;
|
||
|
|
||
|
cancellation_slot proxy_slot(
|
||
|
cancel_handler
|
||
|
? cancel_handler->slot()
|
||
|
: cancellation_slot());
|
||
|
|
||
|
cancellation_state cancel_state(proxy_slot);
|
||
|
|
||
|
(dispatch)(executor_,
|
||
|
spawned_thread_resumer(
|
||
|
spawned_fiber_thread::spawn(allocator_arg_t(),
|
||
|
static_cast<StackAllocator&&>(stack_allocator),
|
||
|
spawn_entry_point<Executor, function_type, handler_type>(
|
||
|
executor_, static_cast<F&&>(f),
|
||
|
static_cast<Handler&&>(handler)),
|
||
|
proxy_slot, cancel_state)));
|
||
|
}
|
||
|
|
||
|
#endif // defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
|
||
|
|
||
|
private:
|
||
|
executor_type executor_;
|
||
|
};
|
||
|
|
||
|
} // namespace detail
|
||
|
|
||
|
template <typename Executor, typename F,
|
||
|
ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
|
||
|
result_of_t<F(basic_yield_context<Executor>)>>::type) CompletionToken>
|
||
|
inline auto spawn(const Executor& ex, F&& function, CompletionToken&& token,
|
||
|
#if defined(ASIO_HAS_BOOST_COROUTINE)
|
||
|
constraint_t<
|
||
|
!is_same<
|
||
|
decay_t<CompletionToken>,
|
||
|
boost::coroutines::attributes
|
||
|
>::value
|
||
|
>,
|
||
|
#endif // defined(ASIO_HAS_BOOST_COROUTINE)
|
||
|
constraint_t<
|
||
|
is_executor<Executor>::value || execution::is_executor<Executor>::value
|
||
|
>)
|
||
|
-> decltype(
|
||
|
async_initiate<CompletionToken,
|
||
|
typename detail::spawn_signature<
|
||
|
result_of_t<F(basic_yield_context<Executor>)>>::type>(
|
||
|
declval<detail::initiate_spawn<Executor>>(),
|
||
|
token, static_cast<F&&>(function)))
|
||
|
{
|
||
|
return async_initiate<CompletionToken,
|
||
|
typename detail::spawn_signature<
|
||
|
result_of_t<F(basic_yield_context<Executor>)>>::type>(
|
||
|
detail::initiate_spawn<Executor>(ex),
|
||
|
token, static_cast<F&&>(function));
|
||
|
}
|
||
|
|
||
|
template <typename ExecutionContext, typename F,
|
||
|
ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
|
||
|
result_of_t<F(basic_yield_context<
|
||
|
typename ExecutionContext::executor_type>)>>::type) CompletionToken>
|
||
|
inline auto spawn(ExecutionContext& ctx, F&& function, CompletionToken&& token,
|
||
|
#if defined(ASIO_HAS_BOOST_COROUTINE)
|
||
|
constraint_t<
|
||
|
!is_same<
|
||
|
decay_t<CompletionToken>,
|
||
|
boost::coroutines::attributes
|
||
|
>::value
|
||
|
>,
|
||
|
#endif // defined(ASIO_HAS_BOOST_COROUTINE)
|
||
|
constraint_t<
|
||
|
is_convertible<ExecutionContext&, execution_context&>::value
|
||
|
>)
|
||
|
-> decltype(
|
||
|
async_initiate<CompletionToken,
|
||
|
typename detail::spawn_signature<
|
||
|
result_of_t<F(basic_yield_context<
|
||
|
typename ExecutionContext::executor_type>)>>::type>(
|
||
|
declval<detail::initiate_spawn<
|
||
|
typename ExecutionContext::executor_type>>(),
|
||
|
token, static_cast<F&&>(function)))
|
||
|
{
|
||
|
return (spawn)(ctx.get_executor(), static_cast<F&&>(function),
|
||
|
static_cast<CompletionToken&&>(token));
|
||
|
}
|
||
|
|
||
|
template <typename Executor, typename F,
|
||
|
ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
|
||
|
result_of_t<F(basic_yield_context<Executor>)>>::type)
|
||
|
CompletionToken>
|
||
|
inline auto spawn(const basic_yield_context<Executor>& ctx,
|
||
|
F&& function, CompletionToken&& token,
|
||
|
#if defined(ASIO_HAS_BOOST_COROUTINE)
|
||
|
constraint_t<
|
||
|
!is_same<
|
||
|
decay_t<CompletionToken>,
|
||
|
boost::coroutines::attributes
|
||
|
>::value
|
||
|
>,
|
||
|
#endif // defined(ASIO_HAS_BOOST_COROUTINE)
|
||
|
constraint_t<
|
||
|
is_executor<Executor>::value || execution::is_executor<Executor>::value
|
||
|
>)
|
||
|
-> decltype(
|
||
|
async_initiate<CompletionToken,
|
||
|
typename detail::spawn_signature<
|
||
|
result_of_t<F(basic_yield_context<Executor>)>>::type>(
|
||
|
declval<detail::initiate_spawn<Executor>>(),
|
||
|
token, static_cast<F&&>(function)))
|
||
|
{
|
||
|
return (spawn)(ctx.get_executor(), static_cast<F&&>(function),
|
||
|
static_cast<CompletionToken&&>(token));
|
||
|
}
|
||
|
|
||
|
#if defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
|
||
|
|
||
|
template <typename Executor, typename StackAllocator, typename F,
|
||
|
ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
|
||
|
result_of_t<F(basic_yield_context<Executor>)>>::type)
|
||
|
CompletionToken>
|
||
|
inline auto spawn(const Executor& ex, allocator_arg_t,
|
||
|
StackAllocator&& stack_allocator, F&& function, CompletionToken&& token,
|
||
|
constraint_t<
|
||
|
is_executor<Executor>::value || execution::is_executor<Executor>::value
|
||
|
>)
|
||
|
-> decltype(
|
||
|
async_initiate<CompletionToken,
|
||
|
typename detail::spawn_signature<
|
||
|
result_of_t<F(basic_yield_context<Executor>)>>::type>(
|
||
|
declval<detail::initiate_spawn<Executor>>(),
|
||
|
token, allocator_arg_t(),
|
||
|
static_cast<StackAllocator&&>(stack_allocator),
|
||
|
static_cast<F&&>(function)))
|
||
|
{
|
||
|
return async_initiate<CompletionToken,
|
||
|
typename detail::spawn_signature<
|
||
|
result_of_t<F(basic_yield_context<Executor>)>>::type>(
|
||
|
detail::initiate_spawn<Executor>(ex), token, allocator_arg_t(),
|
||
|
static_cast<StackAllocator&&>(stack_allocator),
|
||
|
static_cast<F&&>(function));
|
||
|
}
|
||
|
|
||
|
template <typename ExecutionContext, typename StackAllocator, typename F,
|
||
|
ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
|
||
|
result_of_t<F(basic_yield_context<
|
||
|
typename ExecutionContext::executor_type>)>>::type) CompletionToken>
|
||
|
inline auto spawn(ExecutionContext& ctx, allocator_arg_t,
|
||
|
StackAllocator&& stack_allocator, F&& function, CompletionToken&& token,
|
||
|
constraint_t<
|
||
|
is_convertible<ExecutionContext&, execution_context&>::value
|
||
|
>)
|
||
|
-> decltype(
|
||
|
async_initiate<CompletionToken,
|
||
|
typename detail::spawn_signature<
|
||
|
result_of_t<F(basic_yield_context<
|
||
|
typename ExecutionContext::executor_type>)>>::type>(
|
||
|
declval<detail::initiate_spawn<
|
||
|
typename ExecutionContext::executor_type>>(),
|
||
|
token, allocator_arg_t(),
|
||
|
static_cast<StackAllocator&&>(stack_allocator),
|
||
|
static_cast<F&&>(function)))
|
||
|
{
|
||
|
return (spawn)(ctx.get_executor(), allocator_arg_t(),
|
||
|
static_cast<StackAllocator&&>(stack_allocator),
|
||
|
static_cast<F&&>(function), static_cast<CompletionToken&&>(token));
|
||
|
}
|
||
|
|
||
|
template <typename Executor, typename StackAllocator, typename F,
|
||
|
ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
|
||
|
result_of_t<F(basic_yield_context<Executor>)>>::type) CompletionToken>
|
||
|
inline auto spawn(const basic_yield_context<Executor>& ctx, allocator_arg_t,
|
||
|
StackAllocator&& stack_allocator, F&& function, CompletionToken&& token,
|
||
|
constraint_t<
|
||
|
is_executor<Executor>::value || execution::is_executor<Executor>::value
|
||
|
>)
|
||
|
-> decltype(
|
||
|
async_initiate<CompletionToken,
|
||
|
typename detail::spawn_signature<
|
||
|
result_of_t<F(basic_yield_context<Executor>)>>::type>(
|
||
|
declval<detail::initiate_spawn<Executor>>(), token,
|
||
|
allocator_arg_t(), static_cast<StackAllocator&&>(stack_allocator),
|
||
|
static_cast<F&&>(function)))
|
||
|
{
|
||
|
return (spawn)(ctx.get_executor(), allocator_arg_t(),
|
||
|
static_cast<StackAllocator&&>(stack_allocator),
|
||
|
static_cast<F&&>(function), static_cast<CompletionToken&&>(token));
|
||
|
}
|
||
|
|
||
|
#endif // defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
|
||
|
|
||
|
#if defined(ASIO_HAS_BOOST_COROUTINE)
|
||
|
|
||
|
namespace detail {
|
||
|
|
||
|
template <typename Executor, typename Function, typename Handler>
|
||
|
class old_spawn_entry_point
|
||
|
{
|
||
|
public:
|
||
|
template <typename F, typename H>
|
||
|
old_spawn_entry_point(const Executor& ex, F&& f, H&& h)
|
||
|
: executor_(ex),
|
||
|
function_(static_cast<F&&>(f)),
|
||
|
handler_(static_cast<H&&>(h))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void operator()(spawned_thread_base* spawned_thread)
|
||
|
{
|
||
|
const basic_yield_context<Executor> yield(spawned_thread, executor_);
|
||
|
this->call(yield,
|
||
|
void_type<result_of_t<Function(basic_yield_context<Executor>)>>());
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
void call(const basic_yield_context<Executor>& yield, void_type<void>)
|
||
|
{
|
||
|
function_(yield);
|
||
|
static_cast<Handler&&>(handler_)();
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
void call(const basic_yield_context<Executor>& yield, void_type<T>)
|
||
|
{
|
||
|
static_cast<Handler&&>(handler_)(function_(yield));
|
||
|
}
|
||
|
|
||
|
Executor executor_;
|
||
|
Function function_;
|
||
|
Handler handler_;
|
||
|
};
|
||
|
|
||
|
inline void default_spawn_handler() {}
|
||
|
|
||
|
} // namespace detail
|
||
|
|
||
|
template <typename Function>
|
||
|
inline void spawn(Function&& function,
|
||
|
const boost::coroutines::attributes& attributes)
|
||
|
{
|
||
|
associated_executor_t<decay_t<Function>> ex(
|
||
|
(get_associated_executor)(function));
|
||
|
|
||
|
asio::spawn(ex, static_cast<Function&&>(function), attributes);
|
||
|
}
|
||
|
|
||
|
template <typename Handler, typename Function>
|
||
|
void spawn(Handler&& handler, Function&& function,
|
||
|
const boost::coroutines::attributes& attributes,
|
||
|
constraint_t<
|
||
|
!is_executor<decay_t<Handler>>::value &&
|
||
|
!execution::is_executor<decay_t<Handler>>::value &&
|
||
|
!is_convertible<Handler&, execution_context&>::value>)
|
||
|
{
|
||
|
typedef associated_executor_t<decay_t<Handler>> executor_type;
|
||
|
executor_type ex((get_associated_executor)(handler));
|
||
|
|
||
|
(dispatch)(ex,
|
||
|
detail::spawned_thread_resumer(
|
||
|
detail::spawned_coroutine_thread::spawn(
|
||
|
detail::old_spawn_entry_point<executor_type,
|
||
|
decay_t<Function>, void (*)()>(
|
||
|
ex, static_cast<Function&&>(function),
|
||
|
&detail::default_spawn_handler), attributes)));
|
||
|
}
|
||
|
|
||
|
template <typename Executor, typename Function>
|
||
|
void spawn(basic_yield_context<Executor> ctx, Function&& function,
|
||
|
const boost::coroutines::attributes& attributes)
|
||
|
{
|
||
|
(dispatch)(ctx.get_executor(),
|
||
|
detail::spawned_thread_resumer(
|
||
|
detail::spawned_coroutine_thread::spawn(
|
||
|
detail::old_spawn_entry_point<Executor,
|
||
|
decay_t<Function>, void (*)()>(
|
||
|
ctx.get_executor(), static_cast<Function&&>(function),
|
||
|
&detail::default_spawn_handler), attributes)));
|
||
|
}
|
||
|
|
||
|
template <typename Function, typename Executor>
|
||
|
inline void spawn(const Executor& ex, Function&& function,
|
||
|
const boost::coroutines::attributes& attributes,
|
||
|
constraint_t<
|
||
|
is_executor<Executor>::value || execution::is_executor<Executor>::value
|
||
|
>)
|
||
|
{
|
||
|
asio::spawn(asio::strand<Executor>(ex),
|
||
|
static_cast<Function&&>(function), attributes);
|
||
|
}
|
||
|
|
||
|
template <typename Function, typename Executor>
|
||
|
inline void spawn(const strand<Executor>& ex, Function&& function,
|
||
|
const boost::coroutines::attributes& attributes)
|
||
|
{
|
||
|
asio::spawn(asio::bind_executor(
|
||
|
ex, &detail::default_spawn_handler),
|
||
|
static_cast<Function&&>(function), attributes);
|
||
|
}
|
||
|
|
||
|
#if !defined(ASIO_NO_TS_EXECUTORS)
|
||
|
|
||
|
template <typename Function>
|
||
|
inline void spawn(const asio::io_context::strand& s, Function&& function,
|
||
|
const boost::coroutines::attributes& attributes)
|
||
|
{
|
||
|
asio::spawn(asio::bind_executor(
|
||
|
s, &detail::default_spawn_handler),
|
||
|
static_cast<Function&&>(function), attributes);
|
||
|
}
|
||
|
|
||
|
#endif // !defined(ASIO_NO_TS_EXECUTORS)
|
||
|
|
||
|
template <typename Function, typename ExecutionContext>
|
||
|
inline void spawn(ExecutionContext& ctx, Function&& function,
|
||
|
const boost::coroutines::attributes& attributes,
|
||
|
constraint_t<
|
||
|
is_convertible<ExecutionContext&, execution_context&>::value
|
||
|
>)
|
||
|
{
|
||
|
asio::spawn(ctx.get_executor(),
|
||
|
static_cast<Function&&>(function), attributes);
|
||
|
}
|
||
|
|
||
|
#endif // defined(ASIO_HAS_BOOST_COROUTINE)
|
||
|
|
||
|
} // namespace asio
|
||
|
|
||
|
#include "asio/detail/pop_options.hpp"
|
||
|
|
||
|
#endif // ASIO_IMPL_SPAWN_HPP
|