Skip to content

fix(driver): tolerate closed-budget BrokenPipe in uring service_groups - #67

Open
detail-app[bot] wants to merge 1 commit into
mainfrom
detail/bug-fix/fix-driver-tolerate-closed-budget-brokenpipe-in-ur-f8d735
Open

detail-app[bot] wants to merge 1 commit into
mainfrom
detail/bug-fix/fix-driver-tolerate-closed-budget-brokenpipe-in-ur-f8d735

Conversation

@detail-app

@detail-app detail-app Bot commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Detail bug report: View on Detail

Change

Closes #60

Persistent::service_groups in the io_uring driver (crates/bemo/src/driver/uring.rs) republishes returned/checked-out receive-buffer slots on each poll. When a slot is still checked out (delivered to the app and retained), publish allocates replacement storage through the pool's Budget. A closed workload budget makes Budget::reserve return BrokenPipe, but service_groups only tolerated WouldBlock/OutOfMemory — so the BrokenPipe escaped through prepare into Driver::fail, permanently poisoning the shared driver for every workload sharing it. Subsequent polls returned INVALID, try_shutdown failed, and Drop leaked the persistent slab.

This violated the documented ABI contract: closing a workload should leave "other workloads sharing its drivers untouched." The BrokenPipe-on-close convention is already handled gracefully elsewhere (Buffer::response, Pool::take, Pool::admit); only service_groups omitted it.

Fix: Added io::ErrorKind::BrokenPipe to the tolerated error kinds in both the grow and publish match arms of service_groups, so a closed budget stops the closing group from republishing without failing the driver. Affected layers: Rust io_uring driver only; no ABI, JVM, or header changes.

Validation

  • New unit tests in crates/bemo/src/driver.rs (Linux io_uring, #[cfg_attr(miri, ignore)]):
    • closing_a_workload_budget_with_a_checked_out_buffer_does_not_poison_the_shared_driver — receives a chunk, holds the buffer (slot checked out), closes the budget, polls, and asserts the driver stays healthy and try_shutdown completes cleanly with budget.used() == 0. Confirmed to fail with Kind(BrokenPipe) on the unmodified code.
    • persistent_receive_republishes_normally_while_the_workload_budget_is_open — the healthy A/B baseline confirming replacement allocation, a second receive, and clean shutdown while the budget stays open.
  • cargo build --workspace --locked, cargo clippy -p bemo --all-targets --locked -- -D warnings, and cargo fmt -p bemo --check all pass.
  • cargo nextest run -p bemo --locked --lib (31 driver/buffer unit tests) and integration tests for driver, workload, abi, pool, buffers, serving, http, and driver_churn all pass.
  • Backend sweep: forced io_uring (ELIDE_TRANSPORT_TEST_BACKEND=io-uring) and forced epoll (ELIDE_TRANSPORT_TEST_BACKEND=epoll) — both clean except two pre-existing test failures unrelated to this fix (confirmed by baseline runs via git stash).
  • Miri (cargo miri test -p bemo --no-default-features --features rust-allocator --lib --test buffers --test abi --test pool --test workload): 0 errors.
  • ASan and TSan via tools/verify.py (Elide LLVM toolchain, --stages rust): 386 tests, 0 sanitizer violations, only the same two pre-existing assertion failures.
  • Fuzz replay (cargo test --manifest-path fuzz/Cargo.toml --lib --locked): 7/7 pass.
  • make build and make test-native-image: Rust + JVM builds succeed; the C API contract (CapiContract) and gzip contract (ReusableGzipTest) pass through the native-image-built binaries. The CapiTlsChannelTest native-image build could not complete due to a pre-existing BouncyCastle classpath/OOM gap in tools/build.py::test_transport (BouncyCastle is not declared in bemo's build configuration) — unrelated to this Rust-only fix.
  • Platform: Linux kernel 6.1.186, io_uring backend.

Automatic Fixes PRs can be configured here.

@codecov

codecov Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.44444% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.85%. Comparing base (26fb123) to head (108eee4).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
crates/bemo/src/driver.rs 94.18% 5 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main      #67      +/-   ##
============================================
+ Coverage     86.82%   86.85%   +0.02%     
+ Complexity      616      615       -1     
============================================
  Files            60       60              
  Lines         16979    17067      +88     
  Branches        490      490              
============================================
+ Hits          14742    14823      +81     
- Misses         1913     1921       +8     
+ Partials        324      323       -1     
Flag Coverage Δ
jvm 57.47% <ø> (-0.04%) ⬇️
rust 93.02% <94.44%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@codspeed

codspeed Bot commented Oct 17, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

⚠️ 6 benchmarks spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

✅ 117 untouched benchmarks
⏩ 65 skipped benchmarks1


Comparing detail/bug-fix/fix-driver-tolerate-closed-budget-brokenpipe-in-ur-f8d735 (108eee4) with main (26fb123)

Open in CodSpeed

Footnotes

  1. 65 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

This branch has not been deployed

No deployments
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.

[Detail Bug] io_uring: Closing a workload can poison the shared driver when persistent receive buffers are checked out

1 participant