cell_gc/heap.rs
1//! GC heap allocator.
2//!
3//! ### Safety
4//!
5//! First, a few general observations about safety in Rust.
6//!
7//! * If a `mut` reference to a value exists, and other references to the value
8//! also exist, we can definitely crash.
9//!
10//! One way it can happen is that the `mut` value is or contains an
11//! `enum`. Using the other reference, we can borrow a refrence to a
12//! current field of the `enum`. Then, using the `mut` reference, we can
13//! assign some other variant to the `enum`, invalidating the reference.
14//! Rust does not detect that the reference is invalid, so when we use it,
15//! we are accessing gibberish.
16//!
17//! Another way is if a callback is called while we're in the middle of
18//! mutating a value, while it's in an invalid state. When you read
19//! "callback", think of `deref` and `drop` methods, which are called
20//! implicitly all over the place. These callbacks are normally safe,
21//! because they simply can't have a reference to the value that's in an
22//! intermediate state. But if another reference exists, it might be used
23//! to try to read from the half-mutated value.
24//!
25//! * If a data structure contains two paths to a value, under Rust's usual
26//! rules, you can get two `mut` references to that value.
27//!
28//! This is why you tend to build ownership trees in Rust and it's why GC
29//! is particularly challenging in Rust: GCs build an arbitrary graph of
30//! values and references.
31//!
32//! This GC takes the following approach to ensure safety.
33//!
34//! * Minimize access to values stored in the heap. For the most part,
35//! application code *never* sees a direct reference to any value that is
36//! physically stored someplace where it's subject to GC.
37//!
38//! * Minimize the times when direct references to in-heap values exist at
39//! all, and during these operations, prevent control from escaping to
40//! arbitrary application code.
41//!
42//! * Ensure that when any direct references to in-heap values exist, they
43//! obey Rust's rules: for any given value, either only non-`mut`
44//! references, or at most one `mut` reference, exists at a time.
45//!
46//! Thus we are particularly keen to avoid the possibility of "reentering" the
47//! heap, creating new references to in-heap values while others already exist.
48//!
49//! References to heap values therefore exist only during the following
50//! operations:
51//!
52//! * Allocation - That is, moving values into the heap. This is safe because
53//! it never triggers any user code at all while heap references exist.
54//!
55//! * - Heap reads and writes - The only way to do these is via macro-generated
56//! accessors which do not expose references. Reads call `from_heap()` on
57//! in-heap values, which is dangerous because `from_heap()` receives a
58//! direct reference. Writes call `drop()`, which is even more dangerous:
59//! (1) it receives a direct `mut` reference; and (2) it leaves in-heap
60//! values uninitialized.
61//!
62//! * GC marking - The code for this is all macro-generated.
63//!
64//! * GC sweeping - This calls `drop()`, which is dangerous for the reasons
65//! noted above.
66//!
67//! To make this scheme safe, `from_heap()` and `drop()` must be tightly controlled.
68//! `from_heap()` is therefore in an unsafe trait; users are expected to use
69//! the `gc_heap_type!` to autogenerate instances.
70//!
71//! However, **we leave it up to the user to exercise care with `drop()`.**
72//! We suggest *never* implementing `Drop` for a heap type. If you must,
73//! avoid reading pointer fields while dropping, and avoid calling into
74//! arbitrary code.
75
76use std::cell::RefCell;
77use std::collections::HashMap;
78use std::marker::PhantomData;
79use std::ptr;
80use traits::IntoHeapAllocation;
81use pages::{heap_type_id, TypedPage, PageSet, PageSetRef};
82use ptr::{Pointer, UntypedPointer};
83use gcref::GcRef;
84use marking::{mark, MarkingTracer};
85
86pub struct Heap {
87 page_sets: HashMap<usize, PageSet>,
88 pins: RefCell<HashMap<UntypedPointer, usize>>,
89 marking_tracer: Option<MarkingTracer>,
90}
91
92// What does this do? You'll never guess!
93pub type HeapSessionId<'h> = PhantomData<::std::cell::Cell<&'h mut ()>>;
94
95pub struct HeapSession<'h> {
96 id: HeapSessionId<'h>,
97 heap: &'h mut Heap,
98}
99
100/// Create a heap, pass it to a callback, then destroy the heap.
101///
102/// The heap's lifetime is directly tied to this function call, for safety. (So
103/// the API is a little wonky --- but how many heaps were you planning on
104/// creating?)
105pub fn with_heap<R, F>(f: F) -> R
106where
107 F: for<'h> FnOnce(&mut HeapSession<'h>) -> R,
108{
109 Heap::new().enter(f)
110}
111
112impl Heap {
113 /// Create a new, empty heap.
114 pub fn new() -> Heap {
115 Heap {
116 page_sets: HashMap::new(),
117 pins: RefCell::new(HashMap::new()),
118 marking_tracer: Some(MarkingTracer::default()),
119 }
120 }
121
122 /// Run some code using this Heap.
123 ///
124 /// # Example
125 ///
126 /// use cell_gc::{Heap, GCLeaf};
127 ///
128 /// let mut heap = Heap::new();
129 /// heap.enter(|hs| {
130 /// // ... hs.alloc(MyHeapStruct { ... }) ...
131 /// # hs.force_gc();
132 /// });
133 ///
134 pub fn enter<R, F>(&mut self, f: F) -> R
135 where
136 F: for<'h> FnOnce(&mut HeapSession<'h>) -> R,
137 {
138 f(&mut HeapSession::new(self))
139 }
140
141 /// Add the value `*p` to the root set, protecting it from GC.
142 ///
143 /// A value that has been pinned *n* times stays in the root set
144 /// until it has been unpinned *n* times.
145 ///
146 /// # Safety
147 ///
148 /// `p` must point to a live allocation of type `T` in this heap.
149 pub unsafe fn pin<'h, T: IntoHeapAllocation<'h>>(&self, p: Pointer<T::In>) {
150 let mut pins = self.pins.borrow_mut();
151 let entry = pins.entry(p.into()).or_insert(0);
152 *entry += 1;
153 }
154
155 /// Unpin a heap-allocated value (see `pin`).
156 ///
157 /// # Safety
158 ///
159 /// `p` must point to a pinned allocation of type `T` in this heap.
160 pub unsafe fn unpin<'h, T: IntoHeapAllocation<'h>>(&self, p: Pointer<T::In>) {
161 let mut pins = self.pins.borrow_mut();
162 let done = {
163 let entry = pins.entry(p.into()).or_insert(0);
164 assert!(*entry != 0);
165 *entry -= 1;
166 *entry == 0
167 };
168 if done {
169 pins.remove(&p.into());
170 }
171 }
172
173 /// Call the given function on each pinned root.
174 pub fn each_pin<F>(&self, mut f: F)
175 where
176 F: FnMut(UntypedPointer),
177 {
178 for (&ptr, _) in self.pins.borrow().iter() {
179 f(ptr);
180 }
181 }
182
183 pub unsafe fn from_allocation<'h, T: IntoHeapAllocation<'h>>(ptr: Pointer<T::In>) -> *const Heap {
184 (*TypedPage::find(ptr)).header.heap
185 }
186
187 pub unsafe fn get_mark_bit<'h, T: IntoHeapAllocation<'h>>(ptr: Pointer<T::In>) -> bool {
188 (*TypedPage::find(ptr)).get_mark_bit(ptr)
189 }
190
191 pub unsafe fn set_mark_bit<'h, T: IntoHeapAllocation<'h>>(ptr: Pointer<T::In>) {
192 (*TypedPage::find(ptr)).set_mark_bit(ptr);
193 }
194
195 fn take_marking_tracer(&mut self) -> MarkingTracer {
196 self.marking_tracer.take().unwrap()
197 }
198
199 fn replace_marking_tracer(&mut self, tracer: MarkingTracer) {
200 assert!(self.marking_tracer.is_none());
201 assert!(tracer.mark_stack_is_empty());
202 self.marking_tracer = Some(tracer);
203 }
204
205 /// Run the given function with the marking tracer.
206 ///
207 /// The marking tracer is taken out of the heap and replaced again so we can
208 /// have two independent borrows of the heap and the marking tracer and the
209 /// same time.
210 pub(crate) fn with_marking_tracer<F, O>(&mut self, mut f: F) -> O
211 where
212 F: FnMut(&mut Self, &mut MarkingTracer) -> O,
213 {
214 let mut tracer = self.take_marking_tracer();
215 let retval = f(self, &mut tracer);
216 self.replace_marking_tracer(tracer);
217 retval
218 }
219
220 /// Clear all mark bits in preparation for GC.
221 ///
222 /// # Safety
223 ///
224 /// This must be called only at the beginning of a GC cycle.
225 pub(crate) unsafe fn clear_mark_bits(&mut self) {
226 for page_set in self.page_sets.values_mut() {
227 page_set.clear_mark_bits();
228 }
229 }
230
231 fn gc(&mut self) {
232 mark(self);
233
234 // sweep phase
235 for page_set in self.page_sets.values_mut() {
236 unsafe {
237 page_set.sweep();
238 }
239 }
240 }
241}
242
243impl Drop for Heap {
244 fn drop(&mut self) {
245 // Perform a final GC to call destructors on any remaining allocations.
246 assert!(self.pins.borrow().is_empty());
247 self.gc();
248
249 for page_set in self.page_sets.values() {
250 page_set.assert_no_allocations();
251 }
252 }
253}
254
255impl<'h> HeapSession<'h> {
256 fn new(heap: &'h mut Heap) -> HeapSession<'h> {
257 HeapSession {
258 id: PhantomData,
259 heap,
260 }
261 }
262
263 fn get_page_set<'a, T: IntoHeapAllocation<'h> + 'a>(&'a mut self) -> PageSetRef<'a, 'h, T> {
264 let key = heap_type_id::<T>();
265 let heap: *mut Heap = self.heap;
266 let page_ref =
267 self.heap
268 .page_sets
269 .entry(key)
270 .or_insert_with(|| unsafe { PageSet::new(heap) });
271 unsafe { page_ref.unchecked_downcast_mut() }
272 }
273
274 pub fn set_page_limit<T: IntoHeapAllocation<'h>>(&mut self, limit: Option<usize>) {
275 self.get_page_set::<T>().set_page_limit(limit);
276 }
277
278 pub fn try_alloc<T: IntoHeapAllocation<'h>>(&mut self, value: T) -> Option<T::Ref> {
279 // For now, this is done very early, so that if it panics, the heap is
280 // left in an OK state. Better wrapping of raw pointers would make it
281 // possible to do this later, closer to the `ptr::write()` call. (And
282 // the compiler might optimize away this temporary if we do it that
283 // way.) Looking forward to placement new!
284 let u = value.into_heap();
285 unsafe {
286 let alloc = self.get_page_set::<T>().try_alloc();
287 alloc
288 .or_else(|| {
289 self.heap.gc();
290 self.get_page_set::<T>().try_alloc()
291 })
292 .map(move |p| {
293 ptr::write(p.as_raw() as *mut _, u);
294 T::wrap_gcref(GcRef::new(p))
295 })
296 }
297 }
298
299 pub fn alloc<T: IntoHeapAllocation<'h>>(&mut self, value: T) -> T::Ref {
300 self.try_alloc(value)
301 .expect("out of memory (gc did not collect anything)")
302 }
303
304 pub fn force_gc(&mut self) {
305 self.heap.gc();
306 }
307}