1#![doc(html_root_url = "https://docs.rs/toad-stem/0.0.0")]
11#![cfg_attr(any(docsrs, feature = "docs"), feature(doc_cfg))]
12#![allow(clippy::unused_unit)]
15#![deny(missing_docs)]
18#![deny(missing_debug_implementations)]
19#![deny(missing_copy_implementations)]
20#![cfg_attr(not(test), deny(unsafe_code))]
21#![cfg_attr(not(test), warn(unreachable_pub))]
24#![cfg_attr(not(feature = "std"), no_std)]
27
28#[cfg(feature = "alloc")]
29extern crate alloc as std_alloc;
30
31use core::ops::{Deref, DerefMut};
32
33#[cfg(feature = "std")]
34type Inner<T> = std::sync::RwLock<T>;
35
36#[cfg(not(feature = "std"))]
37type Inner<T> = core::cell::RefCell<T>;
38
39#[derive(Debug, Default)]
45pub struct Stem<T>(Inner<T>);
46
47impl<T> Stem<T> {
48 pub const fn new(t: T) -> Self {
50 Self(Inner::new(t))
51 }
52
53 pub fn map_ref<F, R>(&self, f: F) -> R
60 where F: for<'a> FnMut(&'a T) -> R
61 {
62 self.0.map_ref(f)
63 }
64
65 pub fn map_mut<F, R>(&self, f: F) -> R
69 where F: for<'a> FnMut(&'a mut T) -> R
70 {
71 self.0.map_mut(f)
72 }
73}
74
75pub trait StemCellInternal<T> {
84 fn new(t: T) -> Self
86 where Self: Sized;
87
88 fn map_ref<F, R>(&self, f: F) -> R
93 where F: for<'a> FnMut(&'a T) -> R;
94
95 fn map_mut<F, R>(&self, f: F) -> R
100 where F: for<'a> FnMut(&'a mut T) -> R;
101}
102
103#[cfg(feature = "std")]
104impl<T> StemCellInternal<T> for std::sync::RwLock<T> {
105 fn new(t: T) -> Self {
106 Self::new(t)
107 }
108
109 fn map_ref<F, R>(&self, mut f: F) -> R
110 where F: for<'a> FnMut(&'a T) -> R
111 {
112 f(self.read().unwrap().deref())
113 }
114
115 fn map_mut<F, R>(&self, mut f: F) -> R
116 where F: for<'a> FnMut(&'a mut T) -> R
117 {
118 f(self.write().unwrap().deref_mut())
119 }
120}
121
122impl<T> StemCellInternal<T> for core::cell::RefCell<T> {
123 fn new(t: T) -> Self {
124 Self::new(t)
125 }
126
127 fn map_ref<F, R>(&self, mut f: F) -> R
128 where F: for<'a> FnMut(&'a T) -> R
129 {
130 f(self.borrow().deref())
131 }
132
133 fn map_mut<F, R>(&self, mut f: F) -> R
134 where F: for<'a> FnMut(&'a mut T) -> R
135 {
136 f(self.borrow_mut().deref_mut())
137 }
138}
139
140#[cfg(test)]
141mod test {
142 use core::cell::RefCell;
143 use std::sync::{Arc, Barrier, RwLock};
144
145 use super::*;
146
147 #[test]
148 fn refcell_mut() {
149 let s = RefCell::new(Vec::<usize>::new());
150 s.map_mut(|v| v.push(12));
151 s.map_ref(|v| assert_eq!(v, &vec![12usize]));
152 }
153
154 #[test]
155 fn refcell_concurrent_read_does_not_block_or_panic() {
156 let s = RefCell::new(Vec::<usize>::new());
157 s.map_ref(|_| s.map_ref(|_| ()));
158 }
159
160 #[test]
161 fn rwlock_mut() {
162 let s = RwLock::new(Vec::<usize>::new());
163 s.map_mut(|v| v.push(12));
164 s.map_ref(|v| assert_eq!(v, &vec![12usize]));
165 }
166
167 #[test]
168 fn rwlock_concurrent_read_does_not_block_or_panic() {
169 let s = RwLock::new(Vec::<usize>::new());
170 s.map_ref(|_| s.map_ref(|_| ()));
171 }
172
173 #[test]
174 fn stem_modify_blocks_until_refs_dropped() {
175 unsafe {
176 static VEC: Stem<Vec<usize>> = Stem::new(Vec::new());
177
178 static mut START: Option<Arc<Barrier>> = None;
179 static mut READING: Option<Arc<Barrier>> = None;
180 static mut READING_DONE: Option<Arc<Barrier>> = None;
181 static mut MODIFY_DONE: Option<Arc<Barrier>> = None;
182
183 START = Some(Arc::new(Barrier::new(3)));
184 READING = Some(Arc::new(Barrier::new(3)));
185 READING_DONE = Some(Arc::new(Barrier::new(2)));
186 MODIFY_DONE = Some(Arc::new(Barrier::new(3)));
187
188 macro_rules! wait {
189 ($b:ident) => {
190 $b.as_ref().unwrap().clone().wait();
191 };
192 }
193
194 std::thread::spawn(|| {
195 wait!(START);
196 VEC.map_ref(|v| {
197 assert!(v.is_empty());
198 wait!(READING);
199 wait!(READING_DONE);
200 });
201
202 wait!(MODIFY_DONE);
203 });
204
205 std::thread::spawn(|| {
206 wait!(START);
207 wait!(READING);
208 VEC.map_mut(|v| v.push(12)); wait!(MODIFY_DONE);
210 });
211
212 wait!(START);
213 wait!(READING);
214 wait!(READING_DONE);
215 wait!(MODIFY_DONE);
216 VEC.map_ref(|v| assert_eq!(v, &vec![12]));
217 }
218 }
219}