From f469b7058a922bffb2534253901edbe5ee1c6913 Mon Sep 17 00:00:00 2001 From: Denis Blank Date: Tue, 25 Dec 2018 09:30:23 +0100 Subject: [PATCH] Implement make_cancelling_continuable() * Can be used to cancel the chain when being inside a handler --- include/continuable/continuable-base.hpp | 38 +++++++++++++++++-- .../multi/test-continuable-base-destruct.cpp | 26 ++++++++++++- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/include/continuable/continuable-base.hpp b/include/continuable/continuable-base.hpp index 138ca8e..9a7039e 100644 --- a/include/continuable/continuable-base.hpp +++ b/include/continuable/continuable-base.hpp @@ -360,10 +360,11 @@ public: /// \since 2.0.0 template auto fail(continuable_base&& continuation) && { - return std::move(*this).fail( - [continuation = std::move(continuation).freeze()](exception_t) mutable { - std::move(continuation).done(); - }); + return std::move(*this) // + .fail([continuation = std::move(continuation).freeze()] // + (exception_t) mutable { + std::move(continuation).done(); // + }); } /// A method which allows to use an overloaded callable for the error @@ -866,6 +867,35 @@ constexpr auto make_exceptional_continuable(Exception&& exception) { }); } +/// Returns a continuable_base with the parameterized result which never +/// resolves its promise and thus cancels the asynchronous continuation chain. +/// +/// This can be used to cancel an asynchronous continuation chain when +/// returning a continuable_base from a handler where other paths could +/// possibly continue the asynchronous chain. See an example below: +/// ```cpp +/// do_sth().then([weak = this->weak_from_this()]() -> continuable<> { +/// if (auto me = weak.lock()) { +/// return do_sth_more(); +/// } else { +/// // Abort the asynchronous continuation chain since the +/// // weakly referenced object expired previously. +/// return make_cancelling_continuable(); +/// } +/// }); +/// ``` +/// +/// \tparam Signature The fake signature of the returned continuable. +/// +/// \since 4.0.0 +template +auto make_cancelling_continuable() { + static_assert(sizeof...(Signature) > 0, + "Requires at least one type for the fake signature!"); + + return make_continuable([](auto&&) { /* ... */ }); +} + /// Can be used to recover to from a failure handler, /// the result handler which comes after will be called with the /// corresponding result. diff --git a/test/unit-test/multi/test-continuable-base-destruct.cpp b/test/unit-test/multi/test-continuable-base-destruct.cpp index c7f514c..c8344c2 100644 --- a/test/unit-test/multi/test-continuable-base-destruct.cpp +++ b/test/unit-test/multi/test-continuable-base-destruct.cpp @@ -45,7 +45,8 @@ TYPED_TEST(single_dimension_tests, are_called_on_destruct) { ASSERT_ASYNC_TYPES(this->supply(tag1{}), tag1); } -template auto create_incomplete(T* me) { +template +auto create_incomplete(T* me) { return me->make(identity<>{}, identity{}, [](auto&& callback) mutable { // Destruct the callback here auto destroy = std::forward(callback); @@ -53,7 +54,16 @@ template auto create_incomplete(T* me) { }); } -template auto assert_invocation(T* me) { +template +auto create_incomplete_cancelling(T* me) { + return me->make(identity<>{}, identity{}, [](auto&& callback) mutable { + make_cancelling_continuable().next( + std::forward(callback)); + }); +} + +template +auto assert_invocation(T* me) { return me->make(identity<>{}, identity{}, [](auto&& /*callback*/) mutable { FAIL(); }); } @@ -90,6 +100,18 @@ TYPED_TEST(single_dimension_tests, are_not_finished_when_not_continued) { } } +TYPED_TEST(single_dimension_tests, are_not_finished_when_cancelling) { + { + auto chain = create_incomplete_cancelling(this); + ASSERT_ASYNC_INCOMPLETION(std::move(chain)); + } + + { + auto chain = create_incomplete_cancelling(this); + ASSERT_ASYNC_INCOMPLETION(std::move(chain).then(this->supply())); + } +} + TYPED_TEST(single_dimension_tests, freeze_is_kept_across_the_chain) { { auto chain = this->supply().freeze().then([=] { return this->supply(); });