Skip to content

Commit c249e2b

Browse files
committed
Allow customizing whether GcArray is fat or thin
Switch the epsilon collector to use fat pointers for arrays, so that it's easy to transmute back and forth to `&[T]`. This allows us to remove that horrible `epsilon_static_array!` macro. Futher lowers the overhead of the epsilon collector...... TODO: We should be able to eliminate most array headers thanks to this......
1 parent 062074a commit c249e2b

File tree

8 files changed

+328
-167
lines changed

8 files changed

+328
-167
lines changed

libs/context/src/collector.rs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use alloc::sync::Arc;
99

1010
use slog::{Logger, o};
1111

12-
use zerogc::{Gc, GcSafe, GcSystem, Trace, GcSimpleAlloc, NullTrace, TraceImmutable, GcVisitor, GcArray, TrustedDrop};
12+
use zerogc::{Gc, GcSafe, GcSystem, Trace, GcSimpleAlloc, NullTrace, TraceImmutable, GcVisitor, TrustedDrop};
1313

1414
use crate::{CollectorContext};
1515
use crate::state::{CollectionManager, RawContext};
@@ -52,10 +52,12 @@ pub unsafe trait RawCollectorImpl: 'static + Sized {
5252
fn id_for_gc<'a, 'gc, T>(gc: &'a Gc<'gc, T, CollectorId<Self>>) -> &'a CollectorId<Self>
5353
where 'gc: 'a, T: ?Sized + 'gc;
5454

55-
fn id_for_array<'a, 'gc, T>(gc: &'a GcArray<'gc, T, CollectorId<Self>>) -> &'a CollectorId<Self>
55+
// TODO: What if we want to customize 'GcArrayRepr'??
56+
57+
fn id_for_array<'a, 'gc, T>(gc: &'a <CollectorId<Self> as zerogc::CollectorId>::ArrayRepr<'gc, T>) -> &'a CollectorId<Self>
5658
where 'gc: 'a, T: 'gc;
5759

58-
fn resolve_array_len<'gc, T>(gc: GcArray<'gc, T, CollectorId<Self>>) -> usize
60+
fn resolve_array_len<'gc, T>(gc: <CollectorId<Self> as zerogc::CollectorId>::ArrayRepr<'gc, T>) -> usize
5961
where T: 'gc;
6062

