Skip to content

Commit ee21126

Browse files
committed
[context] Use traits to control whether collectors are 'Sync'
No longer dependent on features, although the 'sync' feature is still present to optionally remove our dependency on parking_lot. Even if the feature is on, you still have the option for single-threaded collectors if you want to.
1 parent 48b9e82 commit ee21126

File tree

8 files changed

+259
-148
lines changed

8 files changed

+259
-148
lines changed

libs/context/Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@ slog = "2.7"
1919

2020
[features]
2121
default = [
22-
"sync", # Thread-safety by default
22+
"sync", # Support thread-safety by default
2323
]
24-
# Allow multiple threads to access the garbage collector
24+
# This will allow multiple threads to access the garbage collector
2525
# by creating a seperate context for each.
2626
#
27-
# This can increase overhead by requiring communication between threads.
27+
# Thread safe collectors can have increased overhead
28+
# by requiring communication between threads.
2829
sync = [
2930
"parking_lot",
3031
"crossbeam-utils"

libs/context/src/collector.rs

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use slog::{Logger, o};
99

1010
use zerogc::{Gc, GcSafe, GcSystem, Trace, GcSimpleAlloc};
1111

12-
use crate::{CollectorContext, CollectionManager};
12+
use crate::{CollectorContext};
13+
use crate::state::{CollectionManager, RawContext};
1314

1415
/// A specific implementation of a collector
1516
pub unsafe trait RawCollectorImpl: 'static + Sized {
@@ -18,17 +19,27 @@ pub unsafe trait RawCollectorImpl: 'static + Sized {
1819
/// The simple collector implements this as
1920
/// a trait object pointer.
2021
type GcDynPointer: Copy + Debug + 'static;
22+
2123
/// A pointer to this collector
2224
///
2325
/// Must be a ZST if the collector is a singleton.
2426
type Ptr: CollectorPtr<Self>;
2527

28+
/// The type that manages this collector's state
29+
type Manager: CollectionManager<Self, Context=Self::RawContext>;
30+
31+
/// The context
32+
type RawContext: RawContext<Self>;
33+
2634
/// True if this collector is a singleton
2735
///
2836
/// If the collector allows multiple instances,
2937
/// this *must* be false
3038
const SINGLETON: bool;
3139

40+
/// True if this collector is thread-safe.
41+
const SYNC: bool;
42+
3243
/// Convert the specified value into a dyn pointer
3344
unsafe fn create_dyn_pointer<T: Trace>(t: *mut T) -> Self::GcDynPointer;
3445

@@ -50,14 +61,20 @@ pub unsafe trait RawCollectorImpl: 'static + Sized {
5061
/// The logger associated with this collector
5162
fn logger(&self) -> &Logger;
5263

53-
fn manager(&self) -> &CollectionManager<Self>;
64+
fn manager(&self) -> &Self::Manager;
5465

5566
fn should_collect(&self) -> bool;
5667

5768
fn allocated_size(&self) -> crate::utils::MemorySize;
5869

59-
unsafe fn perform_raw_collection(&self, contexts: &[*mut crate::RawContext<Self>]);
70+
unsafe fn perform_raw_collection(&self, contexts: &[*mut Self::RawContext]);
6071
}
72+
73+
/// A thread safe collector
74+
pub unsafe trait SyncCollector: RawCollectorImpl + Sync {
75+
76+
}
77+
6178
/// A collector implemented as a singleton
6279
///
6380
/// This only has one instance
@@ -322,10 +339,9 @@ pub struct CollectorRef<C: RawCollectorImpl> {
322339
ptr: C::Ptr
323340
}
324341
/// We actually are thread safe ;)
342+
unsafe impl<C: SyncCollector> Send for CollectorRef<C> {}
325343
#[cfg(feature = "sync")]
326-
unsafe impl<C: RawCollectorImpl + Sync> Send for CollectorRef<C> {}
327-
#[cfg(feature = "sync")]
328-
unsafe impl<C: RawCollectorImpl + Sync> Sync for CollectorRef<C> {}
344+
unsafe impl<C: SyncCollector> Sync for CollectorRef<C> {}
329345

330346
/// Internal trait for initializing a collector
331347
#[doc(hidden)]
@@ -384,24 +400,23 @@ impl<C: RawCollectorImpl> CollectorRef<C> {
384400
CollectorId { ptr: self.ptr }
385401
}
386402

