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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
use std::{
    cell::Cell,
    marker::PhantomData,
    mem::ManuallyDrop,
    num::NonZeroUsize,
    ops::Deref,
    ptr::NonNull,
};

pub mod collector;
pub mod traceable;

pub use collector::{
    collect,
    collect_full,
    collect_with_options,
    GcVisitor,
};
use collector::{
    Node,
    OLD_GEN,
    YOUNG_GEN,
};
pub use traceable::Traceable;

use crate::Status;

/// A cycle-collected reference-counted smart pointer.
/// `Gc<T>` provides shared ownership of a value of type `T`, allocated on the heap. Cloining it
/// will produce a new `Gc` instance which pints to the same allocation as the original `Gc`.
///
/// Unlike [`std::rc::Rc`], `Gc` pointers may refer to each other in a way that creates cycles
/// arbitrarily without causing leaks, provided that the program calls [`collect`] to collect those
/// cycles.
///
/// It's important to note a few things about collection:
/// - The default [`collect`] implementation only performs cycle-tracing collection if a node has
///   been in waiting for collection for a while. This is so that we don't pay the cost of tracing
///   for acyclic nodes which may be marked for collection due to the number of outstanding
///   references, but don't actually participate in a cycle. You may use [`collect_full`] to force
///   tracing collection of all pending nodes if you prefer.
/// - Dropping of data is deferred until a call to [`collect`] or [`collect_full`] is made, and the
///   collector is able to determine that the `Gc` is actually dead. The collector guarantees that
///   (in the absence of bugs) the [`Drop`] implementation for your type will be executed if it is
///   determined to be dead, but cannot provide any guarantees on **when** it will be executed.
///     - [`collect`] has weak guarantees even in the presence of acyclic pointers - if a node
///       containing your type is acyclic, but has N strong references, it may take up to `min(N,
///       `[`CollectionOptions::old_gen_threshold`]` + 1)` calls to `collect` for the value to be
///       cleaned up even if all parents are dropped.
///     - The collector does guarantee that if [`collect_full`] is used, the `Drop` implementation
///       for your data will have been executed by the time `collect_full` completes if the value is
///       dead.
/// - The collector does not make any guarantees about the relative order of drops for dead nodes.
///   Even if the order appears stable, you **may not** rely on it for program correctness.
#[derive(Debug)]
pub struct Gc<T: Traceable + 'static> {
    ptr: NonNull<Inner<T>>,
    _t: PhantomData<T>,
}

impl<T> Traceable for Gc<T>
where
    T: Traceable + 'static,
{
    fn visit_children(&self, visitor: &mut GcVisitor) {
        visitor.visit_node(self)
    }
}

impl<T> Gc<T>
where
    T: Traceable + 'static,
{
    /// Construct a new Gc containing `data` which will be automatically cleaned up with it is no
    /// longer reachable, even in the presence of cyclical references.
    pub fn new(data: T) -> Self {
        Self {
            ptr: Box::leak(Box::new(Inner {
                strong: Cell::new(NonZeroUsize::new(1).unwrap()),
                status: Cell::new(Status::Live),
                buffered: Cell::new(false),
                data: ManuallyDrop::new(data),
            }))
            .into(),
            _t: PhantomData,
        }
    }
}