6163
/// Convert the specified value into a dyn pointer
@@ -291,19 +293,16 @@ impl<C: RawCollectorImpl> CollectorId<C> {
291293
unsafe impl<C: RawCollectorImpl> ::zerogc::CollectorId for CollectorId<C> {
292294
type System = CollectorRef<C>;
293295
type RawVecRepr<'gc> = C::RawVecRepr<'gc>;
296+
// TODO: What if clients want to customize this?
297+
type ArrayRepr<'gc, T: 'gc> = zerogc::array::repr::ThinArrayRepr<'gc, T, Self>;
294298

295299
#[inline]
296300
fn from_gc_ptr<'a, 'gc, T>(gc: &'a Gc<'gc, T, Self>) -> &'a Self where T: ?Sized + 'gc, 'gc: 'a {
297301
C::id_for_gc(gc)
298302
}
299303

300304
#[inline]
301-
fn resolve_array_len<'gc, T>(gc: GcArray<'gc, T, Self>) -> usize where T: 'gc {
302-
C::resolve_array_len(gc)
303-
}
304-
305-
#[inline]
306-
fn resolve_array_id<'a, 'gc, T>(gc: &'a GcArray<'gc, T, Self>) -> &'a Self where T: 'gc, 'gc: 'a {
305+
fn resolve_array_id<'a, 'gc, T>(gc: &'a Self::ArrayRepr<'gc, T>) -> &'a Self where T: 'gc, 'gc: 'a {
307306
C::id_for_array(gc)
308307
}
309308

@@ -327,6 +326,12 @@ unsafe impl<C: RawCollectorImpl> ::zerogc::CollectorId for CollectorId<C> {
327326
&*(self as *const CollectorId<C> as *const CollectorRef<C>)
328327
}
329328
}
329+
unsafe impl<C: RawCollectorImpl> zerogc::array::repr::ThinArrayGcAccess for CollectorId<C> {
330+
#[inline]
331+
fn resolve_array_len<'gc, T>(gc: Self::ArrayRepr<'gc, T>) -> usize where T: 'gc {
332+
C::resolve_array_len(gc)
333+
}
334+
}
330335
unsafe impl<'gc, OtherId: zerogc::CollectorId, C: RawCollectorImpl> GcSafe<'gc, OtherId> for CollectorId<C> {}
331336
unsafe impl<C: RawCollectorImpl> Trace for CollectorId<C> {
332337
const NEEDS_TRACE: bool = false;

libs/simple/src/lib.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ use zerogc_context::collector::{RawSimpleAlloc, RawCollectorImpl};
7272
use zerogc_context::handle::{GcHandleList, RawHandleImpl};
7373
use zerogc_context::{CollectionManager as AbstractCollectionManager, RawContext as AbstractRawContext, CollectorContext};
7474
use zerogc::vec::{GcRawVec};
75+
use zerogc::array::repr::{GcArrayRepr, ThinArrayRepr};
7576
use std::cell::Cell;
7677
use std::ffi::c_void;
7778

@@ -142,7 +143,7 @@ pub type CollectorId = ::zerogc_context::CollectorId<RawSimpleCollector>;
142143
/// A garbage collected pointer, allocated in the "simple" collector
143144
pub type Gc<'gc, T> = ::zerogc::Gc<'gc, T, CollectorId>;
144145
/// A garbage collected array, allocated in the "simple" collector
145-
pub type GcArray<'gc, T> = ::zerogc::vec::GcArray<'gc, T, CollectorId>;
146+
pub type GcArray<'gc, T> = ::zerogc::array::GcArray<'gc, T, CollectorId>;
146147
/// A garbage colelcted vector, allocated in the "simple" collector
147148
pub type GcVec<'gc, T> = ::zerogc::vec::GcVec<'gc, T, SimpleCollectorContext>;
148149

@@ -647,10 +648,10 @@ unsafe impl ::zerogc_context::collector::RawCollectorImpl for RawSimpleCollector
647648
}
648649

649650
#[inline]
650-
fn id_for_array<'a, 'gc, T>(gc: &'a GcArray<'gc, T>) -> &'a CollectorId where 'gc: 'a, T: 'gc {
651+
fn id_for_array<'a, 'gc, T>(repr: &'a ThinArrayRepr<'gc, T, CollectorId>) -> &'a CollectorId where 'gc: 'a, T: 'gc {
651652
#[cfg(feature = "multiple-collectors")] {
652653
unsafe {
653-
let header = GcArrayHeader::LAYOUT.from_value_ptr(gc.as_raw_ptr());
654+
let header = GcArrayHeader::LAYOUT.from_value_ptr(repr.as_raw_ptr());
654655
&*(*header).common_header.mark_data.load_snapshot().collector_id_ptr
655656
}
656657
}
@@ -661,7 +662,7 @@ unsafe impl ::zerogc_context::collector::RawCollectorImpl for RawSimpleCollector
661662
}
662663

663664
#[inline]
664-
fn resolve_array_len<'gc, T>(gc: zerogc::GcArray<'gc, T, CollectorId>) -> usize where T: 'gc {
665+
fn resolve_array_len<'gc, T>(gc: ThinArrayRepr<'gc, T, CollectorId>) -> usize where T: 'gc {
665666
unsafe {
666667
let header = GcArrayHeader::LAYOUT.from_value_ptr(gc.as_raw_ptr());
667668
(*header).len
@@ -1002,16 +1003,16 @@ unsafe impl GcVisitor for MarkVisitor<'_> {
10021003
}
10031004

