Skip to main content

rs_matter/utils/sync/
signal.rs

1/*
2 *
3 *    Copyright (c) 2024-2026 Project CHIP Authors
4 *
5 *    Licensed under the Apache License, Version 2.0 (the "License");
6 *    you may not use this file except in compliance with the License.
7 *    You may obtain a copy of the License at
8 *
9 *        http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *    Unless required by applicable law or agreed to in writing, software
12 *    distributed under the License is distributed on an "AS IS" BASIS,
13 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *    See the License for the specific language governing permissions and
15 *    limitations under the License.
16 */
17
18use core::future::poll_fn;
19use core::task::{Context, Poll};
20
21use embassy_sync::blocking_mutex::raw::RawMutex;
22use embassy_sync::waitqueue::WakerRegistration;
23
24use crate::utils::cell::RefCell;
25use crate::utils::init::{init, Init};
26
27use super::blocking::raw::MatterRawMutex;
28use super::blocking::Mutex;
29
30struct State<S> {
31    state: S,
32    waker: WakerRegistration,
33}
34
35impl<S> State<S> {
36    const fn new(state: S) -> Self {
37        Self {
38            state,
39            waker: WakerRegistration::new(),
40        }
41    }
42
43    fn init<I: Init<S>>(state: I) -> impl Init<Self> {
44        init!(Self {
45            state <- state,
46            waker: WakerRegistration::new(),
47        })
48    }
49}
50
51/// `Signal` is an async synchonization primitive that can be viewed as a generalization of the `embassy_sync::Signal` primitive
52/// that takes callback closures.
53///
54/// It allows for waiting on a condition of its state `S` to become true, where whether the condition is met is decided by a callback closure.
55///
56/// It also allows for modifying the state `S` and waking up the waiters - but only as long as a callback closure provides information that
57/// the state is modified in such a way, that the waiters should be notified.
58///
59/// The generic nature of `Signal` allows for a wide range of use cases, including the implementation of:
60/// - the `Notification` primitive
61/// - the `IfMutex` primitive
62pub struct Signal<S, M = MatterRawMutex> {
63    inner: Mutex<RefCell<State<S>>, M>,
64}
65
66impl<S, M> Signal<S, M>
67where
68    M: RawMutex,
69{
70    /// Create a `Signal` with the given initial state `S`.
71    pub const fn new(state: S) -> Self {
72        Self {
73            inner: Mutex::new(RefCell::new(State::new(state))),
74        }
75    }
76
77    /// Create a `Signal` in-place initializer with the given initial state initializer `I`.
78    pub fn init<I: Init<S>>(state: I) -> impl Init<Self> {
79        init!(Self {
80            inner <- Mutex::init(RefCell::init(State::init(state))),
81        })
82    }
83
84    // Modify the state `S` and wake up the waiters if necessary.
85    pub fn modify<F, R>(&self, f: F) -> R
86    where
87        F: FnOnce(&mut S) -> (bool, R),
88    {
89        self.inner.lock(|s| {
90            let mut s = s.borrow_mut();
91
92            let (wake, result) = f(&mut s.state);
93
94            if wake {
95                s.waker.wake();
96            }
97
98            result
99        })
100    }
101
102    // Wait for the condition of the state `S` to become true.
103    pub async fn wait<F, R>(&self, mut f: F) -> R
104    where
105        F: FnMut(&mut S) -> Option<R>,
106    {
107        poll_fn(move |ctx| self.poll_wait(ctx, &mut f)).await
108    }
109
110    // Poll the condition of the state `S` to become true.
111    pub fn poll_wait<F, R>(&self, ctx: &mut Context, f: F) -> Poll<R>
112    where
113        F: FnOnce(&mut S) -> Option<R>,
114    {
115        self.inner.lock(|s| {
116            let mut s = s.borrow_mut();
117
118            if let Some(result) = f(&mut s.state) {
119                Poll::Ready(result)
120            } else {
121                s.waker.register(ctx.waker());
122                Poll::Pending
123            }
124        })
125    }
126}
127
128impl<T, M> Signal<Option<T>, M>
129where
130    M: RawMutex,
131{
132    /// Notify the waiter.
133    pub fn signal(&self, value: T) {
134        self.modify(|state| {
135            *state = Some(value);
136            (true, ())
137        });
138    }
139
140    /// Wait for the notification.
141    pub async fn wait_signalled(&self) -> T {
142        self.wait(|state| state.take()).await
143    }
144}