umsh_sync/
async_condition.rs1use core::cell::{Cell, RefCell};
20use core::future::Future;
21use core::pin::Pin;
22use core::task::{Context, Poll, Waker};
23
24use slab::Slab;
25
26#[derive(Debug)]
28pub struct AsyncCondition {
29 wakers: RefCell<Slab<Waker>>,
30 trigger_counter: Cell<usize>,
31}
32
33impl Default for AsyncCondition {
34 fn default() -> Self {
35 Self::new()
36 }
37}
38
39impl AsyncCondition {
40 pub fn new() -> Self {
41 AsyncCondition {
42 wakers: RefCell::new(Slab::with_capacity(4)),
43 trigger_counter: Cell::new(1),
44 }
45 }
46
47 pub fn wait(&self) -> AsyncConditionWait<'_> {
49 AsyncConditionWait {
50 condition: self,
51 ticket: self.ticket(),
52 }
53 }
54
55 pub fn ticket(&self) -> AsyncConditionTicket {
56 AsyncConditionTicket {
57 key: None,
58 trigger_after: self.trigger_counter.get(),
59 }
60 }
61
62 pub fn trigger(&self) {
64 let wakers = self.wakers.borrow();
65 self.trigger_counter
66 .set(self.trigger_counter.get().wrapping_add(1));
67 for (_, waker) in wakers.iter() {
68 waker.wake_by_ref();
69 }
70 }
71
72 pub fn is_ticket_triggered(&self, ticket: &AsyncConditionTicket) -> bool {
73 ticket.is_terminated() || ticket.trigger_after != self.trigger_counter.get()
74 }
75
76 pub fn forget_ticket(&self, ticket: &mut AsyncConditionTicket) {
77 if let Some(key) = ticket.key.take() {
78 let mut wakers = self.wakers.borrow_mut();
79 assert!(
80 wakers.contains(key),
81 "AsyncConditionTicket contained invalid waker key"
82 );
83 wakers.remove(key);
84 }
85 ticket.trigger_after = 0;
86 }
87
88 pub fn poll_wait(
89 &self,
90 context: &mut Context<'_>,
91 ticket: &mut AsyncConditionTicket,
92 ) -> Poll<()> {
93 if self.is_ticket_triggered(ticket) {
94 ticket.trigger_after = 0;
95 return Poll::Ready(());
96 }
97 let mut wakers = self.wakers.borrow_mut();
98 if let Some(slot) = ticket.key.and_then(|k| wakers.get_mut(k)) {
99 *slot = context.waker().clone();
100 } else {
101 ticket.key = Some(wakers.insert(context.waker().clone()));
102 }
103 Poll::Pending
104 }
105}
106
107#[derive(Debug, Default)]
108pub struct AsyncConditionTicket {
109 key: Option<usize>,
110 trigger_after: usize,
111}
112
113impl AsyncConditionTicket {
114 pub fn is_terminated(&self) -> bool {
115 self.trigger_after == 0
116 }
117}
118
119#[must_use = "futures do nothing unless polled"]
121#[derive(Debug)]
122pub struct AsyncConditionWait<'a> {
123 condition: &'a AsyncCondition,
124 ticket: AsyncConditionTicket,
125}
126
127impl<'a> AsyncConditionWait<'a> {
128 pub fn is_triggered(&self) -> bool {
129 self.condition.is_ticket_triggered(&self.ticket)
130 }
131
132 pub fn is_terminated(&self) -> bool {
133 self.ticket.is_terminated()
134 }
135}
136
137impl<'a> Future for AsyncConditionWait<'a> {
138 type Output = ();
139 fn poll(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<()> {
140 let this = &mut *self;
141 this.condition.poll_wait(context, &mut this.ticket)
142 }
143}
144
145impl<'a> Drop for AsyncConditionWait<'a> {
146 fn drop(&mut self) {
147 self.condition.forget_ticket(&mut self.ticket);
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154 use futures::future::{Either, FutureExt, pending, ready, select};
155
156 #[test]
159 fn test_async_condition() {
160 let condition = AsyncCondition::new();
161 assert_eq!(condition.wait().now_or_never(), None);
162 let waiter = condition.wait();
163 condition.trigger();
164 assert_eq!(waiter.now_or_never(), Some(()));
165
166 futures::executor::block_on(async {
168 let waiter = condition.wait();
169 futures::join!(waiter, async {
170 condition.trigger();
171 });
172 });
173 }
174
175 #[test]
178 fn test_cancel_upon_equivalent() {
179 let condition = AsyncCondition::new();
180
181 let fut = select(pending::<()>(), condition.wait());
183 assert!(fut.now_or_never().is_none());
184
185 let fut = select(pending::<()>(), condition.wait());
187 condition.trigger();
188 let resolved = fut
189 .map(|e| match e {
190 Either::Left(((), _)) => 1,
191 Either::Right(((), _)) => 2,
192 })
193 .now_or_never();
194 assert_eq!(resolved, Some(2));
195 let _ = ready::<()>(()); }
197
198 #[test]
201 fn dropping_one_waiter_does_not_disturb_others() {
202 futures::executor::block_on(async {
203 let condition = AsyncCondition::new();
204 let wait_a = condition.wait();
205 let wait_b = condition.wait();
206
207 drop(wait_a);
208
209 futures::join!(wait_b, async { condition.trigger() });
211 });
212 }
213
214 #[test]
216 fn forget_ticket_terminates() {
217 let condition = AsyncCondition::new();
218 let mut tkt = condition.ticket();
219 assert!(!tkt.is_terminated());
220 condition.forget_ticket(&mut tkt);
221 assert!(tkt.is_terminated());
222 }
223}