aboutsummaryrefslogtreecommitdiff
path: root/embassy-sync/src/signal.rs
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2022-08-22 22:00:06 +0200
committerDario Nieuwenhuis <[email protected]>2022-08-22 22:18:13 +0200
commit5677b13a86beca58aa57ecfd7cea0db7ceb189fa (patch)
tree0c7425dae57acb94cb6ddca27def7e77609369b3 /embassy-sync/src/signal.rs
parent21072bee48ff6ec19b79e0d9527ad8cc34a4e9e0 (diff)
sync: flatten module structure.
Diffstat (limited to 'embassy-sync/src/signal.rs')
-rw-r--r--embassy-sync/src/signal.rs100
1 files changed, 100 insertions, 0 deletions
diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs
new file mode 100644
index 000000000..3f665e388
--- /dev/null
+++ b/embassy-sync/src/signal.rs
@@ -0,0 +1,100 @@
1//! A synchronization primitive for passing the latest value to a task.
2use core::cell::UnsafeCell;
3use core::future::Future;
4use core::mem;
5use core::task::{Context, Poll, Waker};
6
7/// Single-slot signaling primitive.
8///
9/// This is similar to a [`Channel`](crate::channel::mpmc::Channel) with a buffer size of 1, except
10/// "sending" to it (calling [`Signal::signal`]) when full will overwrite the previous value instead
11/// of waiting for the receiver to pop the previous value.
12///
13/// It is useful for sending data between tasks when the receiver only cares about
14/// the latest data, and therefore it's fine to "lose" messages. This is often the case for "state"
15/// updates.
16///
17/// For more advanced use cases, you might want to use [`Channel`](crate::channel::mpmc::Channel) instead.
18///
19/// Signals are generally declared as `static`s and then borrowed as required.
20///
21/// ```
22/// use embassy_sync::signal::Signal;
23///
24/// enum SomeCommand {
25/// On,
26/// Off,
27/// }
28///
29/// static SOME_SIGNAL: Signal<SomeCommand> = Signal::new();
30/// ```
31pub struct Signal<T> {
32 state: UnsafeCell<State<T>>,
33}
34
35enum State<T> {
36 None,
37 Waiting(Waker),
38 Signaled(T),
39}
40
41unsafe impl<T: Send> Send for Signal<T> {}
42unsafe impl<T: Send> Sync for Signal<T> {}
43
44impl<T> Signal<T> {
45 /// Create a new `Signal`.
46 pub const fn new() -> Self {
47 Self {
48 state: UnsafeCell::new(State::None),
49 }
50 }
51}
52
53impl<T: Send> Signal<T> {
54 /// Mark this Signal as signaled.
55 pub fn signal(&self, val: T) {
56 critical_section::with(|_| unsafe {
57 let state = &mut *self.state.get();
58 if let State::Waiting(waker) = mem::replace(state, State::Signaled(val)) {
59 waker.wake();
60 }
61 })
62 }
63
64 /// Remove the queued value in this `Signal`, if any.
65 pub fn reset(&self) {
66 critical_section::with(|_| unsafe {
67 let state = &mut *self.state.get();
68 *state = State::None
69 })
70 }
71
72 /// Manually poll the Signal future.
73 pub fn poll_wait(&self, cx: &mut Context<'_>) -> Poll<T> {
74 critical_section::with(|_| unsafe {
75 let state = &mut *self.state.get();
76 match state {
77 State::None => {
78 *state = State::Waiting(cx.waker().clone());
79 Poll::Pending
80 }
81 State::Waiting(w) if w.will_wake(cx.waker()) => Poll::Pending,
82 State::Waiting(_) => panic!("waker overflow"),
83 State::Signaled(_) => match mem::replace(state, State::None) {
84 State::Signaled(res) => Poll::Ready(res),
85 _ => unreachable!(),
86 },
87 }
88 })
89 }
90
91 /// Future that completes when this Signal has been signaled.
92 pub fn wait(&self) -> impl Future<Output = T> + '_ {
93 futures_util::future::poll_fn(move |cx| self.poll_wait(cx))
94 }
95
96 /// non-blocking method to check whether this signal has been signaled.
97 pub fn signaled(&self) -> bool {
98 critical_section::with(|_| matches!(unsafe { &*self.state.get() }, State::Signaled(_)))
99 }
100}