mirror of
https://github.com/boostorg/asio.git
synced 2026-01-19 04:02:09 +00:00
Add dispatch, post and defer overloads that run a function before completion.
This commit is contained in:
@@ -59,10 +59,11 @@ namespace asio {
|
||||
* The function call operator of @c Init:
|
||||
*
|
||||
* @li Obtains the handler's associated executor object @c ex of type @c Ex by
|
||||
* performing @code auto ex = get_associated_executor(handler); @endcode
|
||||
* performing
|
||||
* @code auto ex = get_associated_executor(completion_handler); @endcode
|
||||
*
|
||||
* @li Obtains the handler's associated allocator object @c alloc by performing
|
||||
* @code auto alloc = get_associated_allocator(handler); @endcode
|
||||
* @code auto alloc = get_associated_allocator(completion_handler); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex>::value</tt> is true, performs
|
||||
* @code prefer(
|
||||
@@ -80,7 +81,7 @@ namespace asio {
|
||||
* @code void() @endcode
|
||||
*/
|
||||
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void()) NullaryToken>
|
||||
auto defer(NullaryToken&& token)
|
||||
inline auto defer(NullaryToken&& token)
|
||||
-> decltype(
|
||||
async_initiate<NullaryToken, void()>(
|
||||
declval<detail::initiate_defer>(), token))
|
||||
@@ -125,10 +126,11 @@ auto defer(NullaryToken&& token)
|
||||
* The function call operator of @c Init:
|
||||
*
|
||||
* @li Obtains the handler's associated executor object @c ex1 of type @c Ex1 by
|
||||
* performing @code auto ex1 = get_associated_executor(handler, ex); @endcode
|
||||
* performing
|
||||
* @code auto ex1 = get_associated_executor(completion_handler, ex); @endcode
|
||||
*
|
||||
* @li Obtains the handler's associated allocator object @c alloc by performing
|
||||
* @code auto alloc = get_associated_allocator(handler); @endcode
|
||||
* @code auto alloc = get_associated_allocator(completion_handler); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex1>::value</tt> is true, constructs a
|
||||
* function object @c f with a member @c executor_ that is initialised with
|
||||
@@ -147,14 +149,14 @@ auto defer(NullaryToken&& token)
|
||||
* work_.get_executor().dispatch(std::move(handler_), a);
|
||||
* work_.reset(); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex>::value</tt> is true, performs
|
||||
* @li If <tt>execution::is_executor<Executor>::value</tt> is true, performs
|
||||
* @code prefer(
|
||||
* require(ex, execution::blocking.never),
|
||||
* execution::relationship.continuation,
|
||||
* execution::allocator(alloc)
|
||||
* ).execute(std::move(f)); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex>::value</tt> is false, performs
|
||||
* @li If <tt>execution::is_executor<Executor>::value</tt> is false, performs
|
||||
* @code ex.defer(std::move(f), alloc); @endcode
|
||||
*
|
||||
* @par Completion Signature
|
||||
@@ -163,9 +165,8 @@ auto defer(NullaryToken&& token)
|
||||
template <typename Executor,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void()) NullaryToken
|
||||
= default_completion_token_t<Executor>>
|
||||
auto defer(const Executor& ex,
|
||||
NullaryToken&& token
|
||||
= default_completion_token_t<Executor>(),
|
||||
inline auto defer(const Executor& ex,
|
||||
NullaryToken&& token = default_completion_token_t<Executor>(),
|
||||
constraint_t<
|
||||
(execution::is_executor<Executor>::value
|
||||
&& can_require<Executor, execution::blocking_t::never_t>::value)
|
||||
@@ -173,10 +174,12 @@ auto defer(const Executor& ex,
|
||||
> = 0)
|
||||
-> decltype(
|
||||
async_initiate<NullaryToken, void()>(
|
||||
declval<detail::initiate_defer_with_executor<Executor>>(), token))
|
||||
declval<detail::initiate_defer_with_executor<Executor>>(),
|
||||
token, detail::empty_work_function()))
|
||||
{
|
||||
return async_initiate<NullaryToken, void()>(
|
||||
detail::initiate_defer_with_executor<Executor>(ex), token);
|
||||
detail::initiate_defer_with_executor<Executor>(ex),
|
||||
token, detail::empty_work_function());
|
||||
}
|
||||
|
||||
/// Submits a completion token or function object for execution.
|
||||
@@ -195,21 +198,343 @@ auto defer(const Executor& ex,
|
||||
template <typename ExecutionContext,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void()) NullaryToken
|
||||
= default_completion_token_t<typename ExecutionContext::executor_type>>
|
||||
auto defer(ExecutionContext& ctx,
|
||||
NullaryToken&& token
|
||||
= default_completion_token_t<typename ExecutionContext::executor_type>(),
|
||||
inline auto defer(ExecutionContext& ctx,
|
||||
NullaryToken&& token = default_completion_token_t<
|
||||
typename ExecutionContext::executor_type>(),
|
||||
constraint_t<
|
||||
is_convertible<ExecutionContext&, execution_context&>::value
|
||||
> = 0)
|
||||
-> decltype(
|
||||
async_initiate<NullaryToken, void()>(
|
||||
declval<detail::initiate_defer_with_executor<
|
||||
typename ExecutionContext::executor_type>>(), token))
|
||||
typename ExecutionContext::executor_type>>(),
|
||||
token, detail::empty_work_function()))
|
||||
{
|
||||
return async_initiate<NullaryToken, void()>(
|
||||
detail::initiate_defer_with_executor<
|
||||
typename ExecutionContext::executor_type>(
|
||||
ctx.get_executor()), token);
|
||||
typename ExecutionContext::executor_type>(ctx.get_executor()),
|
||||
token, detail::empty_work_function());
|
||||
}
|
||||
|
||||
|
||||
/// Submits a function to be run on a specified target executor, and after
|
||||
/// completion submits the completion handler.
|
||||
/**
|
||||
* This function submits a function object for execution on the specified
|
||||
* executor. The function object is queued for execution, and is never called
|
||||
* from the current thread prior to returning from <tt>defer()</tt>. After the
|
||||
* submitted function completes, the completion handler is dispatched to run on
|
||||
* its associated executor.
|
||||
*
|
||||
* The use of @c defer(), rather than @ref post(), indicates the caller's
|
||||
* preference that the executor defer the queueing of the function object. This
|
||||
* may allow the executor to optimise queueing for cases when the function
|
||||
* object represents a continuation of the current call context.
|
||||
*
|
||||
* @param function A nullary function to be executed on the target executor.
|
||||
*
|
||||
* @param ex The target executor.
|
||||
*
|
||||
* @param token The @ref completion_token that will be used to produce a
|
||||
* completion handler. The function signature of the completion handler must be:
|
||||
* @code void handler(); @endcode
|
||||
*
|
||||
* @returns This function returns <tt>async_initiate<NullaryToken,
|
||||
* void()>(Init{ex}, token, forward<Function>(function))</tt>, where @c Init is
|
||||
* a function object type defined as:
|
||||
*
|
||||
* @code class Init
|
||||
* {
|
||||
* public:
|
||||
* using executor_type = Executor;
|
||||
* explicit Init(const Executor& ex) : ex_(ex) {}
|
||||
* executor_type get_executor() const noexcept { return ex_; }
|
||||
* template <typename CompletionHandler>
|
||||
* void operator()(CompletionHandler&& completion_handler,
|
||||
* Function&& function) const;
|
||||
* private:
|
||||
* Executor ex_; // exposition only
|
||||
* }; @endcode
|
||||
*
|
||||
* The function call operator of @c Init:
|
||||
*
|
||||
* @li Obtains the handler's associated executor object @c ex1 of type @c Ex1 by
|
||||
* performing
|
||||
* @code auto ex1 = get_associated_executor(completion_handler, ex); @endcode
|
||||
*
|
||||
* @li Obtains the handler's associated allocator object @c alloc by performing
|
||||
* @code auto alloc = get_associated_allocator(completion_handler); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex1>::value</tt> is true, constructs a
|
||||
* function object wrapper @c f with a member @c executor_ that is initialised
|
||||
* with <tt>prefer(ex1, execution::outstanding_work.tracked)</tt>, a member @c
|
||||
* function_ that is a decay-copy of @c function, a member @c handler_ that is a
|
||||
* decay-copy of @c completion_handler, and a function call operator that
|
||||
* performs:
|
||||
* @code std::move(function_)();
|
||||
* auto a = get_associated_allocator(handler_);
|
||||
* prefer(executor_, execution::allocator(a)).execute(std::move(handler_));
|
||||
* @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex1>::value</tt> is false, constructs a
|
||||
* function object wrapper @c f with a member @c work_ that is initialised with
|
||||
* <tt>make_work_guard(ex1)</tt>, a member @c function_ that is a decay-copy of
|
||||
* @c function, a member @c handler_ that is a decay-copy of @c
|
||||
* completion_handler, and a function call operator that performs:
|
||||
* @code std::move(function_)();
|
||||
* auto a = get_associated_allocator(handler_);
|
||||
* work_.get_executor().dispatch(std::move(handler_), a);
|
||||
* work_.reset(); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Executor>::value</tt> is true, performs
|
||||
* @code prefer(
|
||||
* require(ex, execution::blocking.never),
|
||||
* execution::relationship.fork,
|
||||
* execution::allocator(alloc)
|
||||
* ).execute(std::move(f)); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Executor>::value</tt> is false, performs
|
||||
* @code ex.defer(std::move(f), alloc); @endcode
|
||||
*
|
||||
* @note If the function object throws an exception, that exception is allowed
|
||||
* to propagate to the target executor. The behaviour in this case is dependent
|
||||
* on the executor. For example, boost::asio::io_context will allow the
|
||||
* exception to propagate to the caller that runs the @c io_context, whereas
|
||||
* boost::asio::thread_pool will call @c std::terminate.
|
||||
*
|
||||
* @par Completion Signature
|
||||
* @code void() @endcode
|
||||
*/
|
||||
template <typename Function, typename Executor,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void()) NullaryToken
|
||||
= default_completion_token_t<Executor>>
|
||||
inline auto defer(Function&& function, const Executor& ex,
|
||||
NullaryToken&& token = default_completion_token_t<Executor>(),
|
||||
constraint_t<
|
||||
is_void<result_of_t<decay_t<Function>()>>::value
|
||||
> = 0,
|
||||
constraint_t<
|
||||
(execution::is_executor<Executor>::value
|
||||
&& can_require<Executor, execution::blocking_t::never_t>::value)
|
||||
|| is_executor<Executor>::value
|
||||
> = 0)
|
||||
-> decltype(
|
||||
async_initiate<NullaryToken, void()>(
|
||||
declval<detail::initiate_defer_with_executor<Executor>>(),
|
||||
token, static_cast<Function&&>(function)))
|
||||
{
|
||||
return async_initiate<NullaryToken, void()>(
|
||||
detail::initiate_defer_with_executor<Executor>(ex),
|
||||
token, static_cast<Function&&>(function));
|
||||
}
|
||||
|
||||
/// Submits a function to be run on a specified target executor, and passes the
|
||||
/// result to a completion handler.
|
||||
/**
|
||||
* This function submits a function object for execution on the specified
|
||||
* executor. The function object is queued for execution, and is never called
|
||||
* from the current thread prior to returning from <tt>defer()</tt>. After the
|
||||
* submitted function completes, the completion handler is dispatched along with
|
||||
* the function's result, to run on its associated executor.
|
||||
*
|
||||
* The use of @c defer(), rather than @ref post(), indicates the caller's
|
||||
* preference that the executor defer the queueing of the function object. This
|
||||
* may allow the executor to optimise queueing for cases when the function
|
||||
* object represents a continuation of the current call context.
|
||||
*
|
||||
* @param function A nullary function to be executed on the target executor.
|
||||
*
|
||||
* @param ex The target executor.
|
||||
*
|
||||
* @param token The @ref completion_token that will be used to produce a
|
||||
* completion handler. The function signature of the completion handler must be:
|
||||
* @code void handler(decay_t<result_of_t<decay_t<Function>()>>); @endcode
|
||||
*
|
||||
* @returns This function returns <tt>async_initiate<CompletionToken,
|
||||
* void()>(Init{ex}, token)</tt>, where @c Init is a function object type
|
||||
* defined as:
|
||||
*
|
||||
* @code class Init
|
||||
* {
|
||||
* public:
|
||||
* using executor_type = Executor;
|
||||
* explicit Init(const Executor& ex) : ex_(ex) {}
|
||||
* executor_type get_executor() const noexcept { return ex_; }
|
||||
* template <typename CompletionHandler>
|
||||
* void operator()(CompletionHandler&& completion_handler,
|
||||
* Function&& function) const;
|
||||
* private:
|
||||
* Executor ex_; // exposition only
|
||||
* }; @endcode
|
||||
*
|
||||
* The function call operator of @c Init:
|
||||
*
|
||||
* @li Obtains the handler's associated executor object @c ex1 of type @c Ex1 by
|
||||
* performing
|
||||
* @code auto ex1 = get_associated_executor(completion_handler, ex); @endcode
|
||||
*
|
||||
* @li Obtains the handler's associated allocator object @c alloc by performing
|
||||
* @code auto alloc = get_associated_allocator(completion_handler); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex1>::value</tt> is true, constructs a
|
||||
* function object wrapper @c f with a member @c executor_ that is initialised
|
||||
* with <tt>prefer(ex1, execution::outstanding_work.tracked)</tt>, a member @c
|
||||
* function_ that is a decay-copy of @c function, a member @c handler_ that is a
|
||||
* decay-copy of @c completion_handler, and a function call operator that
|
||||
* performs:
|
||||
* @code auto result = std::move(function_)();
|
||||
* auto a = get_associated_allocator(handler_);
|
||||
* prefer(executor_, execution::allocator(a)).execute(
|
||||
* std::bind(std::move(handler_), std::move(result)));
|
||||
* @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex1>::value</tt> is false, constructs a
|
||||
* function object wrapper @c f with a member @c work_ that is initialised with
|
||||
* <tt>make_work_guard(ex1)</tt>, a member @c function_ that is a decay-copy of
|
||||
* @c function, a member @c handler_ that is a decay-copy of @c
|
||||
* completion_handler, and a function call operator that performs:
|
||||
* @code auto result = std::move(function_)();
|
||||
* auto a = get_associated_allocator(handler_);
|
||||
* work_.get_executor().dispatch(
|
||||
* std::bind(std::move(handler_), std::move(result)), a);
|
||||
* work_.reset(); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Executor>::value</tt> is true, performs
|
||||
* @code prefer(
|
||||
* require(ex, execution::blocking.never),
|
||||
* execution::relationship.fork,
|
||||
* execution::allocator(alloc)
|
||||
* ).execute(std::move(f)); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Executor>::value</tt> is false, performs
|
||||
* @code ex.defer(std::move(f), alloc); @endcode
|
||||
*
|
||||
* @note If the function object throws an exception, that exception is allowed
|
||||
* to propagate to the target executor. The behaviour in this case is dependent
|
||||
* on the executor. For example, boost::asio::io_context will allow the
|
||||
* exception to propagate to the caller that runs the @c io_context, whereas
|
||||
* boost::asio::thread_pool will call @c std::terminate.
|
||||
*
|
||||
* @par Completion Signature
|
||||
* @code void(decay_t<result_of_t<decay_t<Function>()>>) @endcode
|
||||
*/
|
||||
template <typename Function, typename Executor,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(
|
||||
void(decay_t<result_of_t<decay_t<Function>()>>)) CompletionToken
|
||||
= default_completion_token_t<Executor>>
|
||||
inline auto defer(Function&& function, const Executor& ex,
|
||||
CompletionToken&& token = default_completion_token_t<Executor>(),
|
||||
constraint_t<
|
||||
!is_void<result_of_t<decay_t<Function>()>>::value
|
||||
> = 0,
|
||||
constraint_t<
|
||||
(execution::is_executor<Executor>::value
|
||||
&& can_require<Executor, execution::blocking_t::never_t>::value)
|
||||
|| is_executor<Executor>::value
|
||||
> = 0)
|
||||
-> decltype(
|
||||
async_initiate<CompletionToken, void(detail::work_result_t<Function>)>(
|
||||
declval<detail::initiate_defer_with_executor<Executor>>(),
|
||||
token, static_cast<Function&&>(function)))
|
||||
{
|
||||
return async_initiate<CompletionToken, void(detail::work_result_t<Function>)>(
|
||||
detail::initiate_defer_with_executor<Executor>(ex),
|
||||
token, static_cast<Function&&>(function));
|
||||
}
|
||||
|
||||
/// Submits a function to be run on a specified execution context, and after
|
||||
/// completion submits the completion handler.
|
||||
/**
|
||||
* @param function A nullary function to be executed on the target executor.
|
||||
*
|
||||
* @param ctx An execution context, from which the target executor is obtained.
|
||||
*
|
||||
* @param token The @ref completion_token that will be used to produce a
|
||||
* completion handler. The function signature of the completion handler must be:
|
||||
* @code void handler(); @endcode
|
||||
*
|
||||
* @returns <tt>defer(forward<Function>(function), ctx.get_executor(),
|
||||
* forward<NullaryToken>(token))</tt>.
|
||||
*
|
||||
* @note If the function object throws an exception, that exception is allowed
|
||||
* to propagate to the target executor. The behaviour in this case is dependent
|
||||
* on the executor. For example, boost::asio::io_context will allow the
|
||||
* exception to propagate to the caller that runs the @c io_context, whereas
|
||||
* boost::asio::thread_pool will call @c std::terminate.
|
||||
*
|
||||
* @par Completion Signature
|
||||
* @code void() @endcode
|
||||
*/
|
||||
template <typename Function, typename ExecutionContext,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void()) NullaryToken
|
||||
= default_completion_token_t<typename ExecutionContext::executor_type>>
|
||||
inline auto defer(Function&& function, ExecutionContext& ctx,
|
||||
NullaryToken&& token = default_completion_token_t<
|
||||
typename ExecutionContext::executor_type>(),
|
||||
constraint_t<
|
||||
is_void<result_of_t<decay_t<Function>()>>::value
|
||||
> = 0,
|
||||
constraint_t<
|
||||
is_convertible<ExecutionContext&, execution_context&>::value
|
||||
> = 0)
|
||||
-> decltype(
|
||||
async_initiate<NullaryToken, void()>(
|
||||
declval<detail::initiate_defer_with_executor<
|
||||
typename ExecutionContext::executor_type>>(),
|
||||
token, static_cast<Function&&>(function)))
|
||||
{
|
||||
return async_initiate<NullaryToken, void()>(
|
||||
detail::initiate_defer_with_executor<
|
||||
typename ExecutionContext::executor_type>(ctx.get_executor()),
|
||||
token, static_cast<Function&&>(function));
|
||||
}
|
||||
|
||||
/// Submits a function to be run on a specified execution context, and passes
|
||||
/// the result to a completion handler.
|
||||
/**
|
||||
* @param function A nullary function to be executed on the target executor.
|
||||
*
|
||||
* @param ctx An execution context, from which the target executor is obtained.
|
||||
*
|
||||
* @param token The @ref completion_token that will be used to produce a
|
||||
* completion handler. The function signature of the completion handler must be:
|
||||
* @code void handler(); @endcode
|
||||
*
|
||||
* @returns <tt>defer(forward<Function>(function), ctx.get_executor(),
|
||||
* forward<CompletionToken>(token))</tt>.
|
||||
*
|
||||
* @note If the function object throws an exception, that exception is allowed
|
||||
* to propagate to the target executor. The behaviour in this case is dependent
|
||||
* on the executor. For example, boost::asio::io_context will allow the
|
||||
* exception to propagate to the caller that runs the @c io_context, whereas
|
||||
* boost::asio::thread_pool will call @c std::terminate.
|
||||
*
|
||||
* @par Completion Signature
|
||||
* @code void(decay_t<result_of_t<decay_t<Function>()>>) @endcode
|
||||
*/
|
||||
template <typename Function, typename ExecutionContext,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(
|
||||
void(decay_t<result_of_t<decay_t<Function>()>>)) CompletionToken
|
||||
= default_completion_token_t<typename ExecutionContext::executor_type>>
|
||||
inline auto defer(Function&& function, ExecutionContext& ctx,
|
||||
CompletionToken&& token = default_completion_token_t<
|
||||
typename ExecutionContext::executor_type>(),
|
||||
constraint_t<
|
||||
!is_void<result_of_t<decay_t<Function>()>>::value
|
||||
> = 0,
|
||||
constraint_t<
|
||||
is_convertible<ExecutionContext&, execution_context&>::value
|
||||
> = 0)
|
||||
-> decltype(
|
||||
async_initiate<CompletionToken, void(detail::work_result_t<Function>)>(
|
||||
declval<detail::initiate_defer_with_executor<
|
||||
typename ExecutionContext::executor_type>>(),
|
||||
token, static_cast<Function&&>(function)))
|
||||
{
|
||||
return async_initiate<CompletionToken, void(detail::work_result_t<Function>)>(
|
||||
detail::initiate_defer_with_executor<
|
||||
typename ExecutionContext::executor_type>(ctx.get_executor()),
|
||||
token, static_cast<Function&&>(function));
|
||||
}
|
||||
|
||||
} // namespace asio
|
||||
|
||||
@@ -490,6 +490,14 @@ public:
|
||||
Arg1 arg1_;
|
||||
};
|
||||
|
||||
template <typename Handler, typename Arg1>
|
||||
inline move_binder1<decay_t<Handler>, decay_t<Arg1>> move_bind_handler(
|
||||
Handler&& handler, Arg1&& arg1)
|
||||
{
|
||||
return move_binder1<decay_t<Handler>, decay_t<Arg1>>(0,
|
||||
static_cast<Handler&&>(handler), static_cast<Arg1&&>(arg1));
|
||||
}
|
||||
|
||||
template <typename Handler, typename Arg1>
|
||||
inline bool asio_handler_is_continuation(
|
||||
move_binder1<Handler, Arg1>* this_handler)
|
||||
|
||||
@@ -497,7 +497,7 @@ public:
|
||||
handler, *static_cast<const IoExecutor*>(io_ex));
|
||||
|
||||
(initiate_dispatch_with_executor<immediate_ex_type>(immediate_ex))(
|
||||
static_cast<Function&&>(function));
|
||||
static_cast<Function&&>(function), empty_work_function());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -92,15 +92,16 @@ public:
|
||||
return ex_;
|
||||
}
|
||||
|
||||
template <typename CompletionHandler>
|
||||
void operator()(CompletionHandler&& handler,
|
||||
template <typename CompletionHandler, typename Function>
|
||||
void operator()(CompletionHandler&& handler, Function&&,
|
||||
enable_if_t<
|
||||
execution::is_executor<
|
||||
conditional_t<true, executor_type, CompletionHandler>
|
||||
>::value
|
||||
>* = 0,
|
||||
enable_if_t<
|
||||
!detail::is_work_dispatcher_required<
|
||||
!is_work_dispatcher_required<
|
||||
decay_t<Function>,
|
||||
decay_t<CompletionHandler>,
|
||||
Executor
|
||||
>::value
|
||||
@@ -118,21 +119,23 @@ public:
|
||||
static_cast<CompletionHandler&&>(handler)));
|
||||
}
|
||||
|
||||
template <typename CompletionHandler>
|
||||
void operator()(CompletionHandler&& handler,
|
||||
template <typename CompletionHandler, typename Function>
|
||||
void operator()(CompletionHandler&& handler, Function&& function,
|
||||
enable_if_t<
|
||||
execution::is_executor<
|
||||
conditional_t<true, executor_type, CompletionHandler>
|
||||
>::value
|
||||
>* = 0,
|
||||
enable_if_t<
|
||||
detail::is_work_dispatcher_required<
|
||||
is_work_dispatcher_required<
|
||||
decay_t<Function>,
|
||||
decay_t<CompletionHandler>,
|
||||
Executor
|
||||
>::value
|
||||
>* = 0) const
|
||||
{
|
||||
typedef decay_t<CompletionHandler> handler_t;
|
||||
typedef decay_t<Function> function_t;
|
||||
|
||||
typedef associated_executor_t<handler_t, Executor> handler_ex_t;
|
||||
handler_ex_t handler_ex((get_associated_executor)(handler, ex_));
|
||||
@@ -145,19 +148,21 @@ public:
|
||||
execution::relationship.continuation,
|
||||
execution::allocator(alloc)
|
||||
).execute(
|
||||
detail::work_dispatcher<handler_t, handler_ex_t>(
|
||||
work_dispatcher<function_t, handler_t, handler_ex_t>(
|
||||
static_cast<Function&&>(function),
|
||||
static_cast<CompletionHandler&&>(handler), handler_ex));
|
||||
}
|
||||
|
||||
template <typename CompletionHandler>
|
||||
void operator()(CompletionHandler&& handler,
|
||||
template <typename CompletionHandler, typename Function>
|
||||
void operator()(CompletionHandler&& handler, Function&&,
|
||||
enable_if_t<
|
||||
!execution::is_executor<
|
||||
conditional_t<true, executor_type, CompletionHandler>
|
||||
>::value
|
||||
>* = 0,
|
||||
enable_if_t<
|
||||
!detail::is_work_dispatcher_required<
|
||||
!is_work_dispatcher_required<
|
||||
decay_t<Function>,
|
||||
decay_t<CompletionHandler>,
|
||||
Executor
|
||||
>::value
|
||||
@@ -170,21 +175,23 @@ public:
|
||||
static_cast<CompletionHandler&&>(handler)), alloc);
|
||||
}
|
||||
|
||||
template <typename CompletionHandler>
|
||||
void operator()(CompletionHandler&& handler,
|
||||
template <typename CompletionHandler, typename Function>
|
||||
void operator()(CompletionHandler&& handler, Function&& function,
|
||||
enable_if_t<
|
||||
!execution::is_executor<
|
||||
conditional_t<true, executor_type, CompletionHandler>
|
||||
>::value
|
||||
>* = 0,
|
||||
enable_if_t<
|
||||
detail::is_work_dispatcher_required<
|
||||
is_work_dispatcher_required<
|
||||
decay_t<Function>,
|
||||
decay_t<CompletionHandler>,
|
||||
Executor
|
||||
>::value
|
||||
>* = 0) const
|
||||
{
|
||||
typedef decay_t<CompletionHandler> handler_t;
|
||||
typedef decay_t<Function> function_t;
|
||||
|
||||
typedef associated_executor_t<handler_t, Executor> handler_ex_t;
|
||||
handler_ex_t handler_ex((get_associated_executor)(handler, ex_));
|
||||
@@ -192,7 +199,8 @@ public:
|
||||
associated_allocator_t<handler_t> alloc(
|
||||
(get_associated_allocator)(handler));
|
||||
|
||||
ex_.defer(detail::work_dispatcher<handler_t, handler_ex_t>(
|
||||
ex_.defer(work_dispatcher<function_t, handler_t, handler_ex_t>(
|
||||
static_cast<Function&&>(function),
|
||||
static_cast<CompletionHandler&&>(handler), handler_ex), alloc);
|
||||
}
|
||||
|
||||
|
||||
@@ -86,15 +86,16 @@ public:
|
||||
return ex_;
|
||||
}
|
||||
|
||||
template <typename CompletionHandler>
|
||||
void operator()(CompletionHandler&& handler,
|
||||
template <typename CompletionHandler, typename Function>
|
||||
void operator()(CompletionHandler&& handler, Function&&,
|
||||
enable_if_t<
|
||||
execution::is_executor<
|
||||
conditional_t<true, executor_type, CompletionHandler>
|
||||
>::value
|
||||
>* = 0,
|
||||
enable_if_t<
|
||||
!detail::is_work_dispatcher_required<
|
||||
!is_work_dispatcher_required<
|
||||
decay_t<Function>,
|
||||
decay_t<CompletionHandler>,
|
||||
Executor
|
||||
>::value
|
||||
@@ -108,21 +109,23 @@ public:
|
||||
static_cast<CompletionHandler&&>(handler)));
|
||||
}
|
||||
|
||||
template <typename CompletionHandler>
|
||||
void operator()(CompletionHandler&& handler,
|
||||
template <typename CompletionHandler, typename Function>
|
||||
void operator()(CompletionHandler&& handler, Function&& function,
|
||||
enable_if_t<
|
||||
execution::is_executor<
|
||||
conditional_t<true, executor_type, CompletionHandler>
|
||||
>::value
|
||||
>* = 0,
|
||||
enable_if_t<
|
||||
detail::is_work_dispatcher_required<
|
||||
is_work_dispatcher_required<
|
||||
decay_t<Function>,
|
||||
decay_t<CompletionHandler>,
|
||||
Executor
|
||||
>::value
|
||||
>* = 0) const
|
||||
{
|
||||
typedef decay_t<CompletionHandler> handler_t;
|
||||
typedef decay_t<Function> function_t;
|
||||
|
||||
typedef associated_executor_t<handler_t, Executor> handler_ex_t;
|
||||
handler_ex_t handler_ex((get_associated_executor)(handler, ex_));
|
||||
@@ -131,19 +134,21 @@ public:
|
||||
(get_associated_allocator)(handler));
|
||||
|
||||
boost::asio::prefer(ex_, execution::allocator(alloc)).execute(
|
||||
detail::work_dispatcher<handler_t, handler_ex_t>(
|
||||
work_dispatcher<function_t, handler_t, handler_ex_t>(
|
||||
static_cast<Function&&>(function),
|
||||
static_cast<CompletionHandler&&>(handler), handler_ex));
|
||||
}
|
||||
|
||||
template <typename CompletionHandler>
|
||||
void operator()(CompletionHandler&& handler,
|
||||
template <typename CompletionHandler, typename Function>
|
||||
void operator()(CompletionHandler&& handler, Function&&,
|
||||
enable_if_t<
|
||||
!execution::is_executor<
|
||||
conditional_t<true, executor_type, CompletionHandler>
|
||||
>::value
|
||||
>* = 0,
|
||||
enable_if_t<
|
||||
!detail::is_work_dispatcher_required<
|
||||
!is_work_dispatcher_required<
|
||||
decay_t<Function>,
|
||||
decay_t<CompletionHandler>,
|
||||
Executor
|
||||
>::value
|
||||
@@ -156,21 +161,23 @@ public:
|
||||
static_cast<CompletionHandler&&>(handler)), alloc);
|
||||
}
|
||||
|
||||
template <typename CompletionHandler>
|
||||
void operator()(CompletionHandler&& handler,
|
||||
template <typename CompletionHandler, typename Function>
|
||||
void operator()(CompletionHandler&& handler, Function&& function,
|
||||
enable_if_t<
|
||||
!execution::is_executor<
|
||||
conditional_t<true, executor_type, CompletionHandler>
|
||||
>::value
|
||||
>* = 0,
|
||||
enable_if_t<
|
||||
detail::is_work_dispatcher_required<
|
||||
is_work_dispatcher_required<
|
||||
decay_t<Function>,
|
||||
decay_t<CompletionHandler>,
|
||||
Executor
|
||||
>::value
|
||||
>* = 0) const
|
||||
{
|
||||
typedef decay_t<CompletionHandler> handler_t;
|
||||
typedef decay_t<Function> function_t;
|
||||
|
||||
typedef associated_executor_t<handler_t, Executor> handler_ex_t;
|
||||
handler_ex_t handler_ex((get_associated_executor)(handler, ex_));
|
||||
@@ -178,7 +185,8 @@ public:
|
||||
associated_allocator_t<handler_t> alloc(
|
||||
(get_associated_allocator)(handler));
|
||||
|
||||
ex_.dispatch(detail::work_dispatcher<handler_t, handler_ex_t>(
|
||||
ex_.dispatch(work_dispatcher<function_t, handler_t, handler_ex_t>(
|
||||
static_cast<Function&&>(function),
|
||||
static_cast<CompletionHandler&&>(handler), handler_ex), alloc);
|
||||
}
|
||||
|
||||
|
||||
@@ -92,15 +92,16 @@ public:
|
||||
return ex_;
|
||||
}
|
||||
|
||||
template <typename CompletionHandler>
|
||||
void operator()(CompletionHandler&& handler,
|
||||
template <typename CompletionHandler, typename Function>
|
||||
void operator()(CompletionHandler&& handler, Function&&,
|
||||
enable_if_t<
|
||||
execution::is_executor<
|
||||
conditional_t<true, executor_type, CompletionHandler>
|
||||
>::value
|
||||
>* = 0,
|
||||
enable_if_t<
|
||||
!detail::is_work_dispatcher_required<
|
||||
!is_work_dispatcher_required<
|
||||
decay_t<Function>,
|
||||
decay_t<CompletionHandler>,
|
||||
Executor
|
||||
>::value
|
||||
@@ -118,21 +119,23 @@ public:
|
||||
static_cast<CompletionHandler&&>(handler)));
|
||||
}
|
||||
|
||||
template <typename CompletionHandler>
|
||||
void operator()(CompletionHandler&& handler,
|
||||
template <typename CompletionHandler, typename Function>
|
||||
void operator()(CompletionHandler&& handler, Function&& function,
|
||||
enable_if_t<
|
||||
execution::is_executor<
|
||||
conditional_t<true, executor_type, CompletionHandler>
|
||||
>::value
|
||||
>* = 0,
|
||||
enable_if_t<
|
||||
detail::is_work_dispatcher_required<
|
||||
is_work_dispatcher_required<
|
||||
decay_t<Function>,
|
||||
decay_t<CompletionHandler>,
|
||||
Executor
|
||||
>::value
|
||||
>* = 0) const
|
||||
{
|
||||
typedef decay_t<CompletionHandler> handler_t;
|
||||
typedef decay_t<Function> function_t;
|
||||
|
||||
typedef associated_executor_t<handler_t, Executor> handler_ex_t;
|
||||
handler_ex_t handler_ex((get_associated_executor)(handler, ex_));
|
||||
@@ -145,19 +148,21 @@ public:
|
||||
execution::relationship.fork,
|
||||
execution::allocator(alloc)
|
||||
).execute(
|
||||
detail::work_dispatcher<handler_t, handler_ex_t>(
|
||||
work_dispatcher<function_t, handler_t, handler_ex_t>(
|
||||
static_cast<Function&&>(function),
|
||||
static_cast<CompletionHandler&&>(handler), handler_ex));
|
||||
}
|
||||
|
||||
template <typename CompletionHandler>
|
||||
void operator()(CompletionHandler&& handler,
|
||||
template <typename CompletionHandler, typename Function>
|
||||
void operator()(CompletionHandler&& handler, Function&&,
|
||||
enable_if_t<
|
||||
!execution::is_executor<
|
||||
conditional_t<true, executor_type, CompletionHandler>
|
||||
>::value
|
||||
>* = 0,
|
||||
enable_if_t<
|
||||
!detail::is_work_dispatcher_required<
|
||||
!is_work_dispatcher_required<
|
||||
decay_t<Function>,
|
||||
decay_t<CompletionHandler>,
|
||||
Executor
|
||||
>::value
|
||||
@@ -170,21 +175,23 @@ public:
|
||||
static_cast<CompletionHandler&&>(handler)), alloc);
|
||||
}
|
||||
|
||||
template <typename CompletionHandler>
|
||||
void operator()(CompletionHandler&& handler,
|
||||
template <typename CompletionHandler, typename Function>
|
||||
void operator()(CompletionHandler&& handler, Function&& function,
|
||||
enable_if_t<
|
||||
!execution::is_executor<
|
||||
conditional_t<true, executor_type, CompletionHandler>
|
||||
>::value
|
||||
>* = 0,
|
||||
enable_if_t<
|
||||
detail::is_work_dispatcher_required<
|
||||
is_work_dispatcher_required<
|
||||
decay_t<Function>,
|
||||
decay_t<CompletionHandler>,
|
||||
Executor
|
||||
>::value
|
||||
>* = 0) const
|
||||
{
|
||||
typedef decay_t<CompletionHandler> handler_t;
|
||||
typedef decay_t<Function> function_t;
|
||||
|
||||
typedef associated_executor_t<handler_t, Executor> handler_ex_t;
|
||||
handler_ex_t handler_ex((get_associated_executor)(handler, ex_));
|
||||
@@ -192,7 +199,8 @@ public:
|
||||
associated_allocator_t<handler_t> alloc(
|
||||
(get_associated_allocator)(handler));
|
||||
|
||||
ex_.post(detail::work_dispatcher<handler_t, handler_ex_t>(
|
||||
ex_.post(work_dispatcher<function_t, handler_t, handler_ex_t>(
|
||||
static_cast<Function&&>(function),
|
||||
static_cast<CompletionHandler&&>(handler), handler_ex), alloc);
|
||||
}
|
||||
|
||||
|
||||
@@ -110,6 +110,8 @@ using std::is_scalar;
|
||||
|
||||
using std::is_unsigned;
|
||||
|
||||
using std::is_void;
|
||||
|
||||
using std::remove_cv;
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -33,13 +33,30 @@ namespace boost {
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
template <typename Handler, typename Executor, typename = void>
|
||||
struct empty_work_function
|
||||
{
|
||||
void operator()() const noexcept
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Function>
|
||||
struct work_result
|
||||
{
|
||||
typedef decay_t<result_of_t<decay_t<Function>()>> type;
|
||||
};
|
||||
|
||||
template <typename Function>
|
||||
using work_result_t = typename work_result<Function>::type;
|
||||
|
||||
template <typename Function, typename Handler,
|
||||
typename Executor, typename = void>
|
||||
struct is_work_dispatcher_required : true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename Handler, typename Executor>
|
||||
struct is_work_dispatcher_required<Handler, Executor,
|
||||
struct is_work_dispatcher_required<empty_work_function, Handler, Executor,
|
||||
enable_if_t<
|
||||
is_same<
|
||||
typename associated_executor<Handler,
|
||||
@@ -50,27 +67,166 @@ struct is_work_dispatcher_required<Handler, Executor,
|
||||
{
|
||||
};
|
||||
|
||||
template <typename Handler, typename Executor, typename = void>
|
||||
class work_dispatcher
|
||||
template <typename Function,
|
||||
bool IsVoid = is_void<result_of_t<Function()>>::value,
|
||||
bool IsClass = is_class<Function>::value>
|
||||
class work_dispatcher_function
|
||||
{
|
||||
Function function_;
|
||||
|
||||
public:
|
||||
template <typename F>
|
||||
work_dispatcher_function(F&& function)
|
||||
: function_(static_cast<F&&>(function))
|
||||
{
|
||||
}
|
||||
|
||||
work_dispatcher_function(const work_dispatcher_function& other)
|
||||
: function_(other.function_)
|
||||
{
|
||||
}
|
||||
|
||||
work_dispatcher_function(work_dispatcher_function&& other)
|
||||
: function_(static_cast<Function&&>(other.function_))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename Handler>
|
||||
auto bind_result(Handler&& handler)
|
||||
-> decltype(
|
||||
boost::asio::detail::move_bind_handler(
|
||||
static_cast<Handler&&>(handler),
|
||||
static_cast<Function&&>(function_)()))
|
||||
{
|
||||
return boost::asio::detail::move_bind_handler(
|
||||
static_cast<Handler&&>(handler),
|
||||
static_cast<Function&&>(function_)());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Function>
|
||||
class work_dispatcher_function<Function, false, true> : Function
|
||||
{
|
||||
public:
|
||||
template <typename CompletionHandler>
|
||||
work_dispatcher(CompletionHandler&& handler,
|
||||
template <typename F>
|
||||
work_dispatcher_function(F&& function)
|
||||
: Function(static_cast<F&&>(function))
|
||||
{
|
||||
}
|
||||
|
||||
work_dispatcher_function(const work_dispatcher_function& other)
|
||||
: Function(static_cast<const Function&>(other))
|
||||
{
|
||||
}
|
||||
|
||||
work_dispatcher_function(work_dispatcher_function&& other)
|
||||
: Function(static_cast<Function&&>(other))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename Handler>
|
||||
auto bind_result(Handler&& handler)
|
||||
-> decltype(
|
||||
boost::asio::detail::move_bind_handler(
|
||||
static_cast<Handler&&>(handler),
|
||||
static_cast<Function&&>(*static_cast<Function*>(this))()))
|
||||
{
|
||||
return boost::asio::detail::move_bind_handler(
|
||||
static_cast<Handler&&>(handler),
|
||||
static_cast<Function&&>(*static_cast<Function*>(this))());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Function>
|
||||
class work_dispatcher_function<Function, true, false>
|
||||
{
|
||||
Function function_;
|
||||
|
||||
public:
|
||||
template <typename F>
|
||||
work_dispatcher_function(F&& function)
|
||||
: function_(static_cast<Function&&>(function))
|
||||
{
|
||||
}
|
||||
|
||||
work_dispatcher_function(const work_dispatcher_function& other)
|
||||
: function_(other.function_)
|
||||
{
|
||||
}
|
||||
|
||||
work_dispatcher_function(work_dispatcher_function&& other)
|
||||
: function_(static_cast<Function&&>(other.function_))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename Handler>
|
||||
auto bind_result(Handler&& handler)
|
||||
-> decltype(boost::asio::detail::bind_handler(
|
||||
static_cast<Handler&&>(handler)))
|
||||
{
|
||||
static_cast<Function&&>(function_)();
|
||||
return boost::asio::detail::bind_handler(
|
||||
static_cast<Handler&&>(handler));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Function>
|
||||
class work_dispatcher_function<Function, true, true> : Function
|
||||
{
|
||||
public:
|
||||
template <typename F>
|
||||
work_dispatcher_function(F&& function)
|
||||
: Function(static_cast<F&&>(function))
|
||||
{
|
||||
}
|
||||
|
||||
work_dispatcher_function(const work_dispatcher_function& other)
|
||||
: Function(static_cast<const Function&>(other))
|
||||
{
|
||||
}
|
||||
|
||||
work_dispatcher_function(work_dispatcher_function&& other)
|
||||
: Function(static_cast<Function&&>(other))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename Handler>
|
||||
auto bind_result(Handler&& handler)
|
||||
-> decltype(boost::asio::detail::bind_handler(
|
||||
static_cast<Handler&&>(handler)))
|
||||
{
|
||||
static_cast<Function&&>(*static_cast<Function*>(this))();
|
||||
return boost::asio::detail::bind_handler(
|
||||
static_cast<Handler&&>(handler));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Function, typename Handler,
|
||||
typename Executor, typename = void>
|
||||
class work_dispatcher : work_dispatcher_function<Function>
|
||||
{
|
||||
public:
|
||||
template <typename F, typename CompletionHandler>
|
||||
work_dispatcher(F&& function, CompletionHandler&& handler,
|
||||
const Executor& handler_ex)
|
||||
: handler_(static_cast<CompletionHandler&&>(handler)),
|
||||
: work_dispatcher_function<Function>(static_cast<F&&>(function)),
|
||||
handler_(static_cast<CompletionHandler&&>(handler)),
|
||||
executor_(boost::asio::prefer(handler_ex,
|
||||
execution::outstanding_work.tracked))
|
||||
{
|
||||
}
|
||||
|
||||
work_dispatcher(const work_dispatcher& other)
|
||||
: handler_(other.handler_),
|
||||
: work_dispatcher_function<Function>(other),
|
||||
handler_(other.handler_),
|
||||
executor_(other.executor_)
|
||||
{
|
||||
}
|
||||
|
||||
work_dispatcher(work_dispatcher&& other)
|
||||
: handler_(static_cast<Handler&&>(other.handler_)),
|
||||
: work_dispatcher_function<Function>(
|
||||
static_cast<work_dispatcher_function<Function>&&>(other)),
|
||||
handler_(static_cast<Handler&&>(other.handler_)),
|
||||
executor_(static_cast<work_executor_type&&>(other.executor_))
|
||||
{
|
||||
}
|
||||
@@ -79,8 +235,7 @@ public:
|
||||
{
|
||||
associated_allocator_t<Handler> alloc((get_associated_allocator)(handler_));
|
||||
boost::asio::prefer(executor_, execution::allocator(alloc)).execute(
|
||||
boost::asio::detail::bind_handler(
|
||||
static_cast<Handler&&>(handler_)));
|
||||
this->bind_result(static_cast<Handler&&>(handler_)));
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -96,26 +251,32 @@ private:
|
||||
|
||||
#if !defined(BOOST_ASIO_NO_TS_EXECUTORS)
|
||||
|
||||
template <typename Handler, typename Executor>
|
||||
class work_dispatcher<Handler, Executor,
|
||||
template <typename Function, typename Handler, typename Executor>
|
||||
class work_dispatcher<Function, Handler, Executor,
|
||||
enable_if_t<!execution::is_executor<Executor>::value>>
|
||||
: work_dispatcher_function<Function>
|
||||
{
|
||||
public:
|
||||
template <typename CompletionHandler>
|
||||
work_dispatcher(CompletionHandler&& handler, const Executor& handler_ex)
|
||||
: work_(handler_ex),
|
||||
template <typename F, typename CompletionHandler>
|
||||
work_dispatcher(F&& function, CompletionHandler&& handler,
|
||||
const Executor& handler_ex)
|
||||
: work_dispatcher_function<Function>(static_cast<F&&>(function)),
|
||||
work_(handler_ex),
|
||||
handler_(static_cast<CompletionHandler&&>(handler))
|
||||
{
|
||||
}
|
||||
|
||||
work_dispatcher(const work_dispatcher& other)
|
||||
: work_(other.work_),
|
||||
: work_dispatcher_function<Function>(other),
|
||||
work_(other.work_),
|
||||
handler_(other.handler_)
|
||||
{
|
||||
}
|
||||
|
||||
work_dispatcher(work_dispatcher&& other)
|
||||
: work_(static_cast<executor_work_guard<Executor>&&>(other.work_)),
|
||||
: work_dispatcher_function<Function>(
|
||||
static_cast<work_dispatcher_function<Function>&&>(*this)),
|
||||
work_(static_cast<executor_work_guard<Executor>&&>(other.work_)),
|
||||
handler_(static_cast<Handler&&>(other.handler_))
|
||||
{
|
||||
}
|
||||
@@ -124,8 +285,7 @@ public:
|
||||
{
|
||||
associated_allocator_t<Handler> alloc((get_associated_allocator)(handler_));
|
||||
work_.get_executor().dispatch(
|
||||
boost::asio::detail::bind_handler(
|
||||
static_cast<Handler&&>(handler_)), alloc);
|
||||
this->bind_result(static_cast<Handler&&>(handler_)), alloc);
|
||||
work_.reset();
|
||||
}
|
||||
|
||||
|
||||
@@ -100,55 +100,62 @@ public:
|
||||
void operator()()
|
||||
{
|
||||
detail::initiate_dispatch_with_executor<executor_type>(
|
||||
this->get_executor())(static_cast<Handler&&>(handler_));
|
||||
this->get_executor())(static_cast<Handler&&>(handler_),
|
||||
empty_work_function());
|
||||
}
|
||||
|
||||
void operator()() const
|
||||
{
|
||||
detail::initiate_dispatch_with_executor<executor_type>(
|
||||
this->get_executor())(handler_);
|
||||
this->get_executor())(handler_, empty_work_function());
|
||||
}
|
||||
|
||||
template <typename Arg1>
|
||||
void operator()(const Arg1& arg1)
|
||||
{
|
||||
detail::initiate_dispatch_with_executor<executor_type>(
|
||||
this->get_executor())(detail::bind_handler(handler_, arg1));
|
||||
this->get_executor())(detail::bind_handler(handler_, arg1),
|
||||
empty_work_function());
|
||||
}
|
||||
|
||||
template <typename Arg1>
|
||||
void operator()(const Arg1& arg1) const
|
||||
{
|
||||
detail::initiate_dispatch_with_executor<executor_type>(
|
||||
this->get_executor())(detail::bind_handler(handler_, arg1));
|
||||
this->get_executor())(detail::bind_handler(handler_, arg1),
|
||||
empty_work_function());
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2>
|
||||
void operator()(const Arg1& arg1, const Arg2& arg2)
|
||||
{
|
||||
detail::initiate_dispatch_with_executor<executor_type>(
|
||||
this->get_executor())(detail::bind_handler(handler_, arg1, arg2));
|
||||
this->get_executor())(detail::bind_handler(handler_, arg1, arg2),
|
||||
empty_work_function());
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2>
|
||||
void operator()(const Arg1& arg1, const Arg2& arg2) const
|
||||
{
|
||||
detail::initiate_dispatch_with_executor<executor_type>(
|
||||
this->get_executor())(detail::bind_handler(handler_, arg1, arg2));
|
||||
this->get_executor())(detail::bind_handler(handler_, arg1, arg2),
|
||||
empty_work_function());
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2, typename Arg3>
|
||||
void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3)
|
||||
{
|
||||
detail::initiate_dispatch_with_executor<executor_type>(
|
||||
this->get_executor())(detail::bind_handler(handler_, arg1, arg2, arg3));
|
||||
this->get_executor())(detail::bind_handler(handler_, arg1, arg2, arg3),
|
||||
empty_work_function());
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2, typename Arg3>
|
||||
void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) const
|
||||
{
|
||||
detail::initiate_dispatch_with_executor<executor_type>(
|
||||
this->get_executor())(detail::bind_handler(handler_, arg1, arg2, arg3));
|
||||
this->get_executor())(detail::bind_handler(handler_, arg1, arg2, arg3),
|
||||
empty_work_function());
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
|
||||
@@ -157,7 +164,8 @@ public:
|
||||
{
|
||||
detail::initiate_dispatch_with_executor<executor_type>(
|
||||
this->get_executor())(
|
||||
detail::bind_handler(handler_, arg1, arg2, arg3, arg4));
|
||||
detail::bind_handler(handler_, arg1, arg2, arg3, arg4),
|
||||
empty_work_function());
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
|
||||
@@ -166,7 +174,8 @@ public:
|
||||
{
|
||||
detail::initiate_dispatch_with_executor<executor_type>(
|
||||
this->get_executor())(
|
||||
detail::bind_handler(handler_, arg1, arg2, arg3, arg4));
|
||||
detail::bind_handler(handler_, arg1, arg2, arg3, arg4),
|
||||
empty_work_function());
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
|
||||
@@ -176,7 +185,8 @@ public:
|
||||
{
|
||||
detail::initiate_dispatch_with_executor<executor_type>(
|
||||
this->get_executor())(
|
||||
detail::bind_handler(handler_, arg1, arg2, arg3, arg4, arg5));
|
||||
detail::bind_handler(handler_, arg1, arg2, arg3, arg4, arg5),
|
||||
empty_work_function());
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
|
||||
@@ -186,7 +196,8 @@ public:
|
||||
{
|
||||
detail::initiate_dispatch_with_executor<executor_type>(
|
||||
this->get_executor())(
|
||||
detail::bind_handler(handler_, arg1, arg2, arg3, arg4, arg5));
|
||||
detail::bind_handler(handler_, arg1, arg2, arg3, arg4, arg5),
|
||||
empty_work_function());
|
||||
}
|
||||
|
||||
//private:
|
||||
|
||||
@@ -52,10 +52,11 @@ namespace asio {
|
||||
* The function call operator of @c Init:
|
||||
*
|
||||
* @li Obtains the handler's associated executor object @c ex of type @c Ex by
|
||||
* performing @code auto ex = get_associated_executor(handler); @endcode
|
||||
* performing
|
||||
* @code auto ex = get_associated_executor(completion_handler); @endcode
|
||||
*
|
||||
* @li Obtains the handler's associated allocator object @c alloc by performing
|
||||
* @code auto alloc = get_associated_allocator(handler); @endcode
|
||||
* @code auto alloc = get_associated_allocator(completion_handler); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex>::value</tt> is true, performs
|
||||
* @code prefer(ex, execution::allocator(alloc)).execute(
|
||||
@@ -110,10 +111,11 @@ inline auto dispatch(NullaryToken&& token)
|
||||
* The function call operator of @c Init:
|
||||
*
|
||||
* @li Obtains the handler's associated executor object @c ex1 of type @c Ex1 by
|
||||
* performing @code auto ex1 = get_associated_executor(handler, ex); @endcode
|
||||
* performing
|
||||
* @code auto ex1 = get_associated_executor(completion_handler, ex); @endcode
|
||||
*
|
||||
* @li Obtains the handler's associated allocator object @c alloc by performing
|
||||
* @code auto alloc = get_associated_allocator(handler); @endcode
|
||||
* @code auto alloc = get_associated_allocator(completion_handler); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex1>::value</tt> is true, constructs a
|
||||
* function object @c f with a member @c executor_ that is initialised with
|
||||
@@ -132,10 +134,10 @@ inline auto dispatch(NullaryToken&& token)
|
||||
* work_.get_executor().dispatch(std::move(handler_), a);
|
||||
* work_.reset(); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex>::value</tt> is true, performs
|
||||
* @li If <tt>execution::is_executor<Executor>::value</tt> is true, performs
|
||||
* @code prefer(ex, execution::allocator(alloc)).execute(std::move(f)); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex>::value</tt> is false, performs
|
||||
* @li If <tt>execution::is_executor<Executor>::value</tt> is false, performs
|
||||
* @code ex.dispatch(std::move(f), alloc); @endcode
|
||||
*
|
||||
* @par Completion Signature
|
||||
@@ -151,10 +153,12 @@ inline auto dispatch(const Executor& ex,
|
||||
> = 0)
|
||||
-> decltype(
|
||||
async_initiate<NullaryToken, void()>(
|
||||
declval<detail::initiate_dispatch_with_executor<Executor>>(), token))
|
||||
declval<detail::initiate_dispatch_with_executor<Executor>>(),
|
||||
token, detail::empty_work_function()))
|
||||
{
|
||||
return async_initiate<NullaryToken, void()>(
|
||||
detail::initiate_dispatch_with_executor<Executor>(ex), token);
|
||||
detail::initiate_dispatch_with_executor<Executor>(ex),
|
||||
token, detail::empty_work_function());
|
||||
}
|
||||
|
||||
/// Submits a completion token or function object for execution.
|
||||
@@ -165,8 +169,7 @@ inline auto dispatch(const Executor& ex,
|
||||
* completion handler. The function signature of the completion handler must be:
|
||||
* @code void handler(); @endcode
|
||||
*
|
||||
* @returns <tt>dispatch(ctx.get_executor(),
|
||||
* forward<NullaryToken>(token))</tt>.
|
||||
* @returns <tt>dispatch(ctx.get_executor(), forward<NullaryToken>(token))</tt>.
|
||||
*
|
||||
* @par Completion Signature
|
||||
* @code void() @endcode
|
||||
@@ -183,12 +186,323 @@ inline auto dispatch(ExecutionContext& ctx,
|
||||
-> decltype(
|
||||
async_initiate<NullaryToken, void()>(
|
||||
declval<detail::initiate_dispatch_with_executor<
|
||||
typename ExecutionContext::executor_type>>(), token))
|
||||
typename ExecutionContext::executor_type>>(),
|
||||
token, detail::empty_work_function()))
|
||||
{
|
||||
return async_initiate<NullaryToken, void()>(
|
||||
detail::initiate_dispatch_with_executor<
|
||||
typename ExecutionContext::executor_type>(
|
||||
ctx.get_executor()), token);
|
||||
typename ExecutionContext::executor_type>(ctx.get_executor()),
|
||||
token, detail::empty_work_function());
|
||||
}
|
||||
|
||||
/// Submits a function to be run on a specified target executor, and after
|
||||
/// completion submits the completion handler.
|
||||
/**
|
||||
* This function submits a function object for execution using the specified
|
||||
* executor. The function object may be called from the current thread prior to
|
||||
* returning from <tt>dispatch()</tt>. Otherwise, it is queued for execution.
|
||||
* After the submitted function completes, the completion handler is dispatched
|
||||
* to run on its associated executor.
|
||||
*
|
||||
* @param function A nullary function to be executed on the target executor.
|
||||
*
|
||||
* @param ex The target executor.
|
||||
*
|
||||
* @param token The @ref completion_token that will be used to produce a
|
||||
* completion handler. The function signature of the completion handler must be:
|
||||
* @code void handler(); @endcode
|
||||
*
|
||||
* @returns This function returns <tt>async_initiate<NullaryToken,
|
||||
* void()>(Init{ex}, token, forward<Function>(function))</tt>, where @c Init is
|
||||
* a function object type defined as:
|
||||
*
|
||||
* @code class Init
|
||||
* {
|
||||
* public:
|
||||
* using executor_type = Executor;
|
||||
* explicit Init(const Executor& ex) : ex_(ex) {}
|
||||
* executor_type get_executor() const noexcept { return ex_; }
|
||||
* template <typename CompletionHandler>
|
||||
* void operator()(CompletionHandler&& completion_handler,
|
||||
* Function&& function) const;
|
||||
* private:
|
||||
* Executor ex_; // exposition only
|
||||
* }; @endcode
|
||||
*
|
||||
* The function call operator of @c Init:
|
||||
*
|
||||
* @li Obtains the handler's associated executor object @c ex1 of type @c Ex1 by
|
||||
* performing
|
||||
* @code auto ex1 = get_associated_executor(completion_handler, ex); @endcode
|
||||
*
|
||||
* @li Obtains the handler's associated allocator object @c alloc by performing
|
||||
* @code auto alloc = get_associated_allocator(completion_handler); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex1>::value</tt> is true, constructs a
|
||||
* function object wrapper @c f with a member @c executor_ that is initialised
|
||||
* with <tt>prefer(ex1, execution::outstanding_work.tracked)</tt>, a member @c
|
||||
* function_ that is a decay-copy of @c function, a member @c handler_ that is a
|
||||
* decay-copy of @c completion_handler, and a function call operator that
|
||||
* performs:
|
||||
* @code std::move(function_)();
|
||||
* auto a = get_associated_allocator(handler_);
|
||||
* prefer(executor_, execution::allocator(a)).execute(std::move(handler_));
|
||||
* @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex1>::value</tt> is false, constructs a
|
||||
* function object wrapper @c f with a member @c work_ that is initialised with
|
||||
* <tt>make_work_guard(ex1)</tt>, a member @c function_ that is a decay-copy of
|
||||
* @c function, a member @c handler_ that is a decay-copy of @c
|
||||
* completion_handler, and a function call operator that performs:
|
||||
* @code std::move(function_)();
|
||||
* auto a = get_associated_allocator(handler_);
|
||||
* work_.get_executor().dispatch(std::move(handler_), a);
|
||||
* work_.reset(); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Executor>::value</tt> is true, performs
|
||||
* @code prefer(
|
||||
* require(ex, execution::blocking.never),
|
||||
* execution::relationship.fork,
|
||||
* execution::allocator(alloc)
|
||||
* ).execute(std::move(f)); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Executor>::value</tt> is false, performs
|
||||
* @code ex.dispatch(std::move(f), alloc); @endcode
|
||||
*
|
||||
* @note If the function object throws an exception, that exception is allowed
|
||||
* to propagate to the target executor. The behaviour in this case is dependent
|
||||
* on the executor. For example, boost::asio::io_context will allow the
|
||||
* exception to propagate to the caller that runs the @c io_context, whereas
|
||||
* boost::asio::thread_pool will call @c std::terminate.
|
||||
*
|
||||
* @par Completion Signature
|
||||
* @code void() @endcode
|
||||
*/
|
||||
template <typename Function, typename Executor,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void()) NullaryToken
|
||||
= default_completion_token_t<Executor>>
|
||||
inline auto dispatch(Function&& function, const Executor& ex,
|
||||
NullaryToken&& token = default_completion_token_t<Executor>(),
|
||||
constraint_t<
|
||||
is_void<result_of_t<decay_t<Function>()>>::value
|
||||
> = 0,
|
||||
constraint_t<
|
||||
(execution::is_executor<Executor>::value
|
||||
&& can_require<Executor, execution::blocking_t::never_t>::value)
|
||||
|| is_executor<Executor>::value
|
||||
> = 0)
|
||||
-> decltype(
|
||||
async_initiate<NullaryToken, void()>(
|
||||
declval<detail::initiate_dispatch_with_executor<Executor>>(),
|
||||
token, static_cast<Function&&>(function)))
|
||||
{
|
||||
return async_initiate<NullaryToken, void()>(
|
||||
detail::initiate_dispatch_with_executor<Executor>(ex),
|
||||
token, static_cast<Function&&>(function));
|
||||
}
|
||||
|
||||
/// Submits a function to be run on a specified target executor, and passes the
|
||||
/// result to a completion handler.
|
||||
/**
|
||||
* This function submits a function object for execution using the specified
|
||||
* executor. The function object may be called from the current thread prior to
|
||||
* returning from <tt>dispatch()</tt>. Otherwise, it is queued for execution.
|
||||
* After the submitted function completes, the completion handler is dispatched
|
||||
* along with the function's result, to run on its associated executor.
|
||||
*
|
||||
* @param function A nullary function to be executed on the target executor.
|
||||
*
|
||||
* @param ex The target executor.
|
||||
*
|
||||
* @param token The @ref completion_token that will be used to produce a
|
||||
* completion handler. The function signature of the completion handler must be:
|
||||
* @code void handler(decay_t<result_of_t<decay_t<Function>()>>); @endcode
|
||||
*
|
||||
* @returns This function returns <tt>async_initiate<CompletionToken,
|
||||
* void()>(Init{ex}, token)</tt>, where @c Init is a function object type
|
||||
* defined as:
|
||||
*
|
||||
* @code class Init
|
||||
* {
|
||||
* public:
|
||||
* using executor_type = Executor;
|
||||
* explicit Init(const Executor& ex) : ex_(ex) {}
|
||||
* executor_type get_executor() const noexcept { return ex_; }
|
||||
* template <typename CompletionHandler>
|
||||
* void operator()(CompletionHandler&& completion_handler,
|
||||
* Function&& function) const;
|
||||
* private:
|
||||
* Executor ex_; // exposition only
|
||||
* }; @endcode
|
||||
*
|
||||
* The function call operator of @c Init:
|
||||
*
|
||||
* @li Obtains the handler's associated executor object @c ex1 of type @c Ex1 by
|
||||
* performing
|
||||
* @code auto ex1 = get_associated_executor(completion_handler, ex); @endcode
|
||||
*
|
||||
* @li Obtains the handler's associated allocator object @c alloc by performing
|
||||
* @code auto alloc = get_associated_allocator(completion_handler); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex1>::value</tt> is true, constructs a
|
||||
* function object wrapper @c f with a member @c executor_ that is initialised
|
||||
* with <tt>prefer(ex1, execution::outstanding_work.tracked)</tt>, a member @c
|
||||
* function_ that is a decay-copy of @c function, a member @c handler_ that is a
|
||||
* decay-copy of @c completion_handler, and a function call operator that
|
||||
* performs:
|
||||
* @code auto result = std::move(function_)();
|
||||
* auto a = get_associated_allocator(handler_);
|
||||
* prefer(executor_, execution::allocator(a)).execute(
|
||||
* std::bind(std::move(handler_), std::move(result)));
|
||||
* @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex1>::value</tt> is false, constructs a
|
||||
* function object wrapper @c f with a member @c work_ that is initialised with
|
||||
* <tt>make_work_guard(ex1)</tt>, a member @c function_ that is a decay-copy of
|
||||
* @c function, a member @c handler_ that is a decay-copy of @c
|
||||
* completion_handler, and a function call operator that performs:
|
||||
* @code auto result = std::move(function_)();
|
||||
* auto a = get_associated_allocator(handler_);
|
||||
* work_.get_executor().dispatch(
|
||||
* std::bind(std::move(handler_), std::move(result)), a);
|
||||
* work_.reset(); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Executor>::value</tt> is true, performs
|
||||
* @code prefer(
|
||||
* require(ex, execution::blocking.never),
|
||||
* execution::relationship.fork,
|
||||
* execution::allocator(alloc)
|
||||
* ).execute(std::move(f)); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Executor>::value</tt> is false, performs
|
||||
* @code ex.dispatch(std::move(f), alloc); @endcode
|
||||
*
|
||||
* @note If the function object throws an exception, that exception is allowed
|
||||
* to propagate to the target executor. The behaviour in this case is dependent
|
||||
* on the executor. For example, boost::asio::io_context will allow the
|
||||
* exception to propagate to the caller that runs the @c io_context, whereas
|
||||
* boost::asio::thread_pool will call @c std::terminate.
|
||||
*
|
||||
* @par Completion Signature
|
||||
* @code void(decay_t<result_of_t<decay_t<Function>()>>) @endcode
|
||||
*/
|
||||
template <typename Function, typename Executor,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(
|
||||
void(decay_t<result_of_t<decay_t<Function>()>>)) CompletionToken
|
||||
= default_completion_token_t<Executor>>
|
||||
inline auto dispatch(Function&& function, const Executor& ex,
|
||||
CompletionToken&& token = default_completion_token_t<Executor>(),
|
||||
constraint_t<
|
||||
!is_void<result_of_t<decay_t<Function>()>>::value
|
||||
> = 0,
|
||||
constraint_t<
|
||||
(execution::is_executor<Executor>::value
|
||||
&& can_require<Executor, execution::blocking_t::never_t>::value)
|
||||
|| is_executor<Executor>::value
|
||||
> = 0)
|
||||
-> decltype(
|
||||
async_initiate<CompletionToken, void(detail::work_result_t<Function>)>(
|
||||
declval<detail::initiate_dispatch_with_executor<Executor>>(),
|
||||
token, static_cast<Function&&>(function)))
|
||||
{
|
||||
return async_initiate<CompletionToken, void(detail::work_result_t<Function>)>(
|
||||
detail::initiate_dispatch_with_executor<Executor>(ex),
|
||||
token, static_cast<Function&&>(function));
|
||||
}
|
||||
|
||||
/// Submits a function to be run on a specified execution context, and after
|
||||
/// completion submits the completion handler.
|
||||
/**
|
||||
* @param function A nullary function to be executed on the target executor.
|
||||
*
|
||||
* @param ctx An execution context, from which the target executor is obtained.
|
||||
*
|
||||
* @param token The @ref completion_token that will be used to produce a
|
||||
* completion handler. The function signature of the completion handler must be:
|
||||
* @code void handler(); @endcode
|
||||
*
|
||||
* @returns <tt>dispatch(forward<Function>(function), ctx.get_executor(),
|
||||
* forward<NullaryToken>(token))</tt>.
|
||||
*
|
||||
* @note If the function object throws an exception, that exception is allowed
|
||||
* to propagate to the target executor. The behaviour in this case is dependent
|
||||
* on the executor. For example, boost::asio::io_context will allow the
|
||||
* exception to propagate to the caller that runs the @c io_context, whereas
|
||||
* boost::asio::thread_pool will call @c std::terminate.
|
||||
*
|
||||
* @par Completion Signature
|
||||
* @code void() @endcode
|
||||
*/
|
||||
template <typename Function, typename ExecutionContext,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void()) NullaryToken
|
||||
= default_completion_token_t<typename ExecutionContext::executor_type>>
|
||||
inline auto dispatch(Function&& function, ExecutionContext& ctx,
|
||||
NullaryToken&& token = default_completion_token_t<
|
||||
typename ExecutionContext::executor_type>(),
|
||||
constraint_t<
|
||||
is_void<result_of_t<decay_t<Function>()>>::value
|
||||
> = 0,
|
||||
constraint_t<
|
||||
is_convertible<ExecutionContext&, execution_context&>::value
|
||||
> = 0)
|
||||
-> decltype(
|
||||
async_initiate<NullaryToken, void()>(
|
||||
declval<detail::initiate_dispatch_with_executor<
|
||||
typename ExecutionContext::executor_type>>(),
|
||||
token, static_cast<Function&&>(function)))
|
||||
{
|
||||
return async_initiate<NullaryToken, void()>(
|
||||
detail::initiate_dispatch_with_executor<
|
||||
typename ExecutionContext::executor_type>(ctx.get_executor()),
|
||||
token, static_cast<Function&&>(function));
|
||||
}
|
||||
|
||||
/// Submits a function to be run on a specified execution context, and passes
|
||||
/// the result to a completion handler.
|
||||
/**
|
||||
* @param function A nullary function to be executed on the target executor.
|
||||
*
|
||||
* @param ctx An execution context, from which the target executor is obtained.
|
||||
*
|
||||
* @param token The @ref completion_token that will be used to produce a
|
||||
* completion handler. The function signature of the completion handler must be:
|
||||
* @code void handler(); @endcode
|
||||
*
|
||||
* @returns <tt>dispatch(forward<Function>(function), ctx.get_executor(),
|
||||
* forward<CompletionToken>(token))</tt>.
|
||||
*
|
||||
* @note If the function object throws an exception, that exception is allowed
|
||||
* to propagate to the target executor. The behaviour in this case is dependent
|
||||
* on the executor. For example, boost::asio::io_context will allow the
|
||||
* exception to propagate to the caller that runs the @c io_context, whereas
|
||||
* boost::asio::thread_pool will call @c std::terminate.
|
||||
*
|
||||
* @par Completion Signature
|
||||
* @code void(decay_t<result_of_t<decay_t<Function>()>>) @endcode
|
||||
*/
|
||||
template <typename Function, typename ExecutionContext,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(
|
||||
void(decay_t<result_of_t<decay_t<Function>()>>)) CompletionToken
|
||||
= default_completion_token_t<typename ExecutionContext::executor_type>>
|
||||
inline auto dispatch(Function&& function, ExecutionContext& ctx,
|
||||
CompletionToken&& token = default_completion_token_t<
|
||||
typename ExecutionContext::executor_type>(),
|
||||
constraint_t<
|
||||
!is_void<result_of_t<decay_t<Function>()>>::value
|
||||
> = 0,
|
||||
constraint_t<
|
||||
is_convertible<ExecutionContext&, execution_context&>::value
|
||||
> = 0)
|
||||
-> decltype(
|
||||
async_initiate<CompletionToken, void(detail::work_result_t<Function>)>(
|
||||
declval<detail::initiate_dispatch_with_executor<
|
||||
typename ExecutionContext::executor_type>>(),
|
||||
token, static_cast<Function&&>(function)))
|
||||
{
|
||||
return async_initiate<CompletionToken, void(detail::work_result_t<Function>)>(
|
||||
detail::initiate_dispatch_with_executor<
|
||||
typename ExecutionContext::executor_type>(ctx.get_executor()),
|
||||
token, static_cast<Function&&>(function));
|
||||
}
|
||||
|
||||
} // namespace asio
|
||||
|
||||
@@ -109,7 +109,8 @@ public:
|
||||
void post(const IoExecutor& io_exec, Function& function, Handler&)
|
||||
{
|
||||
(boost::asio::detail::initiate_post_with_executor<IoExecutor>(io_exec))(
|
||||
static_cast<Function&&>(function));
|
||||
static_cast<Function&&>(function),
|
||||
boost::asio::detail::empty_work_function());
|
||||
}
|
||||
|
||||
template <typename Function, typename Handler>
|
||||
@@ -269,8 +270,10 @@ public:
|
||||
immediate_ex_type immediate_ex = (get_associated_immediate_executor)(
|
||||
handler, base1_type::get_executor());
|
||||
|
||||
(boost::asio::detail::initiate_dispatch_with_executor<immediate_ex_type>(
|
||||
immediate_ex))(static_cast<Function&&>(function));
|
||||
(boost::asio::detail::initiate_dispatch_with_executor<
|
||||
immediate_ex_type>(immediate_ex))(
|
||||
static_cast<Function&&>(function),
|
||||
boost::asio::detail::empty_work_function());
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
@@ -288,7 +291,8 @@ public:
|
||||
(boost::asio::detail::initiate_post_with_executor<
|
||||
typename base1_type::executor_type>(
|
||||
base1_type::get_executor()))(
|
||||
static_cast<Function&&>(function));
|
||||
static_cast<Function&&>(function),
|
||||
boost::asio::detail::empty_work_function());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -333,8 +337,10 @@ public:
|
||||
immediate_ex_type immediate_ex = (get_associated_immediate_executor)(
|
||||
handler, base1_type::get_executor());
|
||||
|
||||
(boost::asio::detail::initiate_dispatch_with_executor<immediate_ex_type>(
|
||||
immediate_ex))(static_cast<Function&&>(function));
|
||||
(boost::asio::detail::initiate_dispatch_with_executor<
|
||||
immediate_ex_type>(immediate_ex))(
|
||||
static_cast<Function&&>(function),
|
||||
boost::asio::detail::empty_work_function());
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
|
||||
@@ -57,10 +57,11 @@ namespace asio {
|
||||
* The function call operator of @c Init:
|
||||
*
|
||||
* @li Obtains the handler's associated executor object @c ex of type @c Ex by
|
||||
* performing @code auto ex = get_associated_executor(handler); @endcode
|
||||
* performing
|
||||
* @code auto ex = get_associated_executor(completion_handler); @endcode
|
||||
*
|
||||
* @li Obtains the handler's associated allocator object @c alloc by performing
|
||||
* @code auto alloc = get_associated_allocator(handler); @endcode
|
||||
* @code auto alloc = get_associated_allocator(completion_handler); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex>::value</tt> is true, performs
|
||||
* @code prefer(
|
||||
@@ -121,10 +122,11 @@ inline auto post(NullaryToken&& token)
|
||||
* The function call operator of @c Init:
|
||||
*
|
||||
* @li Obtains the handler's associated executor object @c ex1 of type @c Ex1 by
|
||||
* performing @code auto ex1 = get_associated_executor(handler, ex); @endcode
|
||||
* performing
|
||||
* @code auto ex1 = get_associated_executor(completion_handler, ex); @endcode
|
||||
*
|
||||
* @li Obtains the handler's associated allocator object @c alloc by performing
|
||||
* @code auto alloc = get_associated_allocator(handler); @endcode
|
||||
* @code auto alloc = get_associated_allocator(completion_handler); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex1>::value</tt> is true, constructs a
|
||||
* function object @c f with a member @c executor_ that is initialised with
|
||||
@@ -143,14 +145,14 @@ inline auto post(NullaryToken&& token)
|
||||
* work_.get_executor().dispatch(std::move(handler_), a);
|
||||
* work_.reset(); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex>::value</tt> is true, performs
|
||||
* @li If <tt>execution::is_executor<Executor>::value</tt> is true, performs
|
||||
* @code prefer(
|
||||
* require(ex, execution::blocking.never),
|
||||
* execution::relationship.fork,
|
||||
* execution::allocator(alloc)
|
||||
* ).execute(std::move(f)); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex>::value</tt> is false, performs
|
||||
* @li If <tt>execution::is_executor<Executor>::value</tt> is false, performs
|
||||
* @code ex.post(std::move(f), alloc); @endcode
|
||||
*
|
||||
* @par Completion Signature
|
||||
@@ -168,10 +170,12 @@ inline auto post(const Executor& ex,
|
||||
> = 0)
|
||||
-> decltype(
|
||||
async_initiate<NullaryToken, void()>(
|
||||
declval<detail::initiate_post_with_executor<Executor>>(), token))
|
||||
declval<detail::initiate_post_with_executor<Executor>>(),
|
||||
token, detail::empty_work_function()))
|
||||
{
|
||||
return async_initiate<NullaryToken, void()>(
|
||||
detail::initiate_post_with_executor<Executor>(ex), token);
|
||||
detail::initiate_post_with_executor<Executor>(ex),
|
||||
token, detail::empty_work_function());
|
||||
}
|
||||
|
||||
/// Submits a completion token or function object for execution.
|
||||
@@ -199,12 +203,329 @@ inline auto post(ExecutionContext& ctx,
|
||||
-> decltype(
|
||||
async_initiate<NullaryToken, void()>(
|
||||
declval<detail::initiate_post_with_executor<
|
||||
typename ExecutionContext::executor_type>>(), token))
|
||||
typename ExecutionContext::executor_type>>(),
|
||||
token, detail::empty_work_function()))
|
||||
{
|
||||
return async_initiate<NullaryToken, void()>(
|
||||
detail::initiate_post_with_executor<
|
||||
typename ExecutionContext::executor_type>(
|
||||
ctx.get_executor()), token);
|
||||
typename ExecutionContext::executor_type>(ctx.get_executor()),
|
||||
token, detail::empty_work_function());
|
||||
}
|
||||
|
||||
/// Submits a function to be run on a specified target executor, and after
|
||||
/// completion submits the completion handler.
|
||||
/**
|
||||
* This function submits a function object for execution on the specified
|
||||
* executor. The function object is queued for execution, and is never called
|
||||
* from the current thread prior to returning from <tt>post()</tt>. After the
|
||||
* submitted function completes, the completion handler is dispatched to run on
|
||||
* its associated executor.
|
||||
*
|
||||
* The use of @c post(), rather than @ref defer(), indicates the caller's
|
||||
* preference that the function object be eagerly queued for execution.
|
||||
*
|
||||
* @param function A nullary function to be executed on the target executor.
|
||||
*
|
||||
* @param ex The target executor.
|
||||
*
|
||||
* @param token The @ref completion_token that will be used to produce a
|
||||
* completion handler. The function signature of the completion handler must be:
|
||||
* @code void handler(); @endcode
|
||||
*
|
||||
* @returns This function returns <tt>async_initiate<NullaryToken,
|
||||
* void()>(Init{ex}, token, forward<Function>(function))</tt>, where @c Init is
|
||||
* a function object type defined as:
|
||||
*
|
||||
* @code class Init
|
||||
* {
|
||||
* public:
|
||||
* using executor_type = Executor;
|
||||
* explicit Init(const Executor& ex) : ex_(ex) {}
|
||||
* executor_type get_executor() const noexcept { return ex_; }
|
||||
* template <typename CompletionHandler>
|
||||
* void operator()(CompletionHandler&& completion_handler,
|
||||
* Function&& function) const;
|
||||
* private:
|
||||
* Executor ex_; // exposition only
|
||||
* }; @endcode
|
||||
*
|
||||
* The function call operator of @c Init:
|
||||
*
|
||||
* @li Obtains the handler's associated executor object @c ex1 of type @c Ex1 by
|
||||
* performing
|
||||
* @code auto ex1 = get_associated_executor(completion_handler, ex); @endcode
|
||||
*
|
||||
* @li Obtains the handler's associated allocator object @c alloc by performing
|
||||
* @code auto alloc = get_associated_allocator(completion_handler); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex1>::value</tt> is true, constructs a
|
||||
* function object wrapper @c f with a member @c executor_ that is initialised
|
||||
* with <tt>prefer(ex1, execution::outstanding_work.tracked)</tt>, a member @c
|
||||
* function_ that is a decay-copy of @c function, a member @c handler_ that is a
|
||||
* decay-copy of @c completion_handler, and a function call operator that
|
||||
* performs:
|
||||
* @code std::move(function_)();
|
||||
* auto a = get_associated_allocator(handler_);
|
||||
* prefer(executor_, execution::allocator(a)).execute(std::move(handler_));
|
||||
* @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex1>::value</tt> is false, constructs a
|
||||
* function object wrapper @c f with a member @c work_ that is initialised with
|
||||
* <tt>make_work_guard(ex1)</tt>, a member @c function_ that is a decay-copy of
|
||||
* @c function, a member @c handler_ that is a decay-copy of @c
|
||||
* completion_handler, and a function call operator that performs:
|
||||
* @code std::move(function_)();
|
||||
* auto a = get_associated_allocator(handler_);
|
||||
* work_.get_executor().dispatch(std::move(handler_), a);
|
||||
* work_.reset(); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Executor>::value</tt> is true, performs
|
||||
* @code prefer(
|
||||
* require(ex, execution::blocking.never),
|
||||
* execution::relationship.fork,
|
||||
* execution::allocator(alloc)
|
||||
* ).execute(std::move(f)); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Executor>::value</tt> is false, performs
|
||||
* @code ex.post(std::move(f), alloc); @endcode
|
||||
*
|
||||
* @note If the function object throws an exception, that exception is allowed
|
||||
* to propagate to the target executor. The behaviour in this case is dependent
|
||||
* on the executor. For example, boost::asio::io_context will allow the
|
||||
* exception to propagate to the caller that runs the @c io_context, whereas
|
||||
* boost::asio::thread_pool will call @c std::terminate.
|
||||
*
|
||||
* @par Completion Signature
|
||||
* @code void() @endcode
|
||||
*/
|
||||
template <typename Function, typename Executor,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void()) NullaryToken
|
||||
= default_completion_token_t<Executor>>
|
||||
inline auto post(Function&& function, const Executor& ex,
|
||||
NullaryToken&& token = default_completion_token_t<Executor>(),
|
||||
constraint_t<
|
||||
is_void<result_of_t<decay_t<Function>()>>::value
|
||||
> = 0,
|
||||
constraint_t<
|
||||
(execution::is_executor<Executor>::value
|
||||
&& can_require<Executor, execution::blocking_t::never_t>::value)
|
||||
|| is_executor<Executor>::value
|
||||
> = 0)
|
||||
-> decltype(
|
||||
async_initiate<NullaryToken, void()>(
|
||||
declval<detail::initiate_post_with_executor<Executor>>(),
|
||||
token, static_cast<Function&&>(function)))
|
||||
{
|
||||
return async_initiate<NullaryToken, void()>(
|
||||
detail::initiate_post_with_executor<Executor>(ex),
|
||||
token, static_cast<Function&&>(function));
|
||||
}
|
||||
|
||||
/// Submits a function to be run on a specified target executor, and passes the
|
||||
/// result to a completion handler.
|
||||
/**
|
||||
* This function submits a function object for execution on the specified
|
||||
* executor. The function object is queued for execution, and is never called
|
||||
* from the current thread prior to returning from <tt>post()</tt>. After the
|
||||
* submitted function completes, the completion handler is dispatched along with
|
||||
* the function's result, to run on its associated executor.
|
||||
*
|
||||
* The use of @c post(), rather than @ref defer(), indicates the caller's
|
||||
* preference that the function object be eagerly queued for execution.
|
||||
*
|
||||
* @param function A nullary function to be executed on the target executor.
|
||||
*
|
||||
* @param ex The target executor.
|
||||
*
|
||||
* @param token The @ref completion_token that will be used to produce a
|
||||
* completion handler. The function signature of the completion handler must be:
|
||||
* @code void handler(decay_t<result_of_t<decay_t<Function>()>>); @endcode
|
||||
*
|
||||
* @returns This function returns <tt>async_initiate<CompletionToken,
|
||||
* void()>(Init{ex}, token)</tt>, where @c Init is a function object type
|
||||
* defined as:
|
||||
*
|
||||
* @code class Init
|
||||
* {
|
||||
* public:
|
||||
* using executor_type = Executor;
|
||||
* explicit Init(const Executor& ex) : ex_(ex) {}
|
||||
* executor_type get_executor() const noexcept { return ex_; }
|
||||
* template <typename CompletionHandler>
|
||||
* void operator()(CompletionHandler&& completion_handler,
|
||||
* Function&& function) const;
|
||||
* private:
|
||||
* Executor ex_; // exposition only
|
||||
* }; @endcode
|
||||
*
|
||||
* The function call operator of @c Init:
|
||||
*
|
||||
* @li Obtains the handler's associated executor object @c ex1 of type @c Ex1 by
|
||||
* performing
|
||||
* @code auto ex1 = get_associated_executor(completion_handler, ex); @endcode
|
||||
*
|
||||
* @li Obtains the handler's associated allocator object @c alloc by performing
|
||||
* @code auto alloc = get_associated_allocator(completion_handler); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex1>::value</tt> is true, constructs a
|
||||
* function object wrapper @c f with a member @c executor_ that is initialised
|
||||
* with <tt>prefer(ex1, execution::outstanding_work.tracked)</tt>, a member @c
|
||||
* function_ that is a decay-copy of @c function, a member @c handler_ that is a
|
||||
* decay-copy of @c completion_handler, and a function call operator that
|
||||
* performs:
|
||||
* @code auto result = std::move(function_)();
|
||||
* auto a = get_associated_allocator(handler_);
|
||||
* prefer(executor_, execution::allocator(a)).execute(
|
||||
* std::bind(std::move(handler_), std::move(result)));
|
||||
* @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Ex1>::value</tt> is false, constructs a
|
||||
* function object wrapper @c f with a member @c work_ that is initialised with
|
||||
* <tt>make_work_guard(ex1)</tt>, a member @c function_ that is a decay-copy of
|
||||
* @c function, a member @c handler_ that is a decay-copy of @c
|
||||
* completion_handler, and a function call operator that performs:
|
||||
* @code auto result = std::move(function_)();
|
||||
* auto a = get_associated_allocator(handler_);
|
||||
* work_.get_executor().dispatch(
|
||||
* std::bind(std::move(handler_), std::move(result)), a);
|
||||
* work_.reset(); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Executor>::value</tt> is true, performs
|
||||
* @code prefer(
|
||||
* require(ex, execution::blocking.never),
|
||||
* execution::relationship.fork,
|
||||
* execution::allocator(alloc)
|
||||
* ).execute(std::move(f)); @endcode
|
||||
*
|
||||
* @li If <tt>execution::is_executor<Executor>::value</tt> is false, performs
|
||||
* @code ex.post(std::move(f), alloc); @endcode
|
||||
*
|
||||
* @note If the function object throws an exception, that exception is allowed
|
||||
* to propagate to the target executor. The behaviour in this case is dependent
|
||||
* on the executor. For example, boost::asio::io_context will allow the
|
||||
* exception to propagate to the caller that runs the @c io_context, whereas
|
||||
* boost::asio::thread_pool will call @c std::terminate.
|
||||
*
|
||||
* @par Completion Signature
|
||||
* @code void(decay_t<result_of_t<decay_t<Function>()>>) @endcode
|
||||
*/
|
||||
template <typename Function, typename Executor,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(
|
||||
void(decay_t<result_of_t<decay_t<Function>()>>)) CompletionToken
|
||||
= default_completion_token_t<Executor>>
|
||||
inline auto post(Function&& function, const Executor& ex,
|
||||
CompletionToken&& token = default_completion_token_t<Executor>(),
|
||||
constraint_t<
|
||||
!is_void<result_of_t<decay_t<Function>()>>::value
|
||||
> = 0,
|
||||
constraint_t<
|
||||
(execution::is_executor<Executor>::value
|
||||
&& can_require<Executor, execution::blocking_t::never_t>::value)
|
||||
|| is_executor<Executor>::value
|
||||
> = 0)
|
||||
-> decltype(
|
||||
async_initiate<CompletionToken, void(detail::work_result_t<Function>)>(
|
||||
declval<detail::initiate_post_with_executor<Executor>>(),
|
||||
token, static_cast<Function&&>(function)))
|
||||
{
|
||||
return async_initiate<CompletionToken, void(detail::work_result_t<Function>)>(
|
||||
detail::initiate_post_with_executor<Executor>(ex),
|
||||
token, static_cast<Function&&>(function));
|
||||
}
|
||||
|
||||
/// Submits a function to be run on a specified execution context, and after
|
||||
/// completion submits the completion handler.
|
||||
/**
|
||||
* @param function A nullary function to be executed on the target executor.
|
||||
*
|
||||
* @param ctx An execution context, from which the target executor is obtained.
|
||||
*
|
||||
* @param token The @ref completion_token that will be used to produce a
|
||||
* completion handler. The function signature of the completion handler must be:
|
||||
* @code void handler(); @endcode
|
||||
*
|
||||
* @returns <tt>post(forward<Function>(function), ctx.get_executor(),
|
||||
* forward<NullaryToken>(token))</tt>.
|
||||
*
|
||||
* @note If the function object throws an exception, that exception is allowed
|
||||
* to propagate to the target executor. The behaviour in this case is dependent
|
||||
* on the executor. For example, boost::asio::io_context will allow the
|
||||
* exception to propagate to the caller that runs the @c io_context, whereas
|
||||
* boost::asio::thread_pool will call @c std::terminate.
|
||||
*
|
||||
* @par Completion Signature
|
||||
* @code void() @endcode
|
||||
*/
|
||||
template <typename Function, typename ExecutionContext,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void()) NullaryToken
|
||||
= default_completion_token_t<typename ExecutionContext::executor_type>>
|
||||
inline auto post(Function&& function, ExecutionContext& ctx,
|
||||
NullaryToken&& token = default_completion_token_t<
|
||||
typename ExecutionContext::executor_type>(),
|
||||
constraint_t<
|
||||
is_void<result_of_t<decay_t<Function>()>>::value
|
||||
> = 0,
|
||||
constraint_t<
|
||||
is_convertible<ExecutionContext&, execution_context&>::value
|
||||
> = 0)
|
||||
-> decltype(
|
||||
async_initiate<NullaryToken, void()>(
|
||||
declval<detail::initiate_post_with_executor<
|
||||
typename ExecutionContext::executor_type>>(),
|
||||
token, static_cast<Function&&>(function)))
|
||||
{
|
||||
return async_initiate<NullaryToken, void()>(
|
||||
detail::initiate_post_with_executor<
|
||||
typename ExecutionContext::executor_type>(ctx.get_executor()),
|
||||
token, static_cast<Function&&>(function));
|
||||
}
|
||||
|
||||
/// Submits a function to be run on a specified execution context, and passes
|
||||
/// the result to a completion handler.
|
||||
/**
|
||||
* @param function A nullary function to be executed on the target executor.
|
||||
*
|
||||
* @param ctx An execution context, from which the target executor is obtained.
|
||||
*
|
||||
* @param token The @ref completion_token that will be used to produce a
|
||||
* completion handler. The function signature of the completion handler must be:
|
||||
* @code void handler(); @endcode
|
||||
*
|
||||
* @returns <tt>post(forward<Function>(function), ctx.get_executor(),
|
||||
* forward<CompletionToken>(token))</tt>.
|
||||
*
|
||||
* @note If the function object throws an exception, that exception is allowed
|
||||
* to propagate to the target executor. The behaviour in this case is dependent
|
||||
* on the executor. For example, boost::asio::io_context will allow the
|
||||
* exception to propagate to the caller that runs the @c io_context, whereas
|
||||
* boost::asio::thread_pool will call @c std::terminate.
|
||||
*
|
||||
* @par Completion Signature
|
||||
* @code void(decay_t<result_of_t<decay_t<Function>()>>) @endcode
|
||||
*/
|
||||
template <typename Function, typename ExecutionContext,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(
|
||||
void(decay_t<result_of_t<decay_t<Function>()>>)) CompletionToken
|
||||
= default_completion_token_t<typename ExecutionContext::executor_type>>
|
||||
inline auto post(Function&& function, ExecutionContext& ctx,
|
||||
CompletionToken&& token = default_completion_token_t<
|
||||
typename ExecutionContext::executor_type>(),
|
||||
constraint_t<
|
||||
!is_void<result_of_t<decay_t<Function>()>>::value
|
||||
> = 0,
|
||||
constraint_t<
|
||||
is_convertible<ExecutionContext&, execution_context&>::value
|
||||
> = 0)
|
||||
-> decltype(
|
||||
async_initiate<CompletionToken, void(detail::work_result_t<Function>)>(
|
||||
declval<detail::initiate_post_with_executor<
|
||||
typename ExecutionContext::executor_type>>(),
|
||||
token, static_cast<Function&&>(function)))
|
||||
{
|
||||
return async_initiate<CompletionToken, void(detail::work_result_t<Function>)>(
|
||||
detail::initiate_post_with_executor<
|
||||
typename ExecutionContext::executor_type>(ctx.get_executor()),
|
||||
token, static_cast<Function&&>(function));
|
||||
}
|
||||
|
||||
} // namespace asio
|
||||
|
||||
210
test/defer.cpp
210
test/defer.cpp
@@ -16,10 +16,218 @@
|
||||
// Test that header file is self-contained.
|
||||
#include <boost/asio/defer.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include "unit_test.hpp"
|
||||
|
||||
using namespace boost::asio;
|
||||
namespace bindns = std;
|
||||
using bindns::placeholders::_1;
|
||||
using bindns::placeholders::_2;
|
||||
|
||||
class move_only_result
|
||||
{
|
||||
public:
|
||||
explicit move_only_result(int value)
|
||||
: value_(value)
|
||||
{
|
||||
}
|
||||
|
||||
move_only_result(move_only_result&& other)
|
||||
: value_(other.value_)
|
||||
{
|
||||
}
|
||||
|
||||
int value() const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
|
||||
private:
|
||||
int value_;
|
||||
};
|
||||
|
||||
static int function_count = 0;
|
||||
|
||||
void void_function()
|
||||
{
|
||||
++function_count;
|
||||
}
|
||||
|
||||
struct void_function_object
|
||||
{
|
||||
void_function_object() = default;
|
||||
void_function_object(void_function_object&&) = default;
|
||||
|
||||
void operator()() &&
|
||||
{
|
||||
++function_count;
|
||||
}
|
||||
};
|
||||
|
||||
void void_handler(int* count)
|
||||
{
|
||||
++(*count);
|
||||
}
|
||||
|
||||
move_only_result move_only_result_function()
|
||||
{
|
||||
++function_count;
|
||||
return move_only_result(42);
|
||||
}
|
||||
|
||||
struct move_only_result_function_object
|
||||
{
|
||||
move_only_result_function_object() = default;
|
||||
move_only_result_function_object(
|
||||
move_only_result_function_object&&) = default;
|
||||
|
||||
move_only_result operator()() &&
|
||||
{
|
||||
++function_count;
|
||||
return move_only_result(42);
|
||||
}
|
||||
};
|
||||
|
||||
void move_only_result_handler(
|
||||
move_only_result result_in, int* count, int* result_out)
|
||||
{
|
||||
++(*count);
|
||||
*result_out = result_in.value();
|
||||
}
|
||||
|
||||
void defer_function_test()
|
||||
{
|
||||
io_context ctx(1);
|
||||
|
||||
function_count = 0;
|
||||
int handler_count = 0;
|
||||
defer(void_function, ctx.get_executor(),
|
||||
bindns::bind(void_handler, &handler_count));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
defer(void_function, ctx, bindns::bind(void_handler, &handler_count));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
defer(void_function_object(), ctx.get_executor(),
|
||||
bindns::bind(void_handler, &handler_count));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
defer(void_function_object(), ctx,
|
||||
bindns::bind(void_handler, &handler_count));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
int handler_result = 0;
|
||||
defer(move_only_result_function, ctx.get_executor(),
|
||||
bindns::bind(move_only_result_handler, _1,
|
||||
&handler_count, &handler_result));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_result == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_result == 42);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
handler_result = 0;
|
||||
defer(move_only_result_function, ctx,
|
||||
bindns::bind(move_only_result_handler, _1,
|
||||
&handler_count, &handler_result));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_result == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_result == 42);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
handler_result = 0;
|
||||
defer(move_only_result_function_object(), ctx.get_executor(),
|
||||
bindns::bind(move_only_result_handler, _1,
|
||||
&handler_count, &handler_result));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_result == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_result == 42);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
handler_result = 0;
|
||||
defer(move_only_result_function_object(), ctx,
|
||||
bindns::bind(move_only_result_handler, _1,
|
||||
&handler_count, &handler_result));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_result == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_result == 42);
|
||||
}
|
||||
|
||||
BOOST_ASIO_TEST_SUITE
|
||||
(
|
||||
"defer",
|
||||
BOOST_ASIO_TEST_CASE(null_test)
|
||||
BOOST_ASIO_TEST_CASE(defer_function_test)
|
||||
)
|
||||
|
||||
@@ -16,10 +16,218 @@
|
||||
// Test that header file is self-contained.
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include "unit_test.hpp"
|
||||
|
||||
using namespace boost::asio;
|
||||
namespace bindns = std;
|
||||
using bindns::placeholders::_1;
|
||||
using bindns::placeholders::_2;
|
||||
|
||||
class move_only_result
|
||||
{
|
||||
public:
|
||||
explicit move_only_result(int value)
|
||||
: value_(value)
|
||||
{
|
||||
}
|
||||
|
||||
move_only_result(move_only_result&& other)
|
||||
: value_(other.value_)
|
||||
{
|
||||
}
|
||||
|
||||
int value() const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
|
||||
private:
|
||||
int value_;
|
||||
};
|
||||
|
||||
static int function_count = 0;
|
||||
|
||||
void void_function()
|
||||
{
|
||||
++function_count;
|
||||
}
|
||||
|
||||
struct void_function_object
|
||||
{
|
||||
void_function_object() = default;
|
||||
void_function_object(void_function_object&&) = default;
|
||||
|
||||
void operator()() &&
|
||||
{
|
||||
++function_count;
|
||||
}
|
||||
};
|
||||
|
||||
void void_handler(int* count)
|
||||
{
|
||||
++(*count);
|
||||
}
|
||||
|
||||
move_only_result move_only_result_function()
|
||||
{
|
||||
++function_count;
|
||||
return move_only_result(42);
|
||||
}
|
||||
|
||||
struct move_only_result_function_object
|
||||
{
|
||||
move_only_result_function_object() = default;
|
||||
move_only_result_function_object(
|
||||
move_only_result_function_object&&) = default;
|
||||
|
||||
move_only_result operator()() &&
|
||||
{
|
||||
++function_count;
|
||||
return move_only_result(42);
|
||||
}
|
||||
};
|
||||
|
||||
void move_only_result_handler(
|
||||
move_only_result result_in, int* count, int* result_out)
|
||||
{
|
||||
++(*count);
|
||||
*result_out = result_in.value();
|
||||
}
|
||||
|
||||
void dispatch_function_test()
|
||||
{
|
||||
io_context ctx(1);
|
||||
|
||||
function_count = 0;
|
||||
int handler_count = 0;
|
||||
dispatch(void_function, ctx.get_executor(),
|
||||
bindns::bind(void_handler, &handler_count));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
dispatch(void_function, ctx, bindns::bind(void_handler, &handler_count));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
dispatch(void_function_object(), ctx.get_executor(),
|
||||
bindns::bind(void_handler, &handler_count));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
dispatch(void_function_object(), ctx,
|
||||
bindns::bind(void_handler, &handler_count));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
int handler_result = 0;
|
||||
dispatch(move_only_result_function, ctx.get_executor(),
|
||||
bindns::bind(move_only_result_handler, _1,
|
||||
&handler_count, &handler_result));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_result == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_result == 42);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
handler_result = 0;
|
||||
dispatch(move_only_result_function, ctx,
|
||||
bindns::bind(move_only_result_handler, _1,
|
||||
&handler_count, &handler_result));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_result == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_result == 42);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
handler_result = 0;
|
||||
dispatch(move_only_result_function_object(), ctx.get_executor(),
|
||||
bindns::bind(move_only_result_handler, _1,
|
||||
&handler_count, &handler_result));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_result == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_result == 42);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
handler_result = 0;
|
||||
dispatch(move_only_result_function_object(), ctx,
|
||||
bindns::bind(move_only_result_handler, _1,
|
||||
&handler_count, &handler_result));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_result == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_result == 42);
|
||||
}
|
||||
|
||||
BOOST_ASIO_TEST_SUITE
|
||||
(
|
||||
"dispatch",
|
||||
BOOST_ASIO_TEST_CASE(null_test)
|
||||
BOOST_ASIO_TEST_CASE(dispatch_function_test)
|
||||
)
|
||||
|
||||
210
test/post.cpp
210
test/post.cpp
@@ -16,10 +16,218 @@
|
||||
// Test that header file is self-contained.
|
||||
#include <boost/asio/post.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include "unit_test.hpp"
|
||||
|
||||
using namespace boost::asio;
|
||||
namespace bindns = std;
|
||||
using bindns::placeholders::_1;
|
||||
using bindns::placeholders::_2;
|
||||
|
||||
class move_only_result
|
||||
{
|
||||
public:
|
||||
explicit move_only_result(int value)
|
||||
: value_(value)
|
||||
{
|
||||
}
|
||||
|
||||
move_only_result(move_only_result&& other)
|
||||
: value_(other.value_)
|
||||
{
|
||||
}
|
||||
|
||||
int value() const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
|
||||
private:
|
||||
int value_;
|
||||
};
|
||||
|
||||
static int function_count = 0;
|
||||
|
||||
void void_function()
|
||||
{
|
||||
++function_count;
|
||||
}
|
||||
|
||||
struct void_function_object
|
||||
{
|
||||
void_function_object() = default;
|
||||
void_function_object(void_function_object&&) = default;
|
||||
|
||||
void operator()() &&
|
||||
{
|
||||
++function_count;
|
||||
}
|
||||
};
|
||||
|
||||
void void_handler(int* count)
|
||||
{
|
||||
++(*count);
|
||||
}
|
||||
|
||||
move_only_result move_only_result_function()
|
||||
{
|
||||
++function_count;
|
||||
return move_only_result(42);
|
||||
}
|
||||
|
||||
struct move_only_result_function_object
|
||||
{
|
||||
move_only_result_function_object() = default;
|
||||
move_only_result_function_object(
|
||||
move_only_result_function_object&&) = default;
|
||||
|
||||
move_only_result operator()() &&
|
||||
{
|
||||
++function_count;
|
||||
return move_only_result(42);
|
||||
}
|
||||
};
|
||||
|
||||
void move_only_result_handler(
|
||||
move_only_result result_in, int* count, int* result_out)
|
||||
{
|
||||
++(*count);
|
||||
*result_out = result_in.value();
|
||||
}
|
||||
|
||||
void post_function_test()
|
||||
{
|
||||
io_context ctx(1);
|
||||
|
||||
function_count = 0;
|
||||
int handler_count = 0;
|
||||
post(void_function, ctx.get_executor(),
|
||||
bindns::bind(void_handler, &handler_count));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
post(void_function, ctx, bindns::bind(void_handler, &handler_count));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
post(void_function_object(), ctx.get_executor(),
|
||||
bindns::bind(void_handler, &handler_count));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
post(void_function_object(), ctx,
|
||||
bindns::bind(void_handler, &handler_count));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
int handler_result = 0;
|
||||
post(move_only_result_function, ctx.get_executor(),
|
||||
bindns::bind(move_only_result_handler, _1,
|
||||
&handler_count, &handler_result));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_result == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_result == 42);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
handler_result = 0;
|
||||
post(move_only_result_function, ctx,
|
||||
bindns::bind(move_only_result_handler, _1,
|
||||
&handler_count, &handler_result));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_result == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_result == 42);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
handler_result = 0;
|
||||
post(move_only_result_function_object(), ctx.get_executor(),
|
||||
bindns::bind(move_only_result_handler, _1,
|
||||
&handler_count, &handler_result));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_result == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_result == 42);
|
||||
|
||||
function_count = 0;
|
||||
handler_count = 0;
|
||||
handler_result = 0;
|
||||
post(move_only_result_function_object(), ctx,
|
||||
bindns::bind(move_only_result_handler, _1,
|
||||
&handler_count, &handler_result));
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_count == 0);
|
||||
BOOST_ASIO_CHECK(handler_result == 0);
|
||||
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
|
||||
BOOST_ASIO_CHECK(function_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_count == 1);
|
||||
BOOST_ASIO_CHECK(handler_result == 42);
|
||||
}
|
||||
|
||||
BOOST_ASIO_TEST_SUITE
|
||||
(
|
||||
"post",
|
||||
BOOST_ASIO_TEST_CASE(null_test)
|
||||
BOOST_ASIO_TEST_CASE(post_function_test)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user