mirror of
https://github.com/Naios/continuable.git
synced 2025-12-06 16:56:44 +08:00
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:
parent
fa589a1e95
commit
d72e1bfb86
@ -39,8 +39,8 @@
|
|||||||
namespace cti {
|
namespace cti {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
namespace operations {
|
namespace operations {
|
||||||
template <typename Callable, typename... Args>
|
template <typename Callable, typename Executor, typename... Args>
|
||||||
auto async(Callable&& callable, Args&&... args) {
|
auto async(Callable&& callable, Executor&& executor, Args&&... args) {
|
||||||
using result_t =
|
using result_t =
|
||||||
decltype(util::invoke(std::forward<decltype(callable)>(callable),
|
decltype(util::invoke(std::forward<decltype(callable)>(callable),
|
||||||
std::forward<decltype(args)>(args)...));
|
std::forward<decltype(args)>(args)...));
|
||||||
@ -49,17 +49,21 @@ auto async(Callable&& callable, Args&&... args) {
|
|||||||
decltype(base::decoration::invoker_of(identity<result_t>{}))::hint();
|
decltype(base::decoration::invoker_of(identity<result_t>{}))::hint();
|
||||||
|
|
||||||
auto continuation = [callable = std::forward<decltype(callable)>(callable),
|
auto continuation = [callable = std::forward<decltype(callable)>(callable),
|
||||||
|
executor = std::forward<decltype(executor)>(executor),
|
||||||
args = std::make_tuple(std::forward<decltype(args)>(
|
args = std::make_tuple(std::forward<decltype(args)>(
|
||||||
args)...)](auto&& promise) mutable {
|
args)...)](auto&& promise) mutable {
|
||||||
|
|
||||||
auto invoker = base::decoration::invoker_of(identity<result_t>{});
|
auto invoker = base::decoration::invoker_of(identity<result_t>{});
|
||||||
|
|
||||||
using promise_t = decltype(promise);
|
using promise_t = decltype(promise);
|
||||||
|
|
||||||
|
// Invoke the callback
|
||||||
traits::unpack(
|
traits::unpack(
|
||||||
[&](auto&&... args) {
|
[&](auto&&... args) mutable {
|
||||||
// Invoke the promise through the dedicated invoker
|
// 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::forward<decltype(args)>(args)...);
|
||||||
},
|
},
|
||||||
std::move(args));
|
std::move(args));
|
||||||
|
|||||||
@ -32,6 +32,7 @@
|
|||||||
#define CONTINUABLE_OPERATIONS_ASYNC_HPP_INCLUDED
|
#define CONTINUABLE_OPERATIONS_ASYNC_HPP_INCLUDED
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <continuable/detail/core/types.hpp>
|
||||||
#include <continuable/detail/operations/async.hpp>
|
#include <continuable/detail/operations/async.hpp>
|
||||||
|
|
||||||
namespace cti {
|
namespace cti {
|
||||||
@ -41,6 +42,8 @@ namespace cti {
|
|||||||
/// Wraps the given callable inside a continuable_base such that it is
|
/// Wraps the given callable inside a continuable_base such that it is
|
||||||
/// invoked when the asynchronous result is requested to return the result.
|
/// 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
|
/// The behaviour will be equal as when using make_ready_continuable together
|
||||||
/// with continuable_base::then, but async is implemented in
|
/// with continuable_base::then, but async is implemented in
|
||||||
/// a more efficient way:
|
/// 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.
|
/// \param args The arguments which are passed to the callable upon invocation.
|
||||||
///
|
///
|
||||||
@ -65,6 +68,46 @@ namespace cti {
|
|||||||
template <typename Callable, typename... Args>
|
template <typename Callable, typename... Args>
|
||||||
auto async(Callable&& callable, Args&&... args) {
|
auto async(Callable&& callable, Args&&... args) {
|
||||||
return detail::operations::async(std::forward<Callable>(callable),
|
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)...);
|
std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
/// \}
|
/// \}
|
||||||
|
|||||||
@ -44,3 +44,41 @@ TYPED_TEST(single_dimension_tests, operations_async) {
|
|||||||
}),
|
}),
|
||||||
CANARY, 2, CANARY);
|
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);
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user