From f42f969c91f8382d56e8e858334a9bc3041a8571 Mon Sep 17 00:00:00 2001 From: jbaldwin Date: Sat, 27 Nov 2021 09:17:29 -0700 Subject: [PATCH] Fixes #109 fn overload on task promise::return_void bug The return_void() overload was throwing, but this is incorrect per the standard, it should effectively do nothing. This was an error on my part since I over- loaded the non-void task return_value(T) -> void and return_value() -> T fns. This overload isn't possible with the void versions since the signature is identical, thus the bug caused the compiler's coroutine version to re-throw exceptions at the wrong time. --- inc/coro/generator.hpp | 2 +- inc/coro/sync_wait.hpp | 10 ++++++---- inc/coro/task.hpp | 36 +++++++++++++++++++----------------- inc/coro/when_all.hpp | 10 ++++++---- test/test_event.cpp | 14 +++++++------- test/test_latch.cpp | 10 +++++----- test/test_shared_mutex.cpp | 2 +- test/test_task.cpp | 22 +++++++++++----------- 8 files changed, 56 insertions(+), 50 deletions(-) diff --git a/inc/coro/generator.hpp b/inc/coro/generator.hpp index 73e53077..ede42d0d 100644 --- a/inc/coro/generator.hpp +++ b/inc/coro/generator.hpp @@ -41,7 +41,7 @@ class generator_promise auto unhandled_exception() -> void { m_exception = std::current_exception(); } - auto return_void() -> void {} + auto return_void() noexcept -> void {} auto value() const noexcept -> reference_type { return static_cast(*m_value); } diff --git a/inc/coro/sync_wait.hpp b/inc/coro/sync_wait.hpp index a59840a4..a9bbd837 100644 --- a/inc/coro/sync_wait.hpp +++ b/inc/coro/sync_wait.hpp @@ -80,7 +80,7 @@ class sync_wait_task_promise : public sync_wait_task_promise_base return completion_notifier{}; } - auto return_value() -> return_type&& + auto result() -> return_type&& { if (m_exception) { @@ -123,7 +123,9 @@ class sync_wait_task_promise : public sync_wait_task_promise_base return completion_notifier{}; } - auto return_void() -> void + auto return_void() noexcept -> void {} + + auto result() -> void { if (m_exception) { @@ -169,12 +171,12 @@ class sync_wait_task if constexpr (std::is_same_v) { // Propagate exceptions. - m_coroutine.promise().return_void(); + m_coroutine.promise().result(); return; } else { - return m_coroutine.promise().return_value(); + return m_coroutine.promise().result(); } } diff --git a/inc/coro/task.hpp b/inc/coro/task.hpp index 8f61aa9e..4b39963c 100644 --- a/inc/coro/task.hpp +++ b/inc/coro/task.hpp @@ -70,7 +70,7 @@ struct promise final : public promise_base auto return_value(return_type value) -> void { m_return_value = std::move(value); } - auto return_value() const& -> const return_type& + auto result() const& -> const return_type& { if (m_exception_ptr) { @@ -80,7 +80,7 @@ struct promise final : public promise_base return m_return_value; } - auto return_value() && -> return_type&& + auto result() && -> return_type&& { if (m_exception_ptr) { @@ -105,7 +105,9 @@ struct promise : public promise_base auto get_return_object() noexcept -> task_type; - auto return_void() -> void + auto return_void() noexcept -> void {} + + auto result() -> void { if (m_exception_ptr) { @@ -143,7 +145,7 @@ class [[nodiscard]] task explicit task(coroutine_handle handle) : m_coroutine(handle) {} task(const task&) = delete; - task(task&& other) noexcept : m_coroutine(std::exchange(other.m_coroutine, nullptr)) {} + task(task && other) noexcept : m_coroutine(std::exchange(other.m_coroutine, nullptr)) {} ~task() { @@ -153,9 +155,9 @@ class [[nodiscard]] task } } - auto operator=(const task&) -> task& = delete; + auto operator=(const task&)->task& = delete; - auto operator=(task&& other) noexcept -> task& + auto operator=(task&& other) noexcept->task& { if (std::addressof(other) != this) { @@ -173,9 +175,9 @@ class [[nodiscard]] task /** * @return True if the task is in its final suspend or if the task has been destroyed. */ - auto is_ready() const noexcept -> bool { return m_coroutine == nullptr || m_coroutine.done(); } + auto is_ready() const noexcept->bool { return m_coroutine == nullptr || m_coroutine.done(); } - auto resume() -> bool + auto resume()->bool { if (!m_coroutine.done()) { @@ -184,7 +186,7 @@ class [[nodiscard]] task return !m_coroutine.done(); } - auto destroy() -> bool + auto destroy()->bool { if (m_coroutine != nullptr) { @@ -205,12 +207,12 @@ class [[nodiscard]] task if constexpr (std::is_same_v) { // Propagate uncaught exceptions. - this->m_coroutine.promise().return_void(); + this->m_coroutine.promise().result(); return; } else { - return this->m_coroutine.promise().return_value(); + return this->m_coroutine.promise().result(); } } }; @@ -227,12 +229,12 @@ class [[nodiscard]] task if constexpr (std::is_same_v) { // Propagate uncaught exceptions. - this->m_coroutine.promise().return_void(); + this->m_coroutine.promise().result(); return; } else { - return std::move(this->m_coroutine.promise()).return_value(); + return std::move(this->m_coroutine.promise()).result(); } } }; @@ -240,12 +242,12 @@ class [[nodiscard]] task return awaitable{m_coroutine}; } - auto promise() & -> promise_type& { return m_coroutine.promise(); } + auto promise()&->promise_type& { return m_coroutine.promise(); } - auto promise() const& -> const promise_type& { return m_coroutine.promise(); } - auto promise() && -> promise_type&& { return std::move(m_coroutine.promise()); } + auto promise() const&->const promise_type& { return m_coroutine.promise(); } + auto promise()&&->promise_type&& { return std::move(m_coroutine.promise()); } - auto handle() -> coroutine_handle { return m_coroutine; } + auto handle()->coroutine_handle { return m_coroutine; } private: coroutine_handle m_coroutine{nullptr}; diff --git a/inc/coro/when_all.hpp b/inc/coro/when_all.hpp index cc8fc914..5adf59a4 100644 --- a/inc/coro/when_all.hpp +++ b/inc/coro/when_all.hpp @@ -340,7 +340,9 @@ class when_all_task_promise auto unhandled_exception() noexcept -> void { m_exception_ptr = std::current_exception(); } - auto return_void() noexcept -> void + auto return_void() noexcept -> void {} + + auto result() -> void { if (m_exception_ptr) { @@ -393,7 +395,7 @@ class when_all_task { if constexpr (std::is_void_v) { - m_coroutine.promise().return_void(); + m_coroutine.promise().result(); return void_value{}; } else @@ -406,7 +408,7 @@ class when_all_task { if constexpr (std::is_void_v) { - m_coroutine.promise().return_void(); + m_coroutine.promise().result(); return void_value{}; } else @@ -419,7 +421,7 @@ class when_all_task { if constexpr (std::is_void_v) { - m_coroutine.promise().return_void(); + m_coroutine.promise().result(); return void_value{}; } else diff --git a/test/test_event.cpp b/test/test_event.cpp index a50c8946..422babba 100644 --- a/test/test_event.cpp +++ b/test/test_event.cpp @@ -20,7 +20,7 @@ TEST_CASE("event single awaiter", "[event]") REQUIRE_FALSE(task.is_ready()); e.set(); // this will automaticaly resume the task that is awaiting the event. REQUIRE(task.is_ready()); - REQUIRE(task.promise().return_value() == 42); + REQUIRE(task.promise().result() == 42); } auto producer(coro::event& event) -> void @@ -46,7 +46,7 @@ TEST_CASE("event one watcher", "[event]") producer(e); - REQUIRE(value.promise().return_value() == 42); + REQUIRE(value.promise().result() == 42); } TEST_CASE("event multiple watchers", "[event]") @@ -65,9 +65,9 @@ TEST_CASE("event multiple watchers", "[event]") producer(e); - REQUIRE(value1.promise().return_value() == 42); - REQUIRE(value2.promise().return_value() == 42); - REQUIRE(value3.promise().return_value() == 42); + REQUIRE(value1.promise().result() == 42); + REQUIRE(value2.promise().result() == 42); + REQUIRE(value3.promise().result() == 42); } TEST_CASE("event reset", "[event]") @@ -82,7 +82,7 @@ TEST_CASE("event reset", "[event]") REQUIRE_FALSE(value1.is_ready()); producer(e); - REQUIRE(value1.promise().return_value() == 42); + REQUIRE(value1.promise().result() == 42); e.reset(); @@ -92,7 +92,7 @@ TEST_CASE("event reset", "[event]") producer(e); - REQUIRE(value2.promise().return_value() == 42); + REQUIRE(value2.promise().result() == 42); } TEST_CASE("event fifo", "[event]") diff --git a/test/test_latch.cpp b/test/test_latch.cpp index 26c7673e..83cfa963 100644 --- a/test/test_latch.cpp +++ b/test/test_latch.cpp @@ -18,7 +18,7 @@ TEST_CASE("latch count=0", "[latch]") task.resume(); REQUIRE(task.is_ready()); // The latch never waits due to zero count. - REQUIRE(task.promise().return_value() == 42); + REQUIRE(task.promise().result() == 42); } TEST_CASE("latch count=1", "[latch]") @@ -38,7 +38,7 @@ TEST_CASE("latch count=1", "[latch]") l.count_down(); REQUIRE(task.is_ready()); - REQUIRE(task.promise().return_value() == 1); + REQUIRE(task.promise().result() == 1); } TEST_CASE("latch count=1 count_down=5", "[latch]") @@ -58,7 +58,7 @@ TEST_CASE("latch count=1 count_down=5", "[latch]") l.count_down(5); REQUIRE(task.is_ready()); - REQUIRE(task.promise().return_value() == 1); + REQUIRE(task.promise().result() == 1); } TEST_CASE("latch count=5 count_down=1 x5", "[latch]") @@ -86,7 +86,7 @@ TEST_CASE("latch count=5 count_down=1 x5", "[latch]") REQUIRE_FALSE(task.is_ready()); l.count_down(1); REQUIRE(task.is_ready()); - REQUIRE(task.promise().return_value() == 5); + REQUIRE(task.promise().result() == 5); } TEST_CASE("latch count=5 count_down=5", "[latch]") @@ -106,5 +106,5 @@ TEST_CASE("latch count=5 count_down=5", "[latch]") l.count_down(5); REQUIRE(task.is_ready()); - REQUIRE(task.promise().return_value() == 5); + REQUIRE(task.promise().result() == 5); } diff --git a/test/test_shared_mutex.cpp b/test/test_shared_mutex.cpp index 7dbbd73e..d2c95013 100644 --- a/test/test_shared_mutex.cpp +++ b/test/test_shared_mutex.cpp @@ -130,7 +130,7 @@ TEST_CASE("mutex many shared and exclusive waiters interleaved", "[shared_mutex] { if (st.is_ready()) { - stop = st.promise().return_value(); + stop = st.promise().result(); } } } diff --git a/test/test_task.cpp b/test/test_task.cpp index 469bb0b0..3a5a7329 100644 --- a/test/test_task.cpp +++ b/test/test_task.cpp @@ -12,8 +12,8 @@ TEST_CASE("task hello world", "[task]") auto h = []() -> task_type { co_return "Hello"; }(); auto w = []() -> task_type { co_return "World"; }(); - REQUIRE(h.promise().return_value().empty()); - REQUIRE(w.promise().return_value().empty()); + REQUIRE(h.promise().result().empty()); + REQUIRE(w.promise().result().empty()); h.resume(); // task suspends immediately w.resume(); @@ -21,11 +21,11 @@ TEST_CASE("task hello world", "[task]") REQUIRE(h.is_ready()); REQUIRE(w.is_ready()); - auto w_value = std::move(w).promise().return_value(); + auto w_value = std::move(w).promise().result(); - REQUIRE(h.promise().return_value() == "Hello"); + REQUIRE(h.promise().result() == "Hello"); REQUIRE(w_value == "World"); - REQUIRE(w.promise().return_value().empty()); + REQUIRE(w.promise().result().empty()); } TEST_CASE("task void", "[task]") @@ -60,7 +60,7 @@ TEST_CASE("task exception thrown", "[task]") bool thrown{false}; try { - auto value = task.promise().return_value(); + auto value = task.promise().result(); } catch (const std::exception& e) { @@ -164,7 +164,7 @@ TEST_CASE("task multiple suspends return integer", "[task]") task.resume(); // third internal suspend REQUIRE(task.is_ready()); - REQUIRE(task.promise().return_value() == 11); + REQUIRE(task.promise().result() == 11); } TEST_CASE("task resume from promise to coroutine handles of different types", "[task]") @@ -193,7 +193,7 @@ TEST_CASE("task resume from promise to coroutine handles of different types", "[ REQUIRE(task1.is_ready()); REQUIRE(coro_handle1.done()); - REQUIRE(task1.promise().return_value() == 42); + REQUIRE(task1.promise().result() == 42); REQUIRE(task2.is_ready()); REQUIRE(coro_handle2.done()); @@ -208,7 +208,7 @@ TEST_CASE("task throws void", "[task]") REQUIRE_NOTHROW(task.resume()); REQUIRE(task.is_ready()); - REQUIRE_THROWS_AS(task.promise().return_void(), std::runtime_error); + REQUIRE_THROWS_AS(task.promise().result(), std::runtime_error); } TEST_CASE("task throws non-void l-value", "[task]") @@ -220,7 +220,7 @@ TEST_CASE("task throws non-void l-value", "[task]") REQUIRE_NOTHROW(task.resume()); REQUIRE(task.is_ready()); - REQUIRE_THROWS_AS(task.promise().return_value(), std::runtime_error); + REQUIRE_THROWS_AS(task.promise().result(), std::runtime_error); } TEST_CASE("task throws non-void r-value", "[task]") @@ -239,5 +239,5 @@ TEST_CASE("task throws non-void r-value", "[task]") task.resume(); REQUIRE(task.is_ready()); - REQUIRE_THROWS_AS(task.promise().return_value(), std::runtime_error); + REQUIRE_THROWS_AS(task.promise().result(), std::runtime_error); } \ No newline at end of file