// Regression test for the animation frame timer. // // Re-arming a Fcitx5 time event for one more shot has to be setTime() followed by // setOneShot(). Calling setEnabled(true) afterwards switches the source back to // SD_EVENT_ON, and with a time that has already passed the event loop fires it // again on every iteration, which keeps the main thread at 100% CPU. // // g++ -std=c++20 tests/timer_rearm.cpp -o /tmp/timer_rearm \ // $(pkg-config --cflags --libs Fcitx5Utils) && /tmp/timer_rearm #include #include #include #include #include using Clock = std::chrono::steady_clock; static uint64_t usecIn(std::chrono::milliseconds ms) { return std::chrono::duration_cast( (Clock::now() + ms).time_since_epoch()) .count(); } // Arms a timer twice, like two animations in a row, and counts how often the // callback runs during 200 ms. static int run(bool buggy) { fcitx::EventLoop loop; int fired = 0; std::unique_ptr timer; auto arm = [&] { if (!timer) { timer = loop.addTimeEvent(CLOCK_MONOTONIC, usecIn(std::chrono::milliseconds(10)), 1000, [&](fcitx::EventSourceTime *, uint64_t) { fired++; return true; }); timer->setOneShot(); } else { timer->setTime(usecIn(std::chrono::milliseconds(10))); timer->setOneShot(); if (buggy) { timer->setEnabled(true); // what scheduleFrame used to do } } }; auto stop = loop.addTimeEvent(CLOCK_MONOTONIC, usecIn(std::chrono::milliseconds(200)), 1000, [&](fcitx::EventSourceTime *, uint64_t) { loop.exit(); return true; }); auto second = loop.addTimeEvent(CLOCK_MONOTONIC, usecIn(std::chrono::milliseconds(60)), 1000, [&](fcitx::EventSourceTime *, uint64_t) { arm(); // the second animation return true; }); arm(); // the first animation loop.exec(); return fired; } int main() { const int buggy = run(true); const int fixed = run(false); std::printf("old pattern (setTime, setOneShot, setEnabled): %d callbacks in 200 ms\n", buggy); std::printf("new pattern (setTime, setOneShot): %d callbacks in 200 ms\n", fixed); // Two animations, so two shots are right. if (fixed != 2) { std::printf("FAIL: expected exactly 2 callbacks\n"); return 1; } if (buggy <= 2) { std::printf("note: the old pattern did not misbehave on this build\n"); } std::printf("ok\n"); return 0; }