|
| 1 | +//! # RefCells for Neotron OS. |
| 2 | +//! |
| 3 | +//! Like the `RefCell` in the standard library, except that it's thread-safe |
| 4 | +//! and uses the BIOS critical section to make it so. |
| 5 | +
|
| 6 | +// =========================================================================== |
| 7 | +// Modules and Imports |
| 8 | +// =========================================================================== |
| 9 | + |
| 10 | +use core::{ |
| 11 | + cell::UnsafeCell, |
| 12 | + ops::{Deref, DerefMut}, |
| 13 | + sync::atomic::{AtomicBool, Ordering}, |
| 14 | +}; |
| 15 | + |
| 16 | +// =========================================================================== |
| 17 | +// Global Variables |
| 18 | +// =========================================================================== |
| 19 | + |
| 20 | +// None |
| 21 | + |
| 22 | +// =========================================================================== |
| 23 | +// Macros |
| 24 | +// =========================================================================== |
| 25 | + |
| 26 | +// None |
| 27 | + |
| 28 | +// =========================================================================== |
| 29 | +// Public types |
| 30 | +// =========================================================================== |
| 31 | + |
| 32 | +/// Indicates a failure to lock the refcell because it was already locked. |
| 33 | +#[derive(Debug)] |
| 34 | +pub struct LockError; |
| 35 | + |
| 36 | +/// A cell that gives you references, and is thread-safe. |
| 37 | +/// |
| 38 | +/// Uses the BIOS to ensure thread-safety whilst checking if the lock is taken |
| 39 | +/// or not. |
| 40 | +pub struct CsRefCell<T> { |
| 41 | + inner: UnsafeCell<T>, |
| 42 | + locked: AtomicBool, |
| 43 | +} |
| 44 | + |
| 45 | +impl<T> CsRefCell<T> { |
| 46 | + /// Create a new cell. |
| 47 | + pub const fn new(value: T) -> CsRefCell<T> { |
| 48 | + CsRefCell { |
| 49 | + inner: UnsafeCell::new(value), |
| 50 | + locked: AtomicBool::new(false), |
| 51 | + } |
| 52 | + } |
| 53 | + |
| 54 | + /// Try and do something with the lock. |
| 55 | + pub fn with<F, U>(&self, f: F) -> Result<U, LockError> |
| 56 | + where |
| 57 | + F: FnOnce(&mut CsRefCellGuard<T>) -> U, |
| 58 | + { |
| 59 | + let mut guard = self.try_lock()?; |
| 60 | + let result = f(&mut guard); |
| 61 | + drop(guard); |
| 62 | + Ok(result) |
| 63 | + } |
| 64 | + |
| 65 | + /// Lock the cell. |
| 66 | + /// |
| 67 | + /// If you can't lock it (because it is already locked), this function will panic. |
| 68 | + pub fn lock(&self) -> CsRefCellGuard<T> { |
| 69 | + self.try_lock().unwrap() |
| 70 | + } |
| 71 | + |
| 72 | + /// Try and grab the lock. |
| 73 | + /// |
| 74 | + /// It'll fail if it's already been taken. |
| 75 | + /// |
| 76 | + /// It'll panic if the global lock is in a bad state, or you try and |
| 77 | + /// re-enter this function from an interrupt whilst the global lock is held. |
| 78 | + /// Don't do that. |
| 79 | + pub fn try_lock(&self) -> Result<CsRefCellGuard<T>, LockError> { |
| 80 | + let api = crate::API.get(); |
| 81 | + |
| 82 | + if (api.compare_and_swap_bool)(&self.locked, false, true) { |
| 83 | + // succesfully swapped `false` for `true` |
| 84 | + core::sync::atomic::fence(Ordering::Acquire); |
| 85 | + Ok(CsRefCellGuard { parent: self }) |
| 86 | + } else { |
| 87 | + // cell is already locked |
| 88 | + Err(LockError) |
| 89 | + } |
| 90 | + } |
| 91 | +} |
| 92 | + |
| 93 | +/// Mark our type as thread-safe. |
| 94 | +/// |
| 95 | +/// # Safety |
| 96 | +/// |
| 97 | +/// We use the BIOS critical sections to control access to the global lock, and |
| 98 | +/// refcell locks are only tested whilst holding the global lock. Thus it is now |
| 99 | +/// thread-safe. |
| 100 | +unsafe impl<T> Sync for CsRefCell<T> {} |
| 101 | + |
| 102 | +/// Represents an active borrow of a [`CsRefCell`]. |
| 103 | +pub struct CsRefCellGuard<'a, T> { |
| 104 | + parent: &'a CsRefCell<T>, |
| 105 | +} |
| 106 | + |
| 107 | +impl<'a, T> Deref for CsRefCellGuard<'a, T> { |
| 108 | + type Target = T; |
| 109 | + |
| 110 | + fn deref(&self) -> &Self::Target { |
| 111 | + let ptr = self.parent.inner.get(); |
| 112 | + unsafe { &*ptr } |
| 113 | + } |
| 114 | +} |
| 115 | + |
| 116 | +impl<'a, T> DerefMut for CsRefCellGuard<'a, T> { |
| 117 | + fn deref_mut(&mut self) -> &mut Self::Target { |
| 118 | + let ptr = self.parent.inner.get(); |
| 119 | + unsafe { &mut *ptr } |
| 120 | + } |
| 121 | +} |
| 122 | + |
| 123 | +impl<'a, T> Drop for CsRefCellGuard<'a, T> { |
| 124 | + fn drop(&mut self) { |
| 125 | + // We hold this refcell guard exclusively, so this can't race |
| 126 | + self.parent.locked.store(false, Ordering::Release); |
| 127 | + } |
| 128 | +} |
| 129 | + |
| 130 | +// =========================================================================== |
| 131 | +// Private types |
| 132 | +// =========================================================================== |
| 133 | + |
| 134 | +// None |
| 135 | + |
| 136 | +// =========================================================================== |
| 137 | +// Private functions |
| 138 | +// =========================================================================== |
| 139 | + |
| 140 | +// None |
| 141 | + |
| 142 | +// =========================================================================== |
| 143 | +// Public functions |
| 144 | +// =========================================================================== |
| 145 | + |
| 146 | +// None |
| 147 | + |
| 148 | +// =========================================================================== |
| 149 | +// Tests |
| 150 | +// =========================================================================== |
| 151 | + |
| 152 | +// None |
| 153 | + |
| 154 | +// =========================================================================== |
| 155 | +// End of file |
| 156 | +// =========================================================================== |
0 commit comments