From 982a8bbf6aeae3b6ae9f6208fd40007b539bc290 Mon Sep 17 00:00:00 2001 From: Aleksey Loginov Date: Mon, 1 Apr 2024 18:51:31 +0300 Subject: [PATCH 1/2] fix main thread scheduler --- src/rppqt/rppqt/schedulers/main_thread.hpp | 33 +++++++++++++++++-- .../rppqt/test_main_thread_scheduler.cpp | 31 ++++++++++++++++- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/rppqt/rppqt/schedulers/main_thread.hpp b/src/rppqt/rppqt/schedulers/main_thread.hpp index 088ab1095..fe3f133f7 100644 --- a/src/rppqt/rppqt/schedulers/main_thread.hpp +++ b/src/rppqt/rppqt/schedulers/main_thread.hpp @@ -14,6 +14,7 @@ #include // own forwarding #include +#include "rpp/schedulers/fwd.hpp" #include #include @@ -37,17 +38,43 @@ namespace rppqt::schedulers { const auto application = QCoreApplication::instance(); if (!application) - throw utils::no_active_qapplication{"Pointer to application is null. Create QApplication before using main_thread_scheduler!"}; + { + handler.on_error(std::make_exception_ptr(utils::no_active_qapplication{"Pointer to application is null. Create QApplication before using main_thread_scheduler!"})); + return; + } QTimer::singleShot(std::chrono::duration_cast(duration), application, [fn = std::forward(fn), handler = std::forward(handler), ... args = std::forward(args)]() mutable { - if (const auto new_duration = fn(handler, args...)) - defer_for(new_duration->value, std::move(fn), std::move(handler), std::move(args)...); + if (!handler.is_disposed()) + invoke(std::move(fn), std::move(handler), std::move(args)...); }); } static constexpr rpp::schedulers::details::none_disposable get_disposable() { return {}; } static rpp::schedulers::time_point now() { return rpp::schedulers::clock_type::now(); } + + private: + template Fn> + static void invoke(Fn&& fn, Handler&& handler, Args&&... args) + { + if (const auto new_duration = fn(handler, args...)) + defer_for(new_duration->value, std::forward(fn), std::forward(handler), std::forward(args)...); + } + + template Fn> + static void invoke(Fn&& fn, Handler&& handler, Args&&... args) + { + const auto now = rpp::schedulers::clock_type::now(); + if (const auto new_duration = fn(handler, args...)) + defer_for(now + new_duration->value - rpp::schedulers::clock_type::now(), std::forward(fn), std::forward(handler), std::forward(args)...); + } + + template Fn> + static void invoke(Fn&& fn, Handler&& handler, Args&&... args) + { + if (const auto new_tp = fn(handler, args...)) + defer_for(new_tp->value - rpp::schedulers::clock_type::now(), std::forward(fn), std::forward(handler), std::forward(args)...); + } }; public: diff --git a/src/tests/rppqt/test_main_thread_scheduler.cpp b/src/tests/rppqt/test_main_thread_scheduler.cpp index d382e8b5a..2251db9a7 100644 --- a/src/tests/rppqt/test_main_thread_scheduler.cpp +++ b/src/tests/rppqt/test_main_thread_scheduler.cpp @@ -14,13 +14,17 @@ #include #include "mock_observer.hpp" +#include "rpp/disposables/fwd.hpp" +#include "rpp/schedulers/fwd.hpp" #include #include +#include TEST_CASE("main_thread_scheduler schedules actions to main thread") { - auto observer = mock_observer_strategy{}.get_observer().as_dynamic(); + auto d = rpp::composite_disposable_wrapper::make(); + auto observer = mock_observer_strategy{}.get_observer(d).as_dynamic(); int argc{}; QCoreApplication application{argc, nullptr}; @@ -47,6 +51,24 @@ TEST_CASE("main_thread_scheduler schedules actions to main thread") } } + SECTION("nothing happens for disposed handler") + { + std::promise execution_thread{}; + std::thread{[&] { + rppqt::schedulers::main_thread_scheduler::create_worker().schedule([&](const auto&) -> rpp::schedulers::optional_delay_from_now { + execution_thread.set_value(std::this_thread::get_id()); + return {}; + }, + observer); + }}.join(); + + d.dispose(); + + application.exec(); + auto future = execution_thread.get_future(); + REQUIRE(future.wait_for(std::chrono::seconds{1}) == std::future_status::timeout); + } + SECTION("recursive scheduling to main thread") { std::string execution{}; @@ -68,4 +90,11 @@ TEST_CASE("main_thread_scheduler schedules actions to main thread") application.exec(); CHECK(execution == "outer inner outer inner "); } + + SECTION("scheduler can be applied for all types of schedulables") + { + rppqt::schedulers::main_thread_scheduler::create_worker().schedule([&](const auto&) -> rpp::schedulers::optional_delay_from_now { return std::nullopt; }, observer); + rppqt::schedulers::main_thread_scheduler::create_worker().schedule([&](const auto&) -> rpp::schedulers::optional_delay_from_this_timepoint { return std::nullopt; }, observer); + rppqt::schedulers::main_thread_scheduler::create_worker().schedule([&](const auto&) -> rpp::schedulers::optional_delay_to { return std::nullopt; }, observer); + } } From 91adb7e0ee3de8e22c68af0cebe6349dcec18a99 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 15:52:47 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/rppqt/rppqt/schedulers/main_thread.hpp | 1 + src/tests/rppqt/test_main_thread_scheduler.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rppqt/rppqt/schedulers/main_thread.hpp b/src/rppqt/rppqt/schedulers/main_thread.hpp index fe3f133f7..74091acd4 100644 --- a/src/rppqt/rppqt/schedulers/main_thread.hpp +++ b/src/rppqt/rppqt/schedulers/main_thread.hpp @@ -14,6 +14,7 @@ #include // own forwarding #include + #include "rpp/schedulers/fwd.hpp" #include diff --git a/src/tests/rppqt/test_main_thread_scheduler.cpp b/src/tests/rppqt/test_main_thread_scheduler.cpp index 2251db9a7..cb99de881 100644 --- a/src/tests/rppqt/test_main_thread_scheduler.cpp +++ b/src/tests/rppqt/test_main_thread_scheduler.cpp @@ -23,7 +23,7 @@ TEST_CASE("main_thread_scheduler schedules actions to main thread") { - auto d = rpp::composite_disposable_wrapper::make(); + auto d = rpp::composite_disposable_wrapper::make(); auto observer = mock_observer_strategy{}.get_observer(d).as_dynamic(); int argc{};