Repository navigation
feat: support type erasure of pooled handles to unsized types - #585
Conversation
Martin Taillefer (geeknoid)
commented
Jul 17, 2026
- Onboard bytesbuf and layered to plurality
77f2167 to
1756373
Compare
✅ Version increments look sufficient
|
1756373 to
a51db0e
Compare
There was a problem hiding this comment.
Pull request overview
This PR extends the plurality pooled-handle model to support type erasure to unsized types (dyn Trait and slices) for owning/shared handles, and migrates consumers (notably bytesbuf and layered) away from infinity_pool to plurality.
Changes:
- Add DST/type-erasure support for
plurality::Box,plurality::Arc, andplurality::Rcviaunsize::Coercion, including erased reclaim logic in the pool. - Expand correctness + fuzz/property + allocation-tracking tests, plus new benchmark coverage for fat-pointer owning handles.
- Migrate downstream crates to use
pluralityinstead ofinfinity_pool, and update supporting docs/scripts/CI helpers.
Reviewed changes
Copilot reviewed 40 out of 42 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| justfiles/extended.just | Ensure Loom runs with the loom feature enabled when executing loom_*.rs tests. |
| crates/plurality/tests/unsize.rs | New integration tests covering Box/Arc/Rc unsizing to trait objects/slices and drop/reclaim behavior. |
| crates/plurality/tests/smart_ptr.rs | Extend smart-pointer behavior assertions (ordering/hash set semantics). |
| crates/plurality/tests/pool.rs | Add regression test coverage for panicking destructors returning slots. |
| crates/plurality/tests/loom_pool.rs | Update Loom test invocation docs to match new test target/feature usage. |
| crates/plurality/tests/box.rs | Add into_raw/from_raw round-trip tests for pooled Box. |
| crates/plurality/tests/bolero_pool.rs | New Bolero property/fuzz test for core pool invariants. |
| crates/plurality/tests/alloc.rs | Add regression test for panicking destructors returning local slots. |
| crates/plurality/tests/alloc_tracking.rs | Add allocation-tracking assertions for the new dyn-box benchmark bodies. |
| crates/plurality/src/sync.rs | Make Arc<T> support T: ?Sized and add Arc::unsize; use erased drop/reclaim. |
| crates/plurality/src/slot.rs | Add MAX_POOL_SLOTS cap helper and clarify refcount/free-link semantics. |
| crates/plurality/src/rc.rs | Make Rc<T> support T: ?Sized and add Rc::unsize; refcount via erased offsets. |
| crates/plurality/src/pool.rs | Introduce PoolCore for erased teardown; implement erased reclaim from value ptr + size/align. |
| crates/plurality/src/lib.rs | Document type erasure and re-export unsize::Coercion from the crate API. |
| crates/plurality/src/common.rs | Add shared impl macros for T: ?Sized handles and update handle-layout docs. |
| crates/plurality/src/chunk.rs | Remove type parameters from ChunkHeader and related layout/addressing helpers. |
| crates/plurality/src/builder.rs | Build PoolCore with a concrete teardown callback; validate caps via MAX_POOL_SLOTS. |
| crates/plurality/src/boxed.rs | Make Box<T> support T: ?Sized, add Box::unsize + into_raw/from_raw, and erased drop path. |
| crates/plurality/src/alloced.rs | Update Alloc drop path to new local-free API shape. |
| crates/plurality/scripts/perf_report.rs | Extend PERF report generation to include dyn-box benchmarks and ratio formatting. |
| crates/plurality/scripts/correctness.rs | Update correctness harness to new Loom/Bolero test target names and flags. |
| crates/plurality/README.md | Regenerate crate README with new type-erasure API docs and links. |
| crates/plurality/docs/PERF.md | Update published performance report with new operations + dyn-box comparison section. |
| crates/plurality/docs/DESIGN.md | Update design doc to reflect erased handles, PoolCore, and new recovery strategy. |
| crates/plurality/Cargo.toml | Bump version; add unsize dep; add dyn-box benches; rename loom test target; move dev-deps. |
| crates/plurality/benches/shared/ops.rs | Add Marker trait and unsize operations used by alloc benchmarks. |
| crates/plurality/benches/shared/dyn_box_ops.rs | New shared bodies for fat-pointer owning-handle cross-crate comparison. |
| crates/plurality/benches/gungraun_dyn_box/main.rs | New Callgrind bench harness for dyn-box comparison. |
| crates/plurality/benches/gungraun_dyn_box/linux.rs | Linux implementation of dyn-box Callgrind benches. |
| crates/plurality/benches/gungraun_alloc/linux.rs | Add box_unsize/arc_unsize to alloc Callgrind suite. |
| crates/plurality/benches/criterion_dyn_box.rs | New wall-clock criterion benches for dyn-box comparison. |
| crates/plurality/benches/criterion_alloc.rs | Add box_unsize/arc_unsize to criterion allocation suite. |
| crates/layered/src/dynamic.rs | Switch dynamic-service implementation from infinity_pool to plurality with boxed DST handles. |
| crates/layered/Cargo.toml | Replace infinity_pool feature/deps with optional plurality. |
| crates/fetch/Cargo.toml | Formatting-only update to native-tls feature list. |
| crates/data_privacy_macros_impl/Cargo.toml | Formatting-only update to allowed external types list. |
| crates/bytesbuf/src/mem/global.rs | Replace raw infinity_pool block pooling with plurality, using raw-handle escape for self-handle. |
| crates/bytesbuf/Cargo.toml | Replace infinity_pool dependency with plurality. |
| crates/bytesbuf_io/Cargo.toml | Formatting-only update to categories. |
| Cargo.toml | Bump plurality workspace dependency version and add unsize workspace dependency. |
| Cargo.lock | Lockfile updates for plurality 0.1.1 and new unsize dependency usage. |
| .spelling | Add unsize/unsizing to spelling allowlist. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #585 +/- ##
========================================
Coverage 100.0% 100.0%
========================================
Files 359 360 +1
Lines 27800 28002 +202
========================================
+ Hits 27800 28002 +202
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
a51db0e to
9183fbd
Compare
9183fbd to
b0a2cc8
Compare
b0a2cc8 to
b461778
Compare
b461778 to
1cef3fe
Compare
|
See for example this here The Pin unsoundness and |
Ralf Biedert (ralfbiedert)
left a comment
There was a problem hiding this comment.
Resolve possible soundness questions
6180fb8 to
b297816
Compare
|
Ralf Biedert (@ralfbiedert) Good call — rather than feature-gate it, I removed the The vendored version is also stronger than the original with respect to the soundness history you linked:
Happy to walk through |
|
88536d3 to
d80eb12
Compare
Could you-as-a-human please also look at and double check this? I can produce UB: use core::marker::PhantomPinned;
use core::pin::Pin;
use core::sync::atomic::{AtomicPtr, Ordering};
use core::{mem, ptr};
use plurality::{Arc, Pool, coerce};
trait AddressSensitive {
fn initialize(self: Pin<&Self>);
}
struct Value {
self_ptr: AtomicPtr<Value>,
byte: u8,
_pin: PhantomPinned,
}
impl Value {
fn new() -> Self {
Self {
self_ptr: AtomicPtr::new(ptr::null_mut()),
byte: 42,
_pin: PhantomPinned,
}
}
/// This is "safe" because a value initialized through `Pin<&Self>` normally
/// remains at that address until it is dropped.
fn read_pinned_byte(&self) -> u8 {
let self_ptr = self.self_ptr.load(Ordering::Relaxed);
assert!(!self_ptr.is_null());
// SAFETY: `initialize` records this value's pinned address.
unsafe { ptr::read_volatile(ptr::addr_of!((*self_ptr).byte)) }
}
}
impl AddressSensitive for Value {
fn initialize(self: Pin<&Self>) {
self.self_ptr.store(ptr::from_ref(self.get_ref()).cast_mut(), Ordering::Relaxed);
}
}
fn main() {
let pool = Pool::<Value>::new();
let value = pool.alloc_arc(Value::new());
let mut unpinned_alias = value.clone();
let erased: Arc<dyn AddressSensitive> = Arc::unsize(value, coerce!(dyn AddressSensitive));
let pinned = Arc::into_pin(erased);
pinned.as_ref().initialize();
drop(pinned);
let moved = mem::replace(Arc::get_mut(&mut unpinned_alias).unwrap(), Value::new());
drop(unpinned_alias);
drop(pool);
let _ = moved.read_pinned_byte();
}This fails on miri with However, this is not directly related to The Pin contract says
I wasn't able to make this fully safe, but I think the I think it would be prudent to remove at least the pinning API surface for now unless it has seen more human expert eyes. |
- Onboard bytesbuf and layered to plurality
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 904658d0-c00a-4236-b9be-4feba06fe838
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 904658d0-c00a-4236-b9be-4feba06fe838
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 904658d0-c00a-4236-b9be-4feba06fe838
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 904658d0-c00a-4236-b9be-4feba06fe838