Skip to content

Timers: binary heap in a fixed array - #28

Merged
LarryRuane merged 1 commit into
masterfrom
timer-heap
Oct 19, 2026
Merged

LarryRuane merged 1 commit into
masterfrom
timer-heap

Conversation

@LarryRuane

Copy link
Copy Markdown
Owner

pt_sleep() inserted into a deadline-sorted list with interrupts masked. A new sleep usually has the latest deadline, so a typical insert walked every sleeper, and pt_timer_cancel() did the same.

This keeps the sleepers in a binary min-heap instead, an array of PT_NTIMERS (default 16) pointers inside pt_timers_t. A sleep, a cancel, and each wakeup are now O(log N) with interrupts masked. Each env stores its slot in place of next, so it doesn't grow. There's no API change.

  • When every slot is in use, pt_sleep() waits for one, and its sleep starts then. pt_timer_run() hands on a freed slot, including one freed by pt_timer_cancel() (at the next run, since cancel has no protothread_t).
  • Equal deadlines no longer wake in FIFO order. Nothing documented or tested that.

New tests:

  • test_timer_heap checks the heap after every step, with random deadlines and cancels, over 100 seeds.
  • test_timer_full covers waiting for a slot.

Both fail against a broken slot hand-off and a missing sift-up. Passes locally with gcc and clang-22, several PT_NWAIT/PT_NTIMERS settings, ASan/UBSan, C++11, and freestanding C99.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SR7v9BVsFrHiEhQBSqZmAs

pt_sleep() inserted into a deadline-sorted list with interrupts masked.
A new sleep usually has the latest deadline, so a typical insert walked
every sleeper, and pt_timer_cancel() did the same.

Keep the sleepers in a binary min-heap instead, an array of PT_NTIMERS
(default 16) pointers inside pt_timers_t. Each env stores its slot, in
place of next, so the env doesn't grow and cancel is O(log N) too. A
fixed array fits PT_NO_MALLOC.

A sleeper that finds every slot in use waits on the timer set for one.
It sets full_wait in the same critical section as its enqueue, and
pt_timer_run() broadcasts when it sees full_wait and a free slot. A slot
freed by pt_timer_cancel() is handed on at the next pt_timer_run(),
since cancel has no protothread_t to broadcast with.

Equal deadlines no longer wake in FIFO order. Nothing documented or
tested that.

test_timer_heap fills the heap with random deadlines, cancels random
entries, and checks the heap after every step, over 100 seeds.
test_timer_full covers waiting for a slot, including after a cancel.
Both catch a broken slot hand-off and a missing sift-up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SR7v9BVsFrHiEhQBSqZmAs
@LarryRuane
LarryRuane merged commit f00b072 into master Oct 19, 2026
31 checks passed
@LarryRuane
LarryRuane deleted the timer-heap branch October 19, 2026 21:12
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