File timer_service.hpp
File List > astutedds > dcps > timer_service.hpp
Go to the documentation of this file
//
// Copyright (c) 2026, Astute Systems PTY LTD
//
// This file is part of the Astute DDS developed by Astute Systems.
//
// See the commercial LICENSE file in the project root for full license details.
//
// @file timer_service.hpp
// @brief Min-heap based timer scheduler for DCPS-level watchdogs.
//
// The TimerService owns one background thread that waits on a condition
// variable until the next scheduled expiry, invokes the timer's callback,
// and (for periodic timers) rearms it. The heap gives O(log N) insert
// and O(1) peek; cancellation is O(1) via a per-timer flag and lazy
// popping of stale heap entries. This is the substrate for the §1.1b-B
// liveliness, deadline, and (future) sample-lost watchdogs.
//
// Threading contract:
// * All public methods except stop() and the destructor are safe to
// call from timer callbacks. Callbacks run on the timer thread with
// the internal mutex NOT held, so callbacks may re-enter schedule/
// cancel/reset without deadlock.
// * stop() and ~TimerService() must NOT be called from within a
// callback — they wait for the timer thread to join.
// * Callbacks that throw are silently caught so a bad user callback
// cannot kill the timer thread.
//
#ifndef ASTUTEDDS_DCPS_TIMER_SERVICE_HPP
#define ASTUTEDDS_DCPS_TIMER_SERVICE_HPP
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstdint>
#include <functional>
#include <mutex>
#include <queue>
#include <thread>
#include <unordered_map>
#include <vector>
namespace astutedds::dcps
{
using TimerId = std::uint64_t;
constexpr TimerId INVALID_TIMER_ID = 0;
class TimerService
{
public:
TimerService();
~TimerService();
TimerService(const TimerService&) = delete;
TimerService& operator=(const TimerService&) = delete;
void start();
void stop();
TimerId schedule(std::chrono::nanoseconds delay,
std::function<void()> callback);
TimerId schedule_periodic(std::chrono::nanoseconds period,
std::function<void()> callback);
bool cancel(TimerId id);
bool reset(TimerId id, std::chrono::nanoseconds new_delay);
std::size_t timer_count() const;
bool is_running() const;
private:
struct Timer
{
TimerId id{0};
std::chrono::steady_clock::time_point next_fire;
std::chrono::nanoseconds period{0};
std::function<void()> callback;
bool cancelled{false};
};
struct HeapNode
{
std::chrono::steady_clock::time_point when;
TimerId id;
bool operator>(const HeapNode& rhs) const noexcept { return when > rhs.when; }
};
void run_();
TimerId schedule_locked_(std::chrono::nanoseconds first_delay,
std::chrono::nanoseconds period,
std::function<void()> callback);
mutable std::mutex mtx_;
std::condition_variable cv_;
std::thread thread_;
std::atomic<bool> running_{false};
std::atomic<bool> started_{false};
std::atomic<std::uint64_t> next_id_{1};
std::unordered_map<TimerId, Timer> timers_;
std::priority_queue<HeapNode,
std::vector<HeapNode>,
std::greater<HeapNode>> heap_;
};
} // namespace astutedds::dcps
#endif // ASTUTEDDS_DCPS_TIMER_SERVICE_HPP