Skip to content

Commit 44b3463

Browse files
committed
Seperate context implementation into its own crate
Right now, only 'multiple-collectors' and 'sync' are implemented. This maintains a list of what needs to be collected, seperate from the details of how tracing and allocation are performed.
1 parent b580bea commit 44b3463

File tree

10 files changed

+654
-963
lines changed

10 files changed

+654
-963
lines changed

libs/context/Cargo.toml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
[package]
2+
name = "zerogc-context"
3+
description = "Handles the context of a zerogc collector."
4+
version = "0.1.1"
5+
authors = ["Techcable <[email protected]>"]
6+
repository = "https://github.com/DuckLogic/zerogc"
7+
readme = "../../README.md"
8+
license = "MIT"
9+
edition = "2018"
10+
11+
[dependencies]
12+
zerogc = { path = "../..", version = "0.1.0" }
13+
once_cell = { version = "1.4.0", optional = true }
14+
# Concurrency
15+
parking_lot = { version = "0.11", features = ["nightly"], optional = true }
16+
crossbeam-utils = { version = "0.8", optional = true }
17+
# Logging
18+
slog = "2.7"
19+
20+
[features]
21+
default = [
22+
"sync", # Thread-safety by default
23+
"multiple-collectors", # By default, allow multiple collectors
24+
]
25+
# Allow multiple threads to access the garbage collector
26+
# by creating a seperate context for each.
27+
#
28+
# This can increase overhead by requiring communication between threads.
29+
sync = [
30+
"parking_lot",
31+
"crossbeam-utils"
32+
]
33+
# Allow multiple collectors to exist at once
34+
# Otherwise, there's a single global collector (useful in VMs)
35+
#
36+
# Even if multiple collectors are enabled, pointers from
37+
# one collector can't be safely mixed with other collectors.
38+
multiple-collectors = []
39+