impl<T> Gc<T>
where
    T: Traceable + 'static,
{
    /// Retrieve the current number of strong references outstanding.
    pub fn strong_count(this: &Gc<T>) -> usize {
        this.get_inner().strong_count().get()
    }

    /// Returns true if the data in Self hasn't been dropped yet. This will almost always be the
    /// case unless it is called inside of a Drop implementation or if there is a bug present in a
    /// Traceable impl.
    pub fn is_live(this: &Gc<T>) -> bool {
        this.get_inner().is_live()
    }

    /// Get a reference into this Gc.
    ///
    /// Returns None if the pointer is dead, as the data contained in this
    /// pointer is possibly cleaned up.
    pub fn get(this: &Gc<T>) -> Option<&T> {
        if this.get_inner().is_live() {
            Some(this.as_ref())
        } else {
            None
        }
    }

    /// Try to get a mutable referenced into this Gc. Will return None if there are oustanding
    /// strong references to this, or if the pointer is dead.
    pub fn get_mut(this: &mut Gc<T>) -> Option<&mut T> {
        if this.get_inner().strong_count() == NonZeroUsize::new(1).unwrap() && Self::is_live(this) {
            // SAFETY: We are the only reference and our data is still alive.
            unsafe { Some(Self::get_mut_unchecked(this)) }
        } else {
            None
        }
    }

    /// Gets a reference into this Gc without checking if pointer is still
    /// alive.
    ///
    /// # Safety
    /// This gc pointer must not have had its inner data dropped yet
    pub unsafe fn get_unchecked(this: &Gc<T>) -> &T {
        &this.ptr.as_ref().data
    }

    /// Gets a reference into this Gc without checking if pointer is still
    /// alive or has unique access
    ///
    /// # Safety
    /// - This gc pointer must not have had its inner data dropped yet.
    /// - There must be no other aliasing references to the inner data.
    pub unsafe fn get_mut_unchecked(this: &mut Gc<T>) -> &mut T {
        &mut this.ptr.as_mut().data
    }

    /// Returns true if both this & other point to the same allocation.
    pub fn ptr_eq(this: &Gc<T>, other: &Gc<T>) -> bool {
        std::ptr::eq(this.ptr.as_ptr(), other.ptr.as_ptr())
    }
}

impl<T> Gc<T>
where
    T: Traceable + 'static,
{
    fn node(&self) -> Node {
        Node {
            inner_ptr: self.coerce_inner(),
        }
    }

    fn get_inner(&self) -> &Inner<T> {
        // SAFETY: Barring bugs in the collector, self.ptr is not dangling and not null.
        unsafe { self.ptr.as_ref() }
    }

    fn coerce_inner(&self) -> NonNull<Inner<dyn Traceable>> {
        // SAFETY: Barring bugs in the collector, self.ptr is not dangling and not null.
        unsafe { NonNull::new_unchecked(self.ptr.as_ptr() as *mut Inner<dyn Traceable>) }
    }
}

impl<T> Clone for Gc<T>
where
    T: Traceable + 'static,
{
    fn clone(&self) -> Self {
        let inner = self.get_inner();
        inner.mark_live();

        inner.increment_strong_count();

        Self {
            ptr: self.ptr,
            _t: PhantomData,
        }
    }
}

impl<T: Traceable + 'static> Drop for Gc<T> {
    fn drop(&mut self) {
        unsafe {
            if self.ptr.as_ref().strong_count() == NonZeroUsize::new(1).unwrap() {
                // This is the last remaining strong ref to this value so we can
                // safely drop the inner value and de-allocate the container.

                // The collector is the only code which marks node as zombies, and it does so
                // _after_ it has finished processing the node. We can safely de-allocate here
                // without causing a double free.
                //
                // Note that we _must not_ attempt to drop the data, since it was already
                // dropped when the node was marked dead.
                Inner::zombie_safe_drop_data_dealloc(self.ptr);
            } else if self.ptr.as_ref().status.get() == Status::Dead {
                self.ptr.as_ref().decrement_strong_count();
            } else {
                // Indicate that it might be the root of a cycle.
                self.ptr.as_ref().mark_ref_dropped();
                if self.ptr.as_ref().buffered.replace(true) {
                    // Already tracked for possible cyclical deletion, we can drop a strong
                    // reference here without fear of early deletion.
                    self.ptr.as_ref().decrement_strong_count();
                } else {
                    // Convert it to an unsized generic type
                    let ptr = self.coerce_inner();
                    // It's possible that this will turn out to be a member of a cycle, so we need
                    // to add it to the list of items the collector tracks.
                    YOUNG_GEN.with(|gen| {
                        debug_assert!(OLD_GEN.with(|gen| !gen.borrow().contains(&ptr)));

                        let mut gen = gen.borrow_mut();
                        debug_assert!(!gen.contains_key(&ptr));

                        // Because we haven't decremented the strong count yet, we can safely just
                        // add this to the young gen without fear of early
                        // deletion.
                        gen.insert(ptr, 0);
                    });
                }
            }
        }
    }
}

