use alloc::alloc::Layout;
use alloc::rc::Rc;
use core::cell::UnsafeCell;
use core::marker::PhantomData;
use core::mem::ManuallyDrop;
use core::ops::Deref;
use core::ptr::{self, drop_in_place, NonNull};
use core::borrow::Borrow;
use core::cell::Cell;
use core::fmt::{self, Debug, Display, Formatter, Pointer};
use core::cmp::Ordering;
use core::hash::{Hash, Hasher};
use core::panic::{RefUnwindSafe, UnwindSafe};
#[cfg(feature = "nightly")]
use core::{
marker::CoercePointee,
ptr::{metadata, DynMetadata},
};
use crate::counter_marker::{CounterMarker, Mark};
use crate::state::{replace_state_field, state, State, try_state};
use crate::trace::{Context, ContextInner, Finalize, Trace};
use crate::utils::*;
use crate::POSSIBLE_CYCLES;
#[cfg(feature = "weak-ptrs")]
use crate::weak::weak_counter_marker::WeakCounterMarker;
#[cfg_attr(feature = "nightly", derive(CoercePointee))]
#[repr(transparent)]
pub struct Cc<#[cfg_attr(feature = "nightly", pointee)] T: ?Sized + Trace + 'static> {
inner: NonNull<CcBox<T>>,
_phantom: PhantomData<Rc<T>>, }
impl<T: Trace> Cc<T> {
#[must_use = "newly created Cc is immediately dropped"]
#[track_caller]
pub fn new(t: T) -> Cc<T> {
state(|state| {
#[cfg(debug_assertions)]
if state.is_tracing() {
panic!("Cannot create a new Cc while tracing!");
}
#[cfg(feature = "auto-collect")]
super::trigger_collection(state);
Cc {
inner: CcBox::new(t, state),
_phantom: PhantomData,
}
})
}
#[inline]
#[track_caller]
pub fn try_unwrap(self) -> Result<T, Self> {
let cc = ManuallyDrop::new(self); if cc.strong_count() != 1 {
return Err(ManuallyDrop::into_inner(cc));
}
let res = try_state(|state| {
if state.is_collecting() || state.is_dropping() {
return None;
}
#[cfg(feature = "finalization")]
if state.is_finalizing() {
return None;
}
remove_from_list(cc.inner.cast());
let t = unsafe { ptr::read(cc.inner().get_elem()) };
let layout = cc.inner().layout();
#[cfg(feature = "weak-ptrs")]
cc.inner().drop_metadata();
unsafe {
cc_dealloc(cc.inner, layout, state);
}
Some(t)
});
match res {
Ok(Some(t)) => Ok(t),
_ => Err(ManuallyDrop::into_inner(cc)),
}
}
}
impl<T: ?Sized + Trace> Cc<T> {
#[inline]
pub fn ptr_eq(this: &Cc<T>, other: &Cc<T>) -> bool {
ptr::eq(this.inner.as_ptr() as *const (), other.inner.as_ptr() as *const ())
}
#[inline]
pub fn strong_count(&self) -> u32 {
self.counter_marker().counter() as u32
}
#[cfg(feature = "finalization")]
#[inline]
#[track_caller]
pub fn finalize_again(&mut self) {
assert!(
state(|state| !state.is_collecting() && !state.is_finalizing() && !state.is_dropping()),
"Cc::finalize_again cannot be called while collecting"
);
self.counter_marker().set_finalized(false);
}
#[cfg(feature = "finalization")]
#[inline]
pub fn already_finalized(&self) -> bool {
!self.counter_marker().needs_finalization()
}
#[inline]
pub fn mark_alive(&self) {
remove_from_list(self.inner.cast());
}
#[inline(always)]
fn counter_marker(&self) -> &CounterMarker {
&self.inner().counter_marker
}
#[inline(always)]
pub(crate) fn inner(&self) -> &CcBox<T> {
unsafe { self.inner.as_ref() }
}
#[cfg(feature = "weak-ptrs")]
#[inline(always)]
pub(crate) fn inner_ptr(&self) -> NonNull<CcBox<T>> {
self.inner
}
#[cfg(feature = "weak-ptrs")] #[inline(always)]
#[must_use]
pub(crate) fn __new_internal(inner: NonNull<CcBox<T>>) -> Cc<T> {
Cc {
inner,
_phantom: PhantomData,
}
}
}
impl<T: ?Sized + Trace> Clone for Cc<T> {
#[inline]
#[track_caller]
fn clone(&self) -> Self {
#[cfg(debug_assertions)]
if state(|state| state.is_tracing()) {
panic!("Cannot clone while tracing!");
}
if self.counter_marker().increment_counter().is_err() {
panic!("Too many references has been created to a single Cc");
}
self.mark_alive();
Cc {
inner: self.inner,
_phantom: PhantomData,
}
}
}
impl<T: ?Sized + Trace> Deref for Cc<T> {
type Target = T;
#[inline]
#[track_caller]
fn deref(&self) -> &Self::Target {
#[cfg(debug_assertions)]
if state(|state| state.is_tracing()) {
panic!("Cannot deref while tracing!");
}
self.inner().get_elem()
}
}
impl<T: ?Sized + Trace> Drop for Cc<T> {
fn drop(&mut self) {
#[cfg(debug_assertions)]
if state(|state| state.is_tracing()) {
panic!("Cannot drop while tracing!");
}
#[inline]
fn decrement_counter<T: ?Sized + Trace>(cc: &Cc<T>) {
let res = cc.counter_marker().decrement_counter();
debug_assert!(res.is_ok());
}
#[inline]
fn handle_possible_cycle<T: ?Sized + Trace>(cc: &Cc<T>) {
decrement_counter(cc);
add_to_list(cc.inner.cast());
}
if self.counter_marker().is_in_list_or_queue() {
decrement_counter(self);
return;
}
if self.counter_marker().counter() == 1 {
state(|state| {
#[cfg(feature = "finalization")]
if self.counter_marker().needs_finalization() {
let _finalizing_guard = replace_state_field!(finalizing, true, state);
self.counter_marker().set_finalized(true);
self.inner().get_elem().finalize();
if self.counter_marker().counter() != 1 {
handle_possible_cycle(self);
return;
}
}
decrement_counter(self);
remove_from_list(self.inner.cast());
let _dropping_guard = replace_state_field!(dropping, true, state);
let layout = self.inner().layout();
#[cfg(feature = "weak-ptrs")]
{
self.counter_marker().set_dropped(true);
}
unsafe {
drop_in_place(self.inner().get_elem_mut());
#[cfg(feature = "pedantic-debug-assertions")]
debug_assert_eq!(
0, self.counter_marker().counter(),
"Trying to deallocate a CcBox with a reference counter > 0"
);
#[cfg(feature = "weak-ptrs")]
self.inner().drop_metadata();
cc_dealloc(self.inner, layout, state);
}
});
} else {
handle_possible_cycle(self);
}
}
}
unsafe impl<T: ?Sized + Trace> Trace for Cc<T> {
#[inline]
#[track_caller]
fn trace(&self, ctx: &mut Context<'_>) {
CcBox::trace(self.inner.cast(), ctx);
}
}
impl<T: ?Sized + Trace> Finalize for Cc<T> {}
#[repr(C)]
pub(crate) struct CcBox<T: ?Sized + Trace + 'static> {
next: UnsafeCell<Option<NonNull<CcBox<()>>>>,
prev: UnsafeCell<Option<NonNull<CcBox<()>>>>,
metadata: Cell<Metadata>,
counter_marker: CounterMarker,
_phantom: PhantomData<Rc<()>>, elem: UnsafeCell<T>,
}
impl<T: Trace> CcBox<T> {
#[must_use]
fn new(t: T, state: &State) -> NonNull<CcBox<T>> {
let layout = Layout::new::<CcBox<T>>();
#[cfg(feature = "finalization")]
let already_finalized = state.is_finalizing();
#[cfg(not(feature = "finalization"))]
let already_finalized = false;
unsafe {
let ptr: NonNull<CcBox<T>> = cc_alloc(layout, state);
ptr::write(
ptr.as_ptr(),
CcBox {
next: UnsafeCell::new(None),
prev: UnsafeCell::new(None),
metadata: Metadata::new(ptr),
counter_marker: CounterMarker::new_with_counter_to_one(already_finalized),
_phantom: PhantomData,
elem: UnsafeCell::new(t),
},
);
ptr
}
}
#[cfg(all(test, feature = "std"))] #[must_use]
pub(crate) fn new_for_tests(t: T) -> NonNull<CcBox<T>> {
state(|state| CcBox::new(t, state))
}
}
impl<T: ?Sized + Trace> CcBox<T> {
#[inline]
pub(crate) fn get_elem(&self) -> &T {
unsafe { &*self.elem.get() }
}
#[inline]
pub(crate) fn get_elem_mut(&self) -> *mut T {
self.elem.get()
}
#[inline]
pub(crate) fn counter_marker(&self) -> &CounterMarker {
&self.counter_marker
}
#[inline]
pub(crate) fn layout(&self) -> Layout {
#[cfg(feature = "nightly")]
{
self.vtable().vtable.layout()
}
#[cfg(not(feature = "nightly"))]
unsafe {
Layout::for_value(self.vtable().fat_ptr.as_ref())
}
}
#[inline]
fn vtable(&self) -> VTable {
#[cfg(feature = "weak-ptrs")]
unsafe {
if self.counter_marker.has_allocated_for_metadata() {
self.metadata.get().boxed_metadata.as_ref().vtable
} else {
self.metadata.get().vtable
}
}
#[cfg(not(feature = "weak-ptrs"))]
unsafe {
self.metadata.get().vtable
}
}
#[cfg(feature = "weak-ptrs")]
#[inline]
pub(crate) fn get_or_init_metadata(&self) -> NonNull<BoxedMetadata> {
unsafe {
if self.counter_marker.has_allocated_for_metadata() {
self.metadata.get().boxed_metadata
} else {
let vtable = self.metadata.get().vtable;
let ptr = BoxedMetadata::new(vtable, WeakCounterMarker::new(true));
self.metadata.set(Metadata {
boxed_metadata: ptr,
});
self.counter_marker.set_allocated_for_metadata(true);
ptr
}
}
}
#[cfg(feature = "weak-ptrs")]
#[inline(always)]
pub(crate) unsafe fn get_metadata_unchecked(&self) -> NonNull<BoxedMetadata> {
self.metadata.get().boxed_metadata
}
#[cfg(feature = "weak-ptrs")]
#[inline]
pub(crate) fn drop_metadata(&self) {
if self.counter_marker.has_allocated_for_metadata() {
unsafe {
let boxed = self.get_metadata_unchecked();
if boxed.as_ref().weak_counter_marker.counter() == 0 {
dealloc_other(boxed);
} else {
boxed.as_ref().weak_counter_marker.set_accessible(false);
}
}
}
}
#[inline]
pub(super) fn get_next(&self) -> *mut Option<NonNull<CcBox<()>>> {
self.next.get()
}
#[inline]
pub(super) fn get_prev(&self) -> *mut Option<NonNull<CcBox<()>>> {
self.prev.get()
}
}
unsafe impl<T: ?Sized + Trace> Trace for CcBox<T> {
#[inline(always)]
fn trace(&self, ctx: &mut Context<'_>) {
self.get_elem().trace(ctx);
}
}
impl<T: ?Sized + Trace> Finalize for CcBox<T> {
#[inline(always)]
fn finalize(&self) {
self.get_elem().finalize();
}
}
#[inline]
pub(crate) fn remove_from_list(ptr: NonNull<CcBox<()>>) {
let counter_marker = unsafe { ptr.as_ref() }.counter_marker();
if counter_marker.is_in_possible_cycles() {
let _ = POSSIBLE_CYCLES.try_with(|pc| {
#[cfg(feature = "pedantic-debug-assertions")]
debug_assert!(pc.iter().contains(ptr));
counter_marker.mark(Mark::NonMarked);
pc.remove(ptr);
});
} else {
#[cfg(feature = "pedantic-debug-assertions")]
debug_assert! {
POSSIBLE_CYCLES.try_with(|pc| {
!pc.iter().contains(ptr)
}).unwrap_or(true)
};
}
}
#[inline]
pub(crate) fn add_to_list(ptr: NonNull<CcBox<()>>) {
let counter_marker = unsafe { ptr.as_ref() }.counter_marker();
if !counter_marker.is_in_possible_cycles() {
let _ = POSSIBLE_CYCLES.try_with(|pc| {
#[cfg(feature = "pedantic-debug-assertions")]
debug_assert!(!pc.iter().contains(ptr));
debug_assert!(counter_marker.is_not_marked());
pc.add(ptr);
counter_marker.reset_tracing_counter();
counter_marker.mark(Mark::PossibleCycles);
});
} else {
#[cfg(feature = "pedantic-debug-assertions")]
debug_assert! {
POSSIBLE_CYCLES.try_with(|pc| {
pc.iter().contains(ptr)
}).unwrap_or(true)
};
}
}
impl CcBox<()> {
#[inline]
pub(super) fn trace_inner(ptr: NonNull<Self>, ctx: &mut Context<'_>) {
unsafe {
CcBox::get_traceable(ptr).as_ref().trace(ctx);
}
}
#[cfg(feature = "finalization")]
#[inline]
pub(super) fn finalize_inner(ptr: NonNull<Self>) -> bool {
unsafe {
if ptr.as_ref().counter_marker().needs_finalization() {
ptr.as_ref().counter_marker().set_finalized(true);
CcBox::get_traceable(ptr).as_ref().finalize_elem();
true
} else {
false
}
}
}
#[inline]
pub(super) unsafe fn drop_inner(ptr: NonNull<Self>) {
#[cfg(feature = "weak-ptrs")]
{
ptr.as_ref().counter_marker().set_dropped(true);
}
CcBox::get_traceable(ptr).as_mut().drop_elem();
}
#[inline]
fn get_traceable(ptr: NonNull<Self>) -> NonNull<dyn InternalTrace> {
#[cfg(feature = "nightly")]
unsafe {
let vtable = ptr.as_ref().vtable().vtable;
NonNull::from_raw_parts(ptr.cast(), vtable)
}
#[cfg(not(feature = "nightly"))]
unsafe {
ptr.as_ref().vtable().fat_ptr
}
}
#[inline(never)] fn trace(ptr: NonNull<Self>, ctx: &mut Context<'_>) {
let counter_marker = unsafe { ptr.as_ref() }.counter_marker();
match ctx.inner() {
ContextInner::Counting {
root_list,
non_root_list,
queue,
} => {
if counter_marker.is_in_list_or_queue() {
debug_assert!(counter_marker.tracing_counter() < counter_marker.counter());
let res = counter_marker.increment_tracing_counter();
debug_assert!(res.is_ok());
if counter_marker.is_in_list() && counter_marker.counter() == counter_marker.tracing_counter() {
#[cfg(feature = "pedantic-debug-assertions")]
debug_assert!(root_list.iter().contains(ptr));
root_list.remove(ptr);
non_root_list.add(ptr);
}
} else {
if counter_marker.is_in_possible_cycles() {
let res = counter_marker.increment_tracing_counter();
debug_assert!(res.is_ok());
return;
}
counter_marker.reset_tracing_counter();
let res = counter_marker.increment_tracing_counter();
debug_assert!(res.is_ok());
queue.add(ptr);
counter_marker.mark(Mark::InQueue);
}
},
ContextInner::RootTracing { non_root_list, queue } => {
if counter_marker.is_in_list() && counter_marker.counter() == counter_marker.tracing_counter() {
#[cfg(feature = "pedantic-debug-assertions")]
debug_assert!(non_root_list.iter().contains(ptr));
counter_marker.mark(Mark::NonMarked);
non_root_list.remove(ptr);
queue.add(ptr);
counter_marker.mark(Mark::InQueue);
}
},
}
}
}
#[derive(Copy, Clone)]
union Metadata {
vtable: VTable,
#[cfg(feature = "weak-ptrs")]
boxed_metadata: NonNull<BoxedMetadata>,
}
impl Metadata {
#[inline]
fn new<T: Trace>(cc_box: NonNull<CcBox<T>>) -> Cell<Metadata> {
#[cfg(feature = "nightly")]
let vtable = VTable {
vtable: metadata(cc_box.as_ptr() as *mut dyn InternalTrace),
};
#[cfg(not(feature = "nightly"))]
let vtable = VTable {
fat_ptr: unsafe { NonNull::new_unchecked(cc_box.as_ptr() as *mut dyn InternalTrace)
},
};
Cell::new(Metadata {
vtable
})
}
}
#[derive(Copy, Clone)]
struct VTable {
#[cfg(feature = "nightly")]
vtable: DynMetadata<dyn InternalTrace>,
#[cfg(not(feature = "nightly"))]
fat_ptr: NonNull<dyn InternalTrace>,
}
#[cfg(feature = "weak-ptrs")]
pub(crate) struct BoxedMetadata {
vtable: VTable,
pub(crate) weak_counter_marker: WeakCounterMarker,
}
#[cfg(feature = "weak-ptrs")]
impl BoxedMetadata {
#[inline]
fn new(vtable: VTable, weak_counter_marker: WeakCounterMarker) -> NonNull<BoxedMetadata> {
unsafe {
let ptr: NonNull<BoxedMetadata> = alloc_other();
ptr::write(
ptr.as_ptr(),
BoxedMetadata {
vtable,
weak_counter_marker,
},
);
ptr
}
}
}
trait InternalTrace: Trace {
#[cfg(feature = "finalization")]
fn finalize_elem(&self);
unsafe fn drop_elem(&self);
}
impl<T: ?Sized + Trace> InternalTrace for CcBox<T> {
#[cfg(feature = "finalization")]
fn finalize_elem(&self) {
self.get_elem().finalize();
}
unsafe fn drop_elem(&self) {
drop_in_place(self.get_elem_mut());
}
}
impl<T: Trace + Default> Default for Cc<T> {
#[inline]
fn default() -> Self {
Cc::new(<T as Default>::default())
}
}
impl<T: ?Sized + Trace> AsRef<T> for Cc<T> {
#[inline(always)]
fn as_ref(&self) -> &T {
self
}
}
impl<T: ?Sized + Trace> Borrow<T> for Cc<T> {
#[inline(always)]
fn borrow(&self) -> &T {
self
}
}
impl<T: Trace> From<T> for Cc<T> {
#[inline]
fn from(value: T) -> Self {
Cc::new(value)
}
}
impl<T: ?Sized + Trace + Debug> Debug for Cc<T> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Debug::fmt(&**self, f)
}
}
impl<T: ?Sized + Trace + Display> Display for Cc<T> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&**self, f)
}
}
impl<T: ?Sized + Trace> Pointer for Cc<T> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Pointer::fmt(&ptr::addr_of!(**self), f)
}
}
impl<T: ?Sized + Trace + PartialEq> PartialEq for Cc<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl<T: ?Sized + Trace + Eq> Eq for Cc<T> {}
impl<T: ?Sized + Trace + Ord> Ord for Cc<T> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
(**self).cmp(&**other)
}
}
impl<T: ?Sized + Trace + PartialOrd> PartialOrd for Cc<T> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
(**self).partial_cmp(&**other)
}
#[inline]
fn lt(&self, other: &Self) -> bool {
**self < **other
}
#[inline]
fn le(&self, other: &Self) -> bool {
**self <= **other
}
#[inline]
fn gt(&self, other: &Self) -> bool {
**self > **other
}
#[inline]
fn ge(&self, other: &Self) -> bool {
**self >= **other
}
}
impl<T: ?Sized + Trace + Hash> Hash for Cc<T> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl<T: ?Sized + Trace + UnwindSafe> UnwindSafe for Cc<T> {}
impl<T: ?Sized + Trace + RefUnwindSafe> RefUnwindSafe for Cc<T> {}