Skip to content

Commit ec81444

Browse files
committed
Make GcHandle use Generic Associated Types.
Supercedes and closes #27 Include some minor fixes to the lifetime tests for e1a3ce7
1 parent e1a3ce7 commit ec81444

File tree

9 files changed

+147
-90
lines changed

9 files changed

+147
-90
lines changed

libs/context/src/handle.rs

Lines changed: 67 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
//! Implementation of [::zerogc::GcHandle]
22
//!
33
//! Inspired by [Mono's Lock free Gc Handles](https://www.mono-project.com/news/2016/08/16/lock-free-gc-handles/)
4-
use core::ptr::{self, NonNull};
4+
use core::ptr::{self, NonNull, Pointee};
5+
use core::mem;
56
use core::marker::PhantomData;
67
use core::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering};
78

89
use alloc::boxed::Box;
910
use alloc::vec::Vec;
1011

11-
use zerogc::{Trace, GcSafe, GcRebrand, GcVisitor, NullTrace, TraceImmutable, GcHandleSystem, GcBindHandle};
12-
use crate::{Gc, WeakCollectorRef, CollectorId, CollectorContext, CollectorRef, CollectionManager};
12+
use zerogc::{GcRebrand, GcSafe, GcVisitor, HandleCollectorId, NullTrace, Trace, TraceImmutable, TrustedDrop};
13+
use crate::{Gc, WeakCollectorRef, CollectorId, CollectorRef, CollectionManager};
1314
use crate::collector::RawCollectorImpl;
1415

1516
const INITIAL_HANDLE_CAPACITY: usize = 64;
@@ -21,6 +22,10 @@ pub unsafe trait RawHandleImpl: RawCollectorImpl {
2122

2223
fn type_info_of<'gc, T: GcSafe<'gc, CollectorId<Self>>>() -> &'static Self::TypeInfo;
2324

25+
fn resolve_type_info<'gc, T: ?Sized + GcSafe<'gc, CollectorId<Self>>>(
26+
gc: Gc<'gc, T, CollectorId<Self>>
27+
) -> &'static Self::TypeInfo;
28+
2429
fn handle_list(&self) -> &GcHandleList<Self>;
2530
}
2631

