Skip to content

test: cover RegisteredBufferGroup drop in constrained environments#4027

Merged
oferchen merged 3 commits into
masterfrom
test/registered-buffer-group-cleanup-1678
May 14, 2026
Merged

test: cover RegisteredBufferGroup drop in constrained environments#4027
oferchen merged 3 commits into
masterfrom
test/registered-buffer-group-cleanup-1678

Conversation

@oferchen
Copy link
Copy Markdown
Owner

Summary

Adds four focused unit tests in crates/fast_io/src/io_uring/registered_buffers.rs that exercise the documented invariants of RegisteredBufferGroup::Drop under conditions where a regression would either leak userspace memory or silently mutate kernel state.

The audit at docs/audits/io-uring-fixed-buffer-invariants-audit.md (landed in PR #4022, task #2118) records that Drop deliberately does NOT issue IORING_UNREGISTER_BUFFERS; the kernel reclaims the pinning when the ring fd closes. The contract is documented and panic-safe, but no test exercised the constrained-environment cases where:

  • The group is dropped mid-use while the ring fd remains open.
  • The group is dropped with non-default tracking state (in-flight acquires, recorded misses).
  • A RegisteredBufferStats snapshot outlives its source group.
  • RegisteredBufferGroup::new fails after a previous registration is already live, exercising the failure-recovery branch.

This PR adds tests for each of those cases.

Tests added

  • drop_does_not_release_kernel_registration - drops the group while keeping the ring alive, then attempts a fresh registration on the same ring. The second registration is rejected (typically EBUSY) precisely because the prior kernel-side registration is still live - structural proof that Drop did not silently call IORING_UNREGISTER_BUFFERS. After an explicit unregister, fresh registration succeeds, proving the ring itself is not poisoned.
  • drop_with_in_use_tracking_state_is_clean - drives the group through a realistic acquire / miss / release trajectory before dropping, ensuring non-zero acquire / miss counters and a dirty bitset history are tolerated by Drop.
  • stats_snapshot_survives_group_drop - verifies RegisteredBufferStats snapshots are independent of source group lifetime. Documents that telemetry consumers (e.g., the adaptive sizer) do not need lifetime coupling.
  • drop_on_construction_failure_does_not_double_register - exercises the failure-recovery branch where the kernel rejects a duplicate registration. The partially-allocated buffers must be freed before the error propagates, and the ring must remain usable for fresh registration after explicit cleanup.

All tests follow the existing skip-when-io_uring-unavailable pattern via RawIoUring::new(4) early return, so they degrade gracefully on non-Linux CI runners and on constrained container environments without a 5.6+ kernel. Tests targeting the double-register branch additionally skip when running on newer kernels (5.13+) that accept replace-style update semantics, since those cannot be probed by the structural method used here.

Scope

  • Tests only; no public API changes.
  • Uses existing pub and pub(crate) interfaces (new, try_new, checkout, available, stats, unregister, plus direct Submitter::unregister_buffers on the underlying ring).
  • Lives in the same #[cfg(test)] mod tests block as the existing drop / panic / unregister coverage; the surrounding file is already cfg-gated to target_os = "linux" + feature = "io_uring" at the lib.rs level.

References PR #4022 (task #2118) - this PR validates the invariants documented there.

Test plan

  • CI fmt + clippy passes.
  • Linux nextest (stable) runs the four new tests against a 5.6+ kernel and they all pass.
  • macOS and Windows nextest runs skip cleanly (the file is not compiled on those targets).

Adds four focused unit tests that exercise the documented invariants of
`RegisteredBufferGroup::Drop` under conditions where a regression would
either leak userspace memory or silently mutate kernel state:

- `drop_does_not_release_kernel_registration` proves structurally that
  Drop does NOT issue `IORING_UNREGISTER_BUFFERS` by attempting a fresh
  registration on the same live ring after the group is dropped.
- `drop_with_in_use_tracking_state_is_clean` drives the group through a
  realistic acquire / miss / release trajectory before Drop, ensuring
  non-zero counters and a dirty bitset history are tolerated.
- `stats_snapshot_survives_group_drop` verifies that a
  `RegisteredBufferStats` snapshot is independent of the source group's
  lifetime.
- `drop_on_construction_failure_does_not_double_register` exercises the
  failure-recovery branch where the kernel rejects a duplicate
  registration; the partially-allocated buffers must be freed before the
  error propagates, and the ring must remain usable afterward.

All tests follow the existing skip-when-io_uring-unavailable pattern via
`RawIoUring::new(4)` early return, so they degrade gracefully on
non-Linux CI runners and constrained container environments without a
5.6+ kernel.
@github-actions github-actions Bot added the test label May 14, 2026
oferchen added 2 commits May 14, 2026 07:47
Replace direct field access on ParallelThresholds with a ParallelOp
enum and a for_op() / with_op() accessor pair. Dispatch sites now
read thresholds.for_op(ParallelOp::X) rather than coupling to the
struct's field layout, so adding a new operation only requires
extending the enum and struct without editing every call site.

Defaults are unchanged (stat=64, signature=32, metadata=64,
deletion=64). Existing with_stat/with_signature/with_metadata/
with_deletion builders are kept and now delegate to with_op so
callers and tests need no migration.

Updates the five production call sites in generator batch_stat,
receiver candidate selection, receiver signature dispatch, receiver
metadata application, and receiver deletion scanning.
@oferchen oferchen merged commit 31ac731 into master May 14, 2026
39 checks passed
@oferchen oferchen deleted the test/registered-buffer-group-cleanup-1678 branch May 14, 2026 14:58
oferchen added a commit that referenced this pull request May 18, 2026
…4027)

