Skip to content

Commit ee5a62d

Browse files
committed
reduce dupliaction between mark_sweep and mark_sweep_arena2
1 parent 794371d commit ee5a62d

File tree

18 files changed

+98
-1388
lines changed

18 files changed

+98
-1388
lines changed

oscars/src/alloc/arena2/alloc.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,16 @@ impl<T: ?Sized> ArenaHeapItem<T> {
4444
&mut self.value as *mut T
4545
}
4646

47-
pub(crate) fn value_mut(&mut self) -> &mut T {
47+
/// Returns a raw mutable pointer to the value
48+
///
49+
/// This avoids creating a `&mut self` reference, which can lead to stacked borrows
50+
/// if shared references to the heap item exist
51+
pub(crate) fn as_value_ptr(ptr: NonNull<Self>) -> *mut T {
52+
// SAFETY: `&raw mut` computes the field address without creating a reference
53+
unsafe { &raw mut (*ptr.as_ptr()).value }
54+
}
55+
56+
fn value_mut(&mut self) -> &mut T {
4857
&mut self.value
4958
}
5059
}
@@ -133,6 +142,15 @@ impl<'arena> ErasedArenaPointer<'arena> {
133142
self.0.as_ptr()
134143
}
135144

145+
/// Extend the lifetime of this erased arena pointer to 'static
146+
///
147+
/// SAFETY:
148+
///
149+
/// safe because the gc collector owns the arena and keeps it alive
150+
pub(crate) unsafe fn extend_lifetime(self) -> ErasedArenaPointer<'static> {
151+
ErasedArenaPointer(self.0, PhantomData)
152+
}
153+
136154
/// Returns an [`ArenaPointer`] for the current [`ErasedArenaPointer`]
137155
///
138156
/// # Safety
@@ -178,6 +196,15 @@ impl<'arena, T> ArenaPointer<'arena, T> {
178196
pub fn to_erased(self) -> ErasedArenaPointer<'arena> {
179197
self.0
180198
}
199+
200+
/// Extend the lifetime of this arena pointer to 'static
201+
///
202+
/// SAFETY:
203+
///
204+
/// safe because the gc collector owns the arena and keeps it alive
205+
pub(crate) unsafe fn extend_lifetime(self) -> ArenaPointer<'static, T> {
206+
ArenaPointer(self.0.extend_lifetime(), PhantomData)
207+
}
181208
}
182209

183210
const FULL_MASK: u8 = 0b0100_0000;

oscars/src/alloc/arena2/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ fn arc_drop() {
7777
let heap_item_mut = heap_item.as_mut();
7878
// Manually drop the heap item
7979
heap_item_mut.mark_dropped();
80-
drop_in_place(heap_item_mut.value_mut());
80+
drop_in_place(ArenaHeapItem::as_value_ptr(heap_item));
8181
};
8282

8383
assert!(dropped.load(Ordering::SeqCst));

oscars/src/collectors/mark_sweep/cell.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,14 @@ impl<T: ?Sized> GcRefCell<T> {
171171
}
172172
}
173173

174+
// returns a raw pointer to the inner value or `None` if currently mutably borrowed
175+
pub(crate) fn get_raw(&self) -> Option<*mut T> {
176+
match self.borrow.get().borrowed() {
177+
BorrowState::Writing => None,
178+
_ => Some(self.cell.get()),
179+
}
180+
}
181+
174182
/// Mutably borrows the wrapped value, returning an error if the value is currently borrowed.
175183
///
176184
/// The borrow lasts until the returned `GcCellRefMut` exits scope.

