choir/
arc.rs

1use std::{
2    fmt, mem, ops,
3    ptr::NonNull,
4    sync::atomic::{AtomicUsize, Ordering},
5};
6
7/// Note: `pub(super)` is only needed for a hack
8#[derive(Debug)]
9pub(super) struct LinearcInner<T: ?Sized> {
10    pub(super) ref_count: AtomicUsize,
11    pub(super) data: T,
12}
13
14/// Linear atomically referenced pointer.
15/// Analogous to `Arc` but without `Weak` functionality,
16/// and with an extra powers to treat the type as linear.
17/// In particular, it supports atomic destruction with the extraction of data.
18///
19/// See <https://internals.rust-lang.org/t/de-facto-linear-types-and-arc-need-for-an-unwrap-or-drop-api/12939>
20/// And <https://github.com/rust-lang/rust/pull/79665>
21pub struct Linearc<T: ?Sized> {
22    ptr: NonNull<LinearcInner<T>>,
23}
24
25unsafe impl<T: ?Sized> Send for Linearc<T> {}
26unsafe impl<T: ?Sized> Sync for Linearc<T> {}
27
28impl<T: ?Sized> Linearc<T> {
29    #[inline]
30    pub(super) fn from_inner(inner: Box<LinearcInner<T>>) -> Self {
31        Self {
32            ptr: unsafe { NonNull::new_unchecked(Box::into_raw(inner)) },
33        }
34    }
35
36    /// Clone a given pointer.
37    #[inline]
38    pub fn clone(arc: &Self) -> Self {
39        arc.clone()
40    }
41
42    fn into_box(arc: Self) -> Option<Box<LinearcInner<T>>> {
43        let count = unsafe { arc.ptr.as_ref() }
44            .ref_count
45            .fetch_sub(1, Ordering::AcqRel);
46        if count == 1 {
47            let inner = unsafe { Box::from_raw(arc.ptr.as_ptr()) };
48            mem::forget(arc);
49            Some(inner)
50        } else {
51            mem::forget(arc);
52            None
53        }
54    }
55
56    /// Drop a pointer and return true if this was the last instance.
57    #[inline]
58    pub fn drop_last(arc: Self) -> bool {
59        Linearc::into_box(arc).is_some()
60    }
61}
62
63impl<T> Linearc<T> {
64    /// Create a new pointer from data.
65    #[inline]
66    pub fn new(data: T) -> Self {
67        Self::from_inner(Box::new(LinearcInner {
68            ref_count: AtomicUsize::new(1),
69            data,
70        }))
71    }
72
73    /// Move out the value of this pointer if it's the last instance.
74    pub fn into_inner(arc: Self) -> Option<T> {
75        Linearc::into_box(arc).map(|inner| inner.data)
76    }
77}
78
79impl<T: ?Sized> Clone for Linearc<T> {
80    fn clone(&self) -> Self {
81        unsafe { self.ptr.as_ref() }
82            .ref_count
83            .fetch_add(1, Ordering::Release);
84        Self { ptr: self.ptr }
85    }
86}
87
88impl<T: ?Sized> Drop for Linearc<T> {
89    fn drop(&mut self) {
90        let count = unsafe { self.ptr.as_ref() }
91            .ref_count
92            .fetch_sub(1, Ordering::AcqRel);
93        if count == 1 {
94            let _ = unsafe { Box::from_raw(self.ptr.as_ptr()) };
95        }
96    }
97}
98
99impl<T: ?Sized> ops::Deref for Linearc<T> {
100    type Target = T;
101
102    #[inline]
103    fn deref(&self) -> &T {
104        &unsafe { self.ptr.as_ref() }.data
105    }
106}
107
108impl<T: fmt::Debug> fmt::Debug for Linearc<T> {
109    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
110        unsafe { self.ptr.as_ref() }.fmt(formatter)
111    }
112}