10041005
#[inline]
1005-
unsafe fn visit_array<'gc, T, Id>(&mut self, array: &mut ::zerogc::vec::GcArray<'gc, T, Id>) -> Result<(), Self::Err>
1006+
unsafe fn visit_array<'gc, T, Id>(&mut self, array: &mut ::zerogc::array::GcArray<'gc, T, Id>) -> Result<(), Self::Err>
10061007
where T: GcSafe<'gc, Id>, Id: ::zerogc::CollectorId {
10071008
if TypeId::of::<Id>() == TypeId::of::<crate::CollectorId>() {
10081009
/*
10091010
* See comment in 'visit_gc'.
10101011
* Essentially this is a checked cast
10111012
*/
10121013
let array = std::mem::transmute::<
1013-
&mut ::zerogc::vec::GcArray<'gc, T, Id>,
1014-
&mut ::zerogc::vec::GcArray<'gc, T, crate::CollectorId>
1014+
&mut ::zerogc::array::GcArray<'gc, T, Id>,
1015+
&mut ::zerogc::array::GcArray<'gc, T, crate::CollectorId>
10151016
>(array);
10161017
/*
10171018
* Check the collectors match. Otherwise we're mutating

src/array.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//! Defines the interface to garbage collected arrays.
2+
use core::ops::Deref;
3+
use core::ptr::NonNull;
4+
5+
use crate::{CollectorId, GcSafe, GcRebrand};
6+
use zerogc_derive::unsafe_gc_impl;
7+
8+
use self::repr::GcArrayRepr;
9+
10+
pub mod repr;
11+
12+
/// A garbage collected array.
13+
///
14+
/// The length is immutable and cannot change
15+
/// once it has been allocated.
16+
///
17+
/// ## Safety
18+
/// This is a `#[repr(transparent)]` wrapper around
19+
/// [GcArrayRepr].
20+
#[repr(transparent)]
21+
pub struct GcArray<'gc, T: 'gc, Id: CollectorId> {
22+
repr: Id::ArrayRepr<'gc, T>
23+
}
24+
impl<'gc, T: GcSafe<'gc, Id>, Id: CollectorId> GcArray<'gc, T, Id> {
25+
/// Create an array from the specified raw pointer and length
26+
///
27+
/// ## Safety
28+
/// Pointer and length must be valid, and point to a garbage collected
29+
/// value allocated from the corresponding [CollectorId]
30+
#[inline]
31+
pub unsafe fn from_raw_ptr(ptr: NonNull<T>, len: usize) -> Self {
32+
GcArray { repr: Id::ArrayRepr::<'gc, T>::from_raw_parts(ptr, len) }
33+
}
34+
}
35+
// Relax T: GcSafe bound
36+
impl<'gc, T, Id: CollectorId> GcArray<'gc, T, Id> {
37+
/// The value of the array as a slice
38+
#[inline]
39+
pub fn as_slice(self) -> &'gc [T] {
40+
self.repr.as_slice()
41+
}
42+
/// Load a raw pointer to the array's value
43+
#[inline]
44+
pub fn as_raw_ptr(self) -> *mut T {
45+
self.repr.as_raw_ptr()
46+
}
47+
/// Load the length of the array
48+
#[inline]
49+
pub fn len(&self) -> usize {
50+
self.repr.len()
51+
}
52+
/// Check if the array is empty
53+
#[inline]
54+
pub fn is_empty(&self) -> bool {
55+
self.repr.is_empty()
56+
}
57+
/// Resolve the [CollectorId]
58+
#[inline]
59+
pub fn collector_id(&self) -> &'_ Id {
60+
Id::resolve_array_id(&self.repr)
61+
}
62+
/// Get access to the array's underlying representation.
63+
#[inline]
64+
pub fn as_raw_repr(&self) -> &Id::ArrayRepr<'gc, T> {
65+
&self.repr
66+
}
67+
}
68+
impl<'gc, T: GcSafe<'gc, Id>, Id: CollectorId> Deref for GcArray<'gc, T, Id> {
69+
type Target = [T];
70+
71+
#[inline]
72+
fn deref(&self) -> &Self::Target {
73+
self.as_slice()
74+
}
75+
}
76+
impl<'gc, T, Id: CollectorId> Copy for GcArray<'gc, T, Id> {}
77+
impl<'gc, T, Id: CollectorId> Clone for GcArray<'gc, T, Id> {
78+
#[inline]
79+
fn clone(&self) -> Self {
80+
*self
81+
}
82+
}
83+
// Need to implement by hand, because [T] is not GcRebrand
84+
unsafe_gc_impl!(
85+
target => GcArray<'gc, T, Id>,
86+
params => ['gc, T: GcSafe<'gc, Id>, Id: CollectorId],
87+
bounds => {
88+
TraceImmutable => never,
89+
GcRebrand => { where T: GcRebrand<'new_gc, Id>, <T as GcRebrand<'new_gc, Id>>::Branded: Sized + GcSafe<'new_gc, Id> },
90+
},
91+
null_trace => never,
92+
branded_type => GcArray<'new_gc, <T as GcRebrand<'new_gc, Id>>::Branded, Id>,
93+
NEEDS_TRACE => true,
94+
NEEDS_DROP => false,
95+
trace_mut => |self, visitor| {
96+
unsafe { visitor.visit_array(self) }
97+
},
98+
collector_id => Id,
99+
visit_inside_gc => |gc, visitor| {
100+
visitor.visit_gc(gc)
101+
}
102+
);

src/array/repr.rs

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
//! Defines the underlying reprsention of a [GcArray] pointer.
2+
//!
3+
//!
4+
//! Two default implementations are also included:
5+
//! 1. FatArrayRepr - Represents arrays as a fat pointer
6+
//! 2. ThinArrayRepr - Represents arrays as a thin pointer,
7+
//! with the length stored indirectly in the object header.
8+
use core::marker::PhantomData;
9+
use core::ptr::NonNull;
10+
11+
12+
use crate::{CollectorId, Gc};
13+
14+
15+
/// The raw representation of a GcArray pointer.
16+
///
17+
///
18+
/// NOTE: This is only for customizing the *pointer*
19+
/// representation. The in-memory layout of the array and its
20+
/// header can be controlled separately from the pointer
21+
/// (ie. feel free to use a builtin impl even if you have a custom header).
22+
///
23+
/// ## Safety
24+
/// The length and never change (and be valid for
25+
/// the corresponding allocation).
26+
///
27+
/// The underlying 'repr' is responsible
28+
/// for dropping memory as appropriate.
29+
pub unsafe trait GcArrayRepr<'gc, T>: Copy {
30+
/// The repr's collector
31+
type Id: CollectorId;
32+
/// Construct an array representation from a combination
33+
/// of a pointer and length.
34+
///
35+
/// This is the garbage collected equivalent of [std::slice::from_raw_parts]
36+
///
37+
/// ## Safety
38+
/// The combination of pointer + length must be valid.
39+
unsafe fn from_raw_parts(ptr: NonNull<T>, len: usize) -> Self;
40+
/// Get the length of the array
41+
fn len(&self) -> usize;
42+
/// Check if the array is empty
43+
#[inline]
44+
fn is_empty(&self) -> bool {
45+
self.len() == 0
46+
}
47+
/// Load a raw pointer to the array's value
48+
fn as_raw_ptr(&self) -> *mut T;
49+
/// Get the value of the array as a slice
50+
#[inline]
51+
fn as_slice(&self) -> &'gc [T] {
52+
// SAFETY: Guaranteed by the unsafety of the trait
53+
unsafe {
54+
std::slice::from_raw_parts(
55+
self.as_raw_ptr() as *const T,
56+
self.len()
57+
)
58+
}
59+
}
60+
}
61+
62+
/// Represents an array as a fat pointer.
63+
///
64+
/// ## Safety
65+
/// This type is guarenteed to be compatible with `&[T]`.
66+
/// Transmuting back and forth is safe.
67+
#[repr(transparent)]
68+
pub struct FatArrayRepr<'gc, T: 'gc, Id: CollectorId> {
69+
slice: NonNull<[T]>,
70+
marker: PhantomData<Gc<'gc, [T], Id>>
71+
}
72+
impl<'gc, T, Id: CollectorId> Copy for FatArrayRepr<'gc, T, Id> {}
73+
impl<'gc, T, Id: CollectorId> Clone for FatArrayRepr<'gc, T, Id> {
74+
#[inline]
75+
fn clone(&self) -> Self {
76+
*self
77+
}
78+
}
79+
80+
unsafe impl<'gc, T, Id: CollectorId> GcArrayRepr<'gc, T> for FatArrayRepr<'gc, T, Id> {
81+
type Id = Id;
82+
83+
#[inline]
84+
unsafe fn from_raw_parts(ptr: NonNull<T>, len: usize) -> Self {
85+
FatArrayRepr {
86+
slice: NonNull::new_unchecked(
87+
core::ptr::slice_from_raw_parts(
88+
ptr.as_ptr() as *const T, len
89+
) as *mut [T]
90+
),
91+
marker: PhantomData
92+
}
93+
}
94+
95+
#[inline]
96+
fn len(&self) -> usize {
97+
unsafe { self.slice.as_ref().len() }
98+
}
99+
100+
#[inline]
101+
fn as_raw_ptr(&self) -> *mut T {
102+
self.slice.as_ptr() as *mut T
103+
}
104+
105+
#[inline]
106+
fn as_slice(&self) -> &'gc [T] {
107+
unsafe { &*self.slice.as_ptr() }
108+
}
109+
110+
}
111+
112+
/// Represents an array as a thin pointer,
113+
/// storing the length indirectly in the object's header.
114+
///
115+
/// ## Safety
116+
/// This type has the same layout as `&T`,
117+
/// and can be transmuted back and forth
118+
/// (assuming the appropriate invariants are met).
119+
#[repr(transparent)]
120+
pub struct ThinArrayRepr<'gc, T: 'gc, Id: ThinArrayGcAccess> {
121+
elements: NonNull<T>,
122+
marker: PhantomData<Gc<'gc, [T], Id>>
123+
}
124+
impl<'gc, T, Id: ThinArrayGcAccess> Copy for ThinArrayRepr<'gc, T, Id> {}
125+
impl<'gc, T, Id: ThinArrayGcAccess> Clone for ThinArrayRepr<'gc, T, Id> {
126+
#[inline]
127+
fn clone(&self) -> Self {
128+
*self
129+
}
130+
}
131+
unsafe impl<'gc, T, Id: ThinArrayGcAccess> GcArrayRepr<'gc, T> for ThinArrayRepr<'gc, T, Id> {
132+
type Id = Id;
133+
#[inline]
134+
fn len(&self) -> usize {
135+
Id::resolve_array_len(*self)
136+
}
137+
138+
#[inline]
139+
unsafe fn from_raw_parts(ptr: NonNull<T>, len: usize) -> Self {
140+
let res = ThinArrayRepr { elements: ptr, marker: PhantomData };
141+
debug_assert_eq!(res.len(), len);
142+
res
143+
}
144+
145+
#[inline]
146+
fn as_raw_ptr(&self) -> *mut T {
147+
self.elements.as_ptr()
148+
}
149+
}
150+
151+
/// The raw array access used to implement []
152+
///
153+
/// This should be considered an implementation detail.
154+
///
155+
/// Usage of this type is incredibly niche unless you
156+
/// plan on implementing your own collector.
157+
///
158+
/// ## Safety
159+
/// The returned length must be correct.
160+
#[doc(hidden)]
161+
pub unsafe trait ThinArrayGcAccess: CollectorId {
162+
/// Resolve the length of the specified [GcArray]
163+
fn resolve_array_len<'gc, T>(repr: ThinArrayRepr<'gc, T, Self>) -> usize
164+
where T: 'gc;
165+
}

0 commit comments

Comments
 (0)