libs/context/src/collector.rs

Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
//! The interface to a collector
2+
3+
use std::ptr::NonNull;
4+
use std::sync::Arc;
5+
use std::fmt::{self, Debug, Formatter};
6+
7+
use slog::{Logger, o};
8+
9+
use zerogc::{Gc, GcSafe, GcSystem, Trace, GcSimpleAlloc, GcBrand, GcHandle, GcHandleSystem};
10+
11+
use crate::{SimpleCollectorContext, CollectionManager};
12+
13+
/// A specific implementation of a collector
14+
pub unsafe trait RawCollectorImpl: 'static + Sized {
15+
/// A dynamic pointer to a GC object
16+
///
17+
/// The simple collector implements this as
18+
/// a trait object pointer.
19+
type GcDynPointer: Copy + Debug + 'static;
20+
21+
/// Convert the specified value into a dyn pointer
22+
unsafe fn create_dyn_pointer<T: Trace>(t: *mut T) -> Self::GcDynPointer;
23+
24+
#[cfg(not(feature = "multiple-collectors"))]
25+
/// When the collector is a singleton,
26+
/// return the global implementation
27+
fn global_ptr() -> *const Self;
28+
#[cfg(not(feature = "multiple-collectors"))]
29+
fn init_global(logger: Logger);
30+
#[cfg(feature = "multiple-collectors")]
31+
fn init(logger: Logger) -> NonNull<Self>;
32+
/// The id of this collector
33+
#[inline]
34+
fn id(&self) -> CollectorId<Self> {
35+
#[cfg(not(feature = "multiple-collectors"))] {
36+
CollectorId {}
37+
}
38+
#[cfg(feature = "multiple-collectors")] {
39+
CollectorId { rc: NonNull::from(self) }
40+
}
41+
}
42+
unsafe fn gc_write_barrier<'gc, T, V>(
43+
owner: &Gc<'gc, T, CollectorId<Self>>,
44+
value: &Gc<'gc, V, CollectorId<Self>>,
45+
field_offset: usize
46+
) where T: GcSafe + ?Sized + 'gc, V: GcSafe + ?Sized + 'gc;
47+
/// The logger associated with this collector
48+
fn logger(&self) -> &Logger;
49+
50+
fn manager(&self) -> &CollectionManager<Self>;
51+
52+
fn should_collect(&self) -> bool;
53+
54+
fn allocated_size(&self) -> crate::utils::MemorySize;
55+
56+
unsafe fn perform_raw_collection(&self, contexts: &[*mut crate::RawContext<Self>]);
57+
}
58+
59+
impl<C: RawCollectorImpl> PartialEq for CollectorId<C> {
60+
#[inline]
61+
fn eq(&self, other: &Self) -> bool {
62+
#[cfg(feature = "multiple-collectors")] {
63+
self.rc.as_ptr() == other.rc.as_ptr()
64+
}
65+
#[cfg(not(feature = "multiple-collectors"))] {
66+
true // Singleton
67+
}
68+
}
69+
}
70+
impl<C: RawCollectorImpl> Eq for CollectorId<C> {}
71+
impl<C: RawCollectorImpl> Clone for CollectorId<C> {
72+
#[inline]
73+
fn clone(&self) -> Self {
74+
*self
75+
}
76+
}
77+
impl<C: RawCollectorImpl> Copy for CollectorId<C> {}
78+
impl<C: RawCollectorImpl> Debug for CollectorId<C> {
79+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
80+
let mut debug = f.debug_struct("CollectorId");
81+
#[cfg(feature = "multiple-collectors")] {
82+
debug.field("rc", &self.rc);
83+
}
84+
debug.finish()
85+
}
86+
}
87+
88+
/// Uniquely identifies the collector in case there are
89+
/// multiple collectors.
90+
///
91+
/// If there are multiple collectors `cfg!(feature="multiple-collectors")`,
92+
/// we need to use a pointer to tell them apart.
93+
/// Otherwise, this is a zero-sized structure.
94+
///
95+
/// As long as our memory is valid,
96+
/// it implies this pointer is too.
97+
#[repr(C)]
98+
pub struct CollectorId<C: RawCollectorImpl> {
99+
#[cfg(feature = "multiple-collectors")]
100+
/// This is in essence a borrowed reference to
101+
/// the collector. SimpleCollector is implemented as a
102+
/// raw pointer via [Arc::into_raw]
103+
///
104+
/// We don't know whether the underlying memory will be valid.
105+
rc: NonNull<C>,
106+
}
107+
impl<C: RawCollectorImpl> CollectorId<C> {
108+
#[cfg(feature = "multiple-collectors")]
109+
#[inline]
110+
pub unsafe fn from_raw(rc: NonNull<C>) -> CollectorId<C> {
111+
CollectorId { rc }
112+
}
113+
#[cfg(feature = "multiple-collectors")]
114+
#[inline]
115+
pub unsafe fn as_ref(&self) -> &C {
116+
self.rc.as_ref()
117+
}
118+
#[cfg(not(feature = "multiple-collectors"))]
119+
#[inline]
120+
pub unsafe fn as_ref(&self) -> &C {
121+
&*GLOBAL_COLLECTOR.load(Ordering::Acquire)
122+
}
123+
#[cfg(feature = "multiple-collectors")]
124+
pub unsafe fn weak_ref(&self) -> WeakCollectorRef<C> {
125+
let arc = Arc::from_raw(self.rc.as_ptr());
126+
let weak = Arc::downgrade(&arc);
127+
std::mem::forget(arc);
128+
WeakCollectorRef { weak }
129+
}
130+
#[cfg(not(feature = "multiple-collectors"))]
131+
pub unsafe fn weak_ref(&self) -> WeakCollectorRef<C> {
132+
WeakCollectorRef { }
133+
}
134+
}
135+
unsafe impl<C: RawCollectorImpl> ::zerogc::CollectorId for CollectorId<C> {
136+
type System = CollectorRef<C>;
137+
138+
#[inline(always)]
139+
unsafe fn gc_write_barrier<'gc, T, V>(
140+
owner: &Gc<'gc, T, Self>,
141+
value: &Gc<'gc, V, Self>,
142+
field_offset: usize
143+
) where T: GcSafe + ?Sized + 'gc, V: GcSafe + ?Sized + 'gc {
144+
C::gc_write_barrier(owner, value, field_offset)
145+
}
146+
147+
#[inline]
148+
unsafe fn assume_valid_system(&self) -> &'_ Self::System {
149+
// TODO: Make the API nicer? (avoid borrowing and indirection)
150+
#[cfg(feature = "multiple-collectors")] {
151+
assert_eq!(
152+
std::mem::size_of::<Self>(),
153+
std::mem::size_of::<CollectorRef<C>>()
154+
);
155+
&*(self as *const CollectorId<C> as *const CollectorRef<C>)
156+
}
157+
#[cfg(not(feature = "multiple-collectors"))] {
158+
// NOTE: We live forever
159+
const COLLECTOR: CollectorRef = CollectorRef { };
160+
&COLLECTOR
161+
}
162+
}
163+
}
164+
165+
pub struct WeakCollectorRef<C: RawCollectorImpl> {
166+
#[cfg(feature = "multiple-collectors")]
167+
weak: std::sync::Weak<C>,
168+
}
169+
impl<C: RawCollectorImpl> WeakCollectorRef<C> {
170+
#[cfg(feature = "multiple-collectors")]
171+
pub unsafe fn assume_valid(&self) -> CollectorId<C> {
172+
debug_assert!(
173+
self.weak.upgrade().is_some(),
174+
"Dead collector"
175+
);
176+
CollectorId {
177+
rc: NonNull::new_unchecked(self.weak.as_ptr() as *mut _)
178+
}
179+
}
180+
#[cfg(not(feature = "multiple-collectors"))]
181+
pub unsafe fn assume_valid(&self) -> CollectorId<C> {
182+
CollectorId {}
183+
}
184+
pub fn ensure_valid<R>(&self, func: impl FnOnce(CollectorId<C>) -> R) -> R {
185+
self.try_ensure_valid(|id| match id{
186+
Some(id) => func(id),
187+
None => panic!("Dead collector")
188+
})
189+
}
190+
#[cfg(feature = "multiple-collectors")]
191+
pub fn try_ensure_valid<R>(&self, func: impl FnOnce(Option<CollectorId<C>>) -> R) -> R{
192+
let rc = self.weak.upgrade();
193+
func(match rc {
194+
Some(_) => {
195+
Some(unsafe { self.assume_valid() })
196+
},
197+
None => None
198+
})
199+
}
200+
#[cfg(not(feature = "multiple-collectors"))]
201+
pub fn try_ensure_valid<R>(&self, func: impl FnOnce(Option<CollectorId<C>>) -> R) -> R {
202+
// global collector is always valid
203+
func(Some(CollectorId {}))
204+
}
205+
}
206+
207+
pub unsafe trait RawSimpleAlloc: RawCollectorImpl {
208+
fn alloc<'gc, T: GcSafe + 'gc>(context: &'gc SimpleCollectorContext<Self>, value: T) -> Gc<'gc, T, CollectorId<Self>>;
209+
}
210+
unsafe impl<'gc, T, C> GcSimpleAlloc<'gc, T> for SimpleCollectorContext<C>
211+
where T: GcSafe + 'gc, C: RawSimpleAlloc {
212+
#[inline]
213+
fn alloc(&'gc self, value: T) -> Gc<'gc, T, Self::Id> {
214+
C::alloc(self, value)
215+
}
216+
}
217+
218+
/// A reference to the collector.
219+
///
220+
/// TODO: Devise better name
221+
#[repr(C)]
222+
pub struct CollectorRef<C: RawCollectorImpl> {
223+
/// In reality, this is just an [Arc].
224+
///
225+
/// It is implemented as a raw pointer around [Arc::into_raw]
226+
#[cfg(feature = "multiple-collectors")]
227+
rc: NonNull<C>
228+
}
229+
/// We actually are thread safe ;)
230+
#[cfg(feature = "sync")]
231+
unsafe impl<C: RawCollectorImpl + Sync> Send for CollectorRef<C> {}
232+
#[cfg(feature = "sync")]
233+
unsafe impl<C: RawCollectorImpl + Sync> Sync for CollectorRef<C> {}
234+
235+
impl<C: RawCollectorImpl> CollectorRef<C> {
236+
pub fn create() -> Self {
237+
CollectorRef::with_logger(Logger::root(
238+
slog::Discard,
239+
o!()
240+
))
241+
}
242+
#[cfg(not(feature = "multiple-collectors"))]
243+
pub fn with_logger(logger: Logger) -> Self {
244+
unsafe { C::init_global(logger) }
245+
// NOTE: The raw pointer is implicit (now that we're leaked)
246+
CollectorRef {}
247+
}
248+
249+
#[cfg(feature = "multiple-collectors")]
250+
pub fn with_logger(logger: Logger) -> Self {
251+
let raw_ptr = C::init(logger);
252+
CollectorRef { rc: raw_ptr }
253+
}
254+
#[cfg(feature = "multiple-collectors")]
255+
pub(crate) fn clone_internal(&self) -> CollectorRef<C> {
256+
let original = unsafe { Arc::from_raw(self.rc.as_ptr()) };
257+
let cloned = Arc::clone(&original);
258+
std::mem::forget(original);
259+
CollectorRef { rc: unsafe { NonNull::new_unchecked(
260+
Arc::into_raw(cloned) as *mut _
261+
) } }
262+
}
263+
#[cfg(not(feature = "multiple-collectors"))]
264+
pub(crate) fn clone_internal(&self) -> CollectorRef<C> {
265+
CollectorRef {}
266+
}
267+
#[cfg(feature = "multiple-collectors")]
268+
#[inline]
269+
pub fn as_raw(&self) -> &C {
270+
unsafe { self.rc.as_ref() }
271+
}
272+
#[cfg(not(feature = "multiple-collectors"))]
273+
#[inline]
274+
pub fn as_raw(&self) -> &C {
275+
let ptr = GLOBAL_COLLECTOR.load(Ordering::Acquire);
276+
assert!(!ptr.is_null());
277+
unsafe { &*ptr }
278+
}
279+
280+
/// Create a new context bound to this collector
281+
///
282+
/// Warning: Only one collector should be created per thread.
283+
/// Doing otherwise can cause deadlocks/panics.
284+
#[cfg(feature = "sync")]
285+
pub fn create_context(&self) -> SimpleCollectorContext<C> {
286+
unsafe { SimpleCollectorContext::register_root(&self) }
287+
}
288+
/// Convert this collector into a unique context
289+
///
290+
/// The single-threaded implementation only allows a single context,
291+
/// so this method is nessicarry to support it.
292+
pub fn into_context(self) -> SimpleCollectorContext<C> {
293+
#[cfg(feature = "sync")]
294+
{ self.create_context() }
295+
#[cfg(not(feature = "sync"))]
296+
unsafe { SimpleCollectorContext::from_collector(&self) }
297+
}
298+
}
299+
impl<C: RawCollectorImpl> Drop for CollectorRef<C> {
300+
fn drop(&mut self) {
301+
#[cfg(feature = "multiple-collectors")] {
302+
drop(unsafe { Arc::from_raw(self.rc.as_ptr() as *const _) })
303+
}
304+
}
305+
}
306+
307+
unsafe impl<C: RawCollectorImpl> GcSystem for CollectorRef<C> {
308+
type Id = CollectorId<C>;
309+
type Context = SimpleCollectorContext<C>;
310+
}
311+
312+
unsafe impl<'gc, T, C> GcHandleSystem<'gc, T> for CollectorRef<C>
313+
where C: RawHandleImpl<'gc, T>,
314+
T: GcSafe + 'gc,
315+
T: GcBrand<'static, CollectorId<C>>,
316+
T::Branded: GcSafe {
317+
type Handle = C::Handle;
318+
319+
#[inline]
320+
fn create_handle(gc: Gc<'gc, T, Self::Id>) -> Self::Handle {
321+
C::create_handle(gc)
322+
}
323+
}
324+
325+
pub unsafe trait RawHandleImpl<'gc, T>: RawCollectorImpl
326+
where T: GcSafe + 'gc,
327+
T: GcBrand<'static, CollectorId<Self>>,
328+
<T as GcBrand<'static, CollectorId<Self>>>::Branded: GcSafe {
329+
/// The type of handles to the object `T`.
330+
type Handle: GcHandle<<T as GcBrand<'static, CollectorId<Self>>>::Branded, System=CollectorRef<Self>>;
331+
332+
/// Create a handle to the specified GC pointer,
333+
/// which can be used without a context
334+
///
335+
/// The system is implicit in the [Gc]
336+
fn create_handle(gc: Gc<'gc, T, CollectorId<Self>>) -> Self::Handle;
337+
}

0 commit comments

Comments
 (0)