osiris/sync/
atomic.rs

1//! Atomic abstractions for single and multi-core systems.
2
3#[cfg(all(feature = "multi-core", feature = "no-atomic-cas"))]
4compile_error!(
5    "The `multi-core` feature requires atomic-cas operations to be available on the target. Enable the `atomic-cas` feature."
6);
7
8#[cfg(all(feature = "no-atomic-cas", not(target_has_atomic = "8")))]
9compile_error!(
10    "The `atomic-cas` feature requires the target to have atomic operations on at least 8-bit integers."
11);
12
13#[allow(unused_imports)]
14pub use core::sync::atomic::Ordering;
15
16#[inline(always)]
17pub fn irq_free<T>(f: impl FnOnce() -> T) -> T {
18    let state = hal::asm::disable_irq_save();
19    let result = f();
20    hal::asm::enable_irq_restr(state);
21
22    result
23}
24
25// ----------------------------AtomicU8----------------------------
26#[cfg(any(feature = "no-atomic-cas", not(target_has_atomic = "64")))]
27use core::cell::UnsafeCell;
28
29#[cfg(all(feature = "no-atomic-cas"))]
30/// An atomic `u8`.
31pub struct AtomicU8 {
32    value: UnsafeCell<u8>,
33}
34
35#[allow(unused_imports)]
36#[cfg(not(all(feature = "no-atomic-cas")))]
37pub use core::sync::atomic::AtomicU8;
38
39#[cfg(all(feature = "no-atomic-cas"))]
40impl AtomicU8 {
41    /// Creates a new atomic u8.
42    pub const fn new(value: u8) -> Self {
43        Self {
44            value: UnsafeCell::new(value),
45        }
46    }
47
48    /// Loads the value.
49    pub fn load(&self, _: Ordering) -> u8 {
50        todo!("Implement atomic load for u8");
51    }
52
53    /// Stores a value.
54    pub fn store(&self, value: u8, _: Ordering) {
55        todo!("Implement atomic store for u8");
56    }
57
58    /// Compares the value and exchanges it.
59    #[allow(dead_code)]
60    pub fn compare_exchange(
61        &self,
62        current: u8,
63        new: u8,
64        _: Ordering,
65        _: Ordering,
66    ) -> Result<u8, u8> {
67        todo!("Implement atomic compare_exchange for u8");
68    }
69
70    ///fetch a value, apply the function and write back the modified value atomically
71    pub fn fetch_update<F>(&self, _: Ordering, _: Ordering, f: F) -> Result<u8, u8>
72    where
73        F: FnMut(u8) -> Option<u8>,
74    {
75        todo!("Implement atomic fetch_update for u8");
76    }
77}
78
79#[allow(unused_imports)]
80#[cfg(not(all(feature = "no-atomic-cas")))]
81pub use core::sync::atomic::AtomicBool;
82
83#[cfg(all(feature = "no-atomic-cas"))]
84/// An atomic `bool`.
85pub struct AtomicBool {
86    value: UnsafeCell<bool>,
87}
88
89#[cfg(all(feature = "no-atomic-cas"))]
90impl AtomicBool {
91    /// Creates a new atomic bool.
92    pub const fn new(value: bool) -> Self {
93        Self {
94            value: UnsafeCell::new(value),
95        }
96    }
97
98    /// Loads the value.
99    pub fn load(&self, _: Ordering) -> bool {
100        todo!("Implement atomic load for bool");
101    }
102
103    /// Stores a value.
104    pub fn store(&self, value: bool, _: Ordering) {
105        todo!("Implement atomic store for bool");
106    }
107
108    /// Compares the value and exchanges it.
109    pub fn compare_exchange(
110        &self,
111        current: bool,
112        new: bool,
113        _: Ordering,
114        _: Ordering,
115    ) -> Result<bool, bool> {
116        todo!("Implement atomic compare_exchange for bool");
117    }
118}
119
120// ----------------------------AtomicU64----------------------------
121#[allow(unused_imports)]
122#[cfg(target_has_atomic = "64")]
123pub use core::sync::atomic::AtomicU64;
124
125#[cfg(not(target_has_atomic = "64"))]
126/// An atomic `u64` implemented by disabling interrupts around each operation.
127pub struct AtomicU64 {
128    value: UnsafeCell<u64>,
129}
130
131#[cfg(not(target_has_atomic = "64"))]
132unsafe impl Sync for AtomicU64 {}
133
134#[cfg(not(target_has_atomic = "64"))]
135#[allow(dead_code)]
136impl AtomicU64 {
137    /// Creates a new atomic u64.
138    pub const fn new(value: u64) -> Self {
139        Self {
140            value: UnsafeCell::new(value),
141        }
142    }
143
144    /// Loads the value.
145    pub fn load(&self, _: Ordering) -> u64 {
146        irq_free(|| {
147            // SAFETY: Interrupts are disabled, so this read is exclusive with writes.
148            unsafe { *self.value.get() }
149        })
150    }
151
152    /// Stores a value.
153    pub fn store(&self, value: u64, _: Ordering) {
154        irq_free(|| {
155            // SAFETY: Interrupts are disabled, so this write is exclusive with other access.
156            unsafe {
157                *self.value.get() = value;
158            }
159        });
160    }
161
162    /// Compares the value and exchanges it.
163    pub fn compare_exchange(
164        &self,
165        current: u64,
166        new: u64,
167        _: Ordering,
168        _: Ordering,
169    ) -> Result<u64, u64> {
170        irq_free(|| {
171            // SAFETY: Interrupts are disabled, so this read-modify-write is exclusive.
172            unsafe {
173                let value = self.value.get();
174                if *value == current {
175                    *value = new;
176                    Ok(current)
177                } else {
178                    Err(*value)
179                }
180            }
181        })
182    }
183
184    /// Fetches and adds, returning the previous value.
185    pub fn fetch_add(&self, value: u64, _: Ordering) -> u64 {
186        irq_free(|| {
187            // SAFETY: Interrupts are disabled, so this read-modify-write is exclusive.
188            unsafe {
189                let ptr = self.value.get();
190                let old = *ptr;
191                *ptr = old.wrapping_add(value);
192                old
193            }
194        })
195    }
196
197    /// Fetches a value, applies the function and writes it back atomically.
198    #[allow(dead_code)]
199    pub fn fetch_update<F>(&self, _: Ordering, _: Ordering, mut f: F) -> Result<u64, u64>
200    where
201        F: FnMut(u64) -> Option<u64>,
202    {
203        irq_free(|| {
204            // SAFETY: Interrupts are disabled, so this read-modify-write is exclusive.
205            unsafe {
206                let ptr = self.value.get();
207                let old = *ptr;
208                if let Some(new) = f(old) {
209                    *ptr = new;
210                    Ok(old)
211                } else {
212                    Err(old)
213                }
214            }
215        })
216    }
217}