2026-09-07
Every periodic loop written with sleep(interval) or nanosleep() drifts. The reason is straightforward: relative sleeps measure duration from the moment the kernel processes the syscall, not from your intended tick boundary. Every iteration accumulates the wall-clock time spent in your work, plus scheduler latency, plus the syscall overhead itself.
Consider a loop that wants to fire every 10ms:
nanosleep(10ms) → the kernel wakes you 10ms later, but only after the sleep startsThe fix is clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &deadline, NULL). Instead of "sleep for X," you say "sleep until wall-clock time T." The kernel converts the absolute time to a hrtimer expiry and puts you on the timer queue. If T has already passed, the syscall returns immediately with 0 — you're behind and you skip the wait, catching up automatically.
The canonical periodic loop:
clock_gettime(CLOCK_MONOTONIC, &next) once at the topnext.tv_nsec += period_ns (normalize into tv_sec)clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL)Now your deadline is anchored to a fixed monotonic reference. Jitter in any one iteration doesn't propagate — the next wakeup targets the same grid.
Real-world example: Audio callback threads and PLC-style control loops (10kHz sensor sampling, motor control, financial market-making tick generators) all use TIMER_ABSTIME. A relative-sleep 1kHz loop on a moderately loaded Linux system will typically drift 50-500 µs per second. Over a 24-hour trading session, that's tens of thousands of missed ticks. The absolute variant holds the grid within one scheduler quantum (usually <100 µs on a tuned system, single-digit µs with SCHED_FIFO and a tickless kernel).
Rule of thumb: If your loop period matters — meaning the fifth iteration should land at exactly t0 + 5×period, not "roughly 5×period after we started" — never use a relative sleep. The keyword is anchor: your deadline must be computed by adding to a stored timestamp, never by reading the clock inside the loop.
A subtle trap: CLOCK_REALTIME can jump backward (NTP, admin sets the clock). Always use CLOCK_MONOTONIC for periodic loops — it never goes backward and isn't affected by wall-clock adjustments. If you need absolute wall-time deadlines (e.g., "fire at midnight"), use CLOCK_TAI or handle EINTR/negative-jump cases explicitly.
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, ...) so jitter in one iteration doesn't accumulate into the next.
