Skip to content

fix(eventfd): close a wake descriptor only when its last holder drops - #423

Merged
farhan-syah merged 3 commits into
mainfrom
fix/eventfd-notifier-fd-reuse
Oct 19, 2026
Merged

farhan-syah merged 3 commits into
mainfrom
fix/eventfd-notifier-fd-reuse

Conversation

@farhan-syah

@farhan-syah farhan-syah commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

Summary

Closes #419. A wake descriptor closes only when its last holder drops, so a wake for a dead core can never write into a file that reused the descriptor number.

Changes

  • EventFd and EventFdNotifier share an Arc of owned descriptors. The notifier is Clone, not Copy. A wake for a core that is gone lands in its still-open eventfd, which nothing reads. The eventfd is created with EFD_CLOEXEC.
  • nodedb-bridge: the AsyncFd watcher holds the shared wake pair itself, and the descriptor getters return BorrowedFd<'_>, not RawFd. This changes the crate's public API.
  • io_uring reader: a ring that stalls past MAX_STALLED_WAITS is retired and replaced. Before, its unsubmitted entries kept raw descriptors of files that closed when the call returned, and the next call submitted them against whatever file reused the numbers.

A workspace sweep found no other raw descriptor that can outlive its owner.

Test plan

  • notify_after_the_core_dropped_leaves_a_later_file_intact (the issue's reproduction)
  • descriptor_closes_only_when_the_last_holder_drops
  • wakes_after_a_core_panicked_at_open_leave_a_later_file_intact: a real spawn_core panics at engine open; later wakes and a dispatch leave a newly opened file byte-identical, and the request fails with "core-0 is gone"
  • cargo clippy --workspace --all-targets --all-features -- -D warnings, cargo fmt --all -- --check, all scripts/ci/ gates
  • Targeted nextest (bridge, eventfd, spawn, io_uring, dispatch): 953 passed
  • Full suite: CI

A core's wake eventfd closed when its thread exited, while the Control
Plane notifier and the Tokio AsyncFd registrations still held the raw
descriptor number. The kernel reuses the lowest free number, so a late
wake could write into an unrelated file opened afterwards.

The Data Plane EventFd and its notifiers now share the descriptors
through an Arc of OwnedFd, so they close once, when the last holder
drops. The bridge EventFd hands out BorrowedFd instead of RawFd, and
the Tokio wrapper keeps an Arc to the wake pair for the life of its
AsyncFd. The Linux eventfd is also created with EFD_CLOEXEC.

Closes #419
When a batched read stalled, queued submission entries still carried
the raw descriptors of files that close as the batch returns. Entering
the ring again could submit reads against reused descriptor numbers.

A stalled ring is now replaced, or marked poisoned and never entered
again, with reads falling back to a sequential path. Per-read state
and the stall cleanup helpers move into uring_read_state.
Timing thresholds in unit tests fail under machine load. The KV update
test now checks one entry per key and the last written value. The
timeseries ingest and aggregation tests drop their timers and keep the
row-count and result assertions. Throughput belongs in benchmarks.
@farhan-syah
farhan-syah merged commit 8b1318b into main Oct 19, 2026
6 checks passed
@farhan-syah
farhan-syah deleted the fix/eventfd-notifier-fd-reuse branch October 19, 2026 01:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run-ci Opt this PR into the full test suite; re-add to force a re-run

Projects

None yet

Development

Successfully merging this pull request may close these issues.

runtime: EventFdNotifier keeps a raw fd past its core's death — notify() overwrites the header of whichever redb file reused it

1 participant