Conversation
There was a problem hiding this comment.
Pull request overview
This PR hardens multitude’s arena allocator safety invariants by removing panic surfaces in places where unwinding would corrupt drop-list/chunk reclamation, tightening drop-count reconciliation, and clarifying the semantics of max_normal_alloc across docs and comments.
Changes:
- Replace
u16::try_from(...).expect(...)panic surfaces in allocator hot/slow paths withdebug_assert!+assert_unchecked/unwrap_uncheckedwhere proofs guarantee the bounds. - Make no-
stddrop replay robust against panickingT::Dropby introducing an abort-on-unwind guard; fix drop-count persistence to avoid walking past the last valid drop entry. - Improve operational correctness and documentation: close a byte-budget TOCTOU window, use
saturating_addin oversized DST size computations, and update docs/comments aroundmax_normal_allocand panic behavior.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/multitude/src/vec/mutate.rs | Updates mutation-testing rationale wording for inc_len. |
| crates/multitude/src/internal/shared_chunk.rs | Adds no-std abort-on-unwind guard around drop shims and clarifies behavior in comments. |
| crates/multitude/src/internal/local_chunk.rs | Mirrors shared chunk’s no-std abort-on-unwind handling for drop replay. |
| crates/multitude/src/internal/drop_list.rs | Introduces AbortOnUnwind guard (no-std) to force abort on unwinding T::Drop during replay_drops. |
| crates/multitude/src/internal/constants.rs | Clarifies meaning of MAX_NORMAL_ALLOC as a chunk-acquisition threshold. |
| crates/multitude/src/internal/chunk_provider.rs | Refactors reserve_budget to speculative fetch_add with rollback; expands max_normal_alloc semantics docs. |
| crates/multitude/src/bytesbuf.rs | Documents reserve panics, adds refcount overflow guard to ArenaBlockState::clone. |
| crates/multitude/src/arena/refill.rs | Writes mirror_dc as authoritative drop-count value to avoid unsafe over-replay. |
| crates/multitude/src/arena/primitives.rs | Clarifies why oversized routing is slow-path only (chunk acquisition vs bump probe). |
| crates/multitude/src/arena/mod.rs | Applies authoritative mirror_dc drop-count write during reset. |
| crates/multitude/src/arena/inner_value.rs | Removes expect panic surfaces for u16 offsets, replacing with asserted unchecked conversions. |
| crates/multitude/src/arena/inner_slice.rs | Removes post-forget(init_guard) panic surfaces for u16 conversions; expands max_normal_alloc routing docs. |
| crates/multitude/src/arena/alloc_unsized.rs | Uses saturating_add for oversized DST needed computations; removes panic surfaces for u16 conversions. |
| crates/multitude/src/arena_builder.rs | Updates builder docs to correctly describe max_normal_alloc behavior (acquisition-time). |
| crates/multitude/docs/DESIGN.md | Updates design documentation for correct max_normal_alloc semantics. |
Comments suppressed due to low confidence (1)
crates/multitude/src/internal/chunk_provider.rs:170
release_budget's comment still refers to a "reserve_budget Acquire load", butreserve_budgetnow usesfetch_add(..., AcqRel)and no longer does an Acquire load. Please update this comment to match the new synchronization story so it doesn't mislead future changes.
}
/// Release `bytes` from the lifetime byte budget tracker.
fn release_budget(&self, bytes: usize) {
// Release so a subsequent owner-thread `reserve_budget`
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Also updated the stale comment in |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #443 +/- ##
=======================================
Coverage 100.0% 100.0%
=======================================
Files 299 299
Lines 23562 23585 +23
=======================================
+ Hits 23562 23585 +23 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
* `arena/inner_value.rs` (`impl_alloc_inner_arc_with`,
`impl_alloc_inner_value`, `impl_alloc_inner_with`): replace the
`u16::try_from(...).expect("...MAX_CHUNK_BYTES <= u16::MAX...")`
panic surfaces with `debug_assert! + assert_unchecked +
unwrap_unchecked`. The previous bound was wrong (`MAX_CHUNK_BYTES =
65536 > u16::MAX`); the real bound is
`local_max_bump_extent::<A>() = CHUNK_ALIGN − header_size`.
* `arena/inner_slice.rs`: apply the same pattern to the two
oversized-slice sites where the `u16::try_from` followed
`core::mem::forget(init_guard)` — a panic there would have
freed the chunk backing with live un-dropped elements.
* `arena/refill.rs`, `arena/mod.rs::reset`: replace
`mirror_dc.max(chunk_dc)` with `mirror_dc`. The mirror is the
source of truth; the `.max()` silently masked the dangerous
divergence direction (`chunk_dc > mirror_dc` would make
`replay_drops` walk past the last written entry).
* `arena/alloc_unsized.rs`: bare `+` → `saturating_add` in the
oversized DST helpers `needed` computation.
* `internal/chunk_provider.rs::reserve_budget`: replace
`load(Acquire) + check + fetch_add(Relaxed)` with speculative
`fetch_add(AcqRel) + check + fetch_sub(Release) on overflow`,
removing the TOCTOU window with cross-thread `release_budget`
and rejecting on `usize` wraparound.
* `internal/drop_list.rs`, `local_chunk.rs`, `shared_chunk.rs`:
add an `AbortOnUnwind` guard so the `no_std` `replay_drops`
shim call force-aborts on a panicking `T::Drop` instead of
unwinding past `route_release` and leaking the chunk.
* `bytesbuf.rs::ArenaBlockState::clone`: add the
`check_shared_refcount`-style overflow guard, making the
abort-on-overflow convention uniform across every refcounted
type in the crate.
* `bytesbuf.rs::reserve`: add a `# Panics` doc section for the
`u32::MAX` (`4 GiB`) per-reservation cap.
* `arena_builder.rs`, `internal/chunk_provider.rs`,
`internal/constants.rs`, `arena/primitives.rs`,
`arena/inner_slice.rs`, `arena/alloc_unsized.rs`,
`docs/DESIGN.md`: rewrite the `max_normal_alloc` doc/comment
block — the threshold gates *chunk acquisition* in the slow
path, not the bump probe. Requests above the threshold that fit
in the current chunks tail are satisfied directly.
* `vec/mutate.rs::inc_len`: minor wording fix in the
`#[mutants::skip]` rationale.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
arena/inner_value.rs(impl_alloc_inner_arc_with,impl_alloc_inner_value,impl_alloc_inner_with): replace theu16::try_from(...).expect("...MAX_CHUNK_BYTES <= u16::MAX...")panic surfaces withdebug_assert! + assert_unchecked + unwrap_unchecked. The previous bound was wrong (MAX_CHUNK_BYTES = 65536 > u16::MAX); the real bound islocal_max_bump_extent::<A>() = CHUNK_ALIGN − header_size.arena/inner_slice.rs: apply the same pattern to the two oversized-slice sites where theu16::try_fromfollowedcore::mem::forget(init_guard)— a panic there would have freed the chunk backing with live un-dropped elements.arena/refill.rs,arena/mod.rs::reset: replacemirror_dc.max(chunk_dc)withmirror_dc. The mirror is the source of truth; the.max()silently masked the dangerous divergence direction (chunk_dc > mirror_dcwould makereplay_dropswalk past the last written entry).arena/alloc_unsized.rs: bare+→saturating_addin the oversized DST helpersneededcomputation.internal/chunk_provider.rs::reserve_budget: replaceload(Acquire) + check + fetch_add(Relaxed)with speculativefetch_add(AcqRel) + check + fetch_sub(Release) on overflow, removing the TOCTOU window with cross-threadrelease_budgetand rejecting onusizewraparound.internal/drop_list.rs,local_chunk.rs,shared_chunk.rs: add anAbortOnUnwindguard so theno_stdreplay_dropsshim call force-aborts on a panickingT::Dropinstead of unwinding pastroute_releaseand leaking the chunk.bytesbuf.rs::ArenaBlockState::clone: add thecheck_shared_refcount-style overflow guard, making the abort-on-overflow convention uniform across every refcounted type in the crate.bytesbuf.rs::reserve: add a# Panicsdoc section for theu32::MAX(4 GiB) per-reservation cap.arena_builder.rs,internal/chunk_provider.rs,internal/constants.rs,arena/primitives.rs,arena/inner_slice.rs,arena/alloc_unsized.rs,docs/DESIGN.md: rewrite themax_normal_allocdoc/comment block — the threshold gates chunk acquisition in the slow path, not the bump probe. Requests above the threshold that fit in the current chunks tail are satisfied directly.vec/mutate.rs::inc_len: minor wording fix in the#[mutants::skip]rationale.