From ec9afb5256ba7f90950d983530a4cc9cc59067a0 Mon Sep 17 00:00:00 2001 From: Tim Sylvester Date: Mon, 22 Jan 2024 10:53:41 -0800 Subject: [PATCH] UV timer hack (#2042) --- platform/default/src/mbgl/util/timer.cpp | 13 +++++++-- test/util/timer.test.cpp | 34 ++++++++++++++---------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/platform/default/src/mbgl/util/timer.cpp b/platform/default/src/mbgl/util/timer.cpp index 92eb4aa69..fd8f94f65 100644 --- a/platform/default/src/mbgl/util/timer.cpp +++ b/platform/default/src/mbgl/util/timer.cpp @@ -1,11 +1,12 @@ #include -#include - #include +#include #include +#include + namespace mbgl { namespace util { @@ -28,6 +29,14 @@ public: void start(uint64_t timeout, uint64_t repeat, std::function&& cb_) { cb = std::move(cb_); + + // Update the event loop’s concept of “now”. + // This resolves test failures on some systems, where only the following sequence of tests fails: + // Thread.InvokeBeforeChildStarts:Thread.DeleteBeforeChildStarts:Timer.Basic + // This implies that "you have callbacks that block the event loop for ... on the order of a millisecond + // or more," so this may be masking some unexpected interaction between successive `RunLoop` instances. + uv_update_time(reinterpret_cast(RunLoop::getLoopHandle())); + if (uv_timer_start(timer, timerCallback, timeout, repeat) != 0) { throw std::runtime_error("Failed to start timer."); } diff --git a/test/util/timer.test.cpp b/test/util/timer.test.cpp index c3e0d1761..4b4010405 100644 --- a/test/util/timer.test.cpp +++ b/test/util/timer.test.cpp @@ -1,37 +1,43 @@ #include #include -#include +#include #include +#include +#include #include +#include using namespace mbgl::util; TEST(Timer, TEST_REQUIRES_ACCURATE_TIMING(Basic)) { RunLoop loop; - Timer timer; + const auto interval = mbgl::Milliseconds(300); + const auto expectedTotalTime = interval; - auto callback = [&loop] { + const auto first = MonotonicTimer::now(); + const auto elapsed = [=] { + return std::chrono::duration_cast(MonotonicTimer::now() - first); + }; + std::optional callbackTime; + auto callback = [&] { + callbackTime = elapsed(); loop.stop(); }; - auto interval = mbgl::Milliseconds(300); - auto expectedTotalTime = interval; - - auto first = mbgl::Clock::now(); - timer.start(interval, mbgl::Duration::zero(), callback); + Timer timer; + timer.start(interval, mbgl::Duration::zero(), std::move(callback)); loop.run(); - auto totalTime = std::chrono::duration_cast(mbgl::Clock::now() - first); + const auto totalTime = elapsed(); - // These are not high precision timers. Especially libuv uses - // cached time from the beginning of of the main loop iteration - // and it is very prone to fire earlier, which is, odd. - EXPECT_GE(totalTime, expectedTotalTime * 0.8); - EXPECT_LE(totalTime, expectedTotalTime * 1.2); + SCOPED_TRACE("Timer callback: " + (callbackTime ? toString(callbackTime->count()) : "(never)")); + + EXPECT_GE(totalTime.count(), (expectedTotalTime * 0.99).count()); + EXPECT_LE(totalTime.count(), (expectedTotalTime * 1.10).count()); } TEST(Timer, TEST_REQUIRES_ACCURATE_TIMING(Repeat)) {