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}