Skip to main content

fuchsia_async/
condition.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Implements a combined mutex and condition.
6//!
7//! # Example:
8//!
9//! ```no_run
10//!     let condition = Condition::new(0);
11//!     condition.when(|state| if state == 1 { Poll::Ready(()) } else { Poll::Pending }).await;
12//!
13//!     // Elsewhere...
14//!     let guard = condition.lock();
15//!     *guard.lock() = 1;
16//!     for waker in guard.drain_wakers() {
17//!         waker.wake();
18//!     }
19//! ```
20
21use fuchsia_sync::{Condvar, Mutex, MutexGuard};
22use std::future::poll_fn;
23use std::marker::PhantomPinned;
24use std::ops::{Deref, DerefMut};
25use std::pin::{Pin, pin};
26use std::ptr::NonNull;
27use std::sync::Arc;
28use std::task::{Poll, Wake, Waker};
29
30/// An async condition which combines a mutex and a condition variable.
31// Condition is implemented as an intrusive doubly linked list.  Typical use should avoid any
32// additional heap allocations after creation, as the nodes of the list are stored as part of the
33// caller's future.
34#[derive(Default)]
35pub struct Condition<T>(Arc<Mutex<Inner<T>>>);
36
37impl<T> Condition<T> {
38    /// Returns a new condition.
39    pub fn new(data: T) -> Self {
40        Self(Arc::new(Mutex::new(Inner { head: None, count: 0, data })))
41    }
42
43    /// Returns the number of wakers waiting on the condition.
44    pub fn waker_count(&self) -> usize {
45        self.0.lock().count
46    }
47
48    /// Same as `Mutex::lock`.
49    pub fn lock(&self) -> ConditionGuard<'_, T> {
50        ConditionGuard(self.0.lock())
51    }
52
53    /// Returns when `poll` resolves.
54    pub async fn when<R>(
55        &self,
56        mut poll: impl for<'b> FnMut(&mut ConditionGuard<'b, T>) -> Poll<R>,
57    ) -> R {
58        let mut entry = pin!(self.waker_entry());
59        poll_fn(move |cx| {
60            let guard = self.0.lock();
61            let mut cond_guard = ConditionGuard(guard);
62            let result = poll(&mut cond_guard);
63            if result.is_pending() {
64                cond_guard.add_waker(entry.as_mut(), cx.waker().clone());
65            }
66            result
67        })
68        .await
69    }
70
71    /// Returns a new waker entry.
72    pub fn waker_entry(&self) -> WakerEntry<T> {
73        WakerEntry {
74            list: Some(self.0.clone()),
75            node: Node { next: None, prev: None, waker: None, _pinned: PhantomPinned },
76        }
77    }
78}
79
80#[derive(Default)]
81struct Inner<T> {
82    head: Option<NonNull<Node>>,
83    count: usize,
84    data: T,
85}
86
87// SAFETY: Safe because we always access `head` whilst holding the list lock.
88unsafe impl<T: Send> Send for Inner<T> {}
89
90/// Guard returned by `lock`.
91pub struct ConditionGuard<'a, T>(MutexGuard<'a, Inner<T>>);
92
93impl<'a, T> ConditionGuard<'a, T> {
94    /// Adds the waker entry to the condition's list of wakers.
95    ///
96    /// # Panics
97    ///
98    /// This will panic if the waker entry is associated with a different Condition.
99    pub fn add_waker(&mut self, waker_entry: Pin<&mut WakerEntry<T>>, waker: Waker) {
100        // The waker must be associated with right list.
101        if let Some(list) = &waker_entry.list {
102            assert!(list.data_ptr() == &mut *self.0, "Cannot add waker to different Condition");
103        }
104        // SAFETY: We never move the data out.
105        let waker_entry = unsafe { waker_entry.get_unchecked_mut() };
106        // SAFETY: We set list correctly above.
107        unsafe {
108            waker_entry.node.add(&mut *self.0, waker);
109        }
110    }
111
112    /// Returns an iterator that will drain all wakers.  Whilst the drainer exists, a lock is held
113    /// which will prevent new wakers from being added to the list, so depending on your use case,
114    /// you might wish to collect the wakers before calling `wake` on each waker.  NOTE: If the
115    /// drainer is dropped, this will *not* drain elements not visited.
116    pub fn drain_wakers<'b>(&'b mut self) -> Drainer<'b, 'a, T> {
117        Drainer(self)
118    }
119
120    /// Returns the number of wakers registered with the condition.
121    pub fn waker_count(&self) -> usize {
122        self.0.count
123    }
124
125    /// Blocks the current thread until `condition` returns true.
126    ///
127    /// The mutex is unlocked while waiting and re-locked before this function returns.
128    pub fn block_until(&mut self, mut condition: impl FnMut(&mut Self) -> bool) {
129        struct Condv(Condvar);
130
131        impl Wake for Condv {
132            fn wake(self: Arc<Self>) {
133                self.0.notify_one();
134            }
135        }
136
137        let condv = Arc::new(Condv(Condvar::new()));
138        let mut entry = pin!(WakerEntry {
139            list: None,
140            node: Node { next: None, prev: None, waker: None, _pinned: PhantomPinned },
141        });
142
143        while !condition(self) {
144            self.add_waker(entry.as_mut(), Waker::from(condv.clone()));
145            condv.0.wait(&mut self.0);
146        }
147
148        // SAFETY: We don't move data out of the mutable reference.
149        unsafe {
150            entry.get_unchecked_mut().node.remove(&mut *self.0);
151        }
152    }
153}
154
155impl<T> Deref for ConditionGuard<'_, T> {
156    type Target = T;
157
158    fn deref(&self) -> &Self::Target {
159        &self.0.data
160    }
161}
162
163impl<T> DerefMut for ConditionGuard<'_, T> {
164    fn deref_mut(&mut self) -> &mut Self::Target {
165        &mut self.0.data
166    }
167}
168
169/// A waker entry that can be added to a list.
170pub struct WakerEntry<T> {
171    list: Option<Arc<Mutex<Inner<T>>>>,
172    node: Node,
173}
174
175impl<T> Drop for WakerEntry<T> {
176    fn drop(&mut self) {
177        if let Some(list) = &self.list {
178            self.node.remove(&mut *list.lock());
179        }
180    }
181}
182
183// The members here must only be accessed whilst holding the mutex on the list.
184struct Node {
185    next: Option<NonNull<Node>>,
186    prev: Option<NonNull<Node>>,
187    waker: Option<Waker>,
188    _pinned: PhantomPinned,
189}
190
191// SAFETY: Safe because we always access all mebers of `Node` whilst holding the list lock.
192unsafe impl Send for Node {}
193
194impl Node {
195    // # Safety
196    //
197    // The waker *must* have `list` set correctly.
198    unsafe fn add<T>(&mut self, inner: &mut Inner<T>, waker: Waker) {
199        if self.waker.is_none() {
200            self.prev = None;
201            self.next = inner.head;
202            inner.head = Some(self.into());
203            if let Some(mut next) = self.next {
204                // SAFETY: Safe because we have exclusive access to `Inner` and `head` is set
205                // correctly above.
206                unsafe {
207                    next.as_mut().prev = Some(self.into());
208                }
209            }
210            inner.count += 1;
211        }
212        self.waker = Some(waker);
213    }
214
215    fn remove<T>(&mut self, inner: &mut Inner<T>) -> Option<Waker> {
216        if self.waker.is_none() {
217            debug_assert!(self.prev.is_none() && self.next.is_none());
218            return None;
219        }
220        if let Some(mut next) = self.next {
221            // SAFETY: Safe because we have exclusive access to `Inner` and `head` is set correctly.
222            unsafe { next.as_mut().prev = self.prev };
223        }
224        if let Some(mut prev) = self.prev {
225            // SAFETY: Safe because we have exclusive access to `Inner` and `head` is set correctly.
226            unsafe { prev.as_mut().next = self.next };
227        } else {
228            debug_assert_eq!(inner.head, Some(self.into()));
229            inner.head = self.next;
230        }
231        self.prev = None;
232        self.next = None;
233        inner.count -= 1;
234        self.waker.take()
235    }
236}
237
238/// An iterator that will drain waiters.
239pub struct Drainer<'a, 'b, T>(&'a mut ConditionGuard<'b, T>);
240
241impl<T> Iterator for Drainer<'_, '_, T> {
242    type Item = Waker;
243    fn next(&mut self) -> Option<Self::Item> {
244        if let Some(mut head) = self.0.0.head {
245            // SAFETY: Safe because we have exclusive access to `Inner` and `head is set correctly.
246            unsafe { head.as_mut().remove(&mut self.0.0) }
247        } else {
248            None
249        }
250    }
251
252    fn size_hint(&self) -> (usize, Option<usize>) {
253        (self.0.0.count, Some(self.0.0.count))
254    }
255}
256
257impl<T> ExactSizeIterator for Drainer<'_, '_, T> {
258    fn len(&self) -> usize {
259        self.0.0.count
260    }
261}
262
263#[cfg(all(target_os = "fuchsia", test))]
264mod tests {
265    use super::Condition;
266    use crate::TestExecutor;
267    use futures::StreamExt;
268    use futures::stream::FuturesUnordered;
269    use std::pin::pin;
270    use std::sync::atomic::{AtomicU64, Ordering};
271    use std::task::{Poll, Waker};
272
273    #[test]
274    fn test_condition_can_waker_multiple_wakers() {
275        let mut executor = TestExecutor::new();
276        let condition = Condition::new(());
277
278        static COUNT: u64 = 10;
279
280        let counter = AtomicU64::new(0);
281
282        // Use FuturesUnordered so that futures are only polled when explicitly woken.
283        let mut futures = FuturesUnordered::new();
284
285        for _ in 0..COUNT {
286            futures.push(condition.when(|_| {
287                if counter.fetch_add(1, Ordering::Relaxed) >= COUNT {
288                    Poll::Ready(())
289                } else {
290                    Poll::Pending
291                }
292            }));
293        }
294
295        assert!(executor.run_until_stalled(&mut futures.next()).is_pending());
296
297        assert_eq!(counter.load(Ordering::Relaxed), COUNT);
298        assert_eq!(condition.waker_count(), COUNT as usize);
299
300        {
301            let mut guard = condition.lock();
302            let drainer = guard.drain_wakers();
303            assert_eq!(drainer.len(), COUNT as usize);
304            for waker in drainer {
305                waker.wake();
306            }
307        }
308
309        assert!(executor.run_until_stalled(&mut futures.collect::<Vec<_>>()).is_ready());
310        assert_eq!(counter.load(Ordering::Relaxed), COUNT * 2);
311    }
312
313    #[test]
314    fn test_dropping_waker_entry_removes_from_list() {
315        let condition = Condition::new(());
316
317        let entry1 = pin!(condition.waker_entry());
318        condition.lock().add_waker(entry1, Waker::noop().clone());
319
320        {
321            let entry2 = pin!(condition.waker_entry());
322            condition.lock().add_waker(entry2, Waker::noop().clone());
323
324            assert_eq!(condition.waker_count(), 2);
325        }
326
327        assert_eq!(condition.waker_count(), 1);
328        {
329            let mut guard = condition.lock();
330            assert_eq!(guard.drain_wakers().count(), 1);
331        }
332
333        assert_eq!(condition.waker_count(), 0);
334
335        let entry3 = pin!(condition.waker_entry());
336        condition.lock().add_waker(entry3, Waker::noop().clone());
337
338        assert_eq!(condition.waker_count(), 1);
339    }
340
341    #[test]
342    fn test_waker_can_be_added_multiple_times() {
343        let condition = Condition::new(());
344
345        let mut entry1 = pin!(condition.waker_entry());
346        condition.lock().add_waker(entry1.as_mut(), Waker::noop().clone());
347
348        let mut entry2 = pin!(condition.waker_entry());
349        condition.lock().add_waker(entry2.as_mut(), Waker::noop().clone());
350
351        assert_eq!(condition.waker_count(), 2);
352        {
353            let mut guard = condition.lock();
354            assert_eq!(guard.drain_wakers().count(), 2);
355        }
356        assert_eq!(condition.waker_count(), 0);
357
358        condition.lock().add_waker(entry1, Waker::noop().clone());
359        condition.lock().add_waker(entry2, Waker::noop().clone());
360
361        assert_eq!(condition.waker_count(), 2);
362
363        {
364            let mut guard = condition.lock();
365            assert_eq!(guard.drain_wakers().count(), 2);
366        }
367        assert_eq!(condition.waker_count(), 0);
368    }
369
370    #[test]
371    #[should_panic]
372    fn test_adding_waker_to_different_condition() {
373        let condition1 = Condition::new(());
374        let condition2 = Condition::new(());
375
376        let entry2 = pin!(condition2.waker_entry());
377
378        let mut guard = condition1.lock();
379        // The entry is for `condition2` not `condition1` so this should panic.
380        guard.add_waker(entry2, std::task::Waker::noop().clone());
381    }
382
383    #[test]
384    fn test_block_until_immediate() {
385        let condition = Condition::new(42);
386        let mut guard = condition.lock();
387        guard.block_until(|val| **val == 42);
388        assert_eq!(*guard, 42);
389    }
390
391    #[test]
392    fn test_block_until_blocking() {
393        use std::sync::Arc;
394        use std::thread;
395        use std::time::Duration;
396
397        let condition = Arc::new(Condition::new(0));
398        let condition_clone = condition.clone();
399
400        let handle = thread::spawn(move || {
401            // Wait a bit to ensure the other thread has blocked.
402            thread::sleep(Duration::from_millis(50));
403            let mut guard = condition_clone.lock();
404            *guard = 1;
405            for waker in guard.drain_wakers() {
406                waker.wake();
407            }
408        });
409
410        let mut guard = condition.lock();
411        guard.block_until(|val| **val == 1);
412        assert_eq!(*guard, 1);
413
414        handle.join().unwrap();
415    }
416}