Skip to main content

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