Skip to content

refactor(xpp): replace atomic waker slots with mutex in ScheduledIo - #201

Merged
mivinci merged 1 commit into
mainfrom
claude/scheduled-io-mutex
May 28, 2026
Merged

mivinci merged 1 commit into
mainfrom
claude/scheduled-io-mutex

Conversation

@mivinci

@mivinci mivinci commented May 28, 2026

Copy link
Copy Markdown
Owner

Summary

  • Replace 4 atomic pointer waker slots + 3 static helpers with xpp::sys::Mutex<Waiters>, following Tokio's ScheduledIo design
  • Readiness bitmask stays lock-free (hot path); mutex only guards waker slots (cold path)
  • Double-check-under-lock pattern in poll_read/poll_write prevents lost wakes
  • Add 15 unit tests covering all public methods

Motivation

The previous set_waker/fire_waker helpers stored Waker as two separate atomic pointers (Schedule* + SpawnTaskBase*) with manual release/acq_rel ordering. This was a lock-free reimplementation of a mutex but without actual mutual exclusion — the Waker struct was subject to concurrent read/write data races. Tokio uses a Mutex<Waiters> for exactly this reason.

Test plan

  • 15 new ScheduledIoTest cases: accessors, poll_read (not ready, ready, clear+re-poll, waker wake), poll_write (ready, clear+re-poll, not ready), close_fd (sets closed, idempotent, wakes read/write), concurrent poll+event
  • ctest -R xpp in build (C++14)
  • ctest -R xpp in build-cxx20 (C++20)

🤖 Generated with Claude Code

The previous implementation stored wakers as 4 atomic pointers
(Schedule* + SpawnTaskBase* per direction) with manual release/acq_rel
ordering. This was a lock-free reimplementation of a mutex but without
the actual mutual exclusion — the Waker struct itself was subject to
concurrent read/write data races.

Replace with a mutex-protected Waiters struct (xpp::sys::Mutex<Waiters>),
following Tokio's ScheduledIo design:
- Readiness bitmask stays lock-free (atomic<uint8_t>)
- Waker slots (reader/writer) behind a single mutex
- Double-check-under-lock in poll_read/poll_write prevents lost wakes
- wake_read/wake_write take the mutex before firing

This eliminates the data race, reduces fields from 10 to 5, and
removes 3 static helper functions (set_waker/clear_waker/fire_waker).

Also add 15 unit tests covering all public methods: poll_read,
poll_write, clear_readable, clear_writable, close_fd, is_closed,
accessors, and concurrent poll+event.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@mivinci
mivinci merged commit 0f99c24 into main May 28, 2026
5 checks passed
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