impl<T: Traceable + 'static> PartialEq for Gc<T>
where
    T: PartialEq,
{
    fn eq(&self, other: &Self) -> bool {
        self.as_ref() == other.as_ref()
    }
}

impl<T: Traceable + 'static> Eq for Gc<T> where T: Eq {}

impl<T: Traceable + 'static> PartialOrd for Gc<T>
where
    T: PartialOrd,
{
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.as_ref().partial_cmp(other.as_ref())
    }
}

impl<T: Traceable + 'static> Ord for Gc<T>
where
    T: Ord,
{
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.as_ref().cmp(other.as_ref())
    }
}

impl<T> AsRef<T> for Gc<T>
where
    T: Traceable + 'static,
{
    fn as_ref(&self) -> &T {
        &**self
    }
}

impl<T> Deref for Gc<T>
where
    T: Traceable + 'static,
{
    type Target = T;

    fn deref(&self) -> &Self::Target {
        assert!(Self::is_live(self));

        // SAFETY: We asserted that self is live.
        unsafe { Self::get_unchecked(self) }
    }
}

struct Inner<T: Traceable + ?Sized> {
    strong: Cell<NonZeroUsize>,
    status: Cell<Status>,
    buffered: Cell<bool>,
    data: ManuallyDrop<T>,
}

impl<T> std::fmt::Debug for Inner<T>
where
    T: Traceable + ?Sized,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Inner")
            .field("strong", &self.strong)
            .field("status", &self.status)
            .field("buffered", &self.buffered)
            .field(
                "data",
                &format!(
                    "{} @ {:?}",
                    std::any::type_name::<T>(),
                    (&*self.data as *const T)
                ),
            )
            .finish()
    }
}

impl<T> Inner<T>
where
    T: Traceable + ?Sized,
{
    /// # Safety:
    /// - ptr.data must not have been dropped.
    /// - ptr.data must not have any aliasing references.
    unsafe fn drop_data(mut ptr: NonNull<Self>) {
        ManuallyDrop::drop(&mut ptr.as_mut().data)
    }

    /// # Safety:
    /// - ptr must not have been deallocated.
    /// - ptr must have been created by Box::into_raw/leak
    unsafe fn dealloc(ptr: NonNull<Self>) {
        debug_assert_eq!(ptr.as_ref().strong.get().get(), 1);

        let boxed = Box::from_raw(ptr.as_ptr());
        drop(boxed)
    }

    unsafe fn zombie_safe_drop_data_dealloc(ptr: NonNull<Self>) {
        if ptr.as_ref().status.get() != Status::Dead {
            Self::drop_data(ptr)
        }

        Self::dealloc(ptr);
    }

    fn strong_count(&self) -> NonZeroUsize {
        self.strong.get()
    }

    fn increment_strong_count(&self) {
        self.strong.set(
            self.strong
                .get()
                .get()
                .checked_add(1)
                .and_then(NonZeroUsize::new)
                .unwrap(),
        )
    }

    /// # Safety:
    /// - The caller of this function must not attempt to access self after calling this function.
    unsafe fn decrement_strong_count(&self) {
        self.strong
            .set(NonZeroUsize::new(self.strong.get().get() - 1).unwrap())
    }

    /// # Safety:
    /// - The caller of this function must not attempt to access self after calling this function.
    unsafe fn unbuffer_from_collector(&self) {
        self.buffered.set(false);
        self.strong
            .set(NonZeroUsize::new(self.strong.get().get() - 1).unwrap())
    }

    fn is_live(&self) -> bool {
        matches!(
            self.status.get(),
            Status::Live | Status::RecentlyDecremented
        )
    }

    fn mark_live(&self) {
        if self.status.get() == Status::RecentlyDecremented {
            self.status.set(Status::Live);
        }
    }

    fn mark_dead(&self) {
        self.status.set(Status::Dead);
    }

    fn mark_ref_dropped(&self) {
        if self.status.get() == Status::Live {
            self.status.set(Status::RecentlyDecremented);
        }
    }
}