-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcell.rs
More file actions
116 lines (113 loc) · 3.81 KB
/
cell.rs
File metadata and controls
116 lines (113 loc) · 3.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
//! Implements a [GcCell] to allow mutating values
//! inside garbage collected objects.
//!
//!
//! Normally garbage collected objects are immutable,
//! since their references are shared. It's typical
//! for collectors to want to trigger a write barrier
//! before writing to a field. All interior mutability
//! requires triggering appropriate write barriers,
//! which is unsafe.
//!
//! The `zerogc_derive` crate can generate setters
//! for fields that are wrapped in a [GcCell].
//! Just mark the field with `#[zerogc(mutable(public))]`
//! and it'll generate a safe wrapper.
use core::cell::Cell;
use crate::prelude::*;
/// A `Cell` pointing to a garbage collected object.
///
/// This only supports mutating `NullTrace` types directly,
/// because garbage collected pointers need write barriers.
///
/// However, other types can be mutated through auto-generated
/// accessors on the type (using the [GcDirectBarrier] trait).
#[derive(Default, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
#[repr(transparent)]
pub struct GcCell<T: Trace + Copy>(Cell<T>);
impl<T: Trace + Copy> GcCell<T> {
/// Create a new cell
#[inline]
pub fn new(value: T) -> Self {
GcCell(Cell::new(value))
}
/// Get a mutable reference to this cell's value
///
/// This is safe because the `&mut self`
/// guarentes exclusive access to the cell.
#[inline]
pub fn get_mut(&mut self) -> &mut T {
self.0.get_mut()
}
/// Get a pointer to this cell's conent
#[inline]
pub fn as_ptr(&self) -> *mut T {
self.0.as_ptr()
}
/// Get the current value of this cell
#[inline]
pub fn get(&self) -> T {
self.0.get()
}
}
impl<T: NullTrace + Copy> GcCell<T> {
/// Change the interior of this type
/// to the specified type
///
/// The type must be `NullTrace` because
/// garbage collected
/// types need write barriers.
#[inline]
pub fn set(&self, value: T) {
self.0.set(value)
}
}
/// Trigger a write barrier on the inner value
///
/// We are a 'direct' write barrier because `Value` is stored inline
unsafe impl<'gc, OwningRef, Value> crate::GcDirectBarrier<'gc, OwningRef> for GcCell<Value>
where Value: crate::GcDirectBarrier<'gc, OwningRef> + Copy {
#[inline]
unsafe fn write_barrier(
&self, owner: &OwningRef,
field_offset: usize
) {
self.get().write_barrier(owner, field_offset)
}
}
/// GcCell can only support mutating types that are `NullTrace`,
/// because garbage collected types need write barriers.
///
/// However, this is already enforced by the bounds of `GcCell::set`,
/// so we don't need to verify here.
/// In other words is possible to safely trace a `GcCell`
/// with a garbage collected type, as long as it is never mutated.
unsafe impl<T: Trace + Copy> Trace for GcCell<T> {
#[inline]
fn visit<V: GcVisitor>(&mut self, visitor: &mut V) -> Result<(), V::Err> {
visitor.visit(self.get_mut())
}
}
/// See Trace documentation on the safety of mutation
///
/// We require `NullTrace` in order to `set` our internals
unsafe impl<T: GcSafe + NullTrace + Copy> TraceImmutable for GcCell<T> {
#[inline]
fn visit_immutable<V: GcVisitor>(&self, visitor: &mut V) -> Result<(), <V as GcVisitor>::Err> {
let mut value = self.get();
visitor.visit(&mut value)?;
self.set(value);
Ok(())
}
#[inline]
fn visit_dyn_immutable(&self, visitor: &mut GcDynVisitor) -> Result<(), GcDynVisitError> {
self.visit_immutable(visitor)
}
}
unsafe impl<T: GcSafe + Copy + NullTrace> NullTrace for GcCell<T> {}
unsafe impl<T: GcSafe + Copy> GcSafe for GcCell<T> {}
unsafe impl<T: GcType + Copy> GcType for GcCell<T> {
const NEEDS_TRACE: bool = T::NEEDS_TRACE;
/// Since T is Copy, we shouldn't need to be dropped
const NEEDS_DROP: bool = false;
}