Geo*_*ion 4 embedded mutex rust cortex-m
根据关于并发的Rust Embedded Book,在上下文之间共享一些数据的更好方法之一是使用带有 refcells 的互斥锁。我理解它们是如何工作的以及为什么这是必要的。但在某些情况下,间接成本似乎很高。
cortex_m crate 的互斥锁是这样工作的:
cortex_m::interrupt::free(|cs| {
let my_value = my_mutex.borrow(cs).borrow();
// Do something with the value
});
Run Code Online (Sandbox Code Playgroud)
互斥体cs
在提供访问权限之前需要(CriticalSection) 令牌。在临界区,不会发生中断,所以我们知道我们是唯一可以更改和读取值的人。这很好用。
但是,我现在所处的场景是将变量写入一次以进行初始化(在运行时),然后始终将其视为只读值。就我而言,它是 MCU 的时钟速度。这不能是编译时常量。一个为什么从深度睡眠中醒来的例子:根据硬件的状态,可能会选择使用较慢的时钟速度来节省一些能量。因此,在启动时(或者更确切地说是所有 RAM 都消失的唤醒),每次都可以选择不同的时钟速度。
如果我只是想读取值,完成整个临界区设置似乎很浪费。如果这个变量可能再次改变,那么,是的,这是必要的。但这里的情况并非如此。它只会被读取。
有没有更好的方法来以更少的开销读取共享变量而不使用不安全的 Rust?
在一些评论的帮助下,我想出了这个:
use core::cell::UnsafeCell;
use core::sync::atomic::{AtomicBool, Ordering};
/// A cell that can be written to once. After that, the cell is readonly and will panic if written to again.
/// Getting the value will panic if it has not already been set. Try 'try_get(_ref)' to see if it has already been set.
///
/// The cell can be used in embedded environments where a variable is initialized once, but later only written to.
/// This can be used in interrupts as well as it implements Sync.
///
/// Usage:
/// ```rust
/// static MY_VAR: DynamicReadOnlyCell<u32> = DynamicReadOnlyCell::new();
///
/// fn main() {
/// initialize();
/// calculate();
/// }
///
/// fn initialize() {
/// // ...
/// MY_VAR.set(42);
/// // ...
/// }
///
/// fn calculate() {
/// let my_var = MY_VAR.get(); // Will be 42
/// // ...
/// }
/// ```
pub struct DynamicReadOnlyCell<T: Sized> {
data: UnsafeCell<Option<T>>,
is_populated: AtomicBool,
}
impl<T: Sized> DynamicReadOnlyCell<T> {
/// Creates a new unpopulated cell
pub const fn new() -> Self {
DynamicReadOnlyCell {
data: UnsafeCell::new(None),
is_populated: AtomicBool::new(false),
}
}
/// Creates a new cell that is already populated
pub const fn from(data: T) -> Self {
DynamicReadOnlyCell {
data: UnsafeCell::new(Some(data)),
is_populated: AtomicBool::new(true),
}
}
/// Populates the cell with data.
/// Panics if the cell is already populated.
pub fn set(&self, data: T) {
cortex_m::interrupt::free(|_| {
if self.is_populated.load(Ordering::Acquire) {
panic!("Trying to set when the cell is already populated");
}
unsafe {
*self.data.get() = Some(data);
}
self.is_populated.store(true, Ordering::Release);
});
}
/// Gets a reference to the data from the cell.
/// Panics if the cell is not yet populated.
#[inline(always)]
pub fn get_ref(&self) -> &T {
if let Some(data) = self.try_get_ref() {
data
} else {
panic!("Trying to get when the cell hasn't been populated yet");
}
}
/// Gets a reference to the data from the cell.
/// Returns Some(T) if the cell is populated.
/// Returns None if the cell is not populated.
#[inline(always)]
pub fn try_get_ref(&self) -> Option<&T> {
if !self.is_populated.load(Ordering::Acquire) {
None
} else {
Some(unsafe { self.data.get().as_ref().unwrap().as_ref().unwrap() })
}
}
}
impl<T: Sized + Copy> DynamicReadOnlyCell<T> {
/// Gets a copy of the data from the cell.
/// Panics if the cell is not yet populated.
#[inline(always)]
pub fn get(&self) -> T {
*self.get_ref()
}
/// Gets a copy of the data from the cell.
/// Returns Some(T) if the cell is populated.
/// Returns None if the cell is not populated.
#[inline(always)]
pub fn try_get(&self) -> Option<T> {
self.try_get_ref().cloned()
}
}
unsafe impl<T: Sized> Sync for DynamicReadOnlyCell<T> {}
Run Code Online (Sandbox Code Playgroud)
由于原子检查和集合中的临界区,我认为这是安全的。如果你发现任何错误或狡猾的地方,请告诉我。
如果就&'static
足够了,我建议检查一下static_cell
箱子(Repo、Lib.rs、Docs.rs)。
来自自述文件:
use static_cell::StaticCell;
// Statically allocate memory for a `u32`.
static SOME_INT: StaticCell<u32> = StaticCell::new();
// Initialize it at runtime. This returns a `&'static mut`.
let x: &'static mut u32 = SOME_INT.init(42);
assert_eq!(*x, 42);
// Trying to call `.init()` again would panic, because the StaticCell is already initialized.
// SOME_INT.init(42);
Run Code Online (Sandbox Code Playgroud)
我在查看CYW43439 wifi 芯片驱动程序的实现时发现了这个箱子。有一个非常漂亮的宏你可能会发现很有用:
macro_rules! singleton {
($val:expr) => {{
type T = impl Sized;
static STATIC_CELL: StaticCell<T> = StaticCell::new();
STATIC_CELL.init_with(move || $val)
}};
}
// ...
// Init network stack
let stack = &*singleton!(Stack::new(
net_device,
config,
singleton!(StackResources::<1, 2, 8>::new()),
seed
));
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
575 次 |
最近记录: |