diff --git a/include/continuable/detail/operations/async.hpp b/include/continuable/detail/operations/async.hpp index 77f43fa..79841b6 100644 --- a/include/continuable/detail/operations/async.hpp +++ b/include/continuable/detail/operations/async.hpp @@ -39,8 +39,8 @@ namespace cti { namespace detail { namespace operations { -template -auto async(Callable&& callable, Args&&... args) { +template +auto async(Callable&& callable, Executor&& executor, Args&&... args) { using result_t = decltype(util::invoke(std::forward(callable), std::forward(args)...)); @@ -49,18 +49,22 @@ auto async(Callable&& callable, Args&&... args) { decltype(base::decoration::invoker_of(identity{}))::hint(); auto continuation = [callable = std::forward(callable), + executor = std::forward(executor), args = std::make_tuple(std::forward( args)...)](auto&& promise) mutable { - auto invoker = base::decoration::invoker_of(identity{}); 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), - std::forward(args)...); + // and through the given executor + base::on_executor(std::move(executor), std::move(invoker), + std::move(callable), + std::forward(promise), + std::forward(args)...); }, std::move(args)); }; diff --git a/include/continuable/operations/async.hpp b/include/continuable/operations/async.hpp index 7ad0a8f..a48a406 100644 --- a/include/continuable/operations/async.hpp +++ b/include/continuable/operations/async.hpp @@ -32,6 +32,7 @@ #define CONTINUABLE_OPERATIONS_ASYNC_HPP_INCLUDED #include +#include #include 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 auto async(Callable&& callable, Args&&... args) { return detail::operations::async(std::forward(callable), + detail::types::this_thread_executor_tag{}, + std::forward(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(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 +auto async_on(Callable&& callable, Executor&& executor, Args&&... args) { + return detail::operations::async(std::forward(callable), + std::forward(executor), std::forward(args)...); } /// \} diff --git a/test/unit-test/multi/test-continuable-operations-async.cpp b/test/unit-test/multi/test-continuable-operations-async.cpp index 884a2f4..845dea6 100644 --- a/test/unit-test/multi/test-continuable-operations-async.cpp +++ b/test/unit-test/multi/test-continuable-operations-async.cpp @@ -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(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); +}