1use std::{cell, fmt, future::Future, future::poll_fn, pin::Pin, task::Context, task::Poll};
2
3use slab::Slab;
4
5use super::cell::Cell;
6use crate::task::LocalWaker;
7
8#[derive(Clone, Debug, PartialEq, Eq)]
9pub enum ConditionResult<T> {
10 Value(T),
11 Locked,
13 Dropped,
15}
16
17#[derive(Copy, Clone, PartialEq, Eq, Debug)]
18enum State {
19 Normal,
20 Locked,
21 Dropped,
22}
23
24pub struct Condition<T = ()> {
26 inner: Cell<Inner<T>>,
27}
28
29pub struct Waiter<T = ()> {
31 token: usize,
32 inner: Cell<Inner<T>>,
33}
34
35struct Inner<T> {
36 data: Slab<Option<Item<T>>>,
37 count: usize,
38 state: State,
39}
40
41struct Item<T> {
42 val: cell::Cell<ConditionResult<T>>,
43 waker: LocalWaker,
44}
45
46impl Default for Condition<()> {
47 fn default() -> Self {
48 Self::new()
49 }
50}
51
52impl<T> Clone for Condition<T> {
53 fn clone(&self) -> Self {
54 let inner = self.inner.clone();
55 inner.get_mut().count += 1;
56 Self { inner }
57 }
58}
59
60impl<T> fmt::Debug for Condition<T> {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 f.debug_struct("Condition")
63 .field("state", &self.inner.get_ref().state)
64 .finish()
65 }
66}
67
68impl<T> Condition<T> {
69 pub fn new() -> Condition<T> {
71 Condition {
72 inner: Cell::new(Inner {
73 data: Slab::new(),
74 count: 1,
75 state: State::Normal,
76 }),
77 }
78 }
79}
80
81impl<T: Clone> Condition<T> {
82 pub fn wait(&self) -> Waiter<T> {
84 let token = self.inner.get_mut().data.insert(None);
85 Waiter {
86 token,
87 inner: self.inner.clone(),
88 }
89 }
90
91 pub fn notify(&self, val: T) {
93 let inner = self.inner.get_ref();
94 for (_, item) in &inner.data {
95 if let Some(item) = item
96 && item.waker.wake_checked()
97 {
98 item.val.set(ConditionResult::Value(val.clone()));
99 }
100 }
101 }
102
103 pub fn notify_and_lock(&self, val: T) {
107 self.inner.get_mut().state = State::Locked;
108 self.notify(val);
109 }
110}
111
112impl<T: Default> Condition<T> {
113 pub fn notify_default(&self) {
115 let inner = self.inner.get_ref();
116 for (_, item) in &inner.data {
117 if let Some(item) = item
118 && item.waker.wake_checked()
119 {
120 item.val.set(ConditionResult::Value(T::default()));
121 }
122 }
123 }
124}
125
126impl<T> Drop for Condition<T> {
127 fn drop(&mut self) {
128 let inner = self.inner.get_mut();
129 inner.count -= 1;
130 if inner.count == 0 {
131 inner.state = State::Dropped;
132 for (_, item) in &inner.data {
133 if let Some(item) = item
134 && item.waker.wake_checked()
135 {
136 item.val.set(ConditionResult::Dropped);
137 }
138 }
139 }
140 }
141}
142
143impl<T> Waiter<T> {
144 pub async fn ready(&self) -> ConditionResult<T> {
146 poll_fn(|cx| self.poll_ready(cx)).await
147 }
148
149 pub fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<ConditionResult<T>> {
151 let parent = self.inner.get_mut();
152 let inner = unsafe { parent.data.get_unchecked_mut(self.token) };
153
154 if inner.is_none() {
155 if parent.state == State::Normal {
156 let waker = LocalWaker::default();
157 waker.register(cx.waker());
158 *inner = Some(Item {
159 waker,
160 val: cell::Cell::new(ConditionResult::Locked),
161 });
162 return Poll::Pending;
163 }
164 } else {
165 let item = inner.as_mut().unwrap();
166 if !item.waker.register(cx.waker()) {
167 return Poll::Ready(item.val.replace(ConditionResult::Locked));
168 }
169 }
170
171 match parent.state {
172 State::Normal => Poll::Pending,
173 State::Locked => Poll::Ready(ConditionResult::Locked),
174 State::Dropped => Poll::Ready(ConditionResult::Dropped),
175 }
176 }
177}
178
179impl<T> Clone for Waiter<T> {
180 fn clone(&self) -> Self {
181 let token = self.inner.get_mut().data.insert(None);
182 Waiter {
183 token,
184 inner: self.inner.clone(),
185 }
186 }
187}
188
189impl<T: Default> Future for Waiter<T> {
190 type Output = ConditionResult<T>;
191
192 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
193 self.get_mut().poll_ready(cx)
194 }
195}
196
197impl<T: Default> fmt::Debug for Waiter<T> {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 f.debug_struct("Waiter").finish()
200 }
201}
202
203impl<T> Drop for Waiter<T> {
204 fn drop(&mut self) {
205 self.inner.get_mut().data.remove(self.token);
206 }
207}
208
209#[cfg(test)]
210mod tests {
211 use super::*;
212 use crate::future::lazy;
213
214 #[ntex::test]
215 #[allow(clippy::unit_cmp)]
216 async fn test_condition() {
217 let cond = Condition::<()>::new();
218 let mut waiter = cond.wait();
219 assert_eq!(
220 lazy(|cx| Pin::new(&mut waiter).poll(cx)).await,
221 Poll::Pending
222 );
223 cond.notify_default();
224 assert!(format!("{cond:?}").contains("Condition"));
225 assert!(format!("{waiter:?}").contains("Waiter"));
226 assert_eq!(waiter.await, ConditionResult::Value(()));
227
228 let mut waiter = cond.wait();
229 assert_eq!(
230 lazy(|cx| Pin::new(&mut waiter).poll(cx)).await,
231 Poll::Pending
232 );
233 let mut waiter2 = waiter.clone();
234 assert_eq!(
235 lazy(|cx| Pin::new(&mut waiter2).poll(cx)).await,
236 Poll::Pending
237 );
238
239 drop(cond);
240 assert_eq!(waiter.await, ConditionResult::Dropped);
241 assert_eq!(waiter2.await, ConditionResult::Dropped);
242 }
243
244 #[ntex::test]
245 async fn test_condition_poll() {
246 let cond = Condition::default().clone();
247 let waiter = cond.wait();
248 assert_eq!(lazy(|cx| waiter.poll_ready(cx)).await, Poll::Pending);
249 cond.notify_default();
250 waiter.ready().await;
251
252 let waiter2 = waiter.clone();
253 assert_eq!(lazy(|cx| waiter.poll_ready(cx)).await, Poll::Pending);
254 assert_eq!(lazy(|cx| waiter.poll_ready(cx)).await, Poll::Pending);
255 assert_eq!(lazy(|cx| waiter2.poll_ready(cx)).await, Poll::Pending);
256 assert_eq!(lazy(|cx| waiter2.poll_ready(cx)).await, Poll::Pending);
257
258 drop(cond);
259 assert_eq!(
260 lazy(|cx| waiter.poll_ready(cx)).await,
261 Poll::Ready(ConditionResult::Dropped)
262 );
263 assert_eq!(
264 lazy(|cx| waiter.poll_ready(cx)).await,
265 Poll::Ready(ConditionResult::Dropped)
266 );
267 assert_eq!(
268 lazy(|cx| waiter2.poll_ready(cx)).await,
269 Poll::Ready(ConditionResult::Dropped)
270 );
271 assert_eq!(
272 lazy(|cx| waiter2.poll_ready(cx)).await,
273 Poll::Ready(ConditionResult::Dropped)
274 );
275 }
276
277 #[ntex::test]
278 async fn test_condition_with() {
279 let cond = Condition::<String>::new();
280 let waiter = cond.wait();
281 assert_eq!(lazy(|cx| waiter.poll_ready(cx)).await, Poll::Pending);
282 cond.notify("TEST".into());
283 assert_eq!(
284 waiter.ready().await,
285 ConditionResult::Value("TEST".to_string())
286 );
287
288 let waiter2 = waiter.clone();
289 assert_eq!(lazy(|cx| waiter.poll_ready(cx)).await, Poll::Pending);
290 assert_eq!(lazy(|cx| waiter.poll_ready(cx)).await, Poll::Pending);
291 assert_eq!(lazy(|cx| waiter2.poll_ready(cx)).await, Poll::Pending);
292 assert_eq!(lazy(|cx| waiter2.poll_ready(cx)).await, Poll::Pending);
293
294 drop(cond);
295 assert_eq!(
296 lazy(|cx| waiter.poll_ready(cx)).await,
297 Poll::Ready(ConditionResult::Dropped)
298 );
299 assert_eq!(
300 lazy(|cx| waiter.poll_ready(cx)).await,
301 Poll::Ready(ConditionResult::Dropped)
302 );
303 assert_eq!(
304 lazy(|cx| waiter2.poll_ready(cx)).await,
305 Poll::Ready(ConditionResult::Dropped)
306 );
307 assert_eq!(
308 lazy(|cx| waiter2.poll_ready(cx)).await,
309 Poll::Ready(ConditionResult::Dropped)
310 );
311 }
312
313 #[ntex::test]
314 async fn notify_ready() {
315 let cond = Condition::default().clone();
316 let waiter = cond.wait();
317 assert_eq!(lazy(|cx| waiter.poll_ready(cx)).await, Poll::Pending);
318
319 cond.notify_and_lock(());
320 assert_eq!(
321 lazy(|cx| waiter.poll_ready(cx)).await,
322 Poll::Ready(ConditionResult::Value(()))
323 );
324 assert_eq!(
325 lazy(|cx| waiter.poll_ready(cx)).await,
326 Poll::Ready(ConditionResult::Locked)
327 );
328 assert_eq!(
329 lazy(|cx| waiter.poll_ready(cx)).await,
330 Poll::Ready(ConditionResult::Locked)
331 );
332
333 let waiter2 = cond.wait();
334 assert_eq!(
335 lazy(|cx| waiter2.poll_ready(cx)).await,
336 Poll::Ready(ConditionResult::Locked)
337 );
338 }
339
340 #[ntex::test]
341 async fn notify_with_and_lock_ready() {
342 let cond = Condition::<String>::new();
344 let waiter = cond.wait();
345 let waiter2 = cond.wait();
346 assert_eq!(lazy(|cx| waiter.poll_ready(cx)).await, Poll::Pending);
347 assert_eq!(lazy(|cx| waiter2.poll_ready(cx)).await, Poll::Pending);
348
349 cond.notify_and_lock("TEST".into());
350 assert_eq!(
351 lazy(|cx| waiter.poll_ready(cx)).await,
352 Poll::Ready(ConditionResult::Value("TEST".into()))
353 );
354 assert_eq!(
355 lazy(|cx| waiter.poll_ready(cx)).await,
356 Poll::Ready(ConditionResult::Locked)
357 );
358 assert_eq!(
359 lazy(|cx| waiter.poll_ready(cx)).await,
360 Poll::Ready(ConditionResult::Locked)
361 );
362 assert_eq!(
363 lazy(|cx| waiter2.poll_ready(cx)).await,
364 Poll::Ready(ConditionResult::Value("TEST".into()))
365 );
366 assert_eq!(
367 lazy(|cx| waiter2.poll_ready(cx)).await,
368 Poll::Ready(ConditionResult::Locked)
369 );
370
371 let waiter2 = cond.wait();
372 assert_eq!(
373 lazy(|cx| waiter2.poll_ready(cx)).await,
374 Poll::Ready(ConditionResult::Locked)
375 );
376 }
377}