Skip to main content

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