1617 lines
44 KiB
C++
1617 lines
44 KiB
C++
//
|
|
// impl/spawn.hpp
|
|
// ~~~~~~~~~~~~~~
|
|
//
|
|
// Copyright (c) 2003-2023 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 "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_alloc_helpers.hpp"
|
|
#include "asio/detail/handler_cont_helpers.hpp"
|
|
#include "asio/detail/handler_invoke_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/detail/variadic_templates.hpp"
|
|
#include "asio/system_error.hpp"
|
|
|
|
#if defined(ASIO_HAS_STD_TUPLE)
|
|
# include <tuple>
|
|
#endif // defined(ASIO_HAS_STD_TUPLE)
|
|
|
|
#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(ASIO_MOVE_ARG(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<typename decay<F>::type>(
|
|
ASIO_MOVE_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(ASIO_MOVE_ARG(F) f,
|
|
cancellation_slot parent_cancel_slot = cancellation_slot(),
|
|
cancellation_state cancel_state = cancellation_state())
|
|
{
|
|
return spawn(ASIO_MOVE_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(ASIO_MOVE_ARG(F) f,
|
|
spawned_coroutine_thread** spawned_thread_out)
|
|
: function_(ASIO_MOVE_CAST(F)(f)),
|
|
spawned_thread_out_(spawned_thread_out)
|
|
{
|
|
}
|
|
|
|
void operator()(caller_type& caller)
|
|
{
|
|
Function function(ASIO_MOVE_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(ASIO_MOVE_ARG(fiber_type) caller)
|
|
: caller_(ASIO_MOVE_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,
|
|
ASIO_MOVE_ARG(StackAllocator) stack_allocator,
|
|
ASIO_MOVE_ARG(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(),
|
|
ASIO_MOVE_CAST(StackAllocator)(stack_allocator),
|
|
entry_point<typename decay<F>::type>(
|
|
ASIO_MOVE_CAST(F)(f), &spawned_thread));
|
|
callee = fiber_type(ASIO_MOVE_CAST(fiber_type)(callee)).resume();
|
|
spawned_thread->callee_ = ASIO_MOVE_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(ASIO_MOVE_ARG(F) f,
|
|
cancellation_slot parent_cancel_slot = cancellation_slot(),
|
|
cancellation_state cancel_state = cancellation_state())
|
|
{
|
|
return spawn(allocator_arg_t(), boost::context::fixedsize_stack(),
|
|
ASIO_MOVE_CAST(F)(f), parent_cancel_slot, cancel_state);
|
|
}
|
|
|
|
void resume()
|
|
{
|
|
callee_ = fiber_type(ASIO_MOVE_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(ASIO_MOVE_CAST(fiber_type)(caller_)).resume();
|
|
}
|
|
|
|
void destroy()
|
|
{
|
|
fiber_type callee = ASIO_MOVE_CAST(fiber_type)(callee_);
|
|
if (terminal_)
|
|
fiber_type(ASIO_MOVE_CAST(fiber_type)(callee)).resume();
|
|
}
|
|
|
|
private:
|
|
template <typename Function>
|
|
class entry_point
|
|
{
|
|
public:
|
|
template <typename F>
|
|
entry_point(ASIO_MOVE_ARG(F) f,
|
|
spawned_fiber_thread** spawned_thread_out)
|
|
: function_(ASIO_MOVE_CAST(F)(f)),
|
|
spawned_thread_out_(spawned_thread_out)
|
|
{
|
|
}
|
|
|
|
fiber_type operator()(ASIO_MOVE_ARG(fiber_type) caller)
|
|
{
|
|
Function function(ASIO_MOVE_CAST(Function)(function_));
|
|
spawned_fiber_thread spawned_thread(
|
|
ASIO_MOVE_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 ASIO_MOVE_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)
|
|
{
|
|
#if !defined(ASIO_HAS_MOVE)
|
|
spawned_thread->detach();
|
|
spawned_thread->attach(&spawned_thread_);
|
|
#endif // !defined(ASIO_HAS_MOVE)
|
|
}
|
|
|
|
#if defined(ASIO_HAS_MOVE)
|
|
|
|
spawned_thread_resumer(spawned_thread_resumer&& other) ASIO_NOEXCEPT
|
|
: spawned_thread_(other.spawned_thread_)
|
|
{
|
|
other.spawned_thread_ = 0;
|
|
}
|
|
|
|
#else // defined(ASIO_HAS_MOVE)
|
|
|
|
spawned_thread_resumer(
|
|
const spawned_thread_resumer& other) ASIO_NOEXCEPT
|
|
: spawned_thread_(other.spawned_thread_)
|
|
{
|
|
spawned_thread_->detach();
|
|
spawned_thread_->attach(&spawned_thread_);
|
|
}
|
|
|
|
#endif // defined(ASIO_HAS_MOVE)
|
|
|
|
~spawned_thread_resumer()
|
|
{
|
|
if (spawned_thread_)
|
|
spawned_thread_->destroy();
|
|
}
|
|
|
|
void operator()()
|
|
{
|
|
#if defined(ASIO_HAS_MOVE)
|
|
spawned_thread_->attach(&spawned_thread_);
|
|
#endif // defined(ASIO_HAS_MOVE)
|
|
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();
|
|
#if !defined(ASIO_HAS_MOVE)
|
|
spawned_thread->attach(&spawned_thread_);
|
|
#endif // !defined(ASIO_HAS_MOVE)
|
|
}
|
|
|
|
#if defined(ASIO_HAS_MOVE)
|
|
|
|
spawned_thread_destroyer(spawned_thread_destroyer&& other) ASIO_NOEXCEPT
|
|
: spawned_thread_(other.spawned_thread_)
|
|
{
|
|
other.spawned_thread_ = 0;
|
|
}
|
|
|
|
#else // defined(ASIO_HAS_MOVE)
|
|
|
|
spawned_thread_destroyer(
|
|
const spawned_thread_destroyer& other) ASIO_NOEXCEPT
|
|
: spawned_thread_(other.spawned_thread_)
|
|
{
|
|
spawned_thread_->detach();
|
|
spawned_thread_->attach(&spawned_thread_);
|
|
}
|
|
|
|
#endif // defined(ASIO_HAS_MOVE)
|
|
|
|
~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();
|
|
#if !defined(ASIO_HAS_MOVE)
|
|
spawned_thread_->attach(&spawned_thread_);
|
|
#endif // !defined(ASIO_HAS_MOVE)
|
|
}
|
|
|
|
#if defined(ASIO_HAS_MOVE)
|
|
|
|
spawn_handler_base(spawn_handler_base&& other) ASIO_NOEXCEPT
|
|
: yield_(other.yield_),
|
|
spawned_thread_(other.spawned_thread_)
|
|
|
|
{
|
|
other.spawned_thread_ = 0;
|
|
}
|
|
|
|
#else // defined(ASIO_HAS_MOVE)
|
|
|
|
spawn_handler_base(const spawn_handler_base& other) ASIO_NOEXCEPT
|
|
: yield_(other.yield_),
|
|
spawned_thread_(other.spawned_thread_)
|
|
{
|
|
spawned_thread_->detach();
|
|
spawned_thread_->attach(&spawned_thread_);
|
|
}
|
|
|
|
#endif // defined(ASIO_HAS_MOVE)
|
|
|
|
~spawn_handler_base()
|
|
{
|
|
if (spawned_thread_)
|
|
(post)(yield_.executor_, spawned_thread_destroyer(spawned_thread_));
|
|
}
|
|
|
|
executor_type get_executor() const ASIO_NOEXCEPT
|
|
{
|
|
return yield_.executor_;
|
|
}
|
|
|
|
cancellation_slot_type get_cancellation_slot() const ASIO_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 ASIO_MOVE_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 ASIO_MOVE_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 ASIO_MOVE_CAST(return_type)(*result.value_);
|
|
}
|
|
|
|
private:
|
|
result_type& result_;
|
|
};
|
|
|
|
#if defined(ASIO_HAS_VARIADIC_TEMPLATES) \
|
|
&& defined(ASIO_HAS_STD_TUPLE)
|
|
|
|
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()(ASIO_MOVE_ARG(Args)... args)
|
|
{
|
|
return_type value(ASIO_MOVE_CAST(Args)(args)...);
|
|
result_ = &value;
|
|
this->resume();
|
|
}
|
|
|
|
static return_type on_resume(result_type& result)
|
|
{
|
|
return ASIO_MOVE_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,
|
|
ASIO_MOVE_ARG(Args)... args)
|
|
{
|
|
return_type value(ASIO_MOVE_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 ASIO_MOVE_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, ASIO_MOVE_ARG(Args)... args)
|
|
{
|
|
return_type value(ASIO_MOVE_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 ASIO_MOVE_CAST(return_type)(*result.value_);
|
|
}
|
|
|
|
private:
|
|
result_type& result_;
|
|
};
|
|
|
|
#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES)
|
|
// && defined(ASIO_HAS_STD_TUPLE)
|
|
|
|
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_TEMPLATES)
|
|
# if defined(ASIO_HAS_VARIADIC_LAMBDA_CAPTURES)
|
|
|
|
template <typename Initiation, typename... InitArgs>
|
|
static return_type initiate(ASIO_MOVE_ARG(Initiation) init,
|
|
const basic_yield_context<Executor>& yield,
|
|
ASIO_MOVE_ARG(InitArgs)... init_args)
|
|
{
|
|
typename handler_type::result_type result
|
|
= typename handler_type::result_type();
|
|
|
|
yield.spawned_thread_->suspend_with(
|
|
[&]()
|
|
{
|
|
ASIO_MOVE_CAST(Initiation)(init)(
|
|
handler_type(yield, result),
|
|
ASIO_MOVE_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_;
|
|
ASIO_MOVE_ARG(Initiation) init_;
|
|
const basic_yield_context<Executor>& yield_;
|
|
std::tuple<ASIO_MOVE_ARG(InitArgs)...> init_args_;
|
|
|
|
template <std::size_t... I>
|
|
void do_invoke(detail::index_sequence<I...>)
|
|
{
|
|
ASIO_MOVE_CAST(Initiation)(init_)(
|
|
handler_type(yield_, result_),
|
|
ASIO_MOVE_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(ASIO_MOVE_ARG(Initiation) init,
|
|
const basic_yield_context<Executor>& yield,
|
|
ASIO_MOVE_ARG(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, ASIO_MOVE_CAST(Initiation)(init), yield,
|
|
std::tuple<ASIO_MOVE_ARG(InitArgs)...>(
|
|
ASIO_MOVE_CAST(InitArgs)(init_args)...)});
|
|
|
|
return handler_type::on_resume(result);
|
|
}
|
|
|
|
# endif // defined(ASIO_HAS_VARIADIC_LAMBDA_CAPTURES)
|
|
#else // defined(ASIO_HAS_VARIADIC_TEMPLATES)
|
|
|
|
template <typename Initiation>
|
|
static return_type initiate(Initiation init,
|
|
const basic_yield_context<Executor>& yield)
|
|
{
|
|
typename handler_type::result_type result
|
|
= typename handler_type::result_type();
|
|
|
|
struct on_suspend
|
|
{
|
|
Initiation& init_;
|
|
const basic_yield_context<Executor>& yield_;
|
|
typename handler_type::result_type& result_;
|
|
|
|
void do_call()
|
|
{
|
|
ASIO_MOVE_CAST(Initiation)(init_)(
|
|
handler_type(yield_, result_));
|
|
}
|
|
|
|
static void call(void* arg)
|
|
{
|
|
static_cast<on_suspend*>(arg)->do_call();
|
|
}
|
|
} o = { init, yield, result };
|
|
|
|
yield.spawned_thread_->suspend_with(&on_suspend::call, &o);
|
|
|
|
return handler_type::on_resume(result);
|
|
}
|
|
|
|
#define ASIO_PRIVATE_ON_SUSPEND_MEMBERS(n) \
|
|
ASIO_PRIVATE_ON_SUSPEND_MEMBERS_##n
|
|
#define ASIO_PRIVATE_ON_SUSPEND_MEMBERS_1 \
|
|
T1& x1;
|
|
#define ASIO_PRIVATE_ON_SUSPEND_MEMBERS_2 \
|
|
T1& x1; T2& x2;
|
|
#define ASIO_PRIVATE_ON_SUSPEND_MEMBERS_3 \
|
|
T1& x1; T2& x2; T3& x3;
|
|
#define ASIO_PRIVATE_ON_SUSPEND_MEMBERS_4 \
|
|
T1& x1; T2& x2; T3& x3; T4& x4;
|
|
#define ASIO_PRIVATE_ON_SUSPEND_MEMBERS_5 \
|
|
T1& x1; T2& x2; T3& x3; T4& x4; T5& x5;
|
|
#define ASIO_PRIVATE_ON_SUSPEND_MEMBERS_6 \
|
|
T1& x1; T2& x2; T3& x3; T4& x4; T5& x5; T6& x6;
|
|
#define ASIO_PRIVATE_ON_SUSPEND_MEMBERS_7 \
|
|
T1& x1; T2& x2; T3& x3; T4& x4; T5& x5; T6& x6; T7& x7;
|
|
#define ASIO_PRIVATE_ON_SUSPEND_MEMBERS_8 \
|
|
T1& x1; T2& x2; T3& x3; T4& x4; T5& x5; T6& x6; T7& x7; T8& x8;
|
|
|
|
#define ASIO_PRIVATE_INITIATE_DEF(n) \
|
|
template <typename Initiation, ASIO_VARIADIC_TPARAMS(n)> \
|
|
static return_type initiate(Initiation init, \
|
|
const basic_yield_context<Executor>& yield, \
|
|
ASIO_VARIADIC_BYVAL_PARAMS(n)) \
|
|
{ \
|
|
typename handler_type::result_type result \
|
|
= typename handler_type::result_type(); \
|
|
\
|
|
struct on_suspend \
|
|
{ \
|
|
Initiation& init; \
|
|
const basic_yield_context<Executor>& yield; \
|
|
typename handler_type::result_type& result; \
|
|
ASIO_PRIVATE_ON_SUSPEND_MEMBERS(n) \
|
|
\
|
|
void do_call() \
|
|
{ \
|
|
ASIO_MOVE_CAST(Initiation)(init)( \
|
|
handler_type(yield, result), \
|
|
ASIO_VARIADIC_MOVE_ARGS(n)); \
|
|
} \
|
|
\
|
|
static void call(void* arg) \
|
|
{ \
|
|
static_cast<on_suspend*>(arg)->do_call(); \
|
|
} \
|
|
} o = { init, yield, result, ASIO_VARIADIC_BYVAL_ARGS(n) }; \
|
|
\
|
|
yield.spawned_thread_->suspend_with(&on_suspend::call, &o); \
|
|
\
|
|
return handler_type::on_resume(result); \
|
|
} \
|
|
/**/
|
|
ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_INITIATE_DEF)
|
|
#undef ASIO_PRIVATE_INITIATE_DEF
|
|
#undef ASIO_PRIVATE_ON_SUSPEND_MEMBERS
|
|
#undef ASIO_PRIVATE_ON_SUSPEND_MEMBERS_1
|
|
#undef ASIO_PRIVATE_ON_SUSPEND_MEMBERS_2
|
|
#undef ASIO_PRIVATE_ON_SUSPEND_MEMBERS_3
|
|
#undef ASIO_PRIVATE_ON_SUSPEND_MEMBERS_4
|
|
#undef ASIO_PRIVATE_ON_SUSPEND_MEMBERS_5
|
|
#undef ASIO_PRIVATE_ON_SUSPEND_MEMBERS_6
|
|
#undef ASIO_PRIVATE_ON_SUSPEND_MEMBERS_7
|
|
#undef ASIO_PRIVATE_ON_SUSPEND_MEMBERS_8
|
|
|
|
#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES)
|
|
};
|
|
|
|
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,
|
|
ASIO_MOVE_ARG(F) f, ASIO_MOVE_ARG(H) h)
|
|
: executor_(ex),
|
|
function_(ASIO_MOVE_CAST(F)(f)),
|
|
handler_(ASIO_MOVE_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<typename result_of<Function(
|
|
basic_yield_context<Executor>)>::type>());
|
|
}
|
|
|
|
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(), ASIO_MOVE_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,
|
|
typename enable_if<
|
|
is_same<
|
|
typename associated_executor<Handler,
|
|
Executor>::asio_associated_executor_is_unspecialised,
|
|
void
|
|
>::value
|
|
>::type>
|
|
{
|
|
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 ASIO_NOEXCEPT
|
|
{
|
|
return executor_;
|
|
}
|
|
|
|
template <typename Handler, typename F>
|
|
void operator()(ASIO_MOVE_ARG(Handler) handler,
|
|
ASIO_MOVE_ARG(F) f) const
|
|
{
|
|
typedef typename decay<Handler>::type handler_type;
|
|
typedef typename decay<F>::type function_type;
|
|
typedef spawn_cancellation_handler<
|
|
handler_type, Executor> cancel_handler_type;
|
|
|
|
typename associated_cancellation_slot<handler_type>::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_, ASIO_MOVE_CAST(F)(f),
|
|
ASIO_MOVE_CAST(Handler)(handler)),
|
|
proxy_slot, cancel_state)));
|
|
}
|
|
|
|
#if defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
|
|
|
|
template <typename Handler, typename StackAllocator, typename F>
|
|
void operator()(ASIO_MOVE_ARG(Handler) handler, allocator_arg_t,
|
|
ASIO_MOVE_ARG(StackAllocator) stack_allocator,
|
|
ASIO_MOVE_ARG(F) f) const
|
|
{
|
|
typedef typename decay<Handler>::type handler_type;
|
|
typedef typename decay<F>::type function_type;
|
|
typedef spawn_cancellation_handler<
|
|
handler_type, Executor> cancel_handler_type;
|
|
|
|
typename associated_cancellation_slot<handler_type>::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(),
|
|
ASIO_MOVE_CAST(StackAllocator)(stack_allocator),
|
|
spawn_entry_point<Executor, function_type, handler_type>(
|
|
executor_, ASIO_MOVE_CAST(F)(f),
|
|
ASIO_MOVE_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<
|
|
typename result_of<F(basic_yield_context<Executor>)>::type>::type)
|
|
CompletionToken>
|
|
inline ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(CompletionToken,
|
|
typename detail::spawn_signature<
|
|
typename result_of<F(basic_yield_context<Executor>)>::type>::type)
|
|
spawn(const Executor& ex, ASIO_MOVE_ARG(F) function,
|
|
ASIO_MOVE_ARG(CompletionToken) token,
|
|
#if defined(ASIO_HAS_BOOST_COROUTINE)
|
|
typename constraint<
|
|
!is_same<
|
|
typename decay<CompletionToken>::type,
|
|
boost::coroutines::attributes
|
|
>::value
|
|
>::type,
|
|
#endif // defined(ASIO_HAS_BOOST_COROUTINE)
|
|
typename constraint<
|
|
is_executor<Executor>::value || execution::is_executor<Executor>::value
|
|
>::type)
|
|
ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX((
|
|
async_initiate<CompletionToken,
|
|
typename detail::spawn_signature<
|
|
typename result_of<F(basic_yield_context<Executor>)>::type>::type>(
|
|
declval<detail::initiate_spawn<Executor> >(),
|
|
token, ASIO_MOVE_CAST(F)(function))))
|
|
{
|
|
return async_initiate<CompletionToken,
|
|
typename detail::spawn_signature<
|
|
typename result_of<F(basic_yield_context<Executor>)>::type>::type>(
|
|
detail::initiate_spawn<Executor>(ex),
|
|
token, ASIO_MOVE_CAST(F)(function));
|
|
}
|
|
|
|
template <typename ExecutionContext, typename F,
|
|
ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
|
|
typename result_of<F(basic_yield_context<
|
|
typename ExecutionContext::executor_type>)>::type>::type)
|
|
CompletionToken>
|
|
inline ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(CompletionToken,
|
|
typename detail::spawn_signature<
|
|
typename result_of<F(basic_yield_context<
|
|
typename ExecutionContext::executor_type>)>::type>::type)
|
|
spawn(ExecutionContext& ctx, ASIO_MOVE_ARG(F) function,
|
|
ASIO_MOVE_ARG(CompletionToken) token,
|
|
#if defined(ASIO_HAS_BOOST_COROUTINE)
|
|
typename constraint<
|
|
!is_same<
|
|
typename decay<CompletionToken>::type,
|
|
boost::coroutines::attributes
|
|
>::value
|
|
>::type,
|
|
#endif // defined(ASIO_HAS_BOOST_COROUTINE)
|
|
typename constraint<
|
|
is_convertible<ExecutionContext&, execution_context&>::value
|
|
>::type)
|
|
ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX((
|
|
async_initiate<CompletionToken,
|
|
typename detail::spawn_signature<
|
|
typename result_of<F(basic_yield_context<
|
|
typename ExecutionContext::executor_type>)>::type>::type>(
|
|
declval<detail::initiate_spawn<
|
|
typename ExecutionContext::executor_type> >(),
|
|
token, ASIO_MOVE_CAST(F)(function))))
|
|
{
|
|
return (spawn)(ctx.get_executor(), ASIO_MOVE_CAST(F)(function),
|
|
ASIO_MOVE_CAST(CompletionToken)(token));
|
|
}
|
|
|
|
template <typename Executor, typename F,
|
|
ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
|
|
typename result_of<F(basic_yield_context<Executor>)>::type>::type)
|
|
CompletionToken>
|
|
inline ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(CompletionToken,
|
|
typename detail::spawn_signature<
|
|
typename result_of<F(basic_yield_context<Executor>)>::type>::type)
|
|
spawn(const basic_yield_context<Executor>& ctx,
|
|
ASIO_MOVE_ARG(F) function,
|
|
ASIO_MOVE_ARG(CompletionToken) token,
|
|
#if defined(ASIO_HAS_BOOST_COROUTINE)
|
|
typename constraint<
|
|
!is_same<
|
|
typename decay<CompletionToken>::type,
|
|
boost::coroutines::attributes
|
|
>::value
|
|
>::type,
|
|
#endif // defined(ASIO_HAS_BOOST_COROUTINE)
|
|
typename constraint<
|
|
is_executor<Executor>::value || execution::is_executor<Executor>::value
|
|
>::type)
|
|
ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX((
|
|
async_initiate<CompletionToken,
|
|
typename detail::spawn_signature<
|
|
typename result_of<F(basic_yield_context<Executor>)>::type>::type>(
|
|
declval<detail::initiate_spawn<Executor> >(),
|
|
token, ASIO_MOVE_CAST(F)(function))))
|
|
{
|
|
return (spawn)(ctx.get_executor(), ASIO_MOVE_CAST(F)(function),
|
|
ASIO_MOVE_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<
|
|
typename result_of<F(basic_yield_context<Executor>)>::type>::type)
|
|
CompletionToken>
|
|
inline ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(CompletionToken,
|
|
typename detail::spawn_signature<
|
|
typename result_of<F(basic_yield_context<Executor>)>::type>::type)
|
|
spawn(const Executor& ex, allocator_arg_t,
|
|
ASIO_MOVE_ARG(StackAllocator) stack_allocator,
|
|
ASIO_MOVE_ARG(F) function,
|
|
ASIO_MOVE_ARG(CompletionToken) token,
|
|
typename constraint<
|
|
is_executor<Executor>::value || execution::is_executor<Executor>::value
|
|
>::type)
|
|
ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX((
|
|
async_initiate<CompletionToken,
|
|
typename detail::spawn_signature<
|
|
typename result_of<F(basic_yield_context<Executor>)>::type>::type>(
|
|
declval<detail::initiate_spawn<Executor> >(),
|
|
token, allocator_arg_t(),
|
|
ASIO_MOVE_CAST(StackAllocator)(stack_allocator),
|
|
ASIO_MOVE_CAST(F)(function))))
|
|
{
|
|
return async_initiate<CompletionToken,
|
|
typename detail::spawn_signature<
|
|
typename result_of<F(basic_yield_context<Executor>)>::type>::type>(
|
|
detail::initiate_spawn<Executor>(ex), token, allocator_arg_t(),
|
|
ASIO_MOVE_CAST(StackAllocator)(stack_allocator),
|
|
ASIO_MOVE_CAST(F)(function));
|
|
}
|
|
|
|
template <typename ExecutionContext, typename StackAllocator, typename F,
|
|
ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
|
|
typename result_of<F(basic_yield_context<
|
|
typename ExecutionContext::executor_type>)>::type>::type)
|
|
CompletionToken>
|
|
inline ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(CompletionToken,
|
|
typename detail::spawn_signature<
|
|
typename result_of<F(basic_yield_context<
|
|
typename ExecutionContext::executor_type>)>::type>::type)
|
|
spawn(ExecutionContext& ctx, allocator_arg_t,
|
|
ASIO_MOVE_ARG(StackAllocator) stack_allocator,
|
|
ASIO_MOVE_ARG(F) function,
|
|
ASIO_MOVE_ARG(CompletionToken) token,
|
|
typename constraint<
|
|
is_convertible<ExecutionContext&, execution_context&>::value
|
|
>::type)
|
|
ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX((
|
|
async_initiate<CompletionToken,
|
|
typename detail::spawn_signature<
|
|
typename result_of<F(basic_yield_context<
|
|
typename ExecutionContext::executor_type>)>::type>::type>(
|
|
declval<detail::initiate_spawn<
|
|
typename ExecutionContext::executor_type> >(),
|
|
token, allocator_arg_t(),
|
|
ASIO_MOVE_CAST(StackAllocator)(stack_allocator),
|
|
ASIO_MOVE_CAST(F)(function))))
|
|
{
|
|
return (spawn)(ctx.get_executor(), allocator_arg_t(),
|
|
ASIO_MOVE_CAST(StackAllocator)(stack_allocator),
|
|
ASIO_MOVE_CAST(F)(function),
|
|
ASIO_MOVE_CAST(CompletionToken)(token));
|
|
}
|
|
|
|
template <typename Executor, typename StackAllocator, typename F,
|
|
ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
|
|
typename result_of<F(basic_yield_context<Executor>)>::type>::type)
|
|
CompletionToken>
|
|
inline ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(CompletionToken,
|
|
typename detail::spawn_signature<
|
|
typename result_of<F(basic_yield_context<Executor>)>::type>::type)
|
|
spawn(const basic_yield_context<Executor>& ctx, allocator_arg_t,
|
|
ASIO_MOVE_ARG(StackAllocator) stack_allocator,
|
|
ASIO_MOVE_ARG(F) function,
|
|
ASIO_MOVE_ARG(CompletionToken) token,
|
|
typename constraint<
|
|
is_executor<Executor>::value || execution::is_executor<Executor>::value
|
|
>::type)
|
|
ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX((
|
|
async_initiate<CompletionToken,
|
|
typename detail::spawn_signature<
|
|
typename result_of<F(basic_yield_context<Executor>)>::type>::type>(
|
|
declval<detail::initiate_spawn<Executor> >(),
|
|
token, allocator_arg_t(),
|
|
ASIO_MOVE_CAST(StackAllocator)(stack_allocator),
|
|
ASIO_MOVE_CAST(F)(function))))
|
|
{
|
|
return (spawn)(ctx.get_executor(), allocator_arg_t(),
|
|
ASIO_MOVE_CAST(StackAllocator)(stack_allocator),
|
|
ASIO_MOVE_CAST(F)(function),
|
|
ASIO_MOVE_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,
|
|
ASIO_MOVE_ARG(F) f, ASIO_MOVE_ARG(H) h)
|
|
: executor_(ex),
|
|
function_(ASIO_MOVE_CAST(F)(f)),
|
|
handler_(ASIO_MOVE_CAST(H)(h))
|
|
{
|
|
}
|
|
|
|
void operator()(spawned_thread_base* spawned_thread)
|
|
{
|
|
const basic_yield_context<Executor> yield(spawned_thread, executor_);
|
|
this->call(yield,
|
|
void_type<typename result_of<Function(
|
|
basic_yield_context<Executor>)>::type>());
|
|
}
|
|
|
|
private:
|
|
void call(const basic_yield_context<Executor>& yield, void_type<void>)
|
|
{
|
|
function_(yield);
|
|
ASIO_MOVE_OR_LVALUE(Handler)(handler_)();
|
|
}
|
|
|
|
template <typename T>
|
|
void call(const basic_yield_context<Executor>& yield, void_type<T>)
|
|
{
|
|
ASIO_MOVE_OR_LVALUE(Handler)(handler_)(function_(yield));
|
|
}
|
|
|
|
Executor executor_;
|
|
Function function_;
|
|
Handler handler_;
|
|
};
|
|
|
|
inline void default_spawn_handler() {}
|
|
|
|
} // namespace detail
|
|
|
|
template <typename Function>
|
|
inline void spawn(ASIO_MOVE_ARG(Function) function,
|
|
const boost::coroutines::attributes& attributes)
|
|
{
|
|
typedef typename decay<Function>::type function_type;
|
|
|
|
typename associated_executor<function_type>::type ex(
|
|
(get_associated_executor)(function));
|
|
|
|
asio::spawn(ex, ASIO_MOVE_CAST(Function)(function), attributes);
|
|
}
|
|
|
|
template <typename Handler, typename Function>
|
|
void spawn(ASIO_MOVE_ARG(Handler) handler,
|
|
ASIO_MOVE_ARG(Function) function,
|
|
const boost::coroutines::attributes& attributes,
|
|
typename constraint<
|
|
!is_executor<typename decay<Handler>::type>::value &&
|
|
!execution::is_executor<typename decay<Handler>::type>::value &&
|
|
!is_convertible<Handler&, execution_context&>::value>::type)
|
|
{
|
|
typedef typename decay<Handler>::type handler_type;
|
|
typedef typename decay<Function>::type function_type;
|
|
typedef typename associated_executor<handler_type>::type 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,
|
|
function_type, void (*)()>(
|
|
ex, ASIO_MOVE_CAST(Function)(function),
|
|
&detail::default_spawn_handler), attributes)));
|
|
}
|
|
|
|
template <typename Executor, typename Function>
|
|
void spawn(basic_yield_context<Executor> ctx,
|
|
ASIO_MOVE_ARG(Function) function,
|
|
const boost::coroutines::attributes& attributes)
|
|
{
|
|
typedef typename decay<Function>::type function_type;
|
|
|
|
(dispatch)(ctx.get_executor(),
|
|
detail::spawned_thread_resumer(
|
|
detail::spawned_coroutine_thread::spawn(
|
|
detail::old_spawn_entry_point<Executor,
|
|
function_type, void (*)()>(ctx.get_executor(),
|
|
ASIO_MOVE_CAST(Function)(function),
|
|
&detail::default_spawn_handler), attributes)));
|
|
}
|
|
|
|
template <typename Function, typename Executor>
|
|
inline void spawn(const Executor& ex,
|
|
ASIO_MOVE_ARG(Function) function,
|
|
const boost::coroutines::attributes& attributes,
|
|
typename constraint<
|
|
is_executor<Executor>::value || execution::is_executor<Executor>::value
|
|
>::type)
|
|
{
|
|
asio::spawn(asio::strand<Executor>(ex),
|
|
ASIO_MOVE_CAST(Function)(function), attributes);
|
|
}
|
|
|
|
template <typename Function, typename Executor>
|
|
inline void spawn(const strand<Executor>& ex,
|
|
ASIO_MOVE_ARG(Function) function,
|
|
const boost::coroutines::attributes& attributes)
|
|
{
|
|
asio::spawn(asio::bind_executor(
|
|
ex, &detail::default_spawn_handler),
|
|
ASIO_MOVE_CAST(Function)(function), attributes);
|
|
}
|
|
|
|
#if !defined(ASIO_NO_TS_EXECUTORS)
|
|
|
|
template <typename Function>
|
|
inline void spawn(const asio::io_context::strand& s,
|
|
ASIO_MOVE_ARG(Function) function,
|
|
const boost::coroutines::attributes& attributes)
|
|
{
|
|
asio::spawn(asio::bind_executor(
|
|
s, &detail::default_spawn_handler),
|
|
ASIO_MOVE_CAST(Function)(function), attributes);
|
|
}
|
|
|
|
#endif // !defined(ASIO_NO_TS_EXECUTORS)
|
|
|
|
template <typename Function, typename ExecutionContext>
|
|
inline void spawn(ExecutionContext& ctx,
|
|
ASIO_MOVE_ARG(Function) function,
|
|
const boost::coroutines::attributes& attributes,
|
|
typename constraint<is_convertible<
|
|
ExecutionContext&, execution_context&>::value>::type)
|
|
{
|
|
asio::spawn(ctx.get_executor(),
|
|
ASIO_MOVE_CAST(Function)(function), attributes);
|
|
}
|
|
|
|
#endif // defined(ASIO_HAS_BOOST_COROUTINE)
|
|
|
|
} // namespace asio
|
|
|
|
#include "asio/detail/pop_options.hpp"
|
|
|
|
#endif // ASIO_IMPL_SPAWN_HPP
|