@@ -397,24 +402,41 @@ impl<C: RawHandleImpl> GcRawHandle<C> {
397402
trace(value, type_info)
398403
}
399404
}
400-
pub struct GcHandle<T: GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> {
405+
pub struct GcHandle<T: ?Sized + GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> {
401406
inner: NonNull<GcRawHandle<C>>,
402407
collector: WeakCollectorRef<C>,
408+
/// The pointer metadata for the type.
409+
///
410+
/// Assumed to be immutable
411+
/// and not change
412+
/// SAFETY:
413+
/// 1. slices - Length never changes
414+
/// 2. dyn pointers - Never needs
415+
metadata: <T as Pointee>::Metadata,
403416
marker: PhantomData<*mut T>
404417
}
405-
impl<T: GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> GcHandle<T, C> {
418+
impl<T: ?Sized + GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> GcHandle<T, C> {
406419
#[inline]
407420
pub(crate) unsafe fn new(
408421
inner: NonNull<GcRawHandle<C>>,
409-
collector: WeakCollectorRef<C>
422+
collector: WeakCollectorRef<C>,
423+
metadata: <T as Pointee>::Metadata
410424
) -> Self {
411425
GcHandle {
412-
inner, collector,
426+
inner, collector, metadata,
413427
marker: PhantomData
414428
}
415429
}
430+
#[inline]
431+
unsafe fn assume_valid(&self) -> *mut T {
432+
ptr::from_raw_parts_mut(
433+
self.inner.as_ref().value
434+
.load(Ordering::Acquire) as *mut (),
435+
self.metadata
436+
)
437+
}
416438
}
417-
unsafe impl<T: GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> ::zerogc::GcHandle<T> for GcHandle<T, C> {
439+
unsafe impl<T: ?Sized + GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> ::zerogc::GcHandle<T> for GcHandle<T, C> {
418440
type System = CollectorRef<C>;
419441
type Id = CollectorId<C>;
420442

@@ -430,19 +452,17 @@ unsafe impl<T: GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> ::zerogc::GcHa
430452
* since that could starve writers (collectors).
431453
*/
432454
C::Manager::prevent_collection(collector.as_ref(), || {
433-
let value = self.inner.as_ref().value
434-
.load(Ordering::Acquire) as *mut T;
455+
let value = self.assume_valid();
435456
func(&*value)
436457
})
437458
})
438459
}
439-
}
440-
unsafe impl<'new_gc, T, C> GcBindHandle<'new_gc, T> for GcHandle<T, C>
441-
where T: GcSafe<'static, CollectorId<C>>, T: GcRebrand<'new_gc, CollectorId<C>>, T::Branded: Sized,
442-
T::Branded: GcSafe<'new_gc, CollectorId<C>>,
443-
C: RawHandleImpl {
444460
#[inline]
445-
fn bind_to(&self, context: &'new_gc CollectorContext<C>) -> Gc<'new_gc, T::Branded, CollectorId<C>> {
461+
fn bind_to<'new_gc>(
462+
&self,
463+
context: &'new_gc <Self::System as zerogc::GcSystem>::Context
464+
) -> Gc<'new_gc, <T as GcRebrand<'new_gc, Self::Id>>::Branded, Self::Id>
465+
where T: GcRebrand<'new_gc, Self::Id> {
446466
/*
447467
* We can safely assume the object will
448468
* be as valid as long as the context.
@@ -464,15 +484,20 @@ unsafe impl<'new_gc, T, C> GcBindHandle<'new_gc, T> for GcHandle<T, C>
464484
"Collectors mismatch"
465485
);
466486
let inner = self.inner.as_ref();
467-
let value = inner.value.load(Ordering::Acquire)
468-
as *mut T as *mut T::Branded;
487+
/*
488+
* NOTE: Can't use regular pointer-cast
489+
* because of potentially mismatched vtables.
490+
*/
491+
let value = crate::utils::transmute_mismatched::<
492+
*mut T,
493+
*mut T::Branded
494+
>(self.assume_valid());
469495
debug_assert!(!value.is_null());
470496
Gc::from_raw(NonNull::new_unchecked(value))
471497
}
472498
}
473-
474499
}
475-
unsafe impl<T: GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> Trace for GcHandle<T, C> {
500+
unsafe impl<T: ?Sized + GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> Trace for GcHandle<T, C> {
476501
/// See docs on reachability
477502
const NEEDS_TRACE: bool = false;
478503
const NEEDS_DROP: bool = true;
@@ -489,15 +514,18 @@ unsafe impl<T: GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> Trace for GcHa
489514
visitor.trace_gc(gc)
490515
}
491516
}
492-
unsafe impl<T: GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> TraceImmutable for GcHandle<T, C> {
517+
unsafe impl<T: ?Sized + GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> TraceImmutable for GcHandle<T, C> {
493518
#[inline(always)]
494519
fn trace_immutable<V>(&self, _visitor: &mut V) -> Result<(), V::Err>
495520
where V: GcVisitor {
496521
Ok(())
497522
}
498523
}
499-
unsafe impl<T: GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> NullTrace for GcHandle<T, C> {}
500-
impl<T: GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> Clone for GcHandle<T, C> {
524+
unsafe impl<T: ?Sized + GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> NullTrace for GcHandle<T, C> {}
525+
unsafe impl<'gc, T: ?Sized + GcSafe<'static, CollectorId<C>>, C: RawHandleImpl>
526+
GcSafe<'gc, CollectorId<C>> for GcHandle<T, C> {}
527+
unsafe impl<T: ?Sized + GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> TrustedDrop for GcHandle<T, C> {}
528+
impl<T: ?Sized + GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> Clone for GcHandle<T, C> {
501529
fn clone(&self) -> Self {
502530
// NOTE: Dead collector -> invalid handle
503531
let collector = self.collector
@@ -536,11 +564,12 @@ impl<T: GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> Clone for GcHandle<T,
536564
}
537565
GcHandle {
538566
inner: self.inner,
567+
metadata: self.metadata,
539568
collector, marker: PhantomData
540569
}
541570
}
542571
}
543-
impl<T: GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> Drop for GcHandle<T, C> {
572+
impl<T: ?Sized + GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> Drop for GcHandle<T, C> {
544573
fn drop(&mut self) {
545574
self.collector.try_ensure_valid(|id| {
546575
let collector = match id {
@@ -594,24 +623,23 @@ impl<T: GcSafe<'static, CollectorId<C>>, C: RawHandleImpl> Drop for GcHandle<T,
594623
/// This is the same reason that `Arc<T>: Send` requires `T: Sync`
595624
///
596625
/// Requires that the collector is thread-safe.
597-
unsafe impl<T: GcSafe<'static, CollectorId<C>> + Sync, C: RawHandleImpl + Sync> Send for GcHandle<T, C> {}
626+
unsafe impl<T: ?Sized + GcSafe<'static, CollectorId<C>> + Sync, C: RawHandleImpl + Sync> Send for GcHandle<T, C> {}
598627

599628
/// If the underlying type is Sync,
600629
/// it's safe to share garbage collected references between threads.
601630
///
602631
/// Requires that the collector is thread-safe.
603-
unsafe impl<T: GcSafe<'static, CollectorId<C>> + Sync, C: RawHandleImpl + Sync> Sync for GcHandle<T, C> {}
632+
unsafe impl<T: ?Sized + GcSafe<'static, CollectorId<C>> + Sync, C: RawHandleImpl + Sync> Sync for GcHandle<T, C> {}
604633

605634
/// We support handles
606-
unsafe impl<'gc, T, C> GcHandleSystem<'gc, T> for CollectorRef<C>
607-
where C: RawHandleImpl,
608-
T: GcSafe<'gc, CollectorId<C>>,
609-
T: GcRebrand<'static, CollectorId<C>>,
610-
T::Branded: GcSafe<'static, CollectorId<C>> + Sized {
611-
type Handle = GcHandle<T::Branded, C>;
635+
unsafe impl<C> HandleCollectorId for CollectorId<C>
636+
where C: RawHandleImpl {
637+
type Handle<T: GcSafe<'static, Self> + ?Sized> = GcHandle<T, C>;
638+
612639

613640
#[inline]
614-
fn create_handle(gc: Gc<'gc, T, CollectorId<C>>) -> Self::Handle {
641+
fn create_handle<'gc, T>(gc: Gc<'gc, T, CollectorId<C>>) -> Self::Handle<T::Branded>
642+
where T: ?Sized + GcSafe<'gc, Self> + GcRebrand<'static, Self>, T::Branded: GcSafe<'static, Self> {
615643
unsafe {
616644
let collector = gc.collector_id();
617645
let value = gc.as_raw_ptr();
@@ -623,14 +651,18 @@ unsafe impl<'gc, T, C> GcHandleSystem<'gc, T> for CollectorRef<C>
623651
* the handle!!!
624652
*/
625653
raw.type_info.store(
626-
C::type_info_of::<T>()
654+
C::resolve_type_info(gc)
627655
as *const C::TypeInfo
628656
as *mut C::TypeInfo,
629657
Ordering::Release
630658
);
631659
raw.refcnt.store(1, Ordering::Release);
632660
let weak_collector = collector.weak_ref();
633-
GcHandle::new(NonNull::from(raw), weak_collector)
661+
let metadata = crate::utils::transmute_mismatched::<
662+
<T as Pointee>::Metadata,
663+
<T::Branded as Pointee>::Metadata
664+
>(ptr::metadata(value));
665+
GcHandle::new(NonNull::from(raw), weak_collector, metadata)
634666
}
635667
}
636668
}

libs/context/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
const_fn_trait_bound, // So generics + const fn are unstable, huh?
55
generic_associated_types, // Finally!
66
const_trait_impl,
7+
ptr_metadata
78
)]
89
#![allow(
910
clippy::missing_safety_doc, // Entirely internal code

libs/context/src/utils.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//!
33
//! Also used by some collector implementations.
44
use core::fmt::{self, Debug, Formatter, Display};
5+
use core::mem;
56
#[cfg(not(feature = "sync"))]
67
use core::cell::Cell;
78

@@ -18,6 +19,24 @@ macro_rules! field_offset {
1819
}};
1920
}
2021

22+
23+
/// Transmute between two types,
24+
/// without verifying that there sizes are the same
25+
///
26+
/// ## Safety
27+
/// This function has undefined behavior if `T` and `U`
28+
/// have different sizes.
29+
///
30+
/// It also has undefined behavior whenever [mem::transmute] has
31+
/// undefined behavior.
32+
#[inline]
33+
pub unsafe fn transmute_mismatched<T, U>(src: T) -> U {
34+
// NOTE: This assert has zero cost when monomorphized
35+
assert_eq!(mem::size_of::<T>(), mem::size_of::<U>());
36+
let d = mem::ManuallyDrop::new(src);
37+
mem::transmute_copy::<T, U>(&*d)
38+
}
39+
2140
#[cfg(feature = "sync")]
2241
pub type AtomicCell<T> = ::crossbeam_utils::atomic::AtomicCell<T>;
2342
/// Fallback `AtomicCell` implementation when we actually

libs/derive/tests/basic.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
)]
44
use zerogc::{Gc, CollectorId, Trace, GcSafe, NullTrace, epsilon::{self, EpsilonCollectorId}};
55

