Implement async_on for asynchronous execution on a specific executor

* Makes it possible to specify an executor in addition to
  the arguments passed to async.
* The reason why async should not support this directly is
  that it should be closely modelled to std::async.
This commit is contained in:
Denis Blank 2019-01-26 03:46:39 +01:00
parent fa589a1e95
commit d72e1bfb86
3 changed files with 92 additions and 7 deletions

View File

@ -39,8 +39,8 @@
namespace cti {
namespace detail {
namespace operations {
template <typename Callable, typename... Args>
auto async(Callable&& callable, Args&&... args) {
template <typename Callable, typename Executor, typename... Args>
auto async(Callable&& callable, Executor&& executor, Args&&... args) {
using result_t =
decltype(util::invoke(std::forward<decltype(callable)>(callable),
std::forward<decltype(args)>(args)...));
@ -49,17 +49,21 @@ auto async(Callable&& callable, Args&&... args) {
decltype(base::decoration::invoker_of(identity<result_t>{}))::hint();
auto continuation = [callable = std::forward<decltype(callable)>(callable),
executor = std::forward<decltype(executor)>(executor),
args = std::make_tuple(std::forward<decltype(args)>(
args)...)](auto&& promise) mutable {
auto invoker = base::decoration::invoker_of(identity<result_t>{});
using promise_t = decltype(promise);
// Invoke the callback
traits::unpack(
[&](auto&&... args) {
[&](auto&&... args) mutable {
// Invoke the promise through the dedicated invoker
invoker(std::move(callable), std::forward<promise_t>(promise),
// and through the given executor
base::on_executor(std::move(executor), std::move(invoker),
std::move(callable),
std::forward<promise_t>(promise),
std::forward<decltype(args)>(args)...);
},
std::move(args));

View File

@ -32,6 +32,7 @@
#define CONTINUABLE_OPERATIONS_ASYNC_HPP_INCLUDED
#include <utility>
#include <continuable/detail/core/types.hpp>
#include <continuable/detail/operations/async.hpp>
namespace cti {
@ -41,6 +42,8 @@ namespace cti {
/// Wraps the given callable inside a continuable_base such that it is
/// invoked when the asynchronous result is requested to return the result.
///
/// The async function shall be seen as an equivalent to std::async.
///
/// The behaviour will be equal as when using make_ready_continuable together
/// with continuable_base::then, but async is implemented in
/// a more efficient way:
@ -53,7 +56,7 @@ namespace cti {
/// }
/// ```
///
/// \param callable The callable type which is invoked on request
/// \param callable The callable type which is invoked on request.
///
/// \param args The arguments which are passed to the callable upon invocation.
///
@ -65,6 +68,46 @@ namespace cti {
template <typename Callable, typename... Args>
auto async(Callable&& callable, Args&&... args) {
return detail::operations::async(std::forward<Callable>(callable),
detail::types::this_thread_executor_tag{},
std::forward<Args>(args)...);
}
/// Wraps the given callable inside a continuable_base such that it is
/// invoked through the given executor when the asynchronous result
/// is requested to return the result.
///
/// The behaviour will be equal as when using make_ready_continuable together
/// with continuable_base::then and the given executor but async_on
/// is implemented in a more efficient way:
/// ```cpp
/// auto do_sth() {
/// auto executor = [](auto&& work) {
/// // Do something with the work here
/// std::forward<decltype(work)>(work);
/// };
///
/// return async_on([] {
/// do_sth_more();
/// return 0;
/// }, my_executor);
/// }
/// ```
///
/// \param callable The callable type which is invoked on request.
///
/// \param executor The executor that is used to dispatch the given callable.
///
/// \param args The arguments which are passed to the callable upon invocation.
///
/// \returns A continuable_base which asynchronous result type will
/// be computated with the same rules as continuable_base::then .
///
/// \since 4.0.0
///
template <typename Callable, typename Executor, typename... Args>
auto async_on(Callable&& callable, Executor&& executor, Args&&... args) {
return detail::operations::async(std::forward<Callable>(callable),
std::forward<Executor>(executor),
std::forward<Args>(args)...);
}
/// \}

View File

@ -44,3 +44,41 @@ TYPED_TEST(single_dimension_tests, operations_async) {
}),
CANARY, 2, CANARY);
}
TYPED_TEST(single_dimension_tests, operations_async_on_dropping) {
bool invoked = false;
auto executor = [&](auto&& work) {
EXPECT_FALSE(invoked);
invoked = true;
(void)work;
};
ASSERT_ASYNC_INCOMPLETION(async_on(
[] {
FAIL(); //
},
executor));
ASSERT_TRUE(invoked);
}
TYPED_TEST(single_dimension_tests, operations_async_on_executor) {
bool invoked = false;
auto executor = [&](auto&& work) {
// We can move the worker object
auto local = std::forward<decltype(work)>(work);
EXPECT_FALSE(invoked);
// We can invoke the worker object
std::move(local)();
EXPECT_TRUE(invoked);
};
ASSERT_ASYNC_COMPLETION(async_on(
[&invoked] {
EXPECT_FALSE(invoked);
invoked = true;
},
executor));
ASSERT_TRUE(invoked);
}