* test: cover RegisteredBufferGroup drop in constrained environments

Adds four focused unit tests that exercise the documented invariants of
`RegisteredBufferGroup::Drop` under conditions where a regression would
either leak userspace memory or silently mutate kernel state:

- `drop_does_not_release_kernel_registration` proves structurally that
  Drop does NOT issue `IORING_UNREGISTER_BUFFERS` by attempting a fresh
  registration on the same live ring after the group is dropped.
- `drop_with_in_use_tracking_state_is_clean` drives the group through a
  realistic acquire / miss / release trajectory before Drop, ensuring
  non-zero counters and a dirty bitset history are tolerated.
- `stats_snapshot_survives_group_drop` verifies that a
  `RegisteredBufferStats` snapshot is independent of the source group's
  lifetime.
- `drop_on_construction_failure_does_not_double_register` exercises the
  failure-recovery branch where the kernel rejects a duplicate
  registration; the partially-allocated buffers must be freed before the
  error propagates, and the ring must remain usable afterward.

All tests follow the existing skip-when-io_uring-unavailable pattern via
`RawIoUring::new(4)` early return, so they degrade gracefully on
non-Linux CI runners and constrained container environments without a
5.6+ kernel.

* feat: add ParallelOp lookup for per-operation rayon thresholds

Replace direct field access on ParallelThresholds with a ParallelOp
enum and a for_op() / with_op() accessor pair. Dispatch sites now
read thresholds.for_op(ParallelOp::X) rather than coupling to the
struct's field layout, so adding a new operation only requires
extending the enum and struct without editing every call site.

Defaults are unchanged (stat=64, signature=32, metadata=64,
deletion=64). Existing with_stat/with_signature/with_metadata/
with_deletion builders are kept and now delegate to with_op so
callers and tests need no migration.

Updates the five production call sites in generator batch_stat,
receiver candidate selection, receiver signature dispatch, receiver
metadata application, and receiver deletion scanning.

* fix: resolve borrow-checker and Debug-trait errors in cleanup tests
oferchen added a commit that referenced this pull request May 18, 2026
…4027)

* test: cover RegisteredBufferGroup drop in constrained environments

Adds four focused unit tests that exercise the documented invariants of
`RegisteredBufferGroup::Drop` under conditions where a regression would
either leak userspace memory or silently mutate kernel state:

- `drop_does_not_release_kernel_registration` proves structurally that
  Drop does NOT issue `IORING_UNREGISTER_BUFFERS` by attempting a fresh
  registration on the same live ring after the group is dropped.
- `drop_with_in_use_tracking_state_is_clean` drives the group through a
  realistic acquire / miss / release trajectory before Drop, ensuring
  non-zero counters and a dirty bitset history are tolerated.
- `stats_snapshot_survives_group_drop` verifies that a
  `RegisteredBufferStats` snapshot is independent of the source group's
  lifetime.
- `drop_on_construction_failure_does_not_double_register` exercises the
  failure-recovery branch where the kernel rejects a duplicate
  registration; the partially-allocated buffers must be freed before the
  error propagates, and the ring must remain usable afterward.

All tests follow the existing skip-when-io_uring-unavailable pattern via
`RawIoUring::new(4)` early return, so they degrade gracefully on
non-Linux CI runners and constrained container environments without a
5.6+ kernel.

* feat: add ParallelOp lookup for per-operation rayon thresholds

Replace direct field access on ParallelThresholds with a ParallelOp
enum and a for_op() / with_op() accessor pair. Dispatch sites now
read thresholds.for_op(ParallelOp::X) rather than coupling to the
struct's field layout, so adding a new operation only requires
extending the enum and struct without editing every call site.

Defaults are unchanged (stat=64, signature=32, metadata=64,
deletion=64). Existing with_stat/with_signature/with_metadata/
with_deletion builders are kept and now delegate to with_op so
callers and tests need no migration.

Updates the five production call sites in generator batch_stat,
receiver candidate selection, receiver signature dispatch, receiver
metadata application, and receiver deletion scanning.

* fix: resolve borrow-checker and Debug-trait errors in cleanup tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant