critical_section_lock_mut/lib.rs
1#![doc(html_root_url = "https://docs.rs/critical-section-lock-mut/0.1.2")]
2#![no_std]
3//! Lock data with mutable access on a single-core
4//! processor. The locking is provided by a
5//! `critical-section::Mutex`.
6//!
7//! # Example
8//!
9//! ```text
10//! use critical_section_lock_mut::LockMut;
11//!
12//! static SHARED: LockMut<u8> = LockMut::new();
13//!
14//! fn main() {
15//! SHARED.init(3);
16//! SHARED.with_lock(|u| *u += 1);
17//! ```
18
19use core::cell::RefCell;
20
21pub use critical_section;
22use critical_section::Mutex;
23
24/// This datatype provides a lock with interior mutability
25/// for the data inside.
26#[derive(Debug)]
27pub struct LockMut<T>(Mutex<RefCell<Option<T>>>);
28
29impl<T> LockMut<T> {
30 /// Create a new empty `LockMut`.
31 ///
32 /// # Examples
33 ///
34 /// ```no_run
35 /// # use critical_section_lock_mut::LockMut;
36 /// static LOCKED_DATA: LockMut<u8> = LockMut::new();
37 /// ```
38 pub const fn new() -> Self {
39 LockMut(Mutex::new(RefCell::new(None)))
40 }
41
42 /// Initialize a previously-uninitialized `LockMut`.
43 ///
44 /// # Examples
45 ///
46 /// ```no_run
47 /// # use critical_section_lock_mut::LockMut;
48 /// static LOCKED_DATA: LockMut<u8> = LockMut::new();
49 /// LOCKED_DATA.init(5);
50 /// ```
51 ///
52 /// # Panics
53 ///
54 /// Panics if this `LockMut` is to be initialized a second time.
55 pub fn init(&self, val: T) {
56 critical_section::with(|cs| {
57 let mut cell = self.0.borrow(cs).borrow_mut();
58 assert!(cell.is_none(), "lock reinitialized");
59 *cell = Some(val);
60 });
61 }
62
63 /// Locks, then runs the closure `f` with a mutable
64 /// reference to the locked data.
65 ///
66 /// # Examples
67 ///
68 /// ```no_run
69 /// # use critical_section_lock_mut::LockMut;
70 /// static LOCKED_DATA: LockMut<u8> = LockMut::new();
71 /// LOCKED_DATA.init(5);
72 /// LOCKED_DATA.with_lock(|u| *u += 1);
73 /// ```
74 ///
75 /// # Panics
76 ///
77 /// Panics if this `LockMut` is uninitialized.
78 pub fn with_lock<F: FnOnce(&mut T)>(&self, f: F) {
79 critical_section::with(|cs| {
80 // &LockMut<T> → &Mutex<RefCell<Option<T>>> →
81 // &RefCell<Option<T>> → &mut Option<T>
82 let mut cell = self.0.borrow(cs).borrow_mut();
83 // &mut Option<T> → Option<&mut T> → &mut T
84 let val_mut = cell.as_mut().expect("empty lock");
85 f(val_mut);
86 });
87 }
88
89 /// Locks if this `LockMut` is initialized, then runs
90 /// the closure `f` with a mutable reference to the
91 /// locked data. If the `LockMut` is uninitialized,
92 /// this method does nothing.
93 ///
94 /// # Examples
95 ///
96 /// ```no_run
97 /// # use critical_section_lock_mut::LockMut;
98 /// static LOCKED_DATA: LockMut<u8> = LockMut::new();
99 /// LOCKED_DATA.try_with_lock(|_| panic!());
100 /// ```
101 pub fn try_with_lock<F: FnOnce(&mut T)>(&self, f: F) {
102 critical_section::with(|cs| {
103 // &LockMut<T> → &Mutex<RefCell<Option<T>>> →
104 // &RefCell<Option<T>> → &mut Option<T>
105 let mut cell = self.0.borrow(cs).borrow_mut();
106 // &mut Option<T> → Option<&mut T> → &mut T
107 if let Some(val_mut) = cell.as_mut() {
108 f(val_mut);
109 }
110 });
111 }
112}