Table of Contents

Dedicated Timer (HighResolutionTimerService)

A single periodic callback on its own thread, isolated from every other timer in the engine.

Status: โœ… Implemented ยท Visibility: Internal ยท Category: High-Resolution Timers

๐ŸŽฏ What it solves

Some periodic handlers can't tolerate sharing a thread: if a slow callback elsewhere in the process delays the tick, a safety-critical check (a heartbeat, a hard real-time gate) is delayed with it. HighResolutionTimerService dedicates a full thread and the engine's calibrated three-phase wait loop to exactly one callback at a fixed interval, so its timing is never a function of what else is registered elsewhere.

โš™๏ธ How it works (in brief)

Constructing a HighResolutionTimerService does not start it โ€” the thread starts on an explicit Start() call. From then on, the timer maintains a metronome anchor (nextTick += intervalTicks after every fire, never recomputed from "now") and invokes the single callback once per tick with the scheduled and actual Stopwatch timestamps. If the thread falls behind by more than one interval, it skips forward rather than bursting through the backlog, and counts the skipped ticks as missed. Dispose() stops the thread (joined with a 2-second timeout) and removes the timer from the resource tree.

๐Ÿ’ป Usage

// Engine-internal usage โ€” application code does not construct this directly.
var heartbeat = new HighResolutionTimerService(
    name: "HeartbeatMonitor",
    intervalTicks: Stopwatch.Frequency / 1000,  // 1ms
    callback: (scheduled, actual) => HeartbeatMonitor.CheckAlive(),
    parent: resourceRegistry.TimerDedicated,
    logger: loggerFactory.CreateLogger<HighResolutionTimerService>());

heartbeat.Start();   // thread starts here; already visible in the resource tree

// Inspect timing/invocation metrics directly or via the resource graph
Console.WriteLine($"Timing error: {heartbeat.MeanTimingErrorUs:F1}us, invocations: {heartbeat.InvocationCount}");

heartbeat.Dispose(); // stops the thread, removes from the resource tree
Option Default Effect
intervalTicks โ€” (required, > 0) Period between invocations, in Stopwatch ticks; Stopwatch.Frequency / 1000 โ‰ˆ 1ms
parent โ€” (required) Resource-tree parent; dedicated timers register under registry.TimerDedicated

โš ๏ธ Guarantees & limits

  • Full thread isolation โ€” no other timer or callback can delay this one's tick.
  • internal sealed engine type โ€” used by subsystems that own their own dedicated timer (e.g. a heartbeat monitor), not constructible from application code.
  • Callback budget is a soft contract (target < 100ยตs); a slow callback delays only this timer's own subsequent ticks, never another timer's.
  • Exceptions thrown by the callback are caught, logged, and do not stop the timer.
  • 1ms intervals keep one logical core near-fully busy (Yield+Spin, no room for the Sleep phase); 5ms+ intervals let Sleep absorb most of the wait, dropping CPU to near zero.
  • One OS thread (default 1MB stack) per instance โ€” appropriate for a handful of isolated timers, not for dozens of lightweight periodic tasks (use the shared timer for those).
  • MeanTimingErrorUs/MaxTimingErrorUs/InvocationCount/MissedTicks are available both as direct properties and through IMetricSource resource-graph snapshots.

๐Ÿงช Tests