future_utils/
drop_notify.rs

1use std::thread;
2use futures::task::{self, Task};
3use futures::sync::BiLock;
4use futures::{Async, Future};
5use void::Void;
6
7struct Inner {
8    dropped: bool,
9    task_rx: Option<Task>,
10}
11
12/// Created in tandem with a `DropNotice` using the `drop_notify` function. Drop this object to
13/// cause its corresponding `DropNotice` to resolve.
14pub struct DropNotify {
15    inner: BiLock<Inner>,
16}
17
18/// Created in tandem with a `DropNotify` using the `drop_notify` function. `DropNotice` is a
19/// future which resolves to `()` when its corresponding `DropNotify` is dropped.
20pub struct DropNotice {
21    inner: BiLock<Inner>,
22}
23
24/// Create a (`DropNotify`, `DropNotice`) pair. `DropNotice` is a future that resolves to `()` when
25/// the corresponding `DropNotify` is dropped.
26pub fn drop_notify() -> (DropNotify, DropNotice) {
27    let inner = Inner {
28        dropped: false,
29        task_rx: None,
30    };
31    let (lock_notify, lock_notice) = BiLock::new(inner);
32    let drop_notify = DropNotify {
33        inner: lock_notify,
34    };
35    let drop_notice = DropNotice {
36        inner: lock_notice,
37    };
38    (drop_notify, drop_notice)
39}
40
41impl Future for DropNotice {
42    type Item = ();
43    type Error = Void;
44
45    fn poll(&mut self) -> Result<Async<()>, Void> {
46        if let Async::Ready(ref mut inner) = self.inner.poll_lock() {
47            if inner.dropped {
48                return Ok(Async::Ready(()));
49            }
50            inner.task_rx = Some(task::current());
51            Ok(Async::NotReady)
52        } else {
53            // If it's locked then the notifier must be being dropped.
54            Ok(Async::Ready(()))
55        }
56    }
57}
58
59impl Drop for DropNotify {
60    fn drop(&mut self) {
61        loop {
62            if let Async::Ready(ref mut inner) = self.inner.poll_lock() {
63                inner.dropped = true;
64                if let Some(task) = inner.task_rx.take() {
65                    task.notify();
66                }
67                return;
68            }
69            // The other thread must (very temporarily) have the lock. Spin until we get it.
70            thread::yield_now();
71        }
72    }
73}
74