dumpster/sync/mod.rs
1/*
2 dumpster, a cycle-tracking garbage collector for Rust. Copyright (C) 2023 Clayton Ramsey.
3
4 This Source Code Form is subject to the terms of the Mozilla Public
5 License, v. 2.0. If a copy of the MPL was not distributed with this
6 file, You can obtain one at http://mozilla.org/MPL/2.0/.
7*/
8
9//! Thread-safe shared garbage collection.
10//!
11//! Most users of this module will be interested in using [`Gc`] directly out of the box - this will
12//! just work.
13//! Those with more particular needs (such as benchmarking) should turn toward
14//! [`set_collect_condition`] in order to tune exactly when the garbage collector does cleanups.
15//!
16//! # Examples
17//!
18//! ```
19//! use dumpster::sync::Gc;
20//!
21//! let my_gc = Gc::new(100);
22//! let other_gc = my_gc.clone();
23//!
24//! drop(my_gc);
25//! drop(other_gc);
26//!
27//! // contents of the Gc are automatically freed
28//! ```
29
30mod cell;
31mod collect;
32#[cfg(loom)]
33mod loom_ext;
34#[cfg(all(loom, test))]
35mod loom_tests;
36#[cfg(all(test, not(loom)))]
37mod tests;
38
39use std::{
40 alloc::{dealloc, handle_alloc_error, Layout},
41 any::TypeId,
42 borrow::{Borrow, Cow},
43 fmt::Debug,
44 mem::{self, ManuallyDrop, MaybeUninit},
45 num::NonZeroUsize,
46 ops::Deref,
47 ptr::{self, addr_of, addr_of_mut, copy_nonoverlapping, drop_in_place, NonNull},
48 slice,
49};
50
51#[cfg(loom)]
52use loom::{
53 lazy_static,
54 sync::atomic::{fence, AtomicUsize, Ordering},
55};
56#[cfg(not(loom))]
57use std::sync::atomic::{fence, AtomicUsize, Ordering};
58
59use crate::{
60 contains_gcs, panic_deref_of_collected_object,
61 ptr::Nullable,
62 sync::{
63 cell::UCell,
64 collect::{Dfs, PrepareForDestruction},
65 },
66 Trace, TraceWith, Visitor,
67};
68
69use self::collect::{
70 collect_all_await, mark_clean, mark_dirty, n_gcs_dropped, n_gcs_existing, notify_created_gc,
71 notify_dropped_gc,
72};
73
74/// A soft limit on the amount of references that may be made to a `Gc`.
75///
76/// Going above this limit will abort your program (although not
77/// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references.
78///
79/// See comment in `Gc::clone`.
80const MAX_STRONG_COUNT: usize = (isize::MAX) as usize;
81
82/// Allows tracing with all sync visitors.
83#[expect(private_bounds)]
84pub(crate) trait TraceSync:
85 for<'a> TraceWith<Dfs<'a>> + for<'a> TraceWith<PrepareForDestruction<'a>> + TraceWith<Rehydrate>
86{
87}
88
89impl<T> TraceSync for T where
90 T: ?Sized
91 + for<'a> TraceWith<Dfs<'a>>
92 + for<'a> TraceWith<PrepareForDestruction<'a>>
93 + TraceWith<Rehydrate>
94{
95}
96
97/// A thread-safe garbage-collected pointer.
98///
99/// This pointer can be duplicated and then shared across threads.
100/// Garbage collection is performed concurrently.
101///
102/// # Examples
103///
104/// ```
105/// use dumpster::sync::Gc;
106/// use std::sync::atomic::{AtomicUsize, Ordering};
107///
108/// let shared = Gc::new(AtomicUsize::new(0));
109///
110/// std::thread::scope(|s| {
111/// s.spawn(|| {
112/// let other_gc = shared.clone();
113/// other_gc.store(1, Ordering::Relaxed);
114/// });
115///
116/// shared.store(2, Ordering::Relaxed);
117/// });
118///
119/// println!("{}", shared.load(Ordering::Relaxed));
120/// ```
121///
122/// # Interaction with `Drop`
123///
124/// While collecting cycles, it's possible for a `Gc` to exist that points to some deallocated
125/// object.
126/// To prevent undefined behavior, these `Gc`s are marked as dead during collection and rendered
127/// inaccessible.
128/// Dereferencing or cloning a `Gc` during the `Drop` implementation of a `Trace` type could
129/// result in the program panicking to keep the program from accessing memory after freeing it.
130/// If you're accessing a `Gc` during a `Drop` implementation, make sure to use the fallible
131/// operations [`Gc::try_deref`] and [`Gc::try_clone`].
132pub struct Gc<T: Trace + Send + Sync + ?Sized + 'static> {
133 /// The pointer to the allocation.
134 ptr: UCell<Nullable<GcBox<T>>>,
135 /// The tag information of this pointer, used for mutation detection when marking.
136 tag: AtomicUsize,
137}
138
139#[cfg(not(loom))]
140/// The tag of the current sweep operation.
141/// All new allocations are minted with the current tag.
142static CURRENT_TAG: AtomicUsize = AtomicUsize::new(0);
143
144#[cfg(loom)]
145lazy_static! {
146 static ref CURRENT_TAG: AtomicUsize = AtomicUsize::new(0);
147}
148
149#[repr(C)]
150// This is only public to make the `sync_coerce_gc` macro work.
151#[doc(hidden)]
152/// The backing allocation for a [`Gc`].
153pub struct GcBox<T>
154where
155 T: Trace + Send + Sync + ?Sized,
156{
157 /// The "strong" count, which is the number of extant `Gc`s to this allocation.
158 /// If the strong count is zero, a value contained in the allocation may be dropped, but the
159 /// allocation itself must still be valid.
160 strong: AtomicUsize,
161 /// The "weak" count, which is the number of references to this allocation stored in to-collect
162 /// buffers by the collection algorithm.
163 /// If the weak count is zero, the allocation may be destroyed.
164 weak: AtomicUsize,
165 /// The current generation number of the allocation.
166 /// The generation number is assigned to the global generation every time a strong reference is
167 /// created or destroyed or a `Gc` pointing to this allocation is dereferenced.
168 generation: AtomicUsize,
169 /// The actual data stored in the allocation.
170 value: T,
171}
172
173unsafe impl<T> Send for Gc<T> where T: Trace + Send + Sync + ?Sized {}
174unsafe impl<T> Sync for Gc<T> where T: Trace + Send + Sync + ?Sized {}
175
176/// Begin a collection operation of the allocations on the heap.
177///
178/// Due to concurrency issues, this might not collect every single unreachable allocation that
179/// currently exists, but often calling `collect()` will get allocations made by this thread.
180///
181/// # Examples
182///
183/// ```
184/// use dumpster::sync::{collect, Gc};
185///
186/// let gc = Gc::new(vec![1, 2, 3]);
187/// drop(gc);
188///
189/// collect(); // the vector originally in `gc` _might_ be dropped now, but could be dropped later
190/// ```
191pub fn collect() {
192 collect_all_await();
193}
194
195#[derive(Debug)]
196/// Information passed to a [`CollectCondition`] used to determine whether the garbage collector
197/// should start collecting.
198///
199/// A `CollectInfo` is exclusively created by being passed as an argument to the collection
200/// condition.
201/// To set a custom collection condition, refer to [`set_collect_condition`].
202///
203/// # Examples
204///
205/// ```
206/// use dumpster::sync::{set_collect_condition, CollectInfo};
207///
208/// fn my_collect_condition(info: &CollectInfo) -> bool {
209/// (info.n_gcs_dropped_since_last_collect() + info.n_gcs_existing()) % 2 == 0
210/// }
211///
212/// set_collect_condition(my_collect_condition);
213/// ```
214pub struct CollectInfo {
215 /// Dummy value so this is a private structure.
216 _private: (),
217}
218
219/// A function which determines whether the garbage collector should start collecting.
220/// This type primarily exists so that it can be used with [`set_collect_condition`].
221///
222/// # Examples
223///
224/// ```rust
225/// use dumpster::sync::{set_collect_condition, CollectInfo};
226///
227/// fn always_collect(_: &CollectInfo) -> bool {
228/// true
229/// }
230///
231/// set_collect_condition(always_collect);
232/// ```
233pub type CollectCondition = fn(&CollectInfo) -> bool;
234
235#[must_use]
236/// The default collection condition used by the garbage collector.
237///
238/// There are no guarantees about what this function returns, other than that it will return `true`
239/// with sufficient frequency to ensure that all `Gc` operations are amortized _O(1)_ in runtime.
240///
241/// This function isn't really meant to be called by users, but rather it's supposed to be handed
242/// off to [`set_collect_condition`] to return to the default operating mode of the library.
243///
244/// This collection condition applies globally, i.e. to every thread.
245///
246/// # Examples
247///
248/// ```rust
249/// use dumpster::sync::{default_collect_condition, set_collect_condition, CollectInfo};
250///
251/// fn other_collect_condition(info: &CollectInfo) -> bool {
252/// info.n_gcs_existing() >= 25 || default_collect_condition(info)
253/// }
254///
255/// // Use my custom collection condition.
256/// set_collect_condition(other_collect_condition);
257///
258/// // I'm sick of the custom collection condition.
259/// // Return to the original.
260/// set_collect_condition(default_collect_condition);
261/// ```
262pub fn default_collect_condition(info: &CollectInfo) -> bool {
263 info.n_gcs_dropped_since_last_collect() > info.n_gcs_existing()
264}
265
266pub use collect::set_collect_condition;
267
268impl<T> Gc<T>
269where
270 T: Trace + Send + Sync + ?Sized,
271{
272 /// Construct a new garbage-collected value.
273 ///
274 /// # Examples
275 ///
276 /// ```
277 /// use dumpster::sync::Gc;
278 ///
279 /// let _ = Gc::new(0);
280 /// ```
281 pub fn new(value: T) -> Gc<T>
282 where
283 T: Sized,
284 {
285 notify_created_gc();
286 Gc {
287 ptr: UCell::new(Nullable::new(NonNull::from(Box::leak(Box::new(GcBox {
288 strong: AtomicUsize::new(1),
289 weak: AtomicUsize::new(0),
290 generation: AtomicUsize::new(CURRENT_TAG.load(Ordering::Acquire)),
291 value,
292 }))))),
293 tag: AtomicUsize::new(0),
294 }
295 }
296
297 /// Construct a self-referencing `Gc`.
298 ///
299 /// `new_cyclic` first allocates memory for `T`, then constructs a dead `Gc`.
300 /// The dead `Gc` is then passed to `data_fn` to construct a value of `T`, which
301 /// is stored in the allocation. Finally, `new_cyclic` will update the dead self-referential
302 /// `Gc`s and rehydrate them to produce the final value.
303 ///
304 /// # Panics
305 ///
306 /// If `data_fn` panics, the panic is propagated to the caller.
307 /// The allocation is cleaned up normally.
308 ///
309 /// Additionally, if, when attempting to rehydrate the `Gc` members of `F`, the visitor fails to
310 /// reach a `Gc`, this function will panic and reserve the allocation to be cleaned up
311 /// later.
312 ///
313 /// # Notes on safety
314 ///
315 /// Incorrect implementations of `data_fn` may have unusual or strange results.
316 /// Although `dumpster` guarantees that it will be safe, and will do its best to ensure correct
317 /// results, it is generally unwise to allow dead `Gc`s to exist for long.
318 /// If you implement `data_fn` wrong, this may cause panics later on inside of the collection
319 /// process.
320 ///
321 /// # Examples
322 ///
323 /// ```
324 /// use dumpster::{sync::Gc, Trace};
325 ///
326 /// #[derive(Trace)]
327 /// struct Cycle {
328 /// this: Gc<Self>,
329 /// }
330 ///
331 /// let gc = Gc::new_cyclic(|this| Cycle { this });
332 /// assert!(Gc::ptr_eq(&gc, &gc.this));
333 /// ```
334 pub fn new_cyclic<F: FnOnce(Self) -> T>(data_fn: F) -> Self
335 where
336 T: Sized,
337 {
338 /// A struct containing an uninitialized value of `T`.
339 /// May only be used inside `new_cyclic`.
340 #[repr(transparent)]
341 struct Uninitialized<T>(MaybeUninit<T>);
342
343 unsafe impl<V: Visitor, T> TraceWith<V> for Uninitialized<T> {
344 fn accept(&self, _: &mut V) -> Result<(), ()> {
345 Ok(())
346 }
347 }
348
349 /// Data structure for cleaning up the allocation in case we panic along the way.
350 struct CleanUp<T: Trace + Send + Sync + 'static> {
351 /// Is `true` if the [`GcBox::value`] is initialized.
352 initialized: bool,
353 /// Pointer to the `GcBox` with a maybe uninitialized value.
354 ptr: NonNull<GcBox<T>>,
355 }
356
357 impl<T: Trace + Send + Sync + 'static> Drop for CleanUp<T> {
358 fn drop(&mut self) {
359 if self.initialized {
360 // push this `Gc` into the destruction queue
361 unsafe { mark_dirty(self.ptr) };
362 } else {
363 // deallocate because this `Gc` is not initialized
364 unsafe {
365 dealloc(
366 self.ptr.as_ptr().cast::<u8>(),
367 Layout::for_value(self.ptr.as_ref()),
368 );
369 }
370 }
371 }
372 }
373
374 // make an uninitialized allocation
375 notify_created_gc();
376 let mut gcbox = NonNull::from(Box::leak(Box::new(GcBox {
377 strong: AtomicUsize::new(1),
378 weak: AtomicUsize::new(0),
379 generation: AtomicUsize::new(CURRENT_TAG.load(Ordering::Acquire)),
380 value: Uninitialized(MaybeUninit::<T>::uninit()),
381 })));
382 let mut cleanup = CleanUp {
383 ptr: gcbox,
384 initialized: false,
385 };
386
387 // nilgc is a dead Gc
388 let nilgc = Gc {
389 tag: AtomicUsize::new(0),
390 ptr: UCell::new(Nullable::new(gcbox.cast::<GcBox<T>>()).as_null()),
391 };
392 assert!(Gc::is_dead(&nilgc));
393 unsafe {
394 // SAFETY: `gcbox` is a valid pointer to an uninitialized datum that we have allocated.
395 gcbox.as_mut().value = Uninitialized(MaybeUninit::new(data_fn(nilgc)));
396 }
397 cleanup.initialized = true;
398
399 let gcbox = gcbox.cast::<GcBox<T>>();
400 let res = unsafe {
401 // SAFETY: the above unsafe block correctly constructed the Uninitialized value, so it
402 // is safe to cast `gcbox` and then construct a reference.
403 gcbox.as_ref().value.accept(&mut Rehydrate {
404 ptr: Nullable::new(gcbox.cast()),
405 type_id: TypeId::of::<T>(),
406 })
407 };
408
409 assert!(
410 res.is_ok(),
411 "visitor must be able to access all Gc fields of structure when rehydrating dead Gcs"
412 );
413 let gc = Gc {
414 ptr: UCell::new(Nullable::new(gcbox)),
415 tag: AtomicUsize::new(CURRENT_TAG.load(Ordering::Acquire)),
416 };
417
418 let _ = ManuallyDrop::new(cleanup);
419 gc
420 }
421
422 /// Attempt to dereference this `Gc`.
423 ///
424 /// This function will return `None` if `self` is a "dead" `Gc`, which points to an
425 /// already-deallocated object.
426 /// This can only occur if a `Gc` is accessed during the `Drop` implementation of a
427 /// [`Trace`] object.
428 ///
429 /// For a version which panics instead of returning `None`, consider using [`Deref`].
430 ///
431 /// # Examples
432 ///
433 /// For a still-living `Gc`, this always returns `Some`.
434 ///
435 /// ```
436 /// use dumpster::sync::Gc;
437 ///
438 /// let gc1 = Gc::new(0);
439 /// assert!(Gc::try_deref(&gc1).is_some());
440 /// ```
441 ///
442 /// The only way to get a `Gc` that fails on `try_deref` is by accessing a `Gc` during its
443 /// `Drop` implementation.
444 ///
445 /// ```
446 /// use dumpster::{sync::Gc, Trace};
447 /// use std::sync::Mutex;
448 ///
449 /// #[derive(Trace)]
450 /// struct Cycle(Mutex<Option<Gc<Self>>>);
451 ///
452 /// impl Drop for Cycle {
453 /// fn drop(&mut self) {
454 /// let guard = self.0.lock().unwrap();
455 /// let maybe_ref = Gc::try_deref(guard.as_ref().unwrap());
456 /// assert!(maybe_ref.is_none());
457 /// }
458 /// }
459 ///
460 /// let gc1 = Gc::new(Cycle(Mutex::new(None)));
461 /// *gc1.0.lock().unwrap() = Some(gc1.clone());
462 /// # drop(gc1);
463 /// # dumpster::sync::collect();
464 /// ```
465 pub fn try_deref(gc: &Gc<T>) -> Option<&T> {
466 unsafe { (!gc.ptr.get().is_null()).then(|| &**gc) }
467 }
468
469 /// Attempt to clone this `Gc`.
470 ///
471 /// This function will return `None` if `self` is a "dead" `Gc`, which does not point to an
472 /// existing object. For details on dead `Gc`s, refer to [`Gc::is_dead`].
473 ///
474 /// For a version that simply clones the dead `Gc`, use [`Clone`].
475 ///
476 /// # Examples
477 ///
478 /// For a still-living `Gc`, this always returns `Some`.
479 ///
480 /// ```
481 /// use dumpster::sync::Gc;
482 ///
483 /// let gc1 = Gc::new(0);
484 /// let gc2 = Gc::try_clone(&gc1).unwrap();
485 /// ```
486 ///
487 /// The only way to get a `Gc` which fails on `try_clone` is by accessing a `Gc` during its
488 /// `Drop` implementation.
489 ///
490 /// ```
491 /// use dumpster::{sync::Gc, Trace};
492 ///
493 /// #[derive(Trace)]
494 /// struct Cycle(Gc<Self>);
495 ///
496 /// impl Drop for Cycle {
497 /// fn drop(&mut self) {
498 /// let cloned = Gc::try_clone(&self.0);
499 /// assert!(cloned.is_none());
500 /// }
501 /// }
502 ///
503 /// let gc1 = Gc::new_cyclic(|gc| Cycle(gc));
504 /// # drop(gc1);
505 /// # dumpster::sync::collect();
506 /// ```
507 pub fn try_clone(gc: &Gc<T>) -> Option<Gc<T>> {
508 unsafe { (!gc.ptr.get().is_null()).then(|| gc.clone()) }
509 }
510
511 /// Provides a raw pointer to the data.
512 ///
513 /// Panics if `self` is a "dead" `Gc`,
514 /// which points to an already-deallocated object.
515 /// This can only occur if a `Gc` is accessed during the `Drop` implementation of a
516 /// [`Trace`] object.
517 ///
518 /// # Examples
519 ///
520 /// ```
521 /// use dumpster::sync::Gc;
522 /// let x = Gc::new("hello".to_owned());
523 /// let y = Gc::clone(&x);
524 /// let x_ptr = Gc::as_ptr(&x);
525 /// assert_eq!(x_ptr, Gc::as_ptr(&x));
526 /// assert_eq!(unsafe { &*x_ptr }, "hello");
527 /// ```
528 pub fn as_ptr(gc: &Gc<T>) -> *const T {
529 unsafe {
530 let ptr = NonNull::as_ptr(gc.ptr.get().unwrap());
531 addr_of_mut!((*ptr).value)
532 }
533 }
534
535 /// Determine whether two `Gc`s are equivalent by reference.
536 /// Returns `true` if both `this` and `other` point to the same value, in the same style as
537 /// [`std::ptr::eq`].
538 ///
539 /// # Examples
540 ///
541 /// ```
542 /// use dumpster::sync::Gc;
543 ///
544 /// let gc1 = Gc::new(0);
545 /// let gc2 = Gc::clone(&gc1); // points to same spot as `gc1`
546 /// let gc3 = Gc::new(0); // same value, but points to a different object than `gc1`
547 ///
548 /// assert!(Gc::ptr_eq(&gc1, &gc2));
549 /// assert!(!Gc::ptr_eq(&gc1, &gc3));
550 /// ```
551 pub fn ptr_eq(this: &Gc<T>, other: &Gc<T>) -> bool {
552 unsafe { this.ptr.get() }.as_option() == unsafe { other.ptr.get() }.as_option()
553 }
554
555 /// Get the number of references to the value pointed to by this `Gc`.
556 ///
557 /// This does not include internal references generated by the garbage collector.
558 ///
559 /// # Panics
560 ///
561 /// This function may panic if the `Gc` whose reference count we are loading is "dead" (i.e.
562 /// generated through a `Drop` implementation). For further reference, take a look at
563 /// [`Gc::is_dead`].
564 ///
565 /// # Examples
566 ///
567 /// ```
568 /// use dumpster::sync::Gc;
569 ///
570 /// let gc = Gc::new(());
571 /// assert_eq!(Gc::ref_count(&gc).get(), 1);
572 /// let gc2 = gc.clone();
573 /// assert_eq!(Gc::ref_count(&gc).get(), 2);
574 /// drop(gc);
575 /// drop(gc2);
576 /// ```
577 pub fn ref_count(gc: &Self) -> NonZeroUsize {
578 let box_ptr = unsafe { gc.ptr.get() }.expect(
579 "Attempt to dereference Gc to already-collected object. \
580 This means a Gc escaped from a Drop implementation, likely implying a bug in your code.",
581 );
582 let box_ref = unsafe { box_ptr.as_ref() };
583 NonZeroUsize::new(box_ref.strong.load(Ordering::Relaxed))
584 .expect("strong count to a GcBox may never be zero while a Gc to it exists")
585 }
586
587 /// Determine whether this is a dead `Gc`.
588 ///
589 /// A `Gc` is dead if it is not usable as a reference to any value.
590 /// Currently, a dead `Gc` may only be produced by accessing a `Gc` inside of the `Drop`
591 /// implementation of a garbage-collected value or by using the `Gc` provided to the
592 /// construction function in [`Gc::new_cyclic`].
593 ///
594 /// # Examples
595 ///
596 /// ```
597 /// use dumpster::{sync::Gc, Trace};
598 ///
599 /// #[derive(Trace)]
600 /// struct Cycle(Gc<Self>);
601 ///
602 /// impl Drop for Cycle {
603 /// fn drop(&mut self) {
604 /// assert!(Gc::is_dead(&self.0));
605 /// }
606 /// }
607 ///
608 /// let gc1 = Gc::new_cyclic(|gc| Cycle(gc));
609 /// # drop(gc1);
610 /// # dumpster::sync::collect();
611 /// ```
612 #[inline]
613 pub fn is_dead(gc: &Self) -> bool {
614 unsafe { gc.ptr.get() }.is_null()
615 }
616
617 /// Consumes the `Gc<T>`, returning the inner `GcBox<T>` pointer and tag.
618 #[inline]
619 #[must_use]
620 fn into_ptr(this: Self) -> (*const GcBox<T>, usize) {
621 let this = ManuallyDrop::new(this);
622 let tag = &raw const this.tag;
623 let ptr = unsafe { this.ptr.get().as_ptr() };
624 let tag = unsafe { tag.read() }.into_inner();
625 (ptr, tag)
626 }
627
628 /// Constructs a `Gc<T>` from the innner `GcBox<T>` pointer and tag.
629 #[inline]
630 #[must_use]
631 unsafe fn from_ptr(ptr: *const GcBox<T>, tag: usize) -> Self {
632 Self {
633 ptr: UCell::new(Nullable::from_ptr(ptr.cast_mut())),
634 tag: AtomicUsize::new(tag),
635 }
636 }
637
638 /// Kill this `Gc`, making it dead.
639 ///
640 /// # Safety
641 ///
642 /// The caller is responsible for making sure that no other code can access this `Gc` while
643 /// `kill` is running.
644 unsafe fn kill(&self) {
645 self.ptr.set(self.ptr.get().as_null());
646 }
647
648 /// Exists solely for the [`coerce_gc`] macro.
649 #[inline]
650 #[must_use]
651 #[doc(hidden)]
652 pub fn __private_into_ptr(this: Self) -> (*const GcBox<T>, usize) {
653 Self::into_ptr(this)
654 }
655
656 /// Exists solely for the [`coerce_gc`] macro.
657 #[inline]
658 #[must_use]
659 #[doc(hidden)]
660 pub unsafe fn __private_from_ptr(ptr: *const GcBox<T>, tag: usize) -> Self {
661 Self::from_ptr(ptr, tag)
662 }
663}
664
665/// A struct for converting dead `Gc`s into live ones.
666///
667/// This is used in [`Gc::new_cyclic`].
668pub(super) struct Rehydrate {
669 /// The pointer to the currently hydrating [`GcBox`].
670 ptr: Nullable<GcBox<()>>,
671 /// The [`TypeId`] of `T` in `Gc<T>` to be hydrated.
672 type_id: TypeId,
673}
674
675impl Visitor for Rehydrate {
676 fn visit_sync<T>(&mut self, gc: &Gc<T>)
677 where
678 T: Trace + Send + Sync + ?Sized,
679 {
680 if Gc::is_dead(gc) && TypeId::of::<T>() == self.type_id {
681 unsafe {
682 // SAFETY: it is safe to transmute these pointers because we have checked
683 // that they are of the same type.
684 // Additionally, the `GcBox` has been fully initialized, so it is safe to
685 // create a reference here.
686 let cell_ptr = (&raw const gc.ptr).cast::<UCell<Nullable<GcBox<()>>>>();
687 (*cell_ptr).set(self.ptr);
688
689 let box_ref = &*self.ptr.as_ptr();
690 let old_strong = box_ref.strong.fetch_add(1, Ordering::Relaxed);
691 // Check for overflow. See implementation of clone for details.
692 if old_strong > MAX_STRONG_COUNT {
693 std::process::abort();
694 }
695 box_ref
696 .generation
697 .store(CURRENT_TAG.load(Ordering::Acquire), Ordering::Release);
698 notify_created_gc();
699 }
700 }
701 }
702
703 fn visit_unsync<T>(&mut self, _: &crate::unsync::Gc<T>)
704 where
705 T: Trace + ?Sized,
706 {
707 }
708}
709
710impl<T: Trace + Send + Sync + Clone> Gc<T> {
711 /// Makes a mutable reference to the given `Gc`.
712 ///
713 /// If there are other `Gc` pointers to the same allocation, then `make_mut` will
714 /// [`clone`] the inner value to a new allocation to ensure unique ownership. This is also
715 /// referred to as clone-on-write.
716 ///
717 /// [`clone`]: Clone::clone
718 ///
719 /// # Panics
720 ///
721 /// This function may panic if the `Gc` whose reference count we are loading is "dead" (i.e.
722 /// generated through a `Drop` implementation). For further reference, take a look at
723 /// [`Gc::is_dead`].
724 ///
725 /// # Examples
726 ///
727 /// ```
728 /// use dumpster::sync::Gc;
729 ///
730 /// let mut data = Gc::new(5);
731 ///
732 /// *Gc::make_mut(&mut data) += 1; // Won't clone anything
733 /// let mut other_data = Gc::clone(&data); // Won't clone inner data
734 /// *Gc::make_mut(&mut data) += 1; // Clones inner data
735 /// *Gc::make_mut(&mut data) += 1; // Won't clone anything
736 /// *Gc::make_mut(&mut other_data) *= 2; // Won't clone anything
737 ///
738 /// // Now `data` and `other_data` point to different allocations.
739 /// assert_eq!(*data, 8);
740 /// assert_eq!(*other_data, 12);
741 /// ```
742 #[inline]
743 pub fn make_mut(this: &mut Self) -> &mut T {
744 if Gc::is_dead(this) {
745 panic_deref_of_collected_object();
746 }
747
748 // SAFETY: we checked above that the object is alive (not null)
749 let box_ref = unsafe { this.ptr.get().unwrap_unchecked().as_ref() };
750
751 let strong = box_ref.strong.load(Ordering::Acquire);
752 let weak = box_ref.weak.load(Ordering::Acquire);
753
754 if strong != 1 || weak != 0 {
755 // We don't have unique access to the value so we need to clone it.
756 *this = Gc::new(box_ref.value.clone());
757 }
758
759 // SAFETY: we have exclusive access to this `GcBox` because we ensured
760 // that we hold the only reference to this allocation.
761 // No other `Gc`s point to this allocation because the strong count is 1, and there are no
762 // loose pointers internal to the collector because the weak count is 0.
763 unsafe { &mut (*this.ptr.get().as_ptr()).value }
764 }
765}
766
767/// Allows coercing `T` of [`Gc<T>`](Gc).
768///
769/// This means that you can convert a `Gc` containing a strictly-sized type (such as `[T; N]`) into
770/// a `Gc` containing its unsized version (such as `[T]`), all without using nightly-only features.
771///
772/// This is one of two easy ways to create a `Gc<[T]>`; the other method is to use [`FromIterator`].
773///
774/// # Examples
775///
776/// ```
777/// use dumpster::sync::{coerce_gc, Gc};
778///
779/// let gc1: Gc<[u8; 3]> = Gc::new([7, 8, 9]);
780/// let gc2: Gc<[u8]> = coerce_gc!(gc1);
781/// assert_eq!(&gc2[..], &[7, 8, 9]);
782/// ```
783///
784/// Note that although this macro allows for type conversion, it _cannot_ be used for converting
785/// between incompatible types.
786///
787/// ```compile_fail
788/// // This program is incorrect!
789/// use dumpster::sync::{Gc, coerce_gc};
790///
791/// let gc1: Gc<u8> = Gc::new(1);
792/// let gc2: Gc<i8> = coerce_gc!(gc1);
793/// ```
794#[doc(hidden)]
795#[macro_export]
796macro_rules! __sync_coerce_gc {
797 ($gc:expr) => {{
798 // Temporarily convert the `Gc` into a raw pointer to allow for coercion to occur.
799 let (ptr, tag): (*const _, usize) = $crate::sync::Gc::__private_into_ptr($gc);
800 unsafe { $crate::sync::Gc::__private_from_ptr(ptr, tag) }
801 }};
802}
803
804#[doc(inline)]
805pub use crate::__sync_coerce_gc as coerce_gc;
806
807impl<T> Clone for Gc<T>
808where
809 T: Trace + Send + Sync + ?Sized,
810{
811 /// Clone a garbage-collected reference.
812 /// This does not clone the underlying data.
813 /// If this `Gc` is [dead](`Gc::is_dead`), this will produce another dead `Gc`.
814 ///
815 /// For a fallible version, refer to [`Gc::try_clone`].
816 ///
817 /// # Examples
818 ///
819 /// ```
820 /// use dumpster::sync::Gc;
821 /// use std::sync::atomic::{AtomicU8, Ordering};
822 ///
823 /// let gc1 = Gc::new(AtomicU8::new(0));
824 /// let gc2 = gc1.clone();
825 ///
826 /// gc1.store(1, Ordering::Relaxed);
827 /// assert_eq!(gc2.load(Ordering::Relaxed), 1);
828 /// ```
829 ///
830 /// Note that you can also clone a dead `Gc`.
831 ///
832 /// ```
833 /// use dumpster::{sync::Gc, Trace};
834 /// use std::sync::Mutex;
835 ///
836 /// #[derive(Trace)]
837 /// struct Cycle(Gc<Self>);
838 ///
839 /// impl Drop for Cycle {
840 /// fn drop(&mut self) {
841 /// let gc = self.0.clone();
842 /// assert!(Gc::is_dead(&gc));
843 /// }
844 /// }
845 ///
846 /// let gc1 = Gc::new_cyclic(|gc| Cycle(gc));
847 /// # drop(gc1);
848 /// # dumpster::sync::collect();
849 /// ```
850 fn clone(&self) -> Gc<T> {
851 if Gc::is_dead(self) {
852 // Clone dead Gcs by doing a naive copy.
853 return unsafe { ptr::read(self) };
854 }
855 let box_ref = unsafe { self.ptr.get().unwrap().as_ref() };
856
857 // increment strong count before generation to ensure cleanup never underestimates ref count
858 let old_strong = box_ref.strong.fetch_add(1, Ordering::Acquire);
859
860 // We need to guard against massive refcounts in case someone is `mem::forget`ing
861 // Gcs. If we don't do this the count can overflow and users will use-after free. This
862 // branch will never be taken in any realistic program. We abort because such a program is
863 // incredibly degenerate, and we don't care to support it.
864 //
865 // This check is not 100% water-proof: we error when the refcount grows beyond `isize::MAX`.
866 // But we do that check *after* having done the increment, so there is a chance here that
867 // the worst already happened and we actually do overflow the `usize` counter. However, that
868 // requires the counter to grow from `isize::MAX` to `usize::MAX` between the increment
869 // above and the `abort` below, which seems exceedingly unlikely.
870 if old_strong > MAX_STRONG_COUNT {
871 std::process::abort();
872 }
873
874 box_ref
875 .generation
876 .store(CURRENT_TAG.load(Ordering::Acquire), Ordering::Release);
877
878 notify_created_gc();
879 // mark_clean(box_ref); // causes performance drops
880
881 Gc {
882 ptr: UCell::new(unsafe { self.ptr.get() }),
883 tag: AtomicUsize::new(CURRENT_TAG.load(Ordering::Acquire)),
884 }
885 }
886}
887
888impl<T> Drop for Gc<T>
889where
890 T: Trace + Send + Sync + ?Sized,
891{
892 fn drop(&mut self) {
893 let Some(mut ptr) = unsafe { self.ptr.get() }.as_option() else {
894 return;
895 };
896 let box_ref = unsafe { ptr.as_ref() };
897 box_ref.weak.fetch_add(1, Ordering::AcqRel); // ensures that this allocation wasn't freed
898 // while we weren't looking
899 box_ref
900 .generation
901 .store(CURRENT_TAG.load(Ordering::Relaxed), Ordering::Release);
902 match box_ref.strong.fetch_sub(1, Ordering::AcqRel) {
903 0 => unreachable!("strong cannot reach zero while a Gc to it exists"),
904 1 => {
905 mark_clean(box_ref);
906 if box_ref.weak.fetch_sub(1, Ordering::Release) == 1 {
907 // destroyed the last weak reference! we can safely deallocate this
908 let layout = Layout::for_value(box_ref);
909 fence(Ordering::Acquire);
910 unsafe {
911 drop_in_place(ptr.as_mut());
912 dealloc(ptr.as_ptr().cast(), layout);
913 }
914 }
915 }
916 _ => {
917 if contains_gcs(&box_ref.value).unwrap_or(true) {
918 // SAFETY: `ptr` is convertible to a reference
919 // We don't use `box_ref` here because that pointer
920 // only has `SharedReadOnly` permissions under the stacked borrows model
921 // when we need `Unique` for the `TrashCan`.
922 unsafe { mark_dirty(ptr) };
923 }
924 box_ref.weak.fetch_sub(1, Ordering::Release);
925 }
926 }
927 notify_dropped_gc();
928 }
929}
930
931impl CollectInfo {
932 #[must_use]
933 /// Get the number of times that a [`Gc`] has been dropped since the last time a collection
934 /// operation was performed.
935 ///
936 /// # Examples
937 ///
938 /// ```
939 /// use dumpster::sync::{set_collect_condition, CollectInfo};
940 ///
941 /// // Collection condition for whether many Gc's have been dropped.
942 /// fn have_many_gcs_dropped(info: &CollectInfo) -> bool {
943 /// info.n_gcs_dropped_since_last_collect() > 100
944 /// }
945 ///
946 /// set_collect_condition(have_many_gcs_dropped);
947 /// ```
948 pub fn n_gcs_dropped_since_last_collect(&self) -> usize {
949 n_gcs_dropped()
950 }
951
952 #[must_use]
953 /// Get the total number of [`Gc`]s which currently exist.
954 ///
955 /// # Examples
956 ///
957 /// ```
958 /// use dumpster::sync::{set_collect_condition, CollectInfo};
959 ///
960 /// // Collection condition for whether many Gc's currently exist.
961 /// fn do_many_gcs_exist(info: &CollectInfo) -> bool {
962 /// info.n_gcs_existing() > 100
963 /// }
964 ///
965 /// set_collect_condition(do_many_gcs_exist);
966 /// ```
967 pub fn n_gcs_existing(&self) -> usize {
968 n_gcs_existing()
969 }
970}
971
972impl<T: Trace + Send + Sync + ?Sized> Gc<T> {
973 /// Allocates an `GcBox<T>` with sufficient space for
974 /// a value of the provided layout.
975 ///
976 /// The function `mem_to_gc_box` is called with the data pointer
977 /// and must return back a pointer for the `GcBox<T>`.
978 unsafe fn allocate_for_layout(
979 value_layout: Layout,
980 mem_to_gc_box: impl FnOnce(*mut u8) -> *mut GcBox<T>,
981 ) -> *mut GcBox<T> {
982 let layout = Layout::new::<GcBox<()>>()
983 .extend(value_layout)
984 .unwrap()
985 .0
986 .pad_to_align();
987
988 Self::allocate_for_layout_of_box(layout, mem_to_gc_box)
989 }
990
991 /// Allocates an `GcBox<T>` with the given layout.
992 ///
993 /// The function `mem_to_gc_box` is called with the data pointer
994 /// and must return back a pointer for the `GcBox<T>`.
995 unsafe fn allocate_for_layout_of_box(
996 layout: Layout,
997 mem_to_gc_box: impl FnOnce(*mut u8) -> *mut GcBox<T>,
998 ) -> *mut GcBox<T> {
999 // SAFETY: layout has non-zero size because of the `ref_count` field
1000 let ptr = unsafe { std::alloc::alloc(layout) };
1001
1002 if ptr.is_null() {
1003 handle_alloc_error(layout);
1004 }
1005
1006 let inner = mem_to_gc_box(ptr);
1007
1008 unsafe {
1009 (&raw mut (*inner).strong).write(AtomicUsize::new(1));
1010 (&raw mut (*inner).weak).write(AtomicUsize::new(0));
1011 (&raw mut (*inner).generation).write(AtomicUsize::new(0));
1012 }
1013
1014 inner
1015 }
1016}
1017
1018impl<T: Trace + Send + Sync> Gc<[T]> {
1019 /// Allocates an `GcBox<[T]>` with the given length.
1020 fn allocate_for_slice(len: usize) -> *mut GcBox<[T]> {
1021 unsafe {
1022 Self::allocate_for_layout(Layout::array::<T>(len).unwrap(), |mem| {
1023 ptr::slice_from_raw_parts_mut(mem.cast::<T>(), len) as *mut GcBox<[T]>
1024 })
1025 }
1026 }
1027}
1028
1029unsafe impl<V: Visitor, T: Trace + Send + Sync + ?Sized> TraceWith<V> for Gc<T> {
1030 fn accept(&self, visitor: &mut V) -> Result<(), ()> {
1031 visitor.visit_sync(self);
1032 Ok(())
1033 }
1034}
1035
1036impl<T: Trace + Send + Sync + ?Sized> Deref for Gc<T> {
1037 type Target = T;
1038
1039 /// Dereference this pointer, creating a reference to the contained value `T`.
1040 ///
1041 /// # Panics
1042 ///
1043 /// This function may panic if it is called from within the implementation of `std::ops::Drop`
1044 /// of its owning value, since returning such a reference could cause a use-after-free.
1045 /// It is not guaranteed to panic.
1046 ///
1047 /// # Examples
1048 ///
1049 /// The following is a correct time to dereference a `Gc`.
1050 ///
1051 /// ```
1052 /// use dumpster::sync::Gc;
1053 ///
1054 /// let my_gc = Gc::new(0u8);
1055 /// let my_ref: &u8 = &my_gc;
1056 /// ```
1057 ///
1058 /// Dereferencing a `Gc` while dropping is not correct.
1059 ///
1060 /// ```should_panic
1061 /// // This is wrong!
1062 /// use dumpster::{sync::Gc, Trace};
1063 /// use std::sync::Mutex;
1064 ///
1065 /// #[derive(Trace)]
1066 /// struct Bad {
1067 /// s: String,
1068 /// cycle: Mutex<Option<Gc<Bad>>>,
1069 /// }
1070 ///
1071 /// impl Drop for Bad {
1072 /// fn drop(&mut self) {
1073 /// println!("{}", self.cycle.lock().unwrap().as_ref().unwrap().s)
1074 /// }
1075 /// }
1076 ///
1077 /// let foo = Gc::new(Bad {
1078 /// s: "foo".to_string(),
1079 /// cycle: Mutex::new(None),
1080 /// });
1081 /// ```
1082 fn deref(&self) -> &Self::Target {
1083 let box_ref = unsafe {
1084 self.ptr.get().expect(
1085 "Attempting to dereference Gc to already-deallocated object.\
1086 This is caused by accessing a Gc during a Drop implementation, likely implying a bug in your code."
1087 ).as_ref()
1088 };
1089 let current_tag = CURRENT_TAG.load(Ordering::Acquire);
1090 self.tag.store(current_tag, Ordering::Release);
1091 box_ref.generation.store(current_tag, Ordering::Release);
1092 &box_ref.value
1093 }
1094}
1095
1096impl<T> PartialEq<Gc<T>> for Gc<T>
1097where
1098 T: Trace + Send + Sync + ?Sized + PartialEq,
1099{
1100 /// Test for equality on two `Gc`s.
1101 ///
1102 /// Two `Gc`s are equal if their inner values are equal, even if they are stored in different
1103 /// allocations.
1104 /// Because `PartialEq` does not imply reflexivity, and there is no current path for trait
1105 /// specialization, this function does not do a "fast-path" check for reference equality.
1106 /// Therefore, if two `Gc`s point to the same allocation, the implementation of `eq` will still
1107 /// require a direct call to `eq` on the values.
1108 ///
1109 /// # Panics
1110 ///
1111 /// This function may panic if it is called from within the implementation of `std::ops::Drop`
1112 /// of its owning value, since returning such a reference could cause a use-after-free.
1113 /// It is not guaranteed to panic.
1114 /// Additionally, if this `Gc` is moved out of an allocation during a `Drop` implementation, it
1115 /// could later cause a panic.
1116 /// For further details, refer to the main documentation for `Gc`.
1117 ///
1118 /// ```
1119 /// use dumpster::sync::Gc;
1120 ///
1121 /// let gc = Gc::new(6);
1122 /// assert!(gc == Gc::new(6));
1123 /// ```
1124 fn eq(&self, other: &Gc<T>) -> bool {
1125 self.as_ref() == other.as_ref()
1126 }
1127}
1128
1129impl<T> Eq for Gc<T> where T: Trace + Send + Sync + ?Sized + PartialEq {}
1130
1131impl<T: Trace + Send + Sync + ?Sized> AsRef<T> for Gc<T> {
1132 fn as_ref(&self) -> &T {
1133 self
1134 }
1135}
1136
1137impl<T: Trace + Send + Sync + ?Sized> Borrow<T> for Gc<T> {
1138 fn borrow(&self) -> &T {
1139 self
1140 }
1141}
1142
1143impl<T: Trace + Send + Sync + Default> Default for Gc<T> {
1144 fn default() -> Self {
1145 Gc::new(T::default())
1146 }
1147}
1148
1149impl<T: Trace + Send + Sync + ?Sized> std::fmt::Pointer for Gc<T> {
1150 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1151 std::fmt::Pointer::fmt(&addr_of!(**self), f)
1152 }
1153}
1154
1155#[cfg(not(loom))]
1156#[cfg(feature = "coerce-unsized")]
1157impl<T, U> std::ops::CoerceUnsized<Gc<U>> for Gc<T>
1158where
1159 T: std::marker::Unsize<U> + Trace + Send + Sync + ?Sized,
1160 U: Trace + Send + Sync + ?Sized,
1161{
1162}
1163
1164impl<T: Trace + Send + Sync + ?Sized> Debug for Gc<T> {
1165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1166 write!(
1167 f,
1168 "Gc({:?}, {})",
1169 self.ptr,
1170 self.tag.load(Ordering::Acquire)
1171 )
1172 }
1173}
1174
1175impl<T: Trace + Send + Sync> From<T> for Gc<T> {
1176 /// Converts a generic type `T` into an `Gc<T>`
1177 ///
1178 /// The conversion allocates on the heap and moves `t`
1179 /// from the stack into it.
1180 ///
1181 /// # Example
1182 /// ```rust
1183 /// # use dumpster::sync::Gc;
1184 /// let x = 5;
1185 /// let rc = Gc::new(5);
1186 ///
1187 /// assert_eq!(Gc::from(x), rc);
1188 /// ```
1189 fn from(value: T) -> Self {
1190 Gc::new(value)
1191 }
1192}
1193
1194impl<T: Trace + Send + Sync, const N: usize> From<[T; N]> for Gc<[T]> {
1195 /// Converts a [`[T; N]`](prim@array) into an `Gc<[T]>`.
1196 ///
1197 /// The conversion moves the array into a newly allocated `Gc`.
1198 ///
1199 /// # Example
1200 ///
1201 /// ```
1202 /// # use dumpster::sync::Gc;
1203 /// let original: [i32; 3] = [1, 2, 3];
1204 /// let shared: Gc<[i32]> = Gc::from(original);
1205 /// assert_eq!(&[1, 2, 3], &shared[..]);
1206 /// ```
1207 #[inline]
1208 fn from(v: [T; N]) -> Gc<[T]> {
1209 coerce_gc!(Gc::<[T; N]>::from(v))
1210 }
1211}
1212
1213impl<T: Trace + Send + Sync + Clone> From<&[T]> for Gc<[T]> {
1214 /// Allocates a garbage-collected slice and fills it by cloning `slice`'s items.
1215 ///
1216 /// # Example
1217 ///
1218 /// ```
1219 /// # use dumpster::sync::Gc;
1220 /// let original: &[i32] = &[1, 2, 3];
1221 /// let shared: Gc<[i32]> = Gc::from(original);
1222 /// assert_eq!(&[1, 2, 3], &shared[..]);
1223 /// ```
1224 #[inline]
1225 fn from(slice: &[T]) -> Gc<[T]> {
1226 // Panic guard while cloning T elements.
1227 // In the event of a panic, elements that have been written
1228 // into the new GcBox will be dropped, then the memory freed.
1229 struct Guard<T> {
1230 /// pointer to `GcBox` to deallocate on panic
1231 mem: *mut u8,
1232 /// layout of the `GcBox` to deallocate on panic
1233 layout: Layout,
1234 /// pointer to the `GcBox`'s value
1235 elems: *mut T,
1236 /// the number of elements cloned so far
1237 n_elems: usize,
1238 }
1239
1240 impl<T> Drop for Guard<T> {
1241 fn drop(&mut self) {
1242 unsafe {
1243 let slice = slice::from_raw_parts_mut(self.elems, self.n_elems);
1244 ptr::drop_in_place(slice);
1245
1246 dealloc(self.mem, self.layout);
1247 }
1248 }
1249 }
1250
1251 unsafe {
1252 let value_layout = Layout::array::<T>(slice.len()).unwrap();
1253
1254 let layout = Layout::new::<GcBox<()>>()
1255 .extend(value_layout)
1256 .unwrap()
1257 .0
1258 .pad_to_align();
1259
1260 let ptr = Self::allocate_for_layout_of_box(layout, |mem| {
1261 ptr::slice_from_raw_parts_mut(mem.cast::<T>(), slice.len()) as *mut GcBox<[T]>
1262 });
1263
1264 // Pointer to first element
1265 let elems = (&raw mut (*ptr).value).cast::<T>();
1266
1267 let mut guard = Guard {
1268 mem: ptr.cast::<u8>(),
1269 layout,
1270 elems,
1271 n_elems: 0,
1272 };
1273
1274 for (i, item) in slice.iter().enumerate() {
1275 ptr::write(elems.add(i), item.clone());
1276 guard.n_elems += 1;
1277 }
1278
1279 // All clear. Forget the guard so it doesn't free the new GcBox.
1280 mem::forget(guard);
1281
1282 notify_created_gc();
1283
1284 Self {
1285 ptr: UCell::new(Nullable::from_ptr(ptr)),
1286 tag: AtomicUsize::new(0),
1287 }
1288 }
1289 }
1290}
1291
1292impl<T: Trace + Send + Sync + Clone> From<&mut [T]> for Gc<[T]> {
1293 /// Allocates a garbage-collected slice and fills it by cloning `v`'s items.
1294 ///
1295 /// # Example
1296 ///
1297 /// ```
1298 /// # use dumpster::sync::Gc;
1299 /// let mut original = [1, 2, 3];
1300 /// let original: &mut [i32] = &mut original;
1301 /// let shared: Gc<[i32]> = Gc::from(original);
1302 /// assert_eq!(&[1, 2, 3], &shared[..]);
1303 /// ```
1304 #[inline]
1305 fn from(value: &mut [T]) -> Self {
1306 Gc::from(&*value)
1307 }
1308}
1309
1310impl From<&str> for Gc<str> {
1311 /// Allocates a garbage-collected string slice and copies `v` into it.
1312 ///
1313 /// # Example
1314 ///
1315 /// ```
1316 /// # use dumpster::sync::Gc;
1317 /// let shared: Gc<str> = Gc::from("statue");
1318 /// assert_eq!("statue", &shared[..]);
1319 /// ```
1320 #[inline]
1321 fn from(v: &str) -> Self {
1322 let bytes = Gc::<[u8]>::from(v.as_bytes());
1323 let (ptr, tag) = Gc::into_ptr(bytes);
1324 unsafe { Gc::from_ptr(ptr as *const GcBox<str>, tag) }
1325 }
1326}
1327
1328impl From<&mut str> for Gc<str> {
1329 /// Allocates a garbage-collected string slice and copies `v` into it.
1330 ///
1331 /// # Example
1332 ///
1333 /// ```
1334 /// # use dumpster::sync::Gc;
1335 /// let mut original = String::from("statue");
1336 /// let original: &mut str = &mut original;
1337 /// let shared: Gc<str> = Gc::from(original);
1338 /// assert_eq!("statue", &shared[..]);
1339 /// ```
1340 #[inline]
1341 fn from(v: &mut str) -> Self {
1342 Gc::from(&*v)
1343 }
1344}
1345
1346impl From<Gc<str>> for Gc<[u8]> {
1347 /// Converts a garbage-collected string slice into a byte slice.
1348 ///
1349 /// # Example
1350 ///
1351 /// ```
1352 /// # use dumpster::sync::Gc;
1353 /// let string: Gc<str> = Gc::from("eggplant");
1354 /// let bytes: Gc<[u8]> = Gc::from(string);
1355 /// assert_eq!("eggplant".as_bytes(), bytes.as_ref());
1356 /// ```
1357 #[inline]
1358 fn from(value: Gc<str>) -> Self {
1359 let (ptr, tag) = Gc::into_ptr(value);
1360 unsafe { Gc::from_ptr(ptr as *const GcBox<[u8]>, tag) }
1361 }
1362}
1363
1364impl From<String> for Gc<str> {
1365 /// Allocates a garbage-collected string slice and copies `v` into it.
1366 ///
1367 /// # Example
1368 ///
1369 /// ```
1370 /// # use dumpster::sync::Gc;
1371 /// let original: String = "statue".to_owned();
1372 /// let shared: Gc<str> = Gc::from(original);
1373 /// assert_eq!("statue", &shared[..]);
1374 /// ```
1375 #[inline]
1376 fn from(value: String) -> Self {
1377 Self::from(&value[..])
1378 }
1379}
1380
1381impl<T: Trace + Send + Sync> From<Box<T>> for Gc<T> {
1382 /// Move a boxed object to a new, garbage collected, allocation.
1383 ///
1384 /// # Example
1385 ///
1386 /// ```
1387 /// # use dumpster::sync::Gc;
1388 /// let original: Box<i32> = Box::new(1);
1389 /// let shared: Gc<i32> = Gc::from(original);
1390 /// assert_eq!(1, *shared);
1391 /// ```
1392 #[inline]
1393 fn from(src: Box<T>) -> Self {
1394 unsafe {
1395 let layout = Layout::for_value(&*src);
1396 let gc_ptr = Gc::allocate_for_layout(layout, <*mut u8>::cast::<GcBox<T>>);
1397
1398 // Copy value as bytes
1399 ptr::copy_nonoverlapping(
1400 (&raw const *src).cast::<u8>(),
1401 (&raw mut (*gc_ptr).value).cast::<u8>(),
1402 layout.size(),
1403 );
1404
1405 // Free the allocation without dropping its contents
1406 let bptr = Box::into_raw(src);
1407 let src = Box::from_raw(bptr.cast::<mem::ManuallyDrop<T>>());
1408 drop(src);
1409
1410 notify_created_gc();
1411 Self::from_ptr(gc_ptr, 0)
1412 }
1413 }
1414}
1415
1416impl<T: Trace + Send + Sync> From<Vec<T>> for Gc<[T]> {
1417 /// Allocates a garbage-collected slice and moves `vec`'s items into it.
1418 ///
1419 /// # Example
1420 ///
1421 /// ```
1422 /// # use dumpster::sync::Gc;
1423 /// let unique: Vec<i32> = vec![1, 2, 3];
1424 /// let shared: Gc<[i32]> = Gc::from(unique);
1425 /// assert_eq!(&[1, 2, 3], &shared[..]);
1426 /// ```
1427 #[inline]
1428 fn from(vec: Vec<T>) -> Self {
1429 let mut vec = ManuallyDrop::new(vec);
1430 let vec_cap = vec.capacity();
1431 let vec_len = vec.len();
1432 let vec_ptr = vec.as_mut_ptr();
1433
1434 let gc_ptr = Self::allocate_for_slice(vec_len);
1435
1436 unsafe {
1437 let dst_ptr = (&raw mut (*gc_ptr).value).cast::<T>();
1438 ptr::copy_nonoverlapping(vec_ptr, dst_ptr, vec_len);
1439
1440 let _ = Vec::from_raw_parts(vec_ptr, 0, vec_cap);
1441
1442 notify_created_gc();
1443 Self::from_ptr(gc_ptr, 0)
1444 }
1445 }
1446}
1447
1448impl<'a, B: Trace + Send + Sync> From<Cow<'a, B>> for Gc<B>
1449where
1450 B: ToOwned + ?Sized,
1451 Gc<B>: From<&'a B> + From<B::Owned>,
1452{
1453 /// Creates a garbage-collected pointer from a clone-on-write pointer by
1454 /// copying its content.
1455 ///
1456 /// # Example
1457 ///
1458 /// ```rust
1459 /// # use dumpster::sync::Gc;
1460 /// # use std::borrow::Cow;
1461 /// let cow: Cow<'_, str> = Cow::Borrowed("eggplant");
1462 /// let shared: Gc<str> = Gc::from(cow);
1463 /// assert_eq!("eggplant", &shared[..]);
1464 /// ```
1465 #[inline]
1466 fn from(cow: Cow<'a, B>) -> Gc<B> {
1467 match cow {
1468 Cow::Borrowed(s) => Gc::from(s),
1469 Cow::Owned(s) => Gc::from(s),
1470 }
1471 }
1472}
1473
1474impl<T> FromIterator<T> for Gc<[T]>
1475where
1476 T: Trace + Send + Sync,
1477{
1478 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
1479 // Collect into a `Vec` for O(n) performance.
1480 // TODO: this could be slightly optimized by using the `Gc<[]>` layout for perf, but this is
1481 // a later problem.
1482 let mut t_vec = iter.into_iter().collect::<Vec<_>>();
1483 let n = t_vec.len();
1484 // if allocation fails, t_vec will be dropped
1485 let box_ptr = Gc::allocate_for_slice(n);
1486 let gc = unsafe {
1487 copy_nonoverlapping(t_vec.as_ptr(), (*box_ptr).value.as_mut_ptr(), n);
1488 t_vec.set_len(0); // forget old values
1489 Gc::from_ptr(box_ptr, CURRENT_TAG.load(Ordering::Acquire))
1490 };
1491
1492 notify_created_gc();
1493
1494 gc
1495 }
1496}