embedded_gui/
completion.rs1use core::cell::Cell;
8use core::future::Future;
9use core::pin::Pin;
10use core::sync::atomic::{AtomicBool, Ordering};
11use core::task::{Context, Poll, Waker};
12
13use critical_section::Mutex;
14use embedded_graphics_core::pixelcolor::Rgb565;
15use embedded_graphics_framebuf::{FrameBuf, backends::DMACapableFrameBufferBackend};
16
17use crate::display_backend::{AsyncDmaTransfer, DmaTransfer};
18
19pub struct CompletionSlot {
23 signaled: AtomicBool,
24 waker: Mutex<Cell<Option<Waker>>>,
25}
26
27impl CompletionSlot {
28 pub const fn new() -> Self {
30 Self {
31 signaled: AtomicBool::new(false),
32 waker: Mutex::new(Cell::new(None)),
33 }
34 }
35
36 pub fn reset(&self) {
38 self.signaled.store(false, Ordering::Release);
39 critical_section::with(|cs| {
40 self.waker.borrow(cs).set(None);
41 });
42 }
43
44 pub fn signal(&self) {
48 self.signaled.store(true, Ordering::Release);
49 critical_section::with(|cs| {
50 if let Some(waker) = self.waker.borrow(cs).take() {
51 waker.wake();
52 }
53 });
54 }
55}
56
57impl Default for CompletionSlot {
58 fn default() -> Self {
59 Self::new()
60 }
61}
62
63impl CompletionSlot {
64 pub fn is_signaled(&self) -> bool {
66 self.signaled.load(Ordering::Acquire)
67 }
68
69 pub fn poll_wait(&self, cx: &mut Context<'_>) -> Poll<()> {
71 if self.is_signaled() {
72 return Poll::Ready(());
73 }
74 critical_section::with(|cs| {
75 self.waker.borrow(cs).set(Some(cx.waker().clone()));
76 });
77 if self.is_signaled() {
78 Poll::Ready(())
79 } else {
80 Poll::Pending
81 }
82 }
83}
84
85pub struct WaitTransfer<FB>
87where
88 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
89{
90 framebuffer: Option<FrameBuf<Rgb565, FB>>,
91 completion: &'static CompletionSlot,
92}
93
94impl<FB> WaitTransfer<FB>
95where
96 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
97{
98 pub fn new(framebuffer: FrameBuf<Rgb565, FB>, completion: &'static CompletionSlot) -> Self {
100 completion.reset();
101 Self {
102 framebuffer: Some(framebuffer),
103 completion,
104 }
105 }
106
107 pub const fn completion(&self) -> &'static CompletionSlot {
109 self.completion
110 }
111}
112
113impl<FB> DmaTransfer for WaitTransfer<FB>
114where
115 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
116{
117 type Buffer = FrameBuf<Rgb565, FB>;
118
119 fn is_done(&self) -> bool {
120 self.completion.is_signaled()
121 }
122
123 fn wait(self) -> Self::Buffer {
124 while !self.completion.is_signaled() {
125 core::hint::spin_loop();
126 }
127 self.framebuffer
128 .expect("WaitTransfer polled after completion")
129 }
130}
131
132pub struct WaitTransferFuture<FB>
134where
135 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
136{
137 inner: Option<WaitTransfer<FB>>,
138}
139
140impl<FB> AsyncDmaTransfer for WaitTransfer<FB>
141where
142 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
143{
144 type WaitFuture = WaitTransferFuture<FB>;
145
146 fn wait_async(self) -> Self::WaitFuture {
147 WaitTransferFuture { inner: Some(self) }
148 }
149}
150
151impl<FB> Future for WaitTransferFuture<FB>
152where
153 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
154{
155 type Output = FrameBuf<Rgb565, FB>;
156
157 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
158 let this = unsafe { self.get_unchecked_mut() };
159 let inner = this
160 .inner
161 .as_mut()
162 .expect("WaitTransferFuture polled after completion");
163 match inner.completion.poll_wait(cx) {
164 Poll::Ready(()) => Poll::Ready(
165 this.inner
166 .take()
167 .expect("WaitTransferFuture polled after completion")
168 .framebuffer
169 .expect("WaitTransferFuture polled after completion"),
170 ),
171 Poll::Pending => Poll::Pending,
172 }
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 extern crate std;
179 use super::*;
180 use embedded_graphics_core::pixelcolor::RgbColor;
181 use embedded_graphics_framebuf::backends::{EndianCorrectedBuffer, EndianCorrection};
182 use std::sync::Arc;
183 use std::task::{Context, Poll, Wake, Waker};
184
185 type TestBackend = EndianCorrectedBuffer<'static, Rgb565>;
186
187 struct TestWake(Arc<AtomicBool>);
188
189 impl Wake for TestWake {
190 fn wake(self: Arc<Self>) {
191 self.0.store(true, Ordering::Release);
192 }
193 fn wake_by_ref(self: &Arc<Self>) {
194 self.0.store(true, Ordering::Release);
195 }
196 }
197
198 fn make_fb() -> FrameBuf<Rgb565, TestBackend> {
199 let data: &'static mut [Rgb565] = std::vec![Rgb565::BLACK; 4].leak();
200 FrameBuf::new(
201 EndianCorrectedBuffer::new(data, EndianCorrection::ToLittleEndian),
202 2,
203 2,
204 )
205 }
206
207 #[test]
208 fn completion_slot_sync_wait() {
209 static DONE: CompletionSlot = CompletionSlot::new();
210 let fb = make_fb();
211 let xfer = WaitTransfer::new(fb, &DONE);
212 assert!(!xfer.is_done());
213 DONE.signal();
214 assert!(xfer.is_done());
215 let _ = xfer.wait();
216 }
217
218 #[test]
219 fn completion_slot_async_wake() {
220 static DONE: CompletionSlot = CompletionSlot::new();
221 let fb = make_fb();
222 let xfer = WaitTransfer::new(fb, &DONE);
223 let mut fut = xfer.wait_async();
224 let woken = Arc::new(AtomicBool::new(false));
225 let waker = Waker::from(Arc::new(TestWake(Arc::clone(&woken))));
226 let mut cx = Context::from_waker(&waker);
227 assert!(matches!(Pin::new(&mut fut).poll(&mut cx), Poll::Pending));
228 DONE.signal();
229 assert!(matches!(Pin::new(&mut fut).poll(&mut cx), Poll::Ready(_)));
230 }
231}