embassy_supervisor/data_deps/
backed.rs1use core::cell::RefCell;
2use core::ops::Deref;
3use core::task::Poll;
4
5use embassy_sync::blocking_mutex::Mutex;
6use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
7use embassy_sync::signal::Signal;
8use embassy_sync::waitqueue::MultiWakerRegistration;
9use embassy_time::Duration;
10use portable_atomic::{AtomicBool, AtomicU32, Ordering};
11
12use super::{Gated, producer_of};
13use crate::{Coupling, Sig, TaskNode};
14
15static SERVING_EVT: Mutex<CriticalSectionRawMutex, RefCell<MultiWakerRegistration<4>>> =
16 Mutex::new(RefCell::new(MultiWakerRegistration::new()));
17
18const GATE_RETRY: embassy_time::Duration = embassy_time::Duration::from_millis(250);
19
20pub(crate) fn notify_serving() {
21 SERVING_EVT.lock(|w| w.borrow_mut().wake());
22}
23
24fn serving(producer: &TaskNode) -> bool {
25 producer.is_running() && producer.is_ready()
26}
27
28async fn wait_serving(producer: &'static TaskNode) -> bool {
32 use embassy_futures::select::select;
33 let running = producer.is_running();
34 let settled = || serving(producer) || producer.is_running() != running;
35 let woke = core::future::poll_fn(|cx| {
36 if settled() {
37 return Poll::Ready(());
38 }
39 SERVING_EVT.lock(|w| w.borrow_mut().register(cx.waker()));
40 if settled() {
43 Poll::Ready(())
44 } else {
45 Poll::Pending
46 }
47 });
48 let _ = select(woke, embassy_time::Timer::after(GATE_RETRY)).await;
49 serving(producer)
50}
51
52#[repr(C)]
57pub struct Backed<T> {
58 inner: T,
59 requested: AtomicBool,
60 openers: AtomicU32,
61 watch: Signal<CriticalSectionRawMutex, ()>,
62}
63
64impl<T> Backed<T> {
65 pub const fn new(inner: T) -> Self {
67 Self {
68 inner,
69 requested: AtomicBool::new(false),
70 openers: AtomicU32::new(0),
71 watch: Signal::new(),
72 }
73 }
74
75 pub fn openers(&self) -> u32 {
77 self.openers.load(Ordering::Acquire)
78 }
79
80 pub async fn unwatched(&self, cooldown: Duration) {
82 use embassy_futures::select::{Either, select};
83 loop {
84 while self.openers() > 0 {
85 self.watch.wait().await;
86 }
87 self.watch.reset();
88 if let Either::First(()) =
89 select(embassy_time::Timer::after(cooldown), self.watch.wait()).await
90 && self.openers() == 0
91 {
92 return;
93 }
94 }
95 }
96
97 fn admit_reader(&self) {
98 if self.openers.fetch_add(1, Ordering::AcqRel) == 0 {
99 self.watch.signal(());
100 }
101 }
102
103 fn drop_reader(&self) {
104 if self.openers.fetch_sub(1, Ordering::AcqRel) == 1 {
105 self.watch.signal(());
106 }
107 }
108}
109
110pub struct Open<T: 'static> {
114 target: &'static Backed<T>,
115}
116
117impl<T> Open<T> {
118 pub fn signal(&self) -> &'static T {
122 &self.target.inner
123 }
124}
125
126impl<T> Deref for Open<T> {
127 type Target = T;
128 fn deref(&self) -> &T {
129 &self.target.inner
130 }
131}
132
133impl<T> Drop for Open<T> {
134 fn drop(&mut self) {
135 self.target.drop_reader();
136 }
137}
138
139impl<T> Deref for Backed<T> {
140 type Target = T;
141 fn deref(&self) -> &T {
142 &self.inner
143 }
144}
145
146impl<T: Sync + 'static> Gated for Backed<T> {
147 type Handle = Open<T>;
148
149 fn admit(&'static self) -> Open<T> {
150 self.admit_reader();
151 Open { target: self }
152 }
153
154 async fn ensure(&'static self, caller: &'static TaskNode, entry: &'static Coupling) {
155 let Some(producer) = producer_of(caller, entry) else {
156 warn!(
157 "supervisor: {} is gated but no graph declares a writer for it",
158 entry.name()
159 );
160 return;
161 };
162 if !producer.is_running() {
164 #[cfg(feature = "control")]
167 if matches!(producer.mode(), crate::Mode::OnDemand) {
168 warn!(
169 "supervisor: {} gates on OnDemand {}, which a control start \
170 re-enables but does not spawn: this read returns only once \
171 its pool grows it",
172 entry.name(),
173 producer.name()
174 );
175 }
176 #[cfg(not(feature = "control"))]
177 warn!(
178 "supervisor: {} gates on {}, which is not running, and without \
179 `control` there is nothing that can start it: this read will \
180 not return",
181 entry.name(),
182 producer.name()
183 );
184 }
185 loop {
186 if serving(producer) {
187 break;
188 }
189 #[cfg(feature = "control")]
190 if !producer.is_running() && !self.requested.swap(true, Ordering::Relaxed) {
191 crate::request_control(producer, crate::ControlOp::Activate).await;
192 }
193 if !wait_serving(producer).await {
194 self.requested.store(false, Ordering::Relaxed);
195 }
196 }
197 self.requested.store(false, Ordering::Relaxed);
198 }
199}
200
201impl TaskNode {
202 pub async fn retire<T: Sync>(&'static self, s: Sig<Backed<T>>, cooldown: Duration) {
208 loop {
209 s.target.unwatched(cooldown).await;
210 self.clear_ready();
211 if s.target.openers() == 0 {
212 break;
213 }
214 self.set_ready();
215 }
216 #[cfg(feature = "control")]
217 crate::request_control(self, crate::ControlOp::Deactivate).await;
218 }
219}
220
221#[cfg(feature = "coupling-observe")]
222impl<T: crate::Observable> crate::Observable for Backed<T> {
223 fn change_token(&self) -> u32 {
224 self.inner.change_token()
225 }
226}