6-
use zerogc_derive::{Trace, NullTrace};
76
use zerogc::cell::GcCell;
87
use std::marker::PhantomData;
98
use std::fmt::Debug;

libs/simple/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,16 @@ unsafe impl RawHandleImpl for RawSimpleCollector {
230230
<T as StaticGcType>::STATIC_TYPE
231231
}
232232

233+
234+
#[inline]
235+
fn resolve_type_info<'gc, T: ?Sized + GcSafe<'gc, CollectorId>>(
236+
gc: zerogc::Gc<'gc, T, CollectorId>
237+
) -> &'static Self::TypeInfo {
238+
unsafe {
239+
(*GcHeader::from_value_ptr(gc.as_raw_ptr())).type_info
240+
}
241+
}
242+
233243
#[inline]
234244
fn handle_list(&self) -> &GcHandleList<Self> {
235245
&self.handle_list

libs/simple/tests/trait_objects.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
use core::cell::Cell;
33

44
use zerogc::{Trace, safepoint, DynTrace, trait_object_trace, GcSimpleAlloc};
5-
use zerogc_derive::Trace;
65

76
use zerogc_simple::{SimpleCollector, Gc, CollectorId as SimpleCollectorId, GcConfig};
87
use slog::Logger;

0 commit comments

Comments
 (0)