openhcl_boot/single_threaded.rs
1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Support for working with global variables in a single-threaded environment.
5//! In such an environment, it is safe to access globals even if they don't
6//! implement [`Sync`], since there is only one thread that can access them. But
7//! code still needs to be careful to avoid creating multiple _mutable_
8//! references to the same global. These types provide abstractions for doing
9//! this safely.
10
11use core::cell::Cell;
12use core::cell::UnsafeCell;
13use core::ops::Deref;
14use core::ops::DerefMut;
15
16/// A wrapper around a value that implements `Sync` even if `T` does not
17/// implement `Sync`.
18///
19/// This is only safe to use in a single-threaded environment. Do not compile
20/// this type into a multi-threaded environment.
21pub struct SingleThreaded<T>(pub T);
22
23// SAFETY: we must mark this as Sync so that it can be `static`. It is
24// not actually necessarily Sync, so this can only be used in a
25// single-threaded environment.
26unsafe impl<T> Sync for SingleThreaded<T> {}
27
28impl<T> Deref for SingleThreaded<T> {
29 type Target = T;
30
31 fn deref(&self) -> &T {
32 &self.0
33 }
34}
35
36/// A reference returned by [`off_stack`].
37pub struct OffStackRef<'a, T>(&'a mut T, BorrowRef<'a>);
38
39impl<'a, T> OffStackRef<'a, T> {
40 #[track_caller]
41 #[doc(hidden)]
42 pub unsafe fn new_internal(value: &'a UnsafeCell<T>, used: &'a Cell<bool>) -> Self {
43 let r = BorrowRef::try_new(used).expect("function recursed");
44 // SAFETY: we just set `used` to true, so we know that we are the only
45 // one accessing `value`.
46 let value = unsafe { &mut *value.get() };
47 OffStackRef(value, r)
48 }
49
50 /// Leaks the borrow, returning the reference.
51 ///
52 /// This will lead to a panic if there is an attempt to borrow the value
53 /// again (e.g., if the function invoking the `off_stack` macro is called
54 /// again).
55 pub fn leak(this: Self) -> &'a mut T {
56 core::mem::forget(this.1);
57 this.0
58 }
59}
60
61struct BorrowRef<'a>(&'a Cell<bool>);
62
63impl<'a> BorrowRef<'a> {
64 fn try_new(used: &'a Cell<bool>) -> Option<Self> {
65 if used.replace(true) {
66 None
67 } else {
68 Some(Self(used))
69 }
70 }
71}
72
73impl Drop for BorrowRef<'_> {
74 fn drop(&mut self) {
75 self.0.set(false);
76 }
77}
78
79impl<T> Deref for OffStackRef<'_, T> {
80 type Target = T;
81 fn deref(&self) -> &T {
82 self.0
83 }
84}
85
86impl<T> DerefMut for OffStackRef<'_, T> {
87 fn deref_mut(&mut self) -> &mut T {
88 self.0
89 }
90}
91
92/// Returns a mutable reference to a value that is stored as a global `static`
93/// variable rather than exist on the stack.
94///
95/// This is useful for working with large objects that don't fit on the stack.
96/// It is an alternative to using [`SingleThreaded`] with
97/// [`RefCell`](core::cell::RefCell); `RefCell` has the disadvantage of putting
98/// an extra `bool` next to the value in memory, which can waste a lot of space
99/// for heavily-aligned objects.
100///
101/// Panics if this function is called recursively, since this would attempt to
102/// create multiple mutable references to the same global variable.
103///
104/// This only works in a single-threaded environment.
105macro_rules! off_stack {
106 ($ty:ty, $val:expr) => {{
107 use core::cell::Cell;
108 use core::cell::UnsafeCell;
109 use $crate::single_threaded::OffStackRef;
110 use $crate::single_threaded::SingleThreaded;
111
112 static VALUE: SingleThreaded<UnsafeCell<$ty>> = SingleThreaded(UnsafeCell::new($val));
113 static USED: SingleThreaded<Cell<bool>> = SingleThreaded(Cell::new(false));
114
115 // SAFETY: `USED` is always used to track the usage of `VALUE`.
116 unsafe { OffStackRef::new_internal(&VALUE.0, &USED.0) }
117 }};
118}
119pub(crate) use off_stack;