1use std::sync::atomic::{AtomicBool, Ordering};
10use std::sync::Arc;
11
12use tokio::sync::Notify;
13
14#[derive(Default)]
20pub struct Gate {
21 notify: Notify,
22}
23
24impl Gate {
25 pub fn new() -> Self {
27 Self::default()
28 }
29
30 pub fn notify(&self) {
34 self.notify.notify_waiters();
35 }
36
37 pub async fn wait_until<T>(&self, mut check: impl FnMut() -> Option<T>) -> T {
39 loop {
40 let notified = self.notify.notified();
41 tokio::pin!(notified);
42 notified.as_mut().enable();
45 if let Some(value) = check() {
46 return value;
47 }
48 notified.await;
49 }
50 }
51
52 pub async fn wait_until_cancellable<T>(
54 &self,
55 token: &CancelToken,
56 mut check: impl FnMut() -> Option<T>,
57 ) -> Result<T, Cancelled> {
58 loop {
59 let notified = self.notify.notified();
60 let cancelled = token.inner.notify.notified();
61 tokio::pin!(notified);
62 tokio::pin!(cancelled);
63 notified.as_mut().enable();
64 cancelled.as_mut().enable();
65
66 if token.is_cancelled() {
67 return Err(Cancelled);
68 }
69 if let Some(value) = check() {
70 return Ok(value);
71 }
72 tokio::select! {
73 _ = notified => {}
74 _ = cancelled => {}
75 }
76 }
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub struct Cancelled;
83
84impl Cancelled {
85 pub fn into_error(self, context: &str) -> structfs_core_store::Error {
87 structfs_core_store::Error::cancelled(context.to_string())
88 }
89}
90
91#[derive(Default)]
92struct CancelInner {
93 flag: AtomicBool,
94 notify: Notify,
95}
96
97#[derive(Clone, Default)]
103pub struct CancelToken {
104 inner: Arc<CancelInner>,
105}
106
107impl CancelToken {
108 pub fn new() -> Self {
110 Self::default()
111 }
112
113 pub fn cancel(&self) {
115 self.inner.flag.store(true, Ordering::SeqCst);
116 self.inner.notify.notify_waiters();
117 }
118
119 pub fn is_cancelled(&self) -> bool {
121 self.inner.flag.load(Ordering::SeqCst)
122 }
123
124 pub async fn cancelled(&self) {
126 loop {
127 let notified = self.inner.notify.notified();
128 tokio::pin!(notified);
129 notified.as_mut().enable();
130 if self.is_cancelled() {
131 return;
132 }
133 notified.await;
134 }
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use std::sync::Mutex;
142
143 #[tokio::test]
144 async fn wait_resolves_when_predicate_holds() {
145 let gate = Arc::new(Gate::new());
146 let slot: Arc<Mutex<Option<i32>>> = Arc::new(Mutex::new(None));
147
148 let waiter = {
149 let gate = gate.clone();
150 let slot = slot.clone();
151 tokio::spawn(async move { gate.wait_until(|| *slot.lock().unwrap()).await })
152 };
153
154 tokio::task::yield_now().await;
155 *slot.lock().unwrap() = Some(7);
156 gate.notify();
157
158 assert_eq!(waiter.await.unwrap(), 7);
159 }
160
161 #[tokio::test]
162 async fn notify_racing_check_is_not_lost() {
163 for _ in 0..100 {
167 let gate = Arc::new(Gate::new());
168 let flag = Arc::new(AtomicBool::new(false));
169
170 let waiter = {
171 let gate = gate.clone();
172 let flag = flag.clone();
173 tokio::spawn(async move {
174 gate.wait_until(|| flag.load(Ordering::SeqCst).then_some(()))
175 .await
176 })
177 };
178 let notifier = {
179 let gate = gate.clone();
180 let flag = flag.clone();
181 tokio::spawn(async move {
182 flag.store(true, Ordering::SeqCst);
183 gate.notify();
184 })
185 };
186
187 tokio::time::timeout(std::time::Duration::from_secs(5), waiter)
188 .await
189 .expect("lost wakeup")
190 .unwrap();
191 notifier.await.unwrap();
192 }
193 }
194
195 #[tokio::test]
196 async fn cancellation_wakes_parked_wait() {
197 let gate = Arc::new(Gate::new());
198 let token = CancelToken::new();
199
200 let waiter = {
201 let gate = gate.clone();
202 let token = token.clone();
203 tokio::spawn(async move { gate.wait_until_cancellable(&token, || None::<()>).await })
204 };
205
206 tokio::task::yield_now().await;
207 token.cancel();
208 assert_eq!(waiter.await.unwrap(), Err(Cancelled));
209 }
210
211 #[tokio::test]
212 async fn cancel_before_wait_resolves_immediately() {
213 let gate = Gate::new();
214 let token = CancelToken::new();
215 token.cancel();
216 assert_eq!(
217 gate.wait_until_cancellable(&token, || None::<()>).await,
218 Err(Cancelled)
219 );
220 }
221
222 #[tokio::test]
223 async fn predicate_wins_over_no_cancel() {
224 let gate = Gate::new();
225 let token = CancelToken::new();
226 assert_eq!(gate.wait_until_cancellable(&token, || Some(1)).await, Ok(1));
227 }
228
229 #[tokio::test]
230 async fn cancelled_future_resolves() {
231 let token = CancelToken::new();
232 let t2 = token.clone();
233 let waiter = tokio::spawn(async move { t2.cancelled().await });
234 tokio::task::yield_now().await;
235 token.cancel();
236 waiter.await.unwrap();
237 }
238}