Skip to content

Spin through the tick where sleeps wake late - #53

Merged
Jepson2k merged 2 commits into
mainfrom
fix/adaptive-busy-wait
Oct 3, 2026
Merged

Jepson2k merged 2 commits into
mainfrom
fix/adaptive-busy-wait

Conversation

@Jepson2k

@Jepson2k Jepson2k commented Oct 3, 2026

Copy link
Copy Markdown
Collaborator

LoopTimer slept until 1 ms before each deadline and spun the rest. On GitHub's macOS runners (3 vCPUs, load average near 20) a 10 ms sleep wakes 30–90 ms late, so the 100 Hz loop ran at ~40 Hz: status went out at ~8 Hz instead of 20, and acks slipped a tick or more. That's the "macOS loopback runs late" pattern the harness tests worked around. Measured on CI: a spinning thread there sees no stalls, a sleeping one wakes late. A latency-critical QoS class made no difference.

The timer now keeps an estimate of how late its sleeps wake and widens the busy window to threshold + 2 × lateness, capped at the whole tick. On a machine whose sleep is accurate (Linux, Windows) the window stays at the configured 1 ms. While spinning, the estimate fades, so the loop retries sleeping and narrows again if sleep recovers. Float arithmetic only; nothing allocates in the tick.

tests/unit/test_loop_timer_late_sleep.py simulates sleeps that wake 20 ms late. Before: 100 overruns in 200 ticks; after: at most a few. With exact sleep the window stays near 1 ms.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GUfKe4qYwmMZJgoch8LDT6

Jepson2k and others added 2 commits October 3, 2026 12:59
The loop slept to 1 ms before each deadline and spun the rest. On an
oversubscribed VM (GitHub's macOS runners: 3 cores, load near 20) a 10 ms
sleep woke 30-90 ms late, so the 100 Hz loop ran near 40 Hz with status
and acks a tick or more behind. A thread that spins is not preempted
there, one that sleeps is. The timer now estimates how late its sleeps
wake and widens the busy window to match, up to the whole tick; on a
machine whose sleep is accurate the window stays at the configured 1 ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GUfKe4qYwmMZJgoch8LDT6
… clock

The path tests' sampler polled status every 20 ms with a sleep; where
sleeps wake late it caught a fast path too sparsely to see its corners,
and with the loop now on time the arm moves fast enough there to miss
them. It records every published frame as it arrives instead.

The loop-timer tests run on a virtual clock, so a preempted CI runner
cannot fake a late sleep where the test means an accurate one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GUfKe4qYwmMZJgoch8LDT6
@Jepson2k
Jepson2k merged commit 604d1ec into main Oct 3, 2026
13 checks passed
@Jepson2k
Jepson2k deleted the fix/adaptive-busy-wait branch October 3, 2026 18:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant