1use crate::signal::{Signal, SignalWriter, Store};
4use core::{future::poll_fn, task::Poll};
5use portable_atomic::Ordering::{AcqRel, Acquire, Release};
6
7pub struct Watch<T: Copy>(Signal<T>);
9
10impl<T> core::fmt::Debug for Watch<T>
11where
12 T: core::marker::Copy,
13{
14 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
15 fmt.write_fmt(format_args!(
16 "Watch<{}>{{ .. }}",
17 core::any::type_name::<T>()
18 ))
19 }
20}
21
22impl<T: Copy> Default for Watch<T> {
23 fn default() -> Self {
24 Self::new()
25 }
26}
27
28unsafe impl<T: Copy> Send for Watch<T> {}
29unsafe impl<T: Copy> Sync for Watch<T> {}
30
31impl<T: Copy> Watch<T> {
32 pub const fn new() -> Self {
34 Self(Signal::new())
35 }
36
37 pub fn split(&self) -> (WatchWriter<'_, T>, WatchReader<'_, T>) {
39 if self.0.already_split.swap(true, AcqRel) {
40 panic!("`Watch` cannot be split twice");
41 }
42 (
43 WatchWriter(SignalWriter { parent: &self.0 }),
44 WatchReader { parent: &self.0 },
45 )
46 }
47}
48
49#[macro_export]
51macro_rules! make_watch {
52 ( $T:ty ) => {{
53 static WATCH: $crate::watch::Watch<$T> = $crate::watch::Watch::new();
54
55 WATCH.split()
56 }};
57}
58
59#[derive(Clone)]
61pub struct WatchWriter<'a, T: Copy>(SignalWriter<'a, T>);
62
63impl<T> core::fmt::Debug for WatchWriter<'_, T>
64where
65 T: core::marker::Copy,
66{
67 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
68 fmt.write_fmt(format_args!(
69 "WatchWriter<{}>{{ .. }}",
70 core::any::type_name::<T>()
71 ))
72 }
73}
74
75impl<T: Copy> WatchWriter<'_, T> {
76 pub fn write(&mut self, value: T) {
78 self.0.write(value);
79 }
80}
81
82pub struct WatchReader<'a, T: Copy> {
84 parent: &'a Signal<T>,
85}
86
87impl<T> core::fmt::Debug for WatchReader<'_, T>
88where
89 T: core::marker::Copy,
90{
91 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
92 fmt.write_fmt(format_args!(
93 "WatchReader<{}>{{ .. }}",
94 core::any::type_name::<T>()
95 ))
96 }
97}
98
99impl<T: Copy> WatchReader<'_, T> {
100 fn get_inner(&mut self) -> Store<T> {
102 critical_section::with(|_| {
103 unsafe { self.parent.store.get().read() }
105 })
106 }
107
108 fn get_seen(&mut self) -> bool {
110 self.parent.seen.load(Acquire)
111 }
112
113 fn mark_seen(&mut self) {
115 self.parent.seen.store(true, Release);
116 }
117
118 pub fn try_get(&mut self) -> Option<T> {
120 match self.get_inner() {
121 Store::Unset => None,
122 Store::Set(value) => {
123 self.mark_seen();
124 Some(value)
125 }
126 }
127 }
128
129 pub async fn changed(&mut self) -> T {
135 poll_fn(|ctx| {
136 self.parent.waker.register(ctx.waker());
137
138 if self.get_seen() {
139 return Poll::Pending;
140 }
141
142 match self.get_inner() {
143 Store::Unset => Poll::Pending,
144 Store::Set(value) => {
145 self.mark_seen();
146 Poll::Ready(value)
147 }
148 }
149 })
150 .await
151 }
152}
153
154#[cfg(test)]
155#[cfg(not(loom))]
156mod tests {
157 use super::*;
158 use static_cell::StaticCell;
159
160 #[test]
161 fn empty() {
162 let (_writer, mut reader) = make_watch!(u32);
163
164 assert!(reader.try_get().is_none());
165 }
166
167 #[test]
168 fn ping_pong() {
169 let (mut writer, mut reader) = make_watch!(u32);
170
171 writer.write(0xde);
172 assert!(reader.try_get().is_some_and(|value| value == 0xde));
173 }
174
175 #[test]
176 fn latest() {
177 let (mut writer, mut reader) = make_watch!(u32);
178
179 writer.write(0xde);
180 writer.write(0xad);
181 writer.write(0xbe);
182 writer.write(0xef);
183 assert!(reader.try_get().is_some_and(|value| value == 0xef));
184 }
185
186 #[test]
187 fn no_consumption() {
188 let (mut writer, mut reader) = make_watch!(u32);
189
190 writer.write(0xaa);
191 assert!(reader.try_get().is_some_and(|value| value == 0xaa));
192 assert!(reader.try_get().is_some_and(|value| value == 0xaa));
193 }
194
195 #[test]
196 #[should_panic]
197 fn no_multi_split() {
198 fn scary_helper<'a>() -> (WatchWriter<'a, u32>, WatchReader<'a, u32>) {
199 make_watch!(u32)
200 }
201 let (mut _writer1, mut _reader1) = scary_helper();
202 let (mut _writer2, mut _reader2) = scary_helper();
203 }
204
205 #[tokio::test]
206 async fn pending() {
207 let (mut writer, mut reader) = make_watch!(u32);
208
209 writer.write(0xaa);
210
211 assert_eq!(reader.changed().await, 0xaa);
212 }
213
214 #[tokio::test]
215 async fn changed() {
216 static READER: StaticCell<WatchReader<u32>> = StaticCell::new();
217 let (mut writer, mut reader) = make_watch!(u32);
218
219 writer.write(0xaa);
220 assert_eq!(reader.changed().await, 0xaa);
221
222 let reader = READER.init(reader);
223 let handle = tokio::spawn(reader.changed());
224
225 tokio::task::yield_now().await; assert!(!handle.is_finished()); writer.write(0xab);
229 assert!(handle.await.is_ok_and(|value| value == 0xab));
230 }
231
232 #[tokio::test]
233 async fn try_get_marks_seen() {
234 static READER: StaticCell<WatchReader<u32>> = StaticCell::new();
235 let (mut writer, mut reader) = make_watch!(u32);
236
237 writer.write(0xaa);
238 assert!(reader.try_get().is_some_and(|value| value == 0xaa));
239
240 let reader = READER.init(reader);
241 let handle = tokio::spawn(reader.changed());
242
243 tokio::task::yield_now().await; assert!(!handle.is_finished()); }
246}