Skip to main content

flize/
atomic.rs

1use crate::{NullTag, Shared, Shield, Tag};
2use core::{
3    fmt,
4    marker::PhantomData,
5    mem,
6    sync::atomic::{AtomicUsize, Ordering},
7};
8
9fn map_both<T, U, F>(result: Result<T, T>, f: F) -> Result<U, U>
10where
11    F: FnOnce(T) -> U + Copy,
12{
13    result.map(f).map_err(f)
14}
15
16/// An `Atomic` represents a tagged atomic pointer protected by the collection system.
17///
18/// This struct provides methods for manipulating the atomic pointer via
19/// standard atomic operations using `Shared` as the corresponding non atomic version.
20#[repr(transparent)]
21pub struct Atomic<V, T1 = NullTag, T2 = NullTag>
22where
23    T1: Tag,
24    T2: Tag,
25{
26    pub(crate) data: AtomicUsize,
27    _m0: PhantomData<V>,
28    _m1: PhantomData<T1>,
29    _m2: PhantomData<T2>,
30}
31
32impl<V, T1, T2> Atomic<V, T1, T2>
33where
34    T1: Tag,
35    T2: Tag,
36{
37    /// Constructs an `Atomic` from a raw tagged pointer represented as an integer.
38    ///
39    /// # Safety
40    /// Marked unsafe because this is not usually what the user wants.
41    /// `Atomic::null` should be preferred when possible.
42    pub unsafe fn from_raw(raw: usize) -> Self {
43        Self {
44            data: AtomicUsize::new(raw),
45            _m0: PhantomData,
46            _m1: PhantomData,
47            _m2: PhantomData,
48        }
49    }
50
51    /// Constructs a new `Atomic` from a tagged pointer.
52    ///
53    /// # Safety
54    /// The alignment of `V` must free up sufficient low bits so that `T` fits.
55    pub fn new(shared: Shared<'_, V, T1, T2>) -> Self {
56        unsafe { Self::from_raw(shared.into_raw()) }
57    }
58
59    /// Constructs a new `Atomic` with a null value.
60    pub fn null() -> Self {
61        unsafe { Self::from_raw(0) }
62    }
63
64    /// This constructs a `Vec<Atomic>` with null values in an optimized manner.
65    pub fn null_vec(len: usize) -> Vec<Self> {
66        unsafe { mem::transmute(vec![0_usize; len]) }
67    }
68
69    /// Load a the tagged pointer.
70    pub fn load<'collector, 'shield, S>(
71        &self,
72        ordering: Ordering,
73        _shield: &'shield S,
74    ) -> Shared<'shield, V, T1, T2>
75    where
76        S: Shield<'collector>,
77    {
78        let raw = self.data.load(ordering);
79        unsafe { Shared::from_raw(raw) }
80    }
81
82    /// Store a tagged pointer, replacing the previous value.
83    pub fn store(&self, data: Shared<'_, V, T1, T2>, ordering: Ordering) {
84        let raw = data.into_raw();
85        self.data.store(raw, ordering);
86    }
87
88    /// Swap the stored tagged pointer, returning the old one.
89    pub fn swap<'collector, 'shield, S>(
90        &self,
91        new: Shared<'_, V, T1, T2>,
92        ordering: Ordering,
93        _shield: &'shield S,
94    ) -> Shared<'shield, V, T1, T2>
95    where
96        S: Shield<'collector>,
97    {
98        let new_raw = new.into_raw();
99        let old_raw = self.data.swap(new_raw, ordering);
100        unsafe { Shared::from_raw(old_raw) }
101    }
102
103    /// Conditionally swap the stored tagged pointer, always returns the previous value.
104    pub fn compare_and_swap<'collector, 'shield, S>(
105        &self,
106        current: Shared<'_, V, T1, T2>,
107        new: Shared<'_, V, T1, T2>,
108        order: Ordering,
109        _shield: &'shield S,
110    ) -> Shared<'shield, V, T1, T2>
111    where
112        S: Shield<'collector>,
113    {
114        let current_raw = current.into_raw();
115        let new_raw = new.into_raw();
116        let old_raw = self.data.compare_and_swap(current_raw, new_raw, order);
117        unsafe { Shared::from_raw(old_raw) }
118    }
119
120    /// Conditionally exchange the stored tagged pointer, always returns
121    /// the previous value and a result indicating if it was written or not.
122    /// On success this value is guaranteed to be equal to current.
123    pub fn compare_exchange<'collector, 'shield, S>(
124        &self,
125        current: Shared<'_, V, T1, T2>,
126        new: Shared<'_, V, T1, T2>,
127        success: Ordering,
128        failure: Ordering,
129        _shield: &'shield S,
130    ) -> Result<Shared<'shield, V, T1, T2>, Shared<'shield, V, T1, T2>>
131    where
132        S: Shield<'collector>,
133    {
134        let current_raw = current.into_raw();
135        let new_raw = new.into_raw();
136        let result = self
137            .data
138            .compare_exchange(current_raw, new_raw, success, failure);
139
140        map_both(result, |raw| unsafe { Shared::from_raw(raw) })
141    }
142
143    /// Conditionally exchange the stored tagged pointer, always returns
144    /// the previous value and a result indicating if it was written or not.
145    /// On success this value is guaranteed to be equal to current.
146    ///
147    /// This variant may spuriously fail on platforms where LL/SC is used.
148    /// This allows more efficient code generation on those platforms.
149    pub fn compare_exchange_weak<'collector, 'shield, S>(
150        &self,
151        current: Shared<'_, V, T1, T2>,
152        new: Shared<'_, V, T1, T2>,
153        success: Ordering,
154        failure: Ordering,
155        _shield: &'shield S,
156    ) -> Result<Shared<'shield, V, T1, T2>, Shared<'shield, V, T1, T2>>
157    where
158        S: Shield<'collector>,
159    {
160        let current_raw = current.into_raw();
161        let new_raw = new.into_raw();
162        let result = self
163            .data
164            .compare_exchange_weak(current_raw, new_raw, success, failure);
165
166        map_both(result, |raw| unsafe { Shared::from_raw(raw) })
167    }
168}
169
170unsafe impl<V, T1, T2> Send for Atomic<V, T1, T2>
171where
172    T1: Tag,
173    T2: Tag,
174{
175}
176
177unsafe impl<V, T1, T2> Sync for Atomic<V, T1, T2>
178where
179    T1: Tag,
180    T2: Tag,
181{
182}
183
184impl<V, T1, T2> Unpin for Atomic<V, T1, T2>
185where
186    T1: Tag,
187    T2: Tag,
188{
189}
190
191impl<V, T1, T2> fmt::Debug for Atomic<V, T1, T2>
192where
193    T1: Tag,
194    T2: Tag,
195{
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197        use crate::tag;
198        let data = self.data.load(Ordering::SeqCst);
199        let lo = tag::read_tag::<T1>(data, tag::TagPosition::Lo);
200        let hi = tag::read_tag::<T2>(data, tag::TagPosition::Hi);
201
202        f.debug_struct("Atomic")
203            .field("raw", &data)
204            .field("low_tag", &lo)
205            .field("high_tag", &hi)
206            .finish()
207    }
208}