Skip to content

Timer API

Timers in uvent provide lightweight, coroutine-compatible one-shot scheduling.
Each timer fires exactly once, after the configured delay, and integrates directly into the event loop.

Timers are managed by the internal TimerWheel, which efficiently tracks large numbers of timeouts.


Timer class

class alignas(32) Timer
{
public:
    friend class core::EPoller;
    friend class TimerWheel;

    explicit Timer(timer_duration_t duration);

    Timer(const Timer&) = delete;
    Timer& operator=(const Timer&) = delete;
    Timer(Timer&&) = delete;
    Timer& operator=(Timer&&) = delete;

    // Bind a callback executed when the timer expires
    void addFunction(std::function<void(std::any&)> f, std::any arg);
    void addFunction(std::function<void(std::any&)> f, std::any& arg);

    // Bind an Awaitable coroutine which will be resumed once
    template <class AwaitableT>
    void addCoroutine(AwaitableT&& aw)
    {
        using A = std::remove_reference_t<AwaitableT>;

        static_assert(
            requires(A a) { a.get_promise(); },
            "Timer::addCoroutine expects usub::uvent::task::Awaitable<>-like type"
        );

        auto* p = aw.get_promise();
        this->coro = p->get_coroutine_handle();
        this->active = true;
    }

    // Directly bind an existing coroutine handle
    void bind(std::coroutine_handle<> h) noexcept;

public:
    timeout_t      expiryTime;
    timer_duration_t duration_ms;

private:
    std::coroutine_handle<> coro;
    bool active;
    uint64_t id;
    size_t slotIndex{0};
    size_t level{0};
};

Notes

  • duration_ms — delay before the timer fires.
  • addFunction — attach a callback that receives a std::any& payload.
  • addCoroutine — attaches a uvent coroutine; it is resumed exactly once.
  • Timers cannot be copied or moved.
  • Timers are scheduled into the thread-local TimerWheel.

Scheduling

/**
 * @brief Schedules a timer in the timer wheel.
 *
 * Inserts the timer into the timer subsystem so it will fire once
 * after its configured duration.
 *
 * @param timer Pointer to a valid Timer instance.
 *
 * @warning This function does not validate whether the timer is already active.
 *          Only schedule freshly constructed timers.
 */
inline void spawn_timer(utils::Timer* timer);

Timeout callbacks

void addFunction(std::function<void(std::any&)> f, std::any arg);
void addFunction(std::function<void(std::any&)> f, std::any& arg);

The callback is executed once after the timer expires. The stored std::any value is passed to the function at execution time.


Examples

One-shot timeout with a callback

task::Awaitable<void> demo() {
    auto* t = new utils::Timer(2000); // 2 seconds

    std::string payload = "hello timer";

    t->addFunction([](std::any& value) {
        auto& s = std::any_cast<std::string&>(value);
        std::cout << "Timer fired: " << s << std::endl;
    }, payload);

    spawn_timer(t);
    co_return;
}

One-shot resume of a coroutine

task::Awaitable<void> delayed() {
    auto* t = new utils::Timer(1500);

    t->addCoroutine([]() -> task::Awaitable<void> {
        std::cout << "Timer resumed coroutine" << std::endl;
        co_return;
    }());

    spawn_timer(t);
    co_return;
}

Typical mistakes

Uninitialized timers

Always construct a Timer using a valid duration.

Active timers

A timer must not be reused after scheduling; allocate a new one instead.

Callback misuse

Ensure the std::any payload remains valid until firing (for example, avoid passing references to locals).

Lifetime

Do not manually delete a Timer. The runtime cleans it up after completion.


Summary

Feature Description
One-shot fire Timer always triggers exactly once
Callback support addFunction(std::any&)
Coroutine support addCoroutine(Awaitable) and bind(handle)
spawn_timer() Schedules timer in thread-local wheel
Lightweight Designed for high-throughput timeout usage