Skip to content

Commit 6935467

Browse files
committed
fixes
1 parent 702ac0a commit 6935467

File tree

1 file changed

+57
-75
lines changed

1 file changed

+57
-75
lines changed

src/core/arena.rs

Lines changed: 57 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -16,44 +16,26 @@
1616
*/
1717

1818
use slab::Slab;
19-
use std::cell::{Cell, RefCell, RefMut};
19+
use std::cell::{Cell, RefCell};
20+
use std::fmt;
2021
use std::marker::PhantomData;
2122
use std::mem;
2223
use std::ops::{Deref, DerefMut};
2324
use std::process::abort;
2425
use std::ptr::NonNull;
2526
use std::sync::{Mutex, MutexGuard};
2627

27-
pub struct EntryGuard<'a, T> {
28-
entries: RefMut<'a, Slab<T>>,
29-
entry: &'a mut T,
30-
key: usize,
31-
}
32-
33-
impl<T> EntryGuard<'_, T> {
34-
fn remove(mut self) {
35-
self.entries.remove(self.key);
36-
}
37-
}
38-
39-
impl<T> Deref for EntryGuard<'_, T> {
40-
type Target = T;
28+
pub struct InsertError<T>(pub T);
4129

42-
fn deref(&self) -> &Self::Target {
43-
self.entry
44-
}
45-
}
46-
47-
impl<T> DerefMut for EntryGuard<'_, T> {
48-
fn deref_mut(&mut self) -> &mut Self::Target {
49-
self.entry
30+
impl<T> fmt::Debug for InsertError<T> {
31+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32+
f.debug_struct("InsertError").finish_non_exhaustive()
5033
}
5134
}
5235

