141 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			141 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! A synchronization primitive for passing the latest value to a task.
 | |
| use core::cell::Cell;
 | |
| use core::future::{poll_fn, Future};
 | |
| use core::task::{Context, Poll, Waker};
 | |
| 
 | |
| use crate::blocking_mutex::raw::RawMutex;
 | |
| use crate::blocking_mutex::Mutex;
 | |
| 
 | |
| /// Single-slot signaling primitive.
 | |
| ///
 | |
| /// This is similar to a [`Channel`](crate::channel::Channel) with a buffer size of 1, except
 | |
| /// "sending" to it (calling [`Signal::signal`]) when full will overwrite the previous value instead
 | |
| /// of waiting for the receiver to pop the previous value.
 | |
| ///
 | |
| /// It is useful for sending data between tasks when the receiver only cares about
 | |
| /// the latest data, and therefore it's fine to "lose" messages. This is often the case for "state"
 | |
| /// updates.
 | |
| ///
 | |
| /// For more advanced use cases, you might want to use [`Channel`](crate::channel::Channel) instead.
 | |
| ///
 | |
| /// Signals are generally declared as `static`s and then borrowed as required.
 | |
| ///
 | |
| /// ```
 | |
| /// use embassy_sync::signal::Signal;
 | |
| /// use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
 | |
| ///
 | |
| /// enum SomeCommand {
 | |
| ///   On,
 | |
| ///   Off,
 | |
| /// }
 | |
| ///
 | |
| /// static SOME_SIGNAL: Signal<CriticalSectionRawMutex, SomeCommand> = Signal::new();
 | |
| /// ```
 | |
| pub struct Signal<M, T>
 | |
| where
 | |
|     M: RawMutex,
 | |
| {
 | |
|     state: Mutex<M, Cell<State<T>>>,
 | |
| }
 | |
| 
 | |
| enum State<T> {
 | |
|     None,
 | |
|     Waiting(Waker),
 | |
|     Signaled(T),
 | |
| }
 | |
| 
 | |
| impl<M, T> Signal<M, T>
 | |
| where
 | |
|     M: RawMutex,
 | |
| {
 | |
|     /// Create a new `Signal`.
 | |
|     pub const fn new() -> Self {
 | |
|         Self {
 | |
|             state: Mutex::new(Cell::new(State::None)),
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl<M, T> Default for Signal<M, T>
 | |
| where
 | |
|     M: RawMutex,
 | |
| {
 | |
|     fn default() -> Self {
 | |
|         Self::new()
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl<M, T> Signal<M, T>
 | |
| where
 | |
|     M: RawMutex,
 | |
| {
 | |
|     /// Mark this Signal as signaled.
 | |
|     pub fn signal(&self, val: T) {
 | |
|         self.state.lock(|cell| {
 | |
|             let state = cell.replace(State::Signaled(val));
 | |
|             if let State::Waiting(waker) = state {
 | |
|                 waker.wake();
 | |
|             }
 | |
|         })
 | |
|     }
 | |
| 
 | |
|     /// Remove the queued value in this `Signal`, if any.
 | |
|     pub fn reset(&self) {
 | |
|         self.state.lock(|cell| cell.set(State::None));
 | |
|     }
 | |
| 
 | |
|     fn poll_wait(&self, cx: &mut Context<'_>) -> Poll<T> {
 | |
|         self.state.lock(|cell| {
 | |
|             let state = cell.replace(State::None);
 | |
|             match state {
 | |
|                 State::None => {
 | |
|                     cell.set(State::Waiting(cx.waker().clone()));
 | |
|                     Poll::Pending
 | |
|                 }
 | |
|                 State::Waiting(w) if w.will_wake(cx.waker()) => {
 | |
|                     cell.set(State::Waiting(w));
 | |
|                     Poll::Pending
 | |
|                 }
 | |
|                 State::Waiting(w) => {
 | |
|                     cell.set(State::Waiting(cx.waker().clone()));
 | |
|                     w.wake();
 | |
|                     Poll::Pending
 | |
|                 }
 | |
|                 State::Signaled(res) => Poll::Ready(res),
 | |
|             }
 | |
|         })
 | |
|     }
 | |
| 
 | |
|     /// Future that completes when this Signal has been signaled.
 | |
|     pub fn wait(&self) -> impl Future<Output = T> + '_ {
 | |
|         poll_fn(move |cx| self.poll_wait(cx))
 | |
|     }
 | |
| 
 | |
|     /// non-blocking method to try and take the signal value.
 | |
|     pub fn try_take(&self) -> Option<T> {
 | |
|         self.state.lock(|cell| {
 | |
|             let state = cell.replace(State::None);
 | |
|             match state {
 | |
|                 State::Signaled(res) => Some(res),
 | |
|                 state => {
 | |
|                     cell.set(state);
 | |
|                     None
 | |
|                 }
 | |
|             }
 | |
|         })
 | |
|     }
 | |
| 
 | |
|     /// non-blocking method to check whether this signal has been signaled. This does not clear the signal.  
 | |
|     pub fn signaled(&self) -> bool {
 | |
|         self.state.lock(|cell| {
 | |
|             let state = cell.replace(State::None);
 | |
| 
 | |
|             let res = matches!(state, State::Signaled(_));
 | |
| 
 | |
|             cell.set(state);
 | |
| 
 | |
|             res
 | |
|         })
 | |
|     }
 | |
| }
 |