polybar-dwm/include/modules/meta/timer_module.hpp
Malte Bargholz a625e2b79a
fix(timer_module): sleep until next full interval (#2123)
Any timer_module based module would sleep for the set interval and then
continue running. Depending on the start time of polybar this
sleep pattern might not be aligned, which causes such modules to always
update in a shifted manner.
Consider the date module as an example. If the update interval is set to
60 seconds and polybar was started at 13:37:37, polybar would update the
clock at 13:38:37, 13:39:37 and so on.
To make matters worse, if a module would perform lengthy checks this
interval might drift over time, causing even more inconsistent updating.

This patch extends the base module with a sleep_until method that calls
the corresponding function on the sleephandler. Additionally the
timer_module is extended to compute the remaining time until the next
interval passes and sleep accordingly.

Closes #2064

Co-developed-by: Dominik Töllner <dominik.toellner@stud.uni-hannover.de>

Co-authored-by: Malte Bargholz <malte@screenri.de>
2020-06-20 22:15:25 +02:00

64 lines
1.8 KiB
C++

#pragma once
#include "modules/meta/base.hpp"
POLYBAR_NS
namespace modules {
using interval_t = chrono::duration<double>;
template <class Impl>
class timer_module : public module<Impl> {
public:
using module<Impl>::module;
void start() {
this->m_mainthread = thread(&timer_module::runner, this);
}
protected:
void runner() {
this->m_log.trace("%s: Thread id = %i", this->name(), concurrency_util::thread_id(this_thread::get_id()));
const auto check = [&]() -> bool {
std::unique_lock<std::mutex> guard(this->m_updatelock);
return CAST_MOD(Impl)->update();
};
try {
// warm up module output before entering the loop
check();
CAST_MOD(Impl)->broadcast();
while (this->running()) {
if (check()) {
CAST_MOD(Impl)->broadcast();
}
// wait until next full interval to avoid drifting clocks
using clock = chrono::system_clock;
using sys_duration_t = clock::time_point::duration;
auto sys_interval = chrono::duration_cast<sys_duration_t>(m_interval);
clock::time_point now = clock::now();
sys_duration_t adjusted = sys_interval - (now.time_since_epoch() % sys_interval);
// The seemingly arbitrary addition of 500ms is due
// to the fact that if we wait the exact time our
// thread will be woken just a tiny bit prematurely
// and therefore the wrong time will be displayed.
// It is currently unknown why exactly the thread gets
// woken prematurely.
CAST_MOD(Impl)->sleep_until(now + adjusted + 500ms);
}
} catch (const exception& err) {
CAST_MOD(Impl)->halt(err.what());
}
}
protected:
interval_t m_interval{1.0};
};
} // namespace modules
POLYBAR_NS_END