5336
// This is essentially a sharable slab for use within a single thread.
54-
// operations are protected by a RefCell. When an element is retrieved for
55-
// reading or modification, it is wrapped in a EntryGuard which keeps the
56-
// entire slab borrowed until the caller is done working with the element
37+
// Operations are protected by a RefCell, however lookup operations return
38+
// pointers that can be used without RefCell protection.
5739
pub struct Memory<T> {
5840
entries: RefCell<Slab<T>>,
5941
}
@@ -70,58 +52,44 @@ impl<T> Memory<T> {
7052

7153
#[cfg(test)]
7254
pub fn len(&self) -> usize {
73-
let entries = self.entries.borrow();
74-
75-
entries.len()
55+
self.entries.borrow().len()
7656
}
7757

78-
fn insert(&self, e: T) -> Result<usize, ()> {
58+
fn insert(&self, e: T) -> Result<usize, InsertError<T>> {
7959
let mut entries = self.entries.borrow_mut();
8060

8161
// Out of capacity. By preventing inserts beyond the capacity, we
82-
// ensure the underlying memory won't get moved due to a realloc
62+
// ensure the underlying memory won't get moved due to a realloc.
8363
if entries.len() == entries.capacity() {
84-
return Err(());
64+
return Err(InsertError(e));
8565
}
8666

8767
Ok(entries.insert(e))
8868
}
8969

90-
fn get<'a>(&'a self, key: usize) -> Option<EntryGuard<'a, T>> {
91-
let mut entries = self.entries.borrow_mut();
70+
fn remove(&self, key: usize) {
71+
self.entries.borrow_mut().remove(key);
72+
}
9273

93-
let entry = entries.get_mut(key)?;
74+
// Returns a pointer to an entry if it exists.
75+
//
76+
// SAFETY: The returned pointer is guaranteed to be valid until the entry
77+
// is removed or the Memory is dropped.
78+
fn addr_of(&self, key: usize) -> Option<*const T> {
79+
let entries = self.entries.borrow();
80+
81+
let entry = entries.get(key)?;
9482

9583
// Slab element addresses are guaranteed to be stable once created,
96-
// and the only place we remove the element is in EntryGuard's
97-
// remove method which consumes itself, therefore it is safe to
98-
// assume the element will live at least as long as the EntryGuard
99-
// and we can extend the lifetime of the reference beyond the
100-
// RefMut
101-
let entry = unsafe { mem::transmute::<&mut T, &'a mut T>(entry) };
84+
// therefore we can return a pointer to the element and guarantee
85+
// its validity until the element is removed.
10286

103-
Some(EntryGuard {
104-
entries,
105-
entry,
106-
key,
107-
})
87+
Some(entry as *const T)
10888
}
10989

11090
fn key_of(&self, present_element: &T) -> usize {
11191
self.entries.borrow().key_of(present_element)
11292
}
113-
114-
// For tests, as a way to confirm the memory isn't moving. Be careful
115-
// with this. The very first element inserted will be at index 0, but
116-
// if the slab has been used and cleared, then the next element
117-
// inserted may not be at index 0 and calling this method afterward
118-
// will panic
119-
#[cfg(test)]
120-
fn entry0_ptr(&self) -> *const T {
121-
let entries = self.entries.borrow();
122-
123-
entries.get(0).unwrap() as *const T
124-
}
12593
}
12694

12795
pub struct SyncEntryGuard<'a, T> {
@@ -370,18 +338,21 @@ pub struct Rc<T> {
370338

371339
impl<T> Rc<T> {
372340
#[allow(clippy::result_unit_err)]
373-
pub fn new(v: T, memory: &std::rc::Rc<RcMemory<T>>) -> Result<Self, ()> {
374-
let key = memory.insert(RcInner {
375-
refs: Cell::new(1),
376-
memory: std::rc::Rc::clone(memory),
377-
value: v,
378-
})?;
341+
pub fn new(v: T, memory: &std::rc::Rc<RcMemory<T>>) -> Result<Self, InsertError<T>> {
342+
let key = memory
343+
.insert(RcInner {
344+
refs: Cell::new(1),
345+
memory: std::rc::Rc::clone(memory),
346+
value: v,
347+
})
348+
.map_err(|e| InsertError(e.0.value))?;
379349

380350
// Guaranteed to succeed since we inserted the element above
381-
let mut inner = memory.get(key).unwrap();
351+
let ptr: *const RcInner<T> = memory.addr_of(key).unwrap();
382352

383-
// Get a pointer to the inner data
384-
let ptr = (&mut *inner).into();
353+
// SAFETY: ptr is not null and we promise to only use it immutably
354+
// despite casting it to *mut in order to construct NonNull
355+
let ptr = unsafe { NonNull::new_unchecked(ptr as *mut RcInner<T>) };
385356

386357
Ok(Self {
387358
ptr,
@@ -417,10 +388,21 @@ impl<T> Rc<T> {
417388

418389
#[inline(never)]
419390
fn drop_slow(&mut self) {
420-
let inner = self.inner();
421-
let key = inner.memory.key_of(inner);
422-
let entry = inner.memory.get(key).unwrap();
423-
entry.remove();
391+
// Entries contain a std::rc::Rc to the Memory they are contained in,
392+
// and we need to be careful the Memory is not dropped while an entry
393+
// is being removed. To ensure this, we clone the Rc to the Memory
394+
// before removing the entry.
395+
396+
let memory = {
397+
let inner = self.inner();
398+
let memory = std::rc::Rc::clone(&inner.memory);
399+
memory.remove(memory.key_of(inner));
400+
401+
memory
402+
};
403+
404+
// The entry has been removed by this point
405+
drop(memory);
424406
}
425407
}
426408

@@ -626,15 +608,15 @@ mod tests {
626608

627609
let e0a = Rc::new(123 as i32, &memory).unwrap();
628610
assert_eq!(memory.len(), 1);
629-
let p = memory.entry0_ptr();
611+
let p = memory.addr_of(0).unwrap();
630612

631613
let e0b = Rc::clone(&e0a);
632614
assert_eq!(memory.len(), 1);
633-
assert_eq!(memory.entry0_ptr(), p);
615+
assert_eq!(memory.addr_of(0).unwrap(), p);
634616

635617
let e1a = Rc::new(456 as i32, &memory).unwrap();
636618
assert_eq!(memory.len(), 2);
637-
assert_eq!(memory.entry0_ptr(), p);
619+
assert_eq!(memory.addr_of(0).unwrap(), p);
638620

639621
// No room
640622
assert!(Rc::new(789 as i32, &memory).is_err());
@@ -645,7 +627,7 @@ mod tests {
645627

646628
mem::drop(e0b);
647629
assert_eq!(memory.len(), 2);
648-
assert_eq!(memory.entry0_ptr(), p);
630+
assert_eq!(memory.addr_of(0).unwrap(), p);
649631

650632
mem::drop(e0a);
651633
assert_eq!(memory.len(), 1);

0 commit comments

Comments
 (0)