403+
/// Convert this collector into a unique context
404+
///
405+
/// The single-threaded implementation only allows a single context,
406+
/// so this method is nessicary to support it.
407+
pub fn into_context(self) -> CollectorContext<C> {
408+
unsafe { CollectorContext::register_root(&self) }
409+
}
410+
}
411+
impl<C: SyncCollector> CollectorRef<C> {
412+
387413
/// Create a new context bound to this collector
388414
///
389415
/// Warning: Only one collector should be created per thread.
390416
/// Doing otherwise can cause deadlocks/panics.
391-
#[cfg(feature = "sync")]
392417
pub fn create_context(&self) -> CollectorContext<C> {
393418
unsafe { CollectorContext::register_root(&self) }
394419
}
395-
/// Convert this collector into a unique context
396-
///
397-
/// The single-threaded implementation only allows a single context,
398-
/// so this method is nessicarry to support it.
399-
pub fn into_context(self) -> CollectorContext<C> {
400-
#[cfg(feature = "sync")]
401-
{ self.create_context() }
402-
#[cfg(not(feature = "sync"))]
403-
unsafe { CollectorContext::from_collector(&self) }
404-
}
405420
}
406421
impl<C: RawCollectorImpl> Drop for CollectorRef<C> {
407422
#[inline]

libs/context/src/handle.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::marker::PhantomData;
66
use std::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering};
77

88
use zerogc::{Trace, GcSafe, GcBrand, GcVisitor, NullTrace, TraceImmutable, GcHandleSystem, GcBindHandle};
9-
use crate::{Gc, WeakCollectorRef, CollectorId, CollectorContext, CollectorRef};
9+
use crate::{Gc, WeakCollectorRef, CollectorId, CollectorContext, CollectorRef, CollectionManager};
1010
use crate::collector::RawCollectorImpl;
1111

1212
const INITIAL_HANDLE_CAPACITY: usize = 64;
@@ -384,11 +384,8 @@ unsafe impl<T: GcSafe, C: RawHandleImpl> ::zerogc::GcHandle<T> for GcHandle<T, C
384384
type System = CollectorRef<C>;
385385
type Id = CollectorId<C>;
386386

