Skip to main content

osom_lib_arc/carc/
carc.rs

1//! Holds the definition of [`CArc`].
2use core::{
3    borrow::Borrow,
4    cmp::Ordering as CmpOrdering,
5    hash::Hash,
6    ops::Deref,
7    sync::atomic::{Ordering, fence},
8};
9
10use osom_lib_alloc::traits::Allocator;
11use osom_lib_reprc::traits::ReprC;
12use osom_lib_try_clone::TryClone;
13
14use crate::{
15    consts::MAX_REFERENCES,
16    errors::{CArcError, MaxReferencesExceededError},
17};
18
19use super::{internal::InternalArc, weak::CWeak};
20
21/// A smart pointer that can be used to share ownership of a value.
22///
23/// This struct is equivalent to the standard `Arc` type,
24/// but is `#[repr(C)]` and thus safe to use across the ffi boundaries.
25#[repr(transparent)]
26#[must_use]
27#[derive(Debug)]
28pub struct CArc<T, TAllocator: Allocator> {
29    internal: InternalArc<T, TAllocator>,
30}
31
32unsafe impl<T: ReprC, TAllocator: Allocator> ReprC for CArc<T, TAllocator> {
33    const CHECK: () = const {
34        osom_lib_reprc::hidden::is_reprc::<T>();
35        osom_lib_reprc::hidden::is_reprc::<CWeak<T, TAllocator>>();
36        osom_lib_reprc::hidden::is_reprc::<InternalArc<T, TAllocator>>();
37    };
38}
39
40/// The result of abandoning a [`CArc`]. Holds the underlying data and
41/// a weak reference to the [`CArc`].
42#[must_use]
43#[repr(C)]
44#[derive(Debug)]
45pub struct AbandonResult<T, TAllocator: Allocator> {
46    /// The underlying data.
47    pub data: T,
48
49    /// The weak reference to the final strong [`CArc`] reference.
50    pub weak: CWeak<T, TAllocator>,
51}
52
53unsafe impl<T: ReprC, TAllocator: Allocator> ReprC for AbandonResult<T, TAllocator> {
54    const CHECK: () = const {
55        osom_lib_reprc::hidden::is_reprc::<T>();
56        osom_lib_reprc::hidden::is_reprc::<CWeak<T, TAllocator>>();
57    };
58}
59
60impl<T, TAllocator: Allocator> CArc<T, TAllocator> {
61    /// Creates a new [`CArc`] with the default allocator.
62    ///
63    /// # Errors
64    ///
65    /// For details see [`CArcError`].
66    #[inline]
67    pub fn new(value: T) -> Result<Self, CArcError>
68    where
69        TAllocator: Default,
70    {
71        let internal = InternalArc::new(value)?;
72        Ok(Self::from_internal(internal))
73    }
74
75    /// Creates a new [`CArc`] with a custom allocator.
76    ///
77    /// # Errors
78    ///
79    /// For details see [`CArcError`].
80    #[inline]
81    pub fn with_allocator(value: T, allocator: TAllocator) -> Result<Self, CArcError> {
82        let internal = InternalArc::with_allocator(value, allocator)?;
83        Ok(Self::from_internal(internal))
84    }
85
86    /// Returns the number of strong references to the [`CArc`].
87    #[inline(always)]
88    #[must_use]
89    pub fn strong_count(carc: &Self) -> u32 {
90        carc.internal.strong().load(Ordering::Relaxed)
91    }
92
93    /// Returns the number of weak references to the [`CArc`].
94    #[inline(always)]
95    #[must_use]
96    pub fn weak_count(carc: &Self) -> u32 {
97        carc.internal.weak().load(Ordering::Relaxed)
98    }
99
100    /// Returns a reference to the underlying value.
101    #[inline]
102    #[must_use]
103    pub const fn data(carc: &Self) -> &T {
104        carc.internal.data()
105    }
106
107    /// Creates a new [`CWeak`] reference to the [`CArc`].
108    ///
109    /// # Errors
110    ///
111    /// If the weak reference count is too high. Cannot exceed [`MAX_REFERENCES`].
112    pub fn downgrade(carc: &Self) -> Result<CWeak<T, TAllocator>, MaxReferencesExceededError> {
113        let internal_clone = carc.internal.clone();
114        let prev_value = internal_clone.weak().fetch_add(1, Ordering::Relaxed);
115        if prev_value >= MAX_REFERENCES {
116            internal_clone.weak().fetch_sub(1, Ordering::Relaxed);
117            return Err(MaxReferencesExceededError);
118        }
119        Ok(CWeak::from_internal(internal_clone))
120    }
121
122    /// Abandons current [`CArc`].
123    ///
124    /// This function returns `None` if the underlying strong reference counter
125    /// is still positive. Otherwise it returns the underlying data and
126    /// a weak reference to the [`CArc`]. In particular the data will be dropped
127    /// by the caller if that was the last strong reference.
128    #[inline]
129    #[must_use]
130    pub fn abandon(mut carc: Self) -> Option<AbandonResult<T, TAllocator>> {
131        let result = unsafe { CArc::internal_abandon(&mut carc) };
132        core::mem::forget(carc);
133        result
134    }
135
136    /// Converts the [`CArc`] into a raw pointer.
137    ///
138    /// # Notes
139    ///
140    /// * This method does not touch internal reference counters, `self` is simply forgotten.
141    /// * The caller must ensure that the pointer does not outlive the [`CArc`].
142    #[inline(always)]
143    #[must_use]
144    pub const fn into_raw_ptr(carc: Self) -> *mut u8 {
145        let ptr = carc.internal.raw_ptr();
146        core::mem::forget(carc);
147        ptr
148    }
149
150    /// Converts a raw pointer back to a [`CArc`].
151    ///
152    /// # Safety
153    ///
154    /// * The caller must ensure that the pointer came from the previous
155    ///   call to [`CArc::into_raw_ptr`].
156    /// * The caller must ensure that the raw pointer won't be used after
157    ///   the call.
158    ///
159    /// Otherwise the behavior is undefined.
160    #[inline(always)]
161    pub const unsafe fn from_raw_ptr(ptr: *mut u8) -> Self {
162        let internal = InternalArc::from_raw_ptr(ptr);
163        Self { internal }
164    }
165
166    #[inline(always)]
167    pub(super) fn from_internal(internal: InternalArc<T, TAllocator>) -> Self {
168        Self { internal }
169    }
170
171    unsafe fn internal_abandon(carc: &mut Self) -> Option<AbandonResult<T, TAllocator>> {
172        let internal = unsafe { core::ptr::read(&raw const carc.internal) };
173        let prev = internal.strong().fetch_sub(1, Ordering::Release);
174        if prev > 1 {
175            return None;
176        }
177
178        let data = unsafe { internal.read_data() };
179
180        // Synchronize with all prior Release decrements before deallocating.
181        fence(Ordering::Acquire);
182
183        Some(AbandonResult {
184            data,
185            weak: CWeak::from_internal(internal),
186        })
187    }
188}
189
190impl<T, TAllocator: Allocator> Drop for CArc<T, TAllocator> {
191    fn drop(&mut self) {
192        let _ = unsafe { CArc::internal_abandon(self) };
193    }
194}
195
196impl<T, TAllocator: Allocator> AsRef<T> for CArc<T, TAllocator> {
197    fn as_ref(&self) -> &T {
198        CArc::data(self)
199    }
200}
201
202impl<T, TAllocator: Allocator> Deref for CArc<T, TAllocator> {
203    type Target = T;
204
205    fn deref(&self) -> &Self::Target {
206        CArc::data(self)
207    }
208}
209
210impl<T, TAllocator: Allocator> Borrow<T> for CArc<T, TAllocator> {
211    fn borrow(&self) -> &T {
212        CArc::data(self)
213    }
214}
215
216impl<T, TAllocator: Allocator> Clone for CArc<T, TAllocator> {
217    fn clone(&self) -> Self {
218        self.try_clone().expect("CArc strong reference count is too high.")
219    }
220}
221
222impl<T, TAllocator: Allocator> TryClone for CArc<T, TAllocator> {
223    type Error = MaxReferencesExceededError;
224
225    fn try_clone(&self) -> Result<Self, Self::Error> {
226        let internal_clone = self.internal.clone();
227        let prev_value = internal_clone.strong().fetch_add(1, Ordering::Relaxed);
228        if prev_value >= MAX_REFERENCES {
229            internal_clone.strong().fetch_sub(1, Ordering::Relaxed);
230            return Err(MaxReferencesExceededError);
231        }
232
233        Ok(Self {
234            internal: internal_clone,
235        })
236    }
237}
238
239impl<T: PartialEq, TAllocator: Allocator> PartialEq for CArc<T, TAllocator> {
240    fn eq(&self, other: &Self) -> bool {
241        self.internal.raw_equals(&other.internal) || CArc::data(self) == CArc::data(other)
242    }
243}
244
245impl<T: Eq, TAllocator: Allocator> Eq for CArc<T, TAllocator> {}
246
247impl<T: Hash, TAllocator: Allocator> Hash for CArc<T, TAllocator> {
248    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
249        CArc::data(self).hash(state);
250    }
251}
252
253impl<T: PartialOrd, TAllocator: Allocator> PartialOrd for CArc<T, TAllocator> {
254    fn partial_cmp(&self, other: &Self) -> Option<CmpOrdering> {
255        CArc::data(self).partial_cmp(CArc::data(other))
256    }
257}
258
259impl<T: Ord, TAllocator: Allocator> Ord for CArc<T, TAllocator> {
260    fn cmp(&self, other: &Self) -> CmpOrdering {
261        CArc::data(self).cmp(CArc::data(other))
262    }
263}