Skip to content

Timers and Watchdog

Oleksandr Geronime edited this page Jun 27, 2026 · 2 revisions

Timers and Watchdog

Overview

SERP provides two time-based primitives:

  • serp::Timer — schedule a callback to run after a delay, or repeatedly on an interval.
  • serp::Watchdog — enforce a hard timeout on an operation; if the watchdog fires before being reset, the process aborts.

Both primitives are bound to an EventLoop. Their callbacks fire on that loop's thread, making them safe to use alongside service-owned state without additional locking.


serp::Timer

Creating a Timer

static std::shared_ptr<serp::Timer> serp::Timer::create(
    serp::EventLoop& loop,
    std::chrono::duration<...> interval,
    std::function<void()> callback
);
Parameter Description
loop The EventLoop the callback fires on. Typically eventLoop() inside a service.
interval Time between firings (for repeating timers) or delay before the single firing.
callback The function to call when the timer fires.

Controlling the Timer

Method Description
start() Arm the timer. For repeating timers, the first fire occurs after interval.
stop() Disarm the timer. The callback will not fire again until start() is called.
restart() Reset the interval and re-arm. Equivalent to stop() + start().

One-Shot vs. Repeating

By default, timers are repeating — they fire every interval until stopped.

To create a one-shot timer, set the isRepeating flag:

auto timer = serp::Timer::create(loop, std::chrono::milliseconds(500), callback);
timer->setRepeating(false);
timer->start();
// Fires once after 500ms, then stops automatically

Example: Periodic Status Poll

class SensorService : public SensorServiceBase {
protected:
    void onInitialize() override {
        m_pollTimer = serp::Timer::create(
            eventLoop(),
            std::chrono::seconds(1),
            [this]() { pollStatus(); }
        );
        m_pollTimer->start();
    }

    void onDeinitialize() override {
        m_pollTimer->stop();
    }

private:
    void pollStatus() {
        // Runs on SensorService's EventLoop every second
        double reading = m_sensor.read();
        temperature.mutate(reading);
    }

    std::shared_ptr<serp::Timer> m_pollTimer;
};

Example: One-Shot Delayed Action

auto delay = serp::Timer::create(eventLoop(), std::chrono::seconds(3), [this]() {
    applyDeferredSettings();
});
delay->setRepeating(false);
delay->start();

Lifetime Management

Timers are reference-counted via shared_ptr. The timer is automatically stopped and destroyed when the last reference is released. Store the shared_ptr as a member to keep the timer alive:

std::shared_ptr<serp::Timer> m_timer;

Dropping the shared_ptr in onDeinitialize() is safe and will stop the timer.


serp::Watchdog

A watchdog enforces a hard deadline on an operation. If reset() is not called before the timeout expires, the watchdog fires and aborts the process. This makes watchdogs suitable for safety-critical enforcement — they cannot be silently skipped.

Creating a Watchdog

static std::shared_ptr<serp::Watchdog> serp::Watchdog::create(
    serp::EventLoop& loop,
    std::chrono::duration<...> timeout
);

Controlling the Watchdog

Method Description
start() Arm the watchdog. The countdown begins.
reset() Restart the countdown. Call this periodically to confirm liveness.
stop() Disarm the watchdog. The timeout is cancelled.

If the watchdog fires (timeout expires before reset() or stop() is called), the process terminates. This is intentional — a hung service should not silently block the system.

Example: Guarding a Long Operation

class UpdaterService : public UpdaterServiceBase {
    void performUpdate() {
        auto wd = serp::Watchdog::create(eventLoop(), std::chrono::seconds(5));
        wd->start();

        doLongOperation();   // Must complete in under 5 seconds

        wd->reset();         // Confirm completion; restarts countdown
        // or:
        wd->stop();          // Disarm entirely when done
    }
};

Example: Liveness Heartbeat

For long-running loops where individual steps are bounded but the overall loop must keep progressing:

void processLoop() {
    auto wd = serp::Watchdog::create(eventLoop(), std::chrono::seconds(10));
    wd->start();

    while (!m_done) {
        processNextItem();
        wd->reset();   // Must be called at least once every 10 seconds
    }

    wd->stop();
}

CommandProcessor Integration

The Serp::command library's CommandProcessor uses serp::Watchdog internally: each command dispatched through the processor has an associated watchdog. If a command handler does not complete within the configured timeout, the processor aborts. This behavior is automatic and requires no manual watchdog setup in command handlers.


Choosing Between Timer and Watchdog

Scenario Use
Periodic task (polling, heartbeat send) serp::Timer (repeating)
Delayed one-shot action serp::Timer (one-shot)
Enforcing a deadline — abort on timeout serp::Watchdog
Detecting a hung service serp::Watchdog
Async operation timeout with graceful fallback serp::Timer + serp::Promise::reject()

See Also

Clone this wiki locally