diff --git a/src/rppqt/rppqt/schedulers/main_thread.hpp b/src/rppqt/rppqt/schedulers/main_thread.hpp index 088ab1095..74091acd4 100644 --- a/src/rppqt/rppqt/schedulers/main_thread.hpp +++ b/src/rppqt/rppqt/schedulers/main_thread.hpp @@ -15,6 +15,8 @@ #include // own forwarding #include +#include "rpp/schedulers/fwd.hpp" + #include #include #include @@ -37,17 +39,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..cb99de881 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); + } }