|
| 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