2
0
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:
Christopher Kohlhoff
2025-10-29 22:56:44 +11:00
parent 44722ea024
commit 77d09d001d
15 changed files with 1921 additions and 126 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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:

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -110,6 +110,8 @@ using std::is_scalar;
using std::is_unsigned;
using std::is_void;
using std::remove_cv;
template <typename T>

View File

@@ -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();
}

View File

@@ -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:

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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)
)

View File

@@ -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)
)

View File

@@ -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)
)