387-
#[cfg(feature = "sync")]
388387
fn use_critical<R>(&self, func: impl FnOnce(&T) -> R) -> R {
389388
self.collector.ensure_valid(|collector| unsafe {
390-
// Used for 'prevent_collection' method
391-
use crate::SyncCollectorImpl;
392389
/*
393390
* This should be sufficient to ensure
394391
* the value won't be collected or relocated.
@@ -398,17 +395,13 @@ unsafe impl<T: GcSafe, C: RawHandleImpl> ::zerogc::GcHandle<T> for GcHandle<T, C
398395
* This is preferable to using `recursive_read`,
399396
* since that could starve writers (collectors).
400397
*/
401-
collector.as_ref().prevent_collection(|_state| {
398+
C::Manager::prevent_collection(collector.as_ref(), || {
402399
let value = self.inner.as_ref().value
403400
.load(Ordering::Acquire) as *mut T;
404401
func(&*value)
405402
})
406403
})
407404
}
408-
#[cfg(not(feature = "sync"))]
409-
fn use_critical<R>(&self, _func: impl FnOnce(&T) -> R) -> R {
410-
unimplemented!("critical sections for single-collector impl")
411-
}
412405
}
413406
unsafe impl<'new_gc, T, C> GcBindHandle<'new_gc, T> for GcHandle<T, C>
414407
where T: GcSafe, T: GcBrand<'new_gc, CollectorId<C>>,

libs/context/src/lib.rs

Lines changed: 17 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,16 @@ use std::fmt::{self, Debug, Formatter};
1010

1111
use zerogc::prelude::*;
1212

13-
#[cfg(feature = "sync")]
14-
mod sync;
15-
#[cfg(not(feature = "sync"))]
16-
mod nosync;
17-
#[cfg(feature = "sync")]
18-
pub use self::sync::*;
19-
#[cfg(not(feature = "sync"))]
20-
pub use self::nosync::*;
13+
pub mod state;
2114

2215
pub mod utils;
2316
pub mod collector;
2417
pub mod handle;
2518

2619
use crate::collector::{RawCollectorImpl};
27-
#[cfg(feature = "sync")]
28-
use crate::sync::SyncCollectorImpl;
29-
#[cfg(not(feature = "sync"))]
30-
use crate::nosync::NoSyncCollectorImpl;
3120

3221
pub use crate::collector::{WeakCollectorRef, CollectorRef, CollectorId};
22+
pub use crate::state::{CollectionManager, RawContext};
3323

3424
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
3525
pub enum ContextState {
@@ -100,52 +90,42 @@ impl ContextState {
10090
*/
10191
// TODO: Rename to remove 'Simple' from name
10292
pub struct CollectorContext<C: RawCollectorImpl> {
103-
raw: *mut RawContext<C>,
93+
raw: *mut C::RawContext,
10494
/// Whether we are the root context
10595
///
10696
/// Only the root actually owns the `Arc`
10797
/// and is responsible for dropping it
10898
root: bool
10999
}
110100
impl<C: RawCollectorImpl> CollectorContext<C> {
111-
#[cfg(not(feature = "sync"))]
112-
pub(crate) unsafe fn from_collector(collector: &CollectorRef<C>) -> Self {
113-
CollectorContext {
114-
raw: Box::into_raw(ManuallyDrop::into_inner(
115-
RawContext::from_collector(collector.clone_internal())
116-
)),
117-
root: true // We are the exclusive owner
118-
}
119-
}
120-
#[cfg(feature = "sync")]
121101
pub(crate) unsafe fn register_root(collector: &CollectorRef<C>) -> Self {
122102
CollectorContext {
123103
raw: Box::into_raw(ManuallyDrop::into_inner(
124-
RawContext::register_new(&collector)
104+
C::RawContext::register_new(&collector)
125105
)),
126106
root: true, // We are responsible for unregistering
127107
}
128108
}
129109
#[inline]
130110
pub fn collector(&self) -> &C {
131-
unsafe { &(*self.raw).collector.as_raw() }
111+
unsafe { &(*self.raw).collector() }
132112
}
133113
#[inline(always)]
134114
unsafe fn with_shadow_stack<R, T: Trace>(
135115
&self, value: *mut &mut T, func: impl FnOnce() -> R
136116
) -> R {
137-
let old_link = (*(*self.raw).shadow_stack.get()).last;
117+
let old_link = (*(*self.raw).shadow_stack_ptr()).last;
138118
let new_link = ShadowStackLink {
139119
element: C::create_dyn_pointer(value),
140120
prev: old_link
141121
};
142-
(*(*self.raw).shadow_stack.get()).last = &new_link;
122+
(*(*self.raw).shadow_stack_ptr()).last = &new_link;
143123
let result = func();
144124
debug_assert_eq!(
145-
(*(*self.raw).shadow_stack.get()).last,
125+
(*(*self.raw).shadow_stack_ptr()).last,
146126
&new_link
147127
);
148-
(*(*self.raw).shadow_stack.get()).last = new_link.prev;
128+
(*(*self.raw).shadow_stack_ptr()).last = new_link.prev;
149129
result
150130
}
151131
#[cold]
@@ -160,7 +140,7 @@ impl<C: RawCollectorImpl> Drop for CollectorContext<C> {
160140
fn drop(&mut self) {
161141
if self.root {
162142
unsafe {
163-
self.collector().free_context(self.raw);
143+
C::Manager::free_context(self.collector(), self.raw);
164144
}
165145
}
166146
}
@@ -171,25 +151,25 @@ unsafe impl<C: RawCollectorImpl> GcContext for CollectorContext<C> {
171151

172152
#[inline]
173153
unsafe fn basic_safepoint<T: Trace>(&mut self, value: &mut &mut T) {
174-
debug_assert_eq!((*self.raw).state.get(), ContextState::Active);
175-
if (*self.raw).collector.as_raw().should_collect() {
154+
debug_assert_eq!((*self.raw).state(), ContextState::Active);
155+
if (*self.raw).collector().should_collect() {
176156
self.trigger_basic_safepoint(value);
177157
}
178-
debug_assert_eq!((*self.raw).state.get(), ContextState::Active);
158+
debug_assert_eq!((*self.raw).state(), ContextState::Active);
179159
}
180160

181161
unsafe fn freeze(&mut self) {
182-
(*self.raw).collector.as_raw().manager().freeze_context(&*self.raw);
162+
(*self.raw).collector().manager().freeze_context(&*self.raw);
183163
}
184164

185165
unsafe fn unfreeze(&mut self) {
186-
(*self.raw).collector.as_raw().manager().unfreeze_context(&*self.raw);
166+
(*self.raw).collector().manager().unfreeze_context(&*self.raw);
187167
}
188168

189169
#[inline]
190170
unsafe fn recurse_context<T, F, R>(&self, value: &mut &mut T, func: F) -> R
191171
where T: Trace, F: for<'gc> FnOnce(&'gc mut Self, &'gc mut T) -> R {
192-
debug_assert_eq!((*self.raw).state.get(), ContextState::Active);
172+
debug_assert_eq!((*self.raw).state(), ContextState::Active);
193173
self.with_shadow_stack(value, || {
194174
let mut sub_context = ManuallyDrop::new(CollectorContext {
195175
/*
@@ -204,7 +184,7 @@ unsafe impl<C: RawCollectorImpl> GcContext for CollectorContext<C> {
204184
debug_assert!(!sub_context.root);
205185
// No need to run drop code on context.....
206186
std::mem::forget(sub_context);
207-
debug_assert_eq!((*self.raw).state.get(), ContextState::Active);
187+
debug_assert_eq!((*self.raw).state(), ContextState::Active);
208188
result
209189
})
210190
}

libs/context/src/state/mod.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use crate::collector::RawCollectorImpl;
2+
use crate::{ContextState, ShadowStack, CollectorRef};
3+
use std::mem::ManuallyDrop;
4+
use std::fmt::Debug;
5+
6+
/// The internal state of the collector
7+
///
8+
/// Has a thread-safe and thread-unsafe implementation.
9+
10+
#[cfg(feature = "sync")]
11+
pub mod sync;
12+
pub mod nosync;
13+
14+
/// Manages coordination of garbage collections
15+
pub unsafe trait CollectionManager<C>: self::sealed::Sealed
16+
where C: RawCollectorImpl<Manager=Self, RawContext=Self::Context> {
17+
type Context: RawContext<C>;
18+
fn new() -> Self;
19+
fn is_collecting(&self) -> bool;
20+
fn should_trigger_collection(&self) -> bool;
21+
unsafe fn freeze_context(&self, context: &Self::Context);
22+
unsafe fn unfreeze_context(&self, context: &Self::Context);
23+
24+
//
25+
// Extension methods on collector
26+
//
27+
28+
/// Attempt to prevent garbage collection for the duration of the closure
29+
///
30+
/// This method is **OPTIONAL** and will panic if unimplemented.
31+
fn prevent_collection<R>(collector: &C, func: impl FnOnce() -> R) -> R;
32+
33+
/// Free the specified context
34+
///
35+
/// ## Safety
36+
/// - Assumes the specified pointer is valid
37+
/// - Assumes there are no more outstanding borrows to values in the context
38+
unsafe fn free_context(collector: &C, context: *mut Self::Context);
39+
}
40+
41+
/// The underlying state of a context
42+
///
43+
/// Each context is bound to one and only one thread,
44+
/// even if the collector supports multi-threading.
45+
pub unsafe trait RawContext<C>: Debug + self::sealed::Sealed
46+
where C: RawCollectorImpl<RawContext=Self> {
47+
unsafe fn register_new(collector: &CollectorRef<C>) -> ManuallyDrop<Box<Self>>;
48+
/// Trigger a safepoint for this context.
49+
///
50+
/// This implicitly attempts a collection,
51+
/// potentially blocking until completion..
52+
///
53+
/// Undefined behavior if mutated during collection
54+
unsafe fn trigger_safepoint(&self);
55+
/// Borrow a reference to the shadow stack,
56+
/// assuming this context is valid (not active).
57+
///
58+
/// A context is valid if it is either frozen
59+
/// or paused at a safepoint.
60+
#[inline]
61+
unsafe fn assume_valid_shadow_stack(&self) -> &ShadowStack<C> {
62+
match self.state() {
63+
ContextState::Active => unreachable!("active context: {:?}", self),
64+
ContextState::SafePoint { .. } | ContextState::Frozen { .. } => {},
65+
}
66+
&*self.shadow_stack_ptr()
67+
}
68+
/// Get a pointer to the shadow stack
69+
fn shadow_stack_ptr(&self) -> *mut ShadowStack<C>;
70+
/// Get a reference to the collector,
71+
/// assuming that it's valid
72+
unsafe fn collector(&self) -> &C;
73+
fn state(&self) -> ContextState;
74+
}
75+
76+
mod sealed {
77+
pub trait Sealed {}
78+
}

0 commit comments

Comments
 (0)