oscars/src/collectors/mark_sweep/internals/gc_box.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ impl<T: Trace + Finalize + ?Sized> WeakGcBox<T> {
4646
pub(crate) fn erased_inner_ptr(&self) -> NonNull<GcBox<NonTraceable>> {
4747
// SAFETY: `as_heap_ptr` returns a valid pointer to
4848
// `PoolItem` whose lifetime is tied to the pool
49-
let heap_item = unsafe { self.as_heap_ptr().as_mut() };
50-
// SAFETY: We just removed this value from a NonNull
51-
unsafe { NonNull::new_unchecked(heap_item.as_ptr()) }
49+
let heap_item: *mut PoolItem<GcBox<NonTraceable>> = self.as_heap_ptr().as_ptr();
50+
// SAFETY: `PoolItem` is repr(transparent), so pointing to and returning field 0 is valid.
51+
unsafe { NonNull::new_unchecked(&raw mut (*heap_item).0) }
5252
}
5353

5454
pub(crate) fn as_heap_ptr(&self) -> NonNull<PoolItem<GcBox<NonTraceable>>> {

oscars/src/collectors/mark_sweep/internals/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod gc_header;
44
mod vtable;
55

66
pub(crate) use ephemeron::Ephemeron;
7+
pub(crate) use gc_header::{GcHeader, HeaderColor};
78
pub(crate) use vtable::{DropFn, TraceFn, VTable, vtable_of};
89

910
pub use self::gc_box::{GcBox, NonTraceable, WeakGcBox};

oscars/src/collectors/mark_sweep/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ impl MarkSweepGarbageCollector {
171171

172172
// Force drops all elements in the internal tracking queues and clears
173173
// them without regard for reachability.
174+
//
175+
// NOTE: This intentionally differs from arena2's sweep_all_queues.
176+
// arena3 uses`free_slot` calls to reclaim memory.
177+
// arena2 uses a bitmap (`mark_dropped`) and reclaims automatically
174178
fn sweep_all_queues(&self) {
175179
let ephemerons = core::mem::take(&mut *self.ephemeron_queue.borrow_mut());
176180
for ephemeron in ephemerons {

oscars/src/collectors/mark_sweep/pointers/gc.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@ impl<T: Trace> Gc<T> {
4444

4545
impl<T: Trace + ?Sized> Gc<T> {
4646
pub(crate) fn as_sized_inner_ptr(&self) -> NonNull<GcBox<NonTraceable>> {
47-
// SAFETY: use `addr_of_mut!` to get a raw pointer without creating
47+
// SAFETY: use `&raw mut` to get a raw pointer without creating
4848
// a `&mut` reference, avoiding Stacked Borrows UB during GC tracing
49-
let raw: *mut ArenaHeapItem<GcBox<NonTraceable>> = self.as_heap_ptr().as_ptr();
49+
let raw: *mut PoolItem<GcBox<NonTraceable>> = self.as_heap_ptr().as_ptr();
5050
// SAFETY: `raw` is non-null because it comes from `as_heap_ptr()`
51-
// `ArenaHeapItem` is `#[repr(transparent)]` so it shares the same address as field 0
52-
unsafe { NonNull::new_unchecked(core::ptr::addr_of_mut!((*raw).0)) }
51+
// `PoolItem` is `#[repr(transparent)]` so it shares the same address as field 0
52+
unsafe { NonNull::new_unchecked(&raw mut (*raw).0) }
5353
}
5454

5555
pub(crate) fn as_heap_ptr(&self) -> NonNull<PoolItem<GcBox<NonTraceable>>> {
Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,3 @@
11
# Mark sweep collector
22

3-
This is a basic mark-sweep collector using an underlying arena allocator.
4-
5-
## TODO list
6-
7-
- [x] Support weak maps
8-
- [x] Add Tests
9-
10-
11-
## Areas of improvement
12-
13-
The overhead on a single allocation honestly feels a bit high. This may be worthwhile
14-
for now for performance gains and general API, but we should really measure and determine
15-
just how much overhead is being added.
16-
17-
Currently, there is a line drawn between the allocator and the GcBox. This creates very,
18-
very awkward naming (ArenaPointer, ArenaHeapItem, GcBox, etc.). We may be able to combine
19-
the general functionality of the ArenaHeapItem, and GcBox. But also, that would then
20-
restrict the potential ability to switch out allocators as easily ... to be determined.
21-
3+
This is a basic mark-sweep collector using an underlying arena2 allocator.

0 commit comments

Comments
 (0)