Skip to content

Commit 2777153

Browse files
authored
memorypool: optimize rc a little more (#48306)
1 parent 6327179 commit 2777153

File tree

2 files changed

+34
-15
lines changed

2 files changed

+34
-15
lines changed

src/core/memorypool.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ impl<T> Memory<T> {
5757
self.entries.borrow().len()
5858
}
5959

60-
// Returns a key and a pointer to the inserted entry.
60+
// Returns a pointer to the inserted entry.
6161
//
6262
// SAFETY: The returned pointer is guaranteed to be valid until the entry
6363
// is removed or the Memory is dropped.
64-
fn insert(&self, e: T) -> Result<(usize, *const T), InsertError<T>> {
64+
fn insert(&self, e: T) -> Result<*const T, InsertError<T>> {
6565
let mut entries = self.entries.borrow_mut();
6666

6767
// Out of capacity. By preventing inserts beyond the capacity, we
@@ -79,13 +79,19 @@ impl<T> Memory<T> {
7979
// therefore we can return a pointer to the element and guarantee
8080
// its validity until the element is removed.
8181

82-
Ok((key, entry as *const T))
82+
Ok(entry as *const T)
8383
}
8484

85+
// SAFETY: `ptr` must be a valid pointer returned by `insert` that has
86+
// not yet been removed.
8587
#[allow(clippy::let_unit_value)]
86-
fn remove(&self, key: usize) {
88+
unsafe fn remove(&self, ptr: *const T) {
89+
let mut entries = self.entries.borrow_mut();
90+
91+
let key = entries.key_of(&*ptr);
92+
8793
// Ensure remove() method doesn't return a value
88-
let _: () = self.entries.borrow_mut().remove(key);
94+
let _: () = entries.remove(key);
8995
}
9096

9197
// Returns a pointer to an entry if it exists.
@@ -111,10 +117,10 @@ fn unlikely_abort() {
111117
abort();
112118
}
113119

120+
#[repr(C, align(2))]
114121
pub struct RcInner<T> {
115122
refs: Cell<usize>,
116123
memory: Cell<Option<std::rc::Rc<RcMemory<T>>>>,
117-
key: Cell<usize>,
118124
value: T,
119125
}
120126

@@ -163,7 +169,6 @@ impl<T> Rc<T> {
163169
let ptr = Box::leak(Box::new(RcInner {
164170
refs: Cell::new(1),
165171
memory: Cell::new(None),
166-
key: Cell::new(0),
167172
value: v,
168173
}));
169174

@@ -174,11 +179,10 @@ impl<T> Rc<T> {
174179
}
175180

176181
pub fn try_new_in(v: T, memory: &std::rc::Rc<RcMemory<T>>) -> Result<Self, AllocError> {
177-
let (key, ptr) = memory
182+
let ptr = memory
178183
.insert(RcInner {
179184
refs: Cell::new(1),
180185
memory: Cell::new(Some(std::rc::Rc::clone(memory))),
181-
key: Cell::new(0),
182186
value: v,
183187
})
184188
.map_err(|_| AllocError)?;
@@ -187,9 +191,6 @@ impl<T> Rc<T> {
187191
// despite casting it to *mut in order to construct NonNull
188192
let ptr = unsafe { NonNull::new_unchecked(ptr as *mut RcInner<T>) };
189193

190-
// SAFETY: ptr is convertible to a reference
191-
unsafe { ptr.as_ref().key.set(key) };
192-
193194
Ok(Self {
194195
ptr,
195196
phantom: PhantomData,
@@ -215,9 +216,8 @@ impl<T> Rc<T> {
215216
// Rc is moved out of the entry above, and it is dropped only
216217
// after the entry is removed.
217218

218-
let key = self.inner().key.get();
219-
220-
memory.remove(key);
219+
// SAFETY: While this Rc is alive, ptr is always valid
220+
unsafe { memory.remove(self.ptr.as_ref()) };
221221
} else {
222222
// If there is no reference to slab memory, then the memory is
223223
// managed by the system allocator. To free it, we simply convert

src/core/minislab.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
// * remove() returns nothing.
2323
// * All methods/types we don't use are removed.
2424

25+
use std::mem;
26+
2527
#[derive(Debug)]
2628
pub struct RemoveError;
2729

@@ -121,4 +123,21 @@ impl<T> MiniSlab<T> {
121123
pub fn remove(&mut self, key: usize) {
122124
self.try_remove(key).expect("invalid key");
123125
}
126+
127+
#[track_caller]
128+
pub fn key_of(&self, present_element: &T) -> usize {
129+
let element_ptr = present_element as *const T as usize;
130+
let base_ptr = self.entries.as_ptr() as usize;
131+
// Use wrapping subtraction in case the reference is bad
132+
let byte_offset = element_ptr.wrapping_sub(base_ptr);
133+
// The division rounds away any offset of T inside Entry
134+
// The size of Entry<T> is never zero even if T is due to Vacant(usize)
135+
let key = byte_offset / mem::size_of::<Entry<T>>();
136+
// Prevent returning unspecified (but out of bounds) values
137+
if key >= self.entries.len() {
138+
panic!("The reference points to a value outside this slab");
139+
}
140+
// The reference cannot point to a vacant entry, because then it would not be valid
141+
key
142+
}
124143
}

0 commit comments

Comments
 (0)