I've implemented a timer thread whose job is to call functions at a certain interval. The functions may be called multiple times if their interval > 0 millisec otherwise only one time. Even if the timer thread is running, still a new function can be registered. Could you please provide your feedback and improvement factors?
class TimerThread
{
using ClockType = std::chrono::high_resolution_clock;
public:
TimerThread() = default;
TimerThread(TimerThread const &) = delete;
TimerThread & operator=(TimerThread const &) = delete;
~TimerThread() noexcept
{
try
{
stop();
}
catch(std::exception const &ex)
{
std::cout << "Exception: " << ex.what() << std::endl;
}
catch(...)
{
}
}
void registerCallback(std::function<void()> func, uint32_t const interval=0)
{
std::unique_lock<std::mutex> lock{mt_};
timers_.emplace_back(std::move(func), ClockType::now(), interval);
cv_.notify_all();
}
void start(uint32_t const delay=0)
{
if (! start_)
{
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
workerThread_ = std::thread{&TimerThread::run, this};
start_ = true;
}
}
private:
void run()
{
for (auto &t: timers_)
t.prevFireTime_ = ClockType::now();
while (startTimerFlag_.load(std::memory_order_acquire))
{
std::unique_lock<std::mutex> lock{mt_};
cv_.wait(lock, [this]() -> bool {
return ! timers_.empty();
});
for (auto &t: timers_)
if (t.isReady())
t.func_();
}
}
void stop()
{
startTimerFlag_.store(false, std::memory_order_release);
cv_.notify_all();
if (workerThread_.joinable())
workerThread_.join();
}
struct TimerInfo
{
TimerInfo() = default;
TimerInfo(std::function<void()> func, ClockType::time_point prevFireTime, uint32_t const interval):
func_{std::move(func)},
prevFireTime_{prevFireTime},
intervalMilliSec_{interval}
{
}
bool isReady()
{
if (!isFiredFirstTime)
{
isFiredFirstTime = true;
return true;
}
else if (intervalMilliSec_ != 0)
{
auto current = ClockType::now();
uint32_t const duration = std::chrono::duration_cast<std::chrono::milliseconds>(current - prevFireTime_).count();
if (duration >= intervalMilliSec_)
{
prevFireTime_ = current;
return true;
}
}
return false;
}
std::function<void()> func_;
ClockType::time_point prevFireTime_;
uint32_t intervalMilliSec_;
bool isFiredFirstTime{false};
};
std::vector<TimerInfo> timers_;
std::thread workerThread_;
std::mutex mt_;
std::condition_variable cv_;
std::atomic<bool> startTimerFlag_{true};
bool start_{false};
};
int main()
{
TimerThread timer;
timer.registerCallback([](){
std::cout << "Timer 1 - " << std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count() << std::endl;
}, 1000);
timer.registerCallback([](){
std::cout << "Timer 2 - " << std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count() << std::endl;
}, 2000);
timer.registerCallback([](){
std::cout << "Timer 3 - " << std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count() << std::endl;
});
timer.start();
std::this_thread::sleep_for(std::chrono::seconds(5));
LOG("Terminating main()...");
return 0;
}