zng_var/
future.rs

1use super::*;
2
3use std::{pin::Pin, task::Poll};
4
5/// See [`Var::wait_update`].
6pub(crate) struct WaitUpdateFut<'a, V: AnyVar> {
7    var: &'a V,
8    update_id: VarUpdateId,
9}
10impl<'a, V: AnyVar> WaitUpdateFut<'a, V> {
11    pub(super) fn new(var: &'a V) -> Self {
12        Self {
13            update_id: var.last_update(),
14            var,
15        }
16    }
17
18    fn poll_impl(&mut self, cx: &mut std::task::Context<'_>) -> Poll<VarUpdateId> {
19        let update_id = self.var.last_update();
20        if update_id != self.update_id {
21            // has changed since init or last poll
22            self.update_id = update_id;
23            Poll::Ready(update_id)
24        } else {
25            // has not changed since init or last poll, register hook
26            let waker = cx.waker().clone();
27            let handle = self.var.hook_any(Box::new(move |_| {
28                waker.wake_by_ref();
29                false
30            }));
31
32            // check if changed in parallel while was registering hook
33            let update_id = self.var.last_update();
34            if update_id != self.update_id {
35                // changed in parallel
36                // the hook will be dropped (handle not perm), it may wake in parallel too, but poll checks again.
37                self.update_id = update_id;
38                Poll::Ready(update_id)
39            } else {
40                // really not ready yet
41                handle.perm();
42                Poll::Pending
43            }
44        }
45    }
46}
47impl<V: AnyVar> Future for WaitUpdateFut<'_, V> {
48    type Output = VarUpdateId;
49
50    fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
51        self.poll_impl(cx)
52    }
53}
54
55/// See [`Var::wait_animation`].
56pub(crate) struct WaitIsNotAnimatingFut<'a, V: AnyVar> {
57    var: &'a V,
58    observed_animation_start: bool,
59}
60impl<'a, V: AnyVar> WaitIsNotAnimatingFut<'a, V> {
61    pub(super) fn new(var: &'a V) -> Self {
62        Self {
63            observed_animation_start: var.is_animating(),
64            var,
65        }
66    }
67}
68impl<V: AnyVar> Future for WaitIsNotAnimatingFut<'_, V> {
69    type Output = ();
70
71    fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<()> {
72        if !self.var.capabilities().contains(VarCapability::NEW) {
73            // var cannot have new value, ready to avoid deadlock.
74            self.observed_animation_start = false;
75            return Poll::Ready(());
76        }
77        if self.observed_animation_start {
78            // already observed `is_animating` in a previous poll.
79
80            if self.var.is_animating() {
81                // still animating, but received poll so an animation was overridden and stopped.
82                // try hook with new animation.
83
84                while self.var.capabilities().contains(VarCapability::NEW) {
85                    let waker = cx.waker().clone();
86                    let r = self.var.hook_animation_stop(Box::new(move || {
87                        waker.wake_by_ref();
88                    }));
89                    if r.is_err() {
90                        // failed to hook with new animation too.
91                        if self.var.is_animating() {
92                            // but has yet another animation, try again.
93                            continue;
94                        } else {
95                            // observed `is_animating` changing to `false`.
96                            self.observed_animation_start = false;
97                            return Poll::Ready(());
98                        }
99                    } else {
100                        // new animation hook setup ok, break loop.
101                        return Poll::Pending;
102                    }
103                }
104
105                // var no longer has the `NEW` capability.
106                self.observed_animation_start = false;
107                Poll::Ready(())
108            } else {
109                // now observed change to `false`.
110                self.observed_animation_start = false;
111                Poll::Ready(())
112            }
113        } else {
114            // have not observed `is_animating` yet.
115
116            // hook with normal var updates, `is_animating && is_new` is always `true`.
117            let waker = cx.waker().clone();
118            let start_hook = self.var.hook_any(Box::new(move |_| {
119                waker.wake_by_ref();
120                false
121            }));
122
123            if self.var.is_animating() {
124                // observed `is_animating` already, changed in other thread during the `hook` setup.
125                self.observed_animation_start = true;
126
127                while self.var.capabilities().contains(VarCapability::NEW) {
128                    // hook with animation stop.
129                    let waker = cx.waker().clone();
130                    let r = self.var.hook_animation_stop(Box::new(move || {
131                        waker.wake_by_ref();
132                    }));
133                    if r.is_err() {
134                        // failed to hook, animation already stopped during hook setup.
135                        if self.var.is_animating() {
136                            // but var is still animating, reason a new animation replaced the previous one (that stopped).
137                            // continue to hook with new animation.
138                            continue;
139                        } else {
140                            // we have observed `is_animating` changing to `false` in one poll call.
141                            self.observed_animation_start = false;
142                            return Poll::Ready(());
143                        }
144                    } else {
145                        // animation hook setup ok, break loop.
146                        return Poll::Pending;
147                    }
148                }
149
150                // var no longer has the `NEW` capability.
151                self.observed_animation_start = false;
152                Poll::Ready(())
153            } else {
154                // updates hook ok.
155                start_hook.perm();
156                Poll::Pending
157            }
158        }
159    }
160}