Skip to main content

osom_lib_arc/carc/
weak.rs

1//! Holds the definition of [`CWeak`].
2use core::sync::atomic::{Ordering, fence};
3
4use osom_lib_alloc::traits::Allocator;
5use osom_lib_reprc::traits::ReprC;
6use osom_lib_try_clone::TryClone;
7
8use crate::{
9    consts::MAX_REFERENCES,
10    errors::{MaxReferencesExceededError, WeakUpgradeError},
11};
12
13use super::{carc::CArc, internal::InternalArc};
14
15/// A weak reference to the underlying [`CArc`].
16///
17/// This object cannot inspect the underlying value (unless `T` is `Copy`).
18/// But it does track weak references, and each weak reference can build
19/// a strong reference, assuming any other strong reference is alive.
20#[repr(transparent)]
21#[must_use]
22#[derive(Debug)]
23pub struct CWeak<T, TAllocator: Allocator> {
24    internal: InternalArc<T, TAllocator>,
25}
26
27unsafe impl<T: ReprC, TAllocator: Allocator> ReprC for CWeak<T, TAllocator> {
28    const CHECK: () = const {
29        osom_lib_reprc::hidden::is_reprc::<T>();
30        osom_lib_reprc::hidden::is_reprc::<InternalArc<T, TAllocator>>();
31    };
32}
33
34impl<T, TAllocator: Allocator> CWeak<T, TAllocator> {
35    /// Returns the number of strong references to the [`CWeak`].
36    #[inline(always)]
37    #[must_use]
38    pub fn strong_count(&self) -> u32 {
39        self.internal.strong().load(Ordering::Relaxed)
40    }
41
42    /// Returns the number of weak references to the [`CWeak`].
43    #[inline(always)]
44    #[must_use]
45    pub fn weak_count(&self) -> u32 {
46        self.internal.weak().load(Ordering::Relaxed)
47    }
48
49    /// Returns a reference to the underlying data.
50    ///
51    /// This function is only available if `T` implements `Copy`.
52    /// That is because being `Copy` means it is not `Drop`. And
53    /// so we don't need strong references to keep the data alive.
54    #[inline]
55    #[must_use]
56    pub fn data(&self) -> &T
57    where
58        T: Copy,
59    {
60        self.internal.data()
61    }
62
63    /// Upgrades current weak reference to the strong [`CArc`].
64    ///
65    /// # Errors
66    ///
67    /// For details see [`WeakUpgradeError`].
68    pub fn upgrade(&self) -> Result<CArc<T, TAllocator>, WeakUpgradeError> {
69        let strong = self.internal.strong();
70        let mut current = strong.load(Ordering::Relaxed);
71
72        loop {
73            if current == 0 {
74                return Err(WeakUpgradeError::NoStrongReferencesAlive);
75            }
76
77            if current >= MAX_REFERENCES {
78                return Err(WeakUpgradeError::MaxReferencesExceeded);
79            }
80
81            match strong.compare_exchange_weak(current, current + 1, Ordering::Acquire, Ordering::Relaxed) {
82                Ok(_) => return Ok(CArc::from_internal(self.internal.clone())),
83                Err(new) => current = new,
84            }
85        }
86    }
87
88    /// Abandons current weak reference.
89    ///
90    /// If the internal weak counter is positive it returns false.
91    ///
92    /// Otherwise it deallocates the underlying memory and returns true.
93    /// In particular only single (the last) [`CWeak`] returns true
94    /// by calling this.
95    #[inline(always)]
96    #[must_use]
97    pub fn abandon(mut self) -> bool {
98        let result = self.internal_abandon();
99        core::mem::forget(self);
100        result
101    }
102
103    #[inline]
104    pub(super) fn from_internal(internal: InternalArc<T, TAllocator>) -> Self {
105        Self { internal }
106    }
107
108    fn internal_abandon(&mut self) -> bool {
109        let internal = unsafe { core::ptr::read(&raw const self.internal) };
110        let prev = internal.weak().fetch_sub(1, Ordering::Release);
111        if prev > 1 {
112            return false;
113        }
114
115        // Synchronize with all prior Release decrements before deallocating.
116        fence(Ordering::Acquire);
117        unsafe { internal.deallocate_memory() };
118        true
119    }
120}
121
122impl<T, TAllocator: Allocator> Drop for CWeak<T, TAllocator> {
123    fn drop(&mut self) {
124        let _ = self.internal_abandon();
125    }
126}
127
128impl<T, TAllocator: Allocator> Clone for CWeak<T, TAllocator> {
129    fn clone(&self) -> Self {
130        self.try_clone()
131            .expect("CWeak weak reference count is too high. Cannot exceed {MAX_REFERENCES}")
132    }
133}
134
135impl<T, TAllocator: Allocator> TryClone for CWeak<T, TAllocator> {
136    type Error = MaxReferencesExceededError;
137
138    fn try_clone(&self) -> Result<Self, Self::Error> {
139        let internal_clone = self.internal.clone();
140        let prev_value = internal_clone.weak().fetch_add(1, Ordering::Relaxed);
141        if prev_value >= MAX_REFERENCES {
142            internal_clone.weak().fetch_sub(1, Ordering::Relaxed);
143            return Err(MaxReferencesExceededError);
144        }
145        Ok(Self {
146            internal: internal_clone,
147        })
148    }
149}