Skip to main content

arctic/concurrent/
value.rs

1//! Values that can safely be stored in a [`ConcurrentMap`][crate::concurrent::Map],
2//! and referenced behind an [`smr::Guard`].
3
4use core::borrow::Borrow;
5use core::fmt::Debug;
6use core::mem::ManuallyDrop;
7use core::ops::Deref;
8
9use crate::concurrent::smr;
10use crate::concurrent::smr::Guard as _;
11use crate::sequential;
12pub use crate::sequential::value::Arc;
13
14/// Values that can safely be stored in a [`ConcurrentMap`][crate::concurrent::Map].
15///
16/// Values may be either inline or indirect. An inline
17/// value (e.g., [`u64`]) is stored directly in an edge and can be freely
18/// copied. An indirect value (e.g., [`Box<T>`]) is a pointer to a separate
19/// allocation; the pointer is stored in an edge.
20///
21/// Note: we don't need [`Send`] or [`Sync`] bounds here.
22/// It's fine to create a concurrent map with non-Sync
23/// values; the map instance just won't implement Sync.
24pub trait Value: sequential::Value + Borrow<Self::Borrowed> {
25    /// We need this extra layer of indirection relative to [`SequentialMap`][crate::sequential::Map]
26    /// because edges can be concurrently modified.
27    ///
28    /// For an inline value, the sequential map can return a reference
29    /// to the edge containing the value; the borrow checker ensures
30    /// the edge is immutable. This is not true for the concurrent map,
31    /// which instead needs to copy out the value and return a reference to
32    /// the copy.
33    ///
34    /// For an indirect value, the concurrent map copies out a pointer
35    /// and interprets it as reference.
36    type Borrowed;
37
38    /// This is a type-level function that allows inline values to
39    /// discard a [`smr::Guard`].
40    type Guard<G>: smr::Guard<Self> + From<G>
41    where
42        G: smr::Guard<Self>;
43
44    /// # Safety
45    ///
46    /// Caller must guarantee the following:
47    /// - `raw` was created from [sequential::Value::into_raw`]
48    /// - There are no calls to [`sequential::Value::from_raw_unchecked`] while `raw` is live
49    /// - This value is not mutated while `raw` is live
50    unsafe fn borrow_from_raw_unchecked(raw: &u64) -> &Self::Borrowed;
51}
52
53macro_rules! impl_integer {
54    ($($ty:ty),*) => {
55        $(
56            impl Value for $ty {
57                type Borrowed = Self;
58
59                type Guard<G>
60                    = smr::no_op::Guard<G, Self>
61                where
62                    G: smr::Guard<Self>;
63
64                #[inline]
65                unsafe fn borrow_from_raw_unchecked(raw: &u64) -> &Self::Borrowed {
66                    unsafe { core::mem::transmute::<&u64, &Self>(raw) }
67                }
68            }
69        )*
70    };
71}
72
73impl_integer!(u64, i64);
74
75// Note: references are inline values because a
76// `&T` itself can be freely copied, even if
77// `T` is not `Copy`.
78impl<'v, T: 'v + Sized> Value for &'v T {
79    type Borrowed = Self;
80
81    type Guard<G>
82        = smr::no_op::Guard<G, Self>
83    where
84        G: smr::Guard<Self>;
85
86    #[inline]
87    unsafe fn borrow_from_raw_unchecked(raw: &u64) -> &Self::Borrowed {
88        unsafe { core::mem::transmute::<&u64, &Self>(raw) }
89    }
90}
91
92impl<T: Sized> Value for Box<T> {
93    type Borrowed = T;
94
95    type Guard<G>
96        = G
97    where
98        G: smr::Guard<Self>;
99
100    #[inline]
101    unsafe fn borrow_from_raw_unchecked(raw: &u64) -> &Self::Borrowed {
102        let borrow = unsafe { core::ptr::with_exposed_provenance::<T>((*raw) as usize).as_ref() };
103        if_validate!(borrow.unwrap(), unsafe { borrow.unwrap_unchecked() })
104    }
105}
106
107impl<T: Sized> Value for Arc<T> {
108    type Borrowed = ArcRef<T>;
109
110    type Guard<G>
111        = G
112    where
113        G: smr::Guard<Self>;
114
115    #[inline]
116    unsafe fn borrow_from_raw_unchecked(raw: &u64) -> &Self::Borrowed {
117        let borrow = unsafe {
118            core::ptr::with_exposed_provenance::<T>((*raw) as usize)
119                .cast::<ArcRef<T>>()
120                .as_ref()
121        };
122        if_validate!(borrow.unwrap(), unsafe { borrow.unwrap_unchecked() })
123    }
124}
125
126impl<T> Borrow<ArcRef<T>> for crate::sequential::value::Arc<T> {
127    fn borrow(&self) -> &ArcRef<T> {
128        unsafe { core::mem::transmute::<&T, &ArcRef<T>>(self.0.as_ref()) }
129    }
130}
131
132/// Transparent wrapper for [`Arc<T>`] pointee that can
133/// be safely cloned into an [`Arc<T>`] via [`ToOwned`].
134#[repr(transparent)]
135#[derive(Debug)]
136pub struct ArcRef<T>(T);
137
138impl<T> Deref for ArcRef<T> {
139    type Target = T;
140    #[inline]
141    fn deref(&self) -> &Self::Target {
142        &self.0
143    }
144}
145
146impl<T> ToOwned for ArcRef<T> {
147    type Owned = Arc<T>;
148    /// Clone into an owned `Arc` by incrementing the strong reference count.
149    fn to_owned(&self) -> Self::Owned {
150        // SAFETY: `ArcRef` is `repr(transparent)`
151        let ptr = unsafe { core::mem::transmute::<&Self, &T>(self) };
152
153        // SAFETY: SMR guarantees `ptr` is not yet freed,
154        // so strong count must be >= 1
155        unsafe { crate::sync::Arc::increment_strong_count(ptr) };
156
157        // SAFETY: `ptr` was returned from `Arc::into_raw`
158        Arc(unsafe { crate::sync::Arc::from_raw(ptr) })
159    }
160}
161
162/// Guard that provides read-only access to a removed value while
163/// preventing the value from being freed. Retires the value on drop.
164///
165/// Note: this value may still be concurrently accessed by other
166/// threads, so this guard cannot safely provide mutable access.
167pub struct Owned<G: smr::Guard<V>, V: Value> {
168    guard: V::Guard<G>,
169    raw: u64,
170}
171
172impl<G, V> Owned<G, V>
173where
174    G: smr::Guard<V>,
175    V: Value,
176{
177    pub(crate) unsafe fn wrap(guard: G, raw: u64) -> Self {
178        Self {
179            guard: V::Guard::<G>::from(guard),
180            raw,
181        }
182    }
183}
184
185impl<G, V> Deref for Owned<G, V>
186where
187    G: smr::Guard<V>,
188    V: Value,
189{
190    type Target = V::Borrowed;
191
192    #[inline]
193    fn deref(&self) -> &Self::Target {
194        unsafe { V::borrow_from_raw_unchecked(&self.raw) }
195    }
196}
197
198impl<G: smr::Guard<V>, V: Value> Drop for Owned<G, V> {
199    fn drop(&mut self) {
200        unsafe { self.guard.retire_value(self.raw) }
201    }
202}
203
204impl<G, V> Debug for Owned<G, V>
205where
206    G: smr::Guard<V>,
207    V: Value,
208    V::Borrowed: Debug,
209{
210    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
211        self.deref().fmt(f)
212    }
213}
214
215/// Guard that provides read-only access to a value while
216/// preventing the value from being freed.
217pub struct Shared<G: smr::Guard<V>, V: Value> {
218    _guard: V::Guard<G>,
219    raw: u64,
220}
221
222impl<G, V> Shared<G, V>
223where
224    G: smr::Guard<V>,
225    V: Value,
226{
227    pub(crate) unsafe fn wrap(guard: G, raw: u64) -> Self {
228        Self {
229            _guard: V::Guard::<G>::from(guard),
230            raw,
231        }
232    }
233}
234
235impl<G, V> Deref for Shared<G, V>
236where
237    G: smr::Guard<V>,
238    V: Value,
239{
240    type Target = V::Borrowed;
241
242    #[inline]
243    fn deref(&self) -> &Self::Target {
244        unsafe { V::borrow_from_raw_unchecked(&self.raw) }
245    }
246}
247
248impl<G, V> Debug for Shared<G, V>
249where
250    G: smr::Guard<V>,
251    V: Value,
252    V::Borrowed: Debug,
253{
254    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
255        self.deref().fmt(f)
256    }
257}
258
259/// Guard that provides read-only access to both the old
260/// and new values of an atomic update operation,
261/// preventing both from being freed.
262///
263/// Retires the old value on drop.
264pub struct Updated<G: smr::Guard<V>, V: Value> {
265    guard: V::Guard<G>,
266    old: u64,
267    new: u64,
268}
269
270impl<G, V> Updated<G, V>
271where
272    G: smr::Guard<V>,
273    V: Value,
274{
275    pub(crate) unsafe fn wrap(guard: G, old: u64, new: u64) -> Self {
276        Self {
277            guard: V::Guard::<G>::from(guard),
278            old,
279            new,
280        }
281    }
282
283    /// Return the old value before updating.
284    #[inline]
285    pub fn old(&self) -> &V::Borrowed {
286        unsafe { V::borrow_from_raw_unchecked(&self.old) }
287    }
288
289    /// Return the new value after updating.
290    #[inline]
291    #[expect(clippy::new_ret_no_self)]
292    pub fn new(&self) -> &V::Borrowed {
293        unsafe { V::borrow_from_raw_unchecked(&self.new) }
294    }
295}
296
297impl<G: smr::Guard<V>, V: Value> Drop for Updated<G, V> {
298    fn drop(&mut self) {
299        unsafe { self.guard.retire_value(self.old) }
300    }
301}
302
303impl<G, V> Debug for Updated<G, V>
304where
305    G: smr::Guard<V>,
306    V: Value,
307    V::Borrowed: Debug,
308{
309    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
310        f.debug_struct("Updated")
311            .field("old", self.old())
312            .field("new", self.new())
313            .finish()
314    }
315}
316
317/// Guard that provides read-only access to both the old
318/// and new values of an atomic upsert operation,
319/// preventing both from being freed.
320///
321/// Retires the old value on drop, if it existed.
322pub struct Upserted<G: smr::Guard<V>, V: Value> {
323    guard: V::Guard<G>,
324    old: Option<u64>,
325    new: u64,
326}
327
328impl<G, V> Upserted<G, V>
329where
330    G: smr::Guard<V>,
331    V: Value,
332{
333    pub(crate) unsafe fn wrap(guard: G, old: Option<u64>, new: u64) -> Self {
334        Self {
335            guard: V::Guard::<G>::from(guard),
336            old,
337            new,
338        }
339    }
340
341    pub(crate) fn try_into_inserted(self) -> Result<Shared<G, V>, Self> {
342        // https://internals.rust-lang.org/t/move-out-of-deref-for-manuallydrop/19216
343        let upserted = ManuallyDrop::new(self);
344
345        match upserted.old {
346            None => Ok(Shared {
347                // HACK: work around not being able to move out of deref
348                _guard: unsafe { core::ptr::read(&upserted.guard) },
349                raw: upserted.new,
350            }),
351            Some(_) => Err(ManuallyDrop::into_inner(upserted)),
352        }
353    }
354
355    /// Return the old value before upserting.
356    #[inline]
357    pub fn old(&self) -> Option<&V::Borrowed> {
358        self.old
359            .as_ref()
360            .map(|old| unsafe { V::borrow_from_raw_unchecked(old) })
361    }
362
363    /// Return the new value after upserting.
364    #[inline]
365    #[expect(clippy::new_ret_no_self)]
366    pub fn new(&self) -> &V::Borrowed {
367        unsafe { V::borrow_from_raw_unchecked(&self.new) }
368    }
369}
370
371impl<G: smr::Guard<V>, V: Value> Drop for Upserted<G, V> {
372    fn drop(&mut self) {
373        let Some(old) = self.old else { return };
374        unsafe { self.guard.retire_value(old) }
375    }
376}
377
378impl<G, V> Debug for Upserted<G, V>
379where
380    G: smr::Guard<V>,
381    V: Value,
382    V::Borrowed: Debug,
383{
384    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
385        f.debug_struct("Upserted")
386            .field("old", &self.old())
387            .field("new", self.new())
388            .finish()
389    }
390}