rune/runtime/
access.rs

1use core::cell::Cell;
2use core::fmt;
3use core::mem::ManuallyDrop;
4use core::ptr::NonNull;
5
6/// Test if exclusively held.
7const EXCLUSIVE: usize = 1usize.rotate_right(2);
8/// Sentinel value to indicate that access is taken.
9const MOVED: usize = 1usize.rotate_right(1);
10/// Mask indicating if the value is exclusively set or moved.
11const MASK: usize = EXCLUSIVE | MOVED;
12
13/// An error raised when failing to access a value.
14///
15/// Access errors can be raised for various reasons, such as:
16/// * The value you are trying to access is an empty placeholder.
17/// * The value is already being accessed in an incompatible way, such as trying
18///   to access a value exclusively twice.
19/// * The value has been taken and is no longer present.
20#[derive(Debug)]
21#[cfg_attr(test, derive(PartialEq))]
22#[non_exhaustive]
23pub struct AccessError {
24    kind: AccessErrorKind,
25}
26
27impl AccessError {
28    #[inline]
29    const fn new(kind: AccessErrorKind) -> Self {
30        Self { kind }
31    }
32}
33
34impl fmt::Display for AccessError {
35    #[inline]
36    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37        match &self.kind {
38            AccessErrorKind::NotAccessibleRef(s) => write!(
39                f,
40                "Cannot read, value has snapshot {s} and is not available for reading"
41            ),
42            AccessErrorKind::NotAccessibleMut(s) => write!(
43                f,
44                "Cannot write, value has snapshot {s} and is not available for writing"
45            ),
46            AccessErrorKind::NotAccessibleTake(s) => write!(
47                f,
48                "Cannot take, value has snapshot {s} and is not available for taking"
49            ),
50        }
51    }
52}
53
54impl core::error::Error for AccessError {}
55
56#[derive(Debug)]
57#[cfg_attr(test, derive(PartialEq))]
58enum AccessErrorKind {
59    NotAccessibleRef(Snapshot),
60    NotAccessibleMut(Snapshot),
61    NotAccessibleTake(Snapshot),
62}
63
64/// Snapshot that can be used to indicate how the value was being accessed at
65/// the time of an error.
66#[derive(PartialEq)]
67#[repr(transparent)]
68pub(crate) struct Snapshot(usize);
69
70impl Snapshot {
71    /// Test if the snapshot indicates that the value is readable.
72    pub(crate) fn is_readable(&self) -> bool {
73        self.0 & MASK == 0
74    }
75
76    /// Test if the snapshot indicates that the value is writable.
77    pub(crate) fn is_writable(&self) -> bool {
78        self.0 & MASK == 0
79    }
80
81    /// Test if access is exclusively held.
82    pub(crate) fn is_exclusive(&self) -> bool {
83        self.0 & MASK != 0
84    }
85
86    /// The number of times a value is shared.
87    pub(crate) fn shared(&self) -> usize {
88        self.0 & !MASK
89    }
90}
91
92impl fmt::Display for Snapshot {
93    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94        if self.0 & MOVED != 0 {
95            write!(f, "M")?;
96        } else {
97            write!(f, "-")?;
98        }
99
100        if self.0 & EXCLUSIVE != 0 {
101            write!(f, "X")?;
102        } else {
103            write!(f, "-")?;
104        }
105
106        write!(f, "{:06}", self.shared())?;
107        Ok(())
108    }
109}
110
111impl fmt::Debug for Snapshot {
112    #[inline]
113    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114        write!(f, "Snapshot({self})")
115    }
116}
117
118/// Access flags.
119///
120/// These accomplish the following things:
121/// * Indicates if a value is exclusively held.
122/// * Indicates if a value is taken .
123/// * Indicates if a value is shared, and if so by how many.
124#[repr(transparent)]
125pub(crate) struct Access(Cell<usize>);
126
127impl Access {
128    /// Construct a new default access.
129    pub(crate) const fn new() -> Self {
130        Self(Cell::new(0))
131    }
132
133    /// Test if we can have shared access without modifying the internal count.
134    #[inline(always)]
135    pub(crate) fn is_shared(&self) -> bool {
136        self.0.get() & MASK == 0
137    }
138
139    /// Test if we can have exclusive access without modifying the internal
140    /// count.
141    #[inline(always)]
142    pub(crate) fn is_exclusive(&self) -> bool {
143        self.0.get() == 0
144    }
145
146    /// Test if the data has been taken.
147    #[inline(always)]
148    pub(crate) fn is_taken(&self) -> bool {
149        self.0.get() & MOVED != 0
150    }
151
152    /// Mark that we want shared access to the given access token.
153    pub(crate) fn shared(&self) -> Result<AccessGuard<'_>, AccessError> {
154        self.try_shared()?;
155        Ok(AccessGuard(self))
156    }
157
158    #[inline(always)]
159    pub(crate) fn try_shared(&self) -> Result<(), AccessError> {
160        let state = self.0.get();
161
162        if state & MASK != 0 {
163            debug_assert_eq!(
164                state & !MASK,
165                0,
166                "count should be zero, but was {}",
167                Snapshot(state)
168            );
169            return Err(AccessError::new(AccessErrorKind::NotAccessibleRef(
170                Snapshot(state),
171            )));
172        }
173
174        // NB: Max number of shared.
175        if state == !MASK {
176            crate::alloc::abort();
177        }
178
179        self.0.set(state + 1);
180        Ok(())
181    }
182
183    /// Mark that we want exclusive access to the given access token.
184    #[inline(always)]
185    pub(crate) fn exclusive(&self) -> Result<AccessGuard<'_>, AccessError> {
186        self.try_exclusive()?;
187        Ok(AccessGuard(self))
188    }
189
190    #[inline(always)]
191    pub(crate) fn try_exclusive(&self) -> Result<(), AccessError> {
192        let state = self.0.get();
193
194        if state != 0 {
195            return Err(AccessError::new(AccessErrorKind::NotAccessibleMut(
196                Snapshot(state),
197            )));
198        }
199
200        self.0.set(state | EXCLUSIVE);
201        Ok(())
202    }
203
204    /// Mark that we want to mark the given access as "taken".
205    ///
206    /// I.e. whatever guarded data is no longer available.
207    #[inline(always)]
208    pub(crate) fn try_take(&self) -> Result<(), AccessError> {
209        let state = self.0.get();
210
211        if state != 0 {
212            return Err(AccessError::new(AccessErrorKind::NotAccessibleTake(
213                Snapshot(state),
214            )));
215        }
216
217        self.0.set(state | MOVED);
218        Ok(())
219    }
220
221    /// Unconditionally mark the given access as "taken".
222    #[inline(always)]
223    pub(crate) fn take(&self) {
224        let state = self.0.get();
225        self.0.set(state | MOVED);
226    }
227
228    /// Release the current access, unless it's moved.
229    #[inline(always)]
230    pub(super) fn release(&self) {
231        let b = self.0.get();
232
233        let b = if b & EXCLUSIVE != 0 {
234            b & !EXCLUSIVE
235        } else {
236            debug_assert_ne!(b & !MASK, 0, "count should be zero but was {}", Snapshot(b));
237            b - 1
238        };
239
240        self.0.set(b);
241    }
242
243    /// Get a snapshot of current access.
244    #[inline(always)]
245    pub(super) fn snapshot(&self) -> Snapshot {
246        Snapshot(self.0.get())
247    }
248}
249
250impl fmt::Debug for Access {
251    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252        write!(f, "{}", Snapshot(self.0.get()))
253    }
254}
255
256/// A guard around some specific access access.
257#[repr(transparent)]
258pub(crate) struct AccessGuard<'a>(&'a Access);
259
260impl AccessGuard<'_> {
261    /// Convert into a raw guard which does not have a lifetime associated with
262    /// it. Droping the raw guard will release the resource.
263    ///
264    /// # Safety
265    ///
266    /// Since we're losing track of the lifetime, caller must ensure that the
267    /// access outlives the guard.
268    pub(crate) unsafe fn into_raw(self) -> RawAccessGuard {
269        RawAccessGuard(NonNull::from(ManuallyDrop::new(self).0))
270    }
271}
272
273impl Drop for AccessGuard<'_> {
274    fn drop(&mut self) {
275        self.0.release();
276    }
277}
278
279/// A raw guard around some level of access which will be released once the guard is dropped.
280#[repr(transparent)]
281pub(crate) struct RawAccessGuard(NonNull<Access>);
282
283impl Drop for RawAccessGuard {
284    fn drop(&mut self) {
285        unsafe { self.0.as_ref().release() }
286    }
287}