zng_var/
future.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use super::*;

use std::{pin::Pin, task::Poll};

/// See [`Var::wait_update`].
pub(crate) struct WaitUpdateFut<'a, V: AnyVar> {
    var: &'a V,
    update_id: VarUpdateId,
}
impl<'a, V: AnyVar> WaitUpdateFut<'a, V> {
    pub(super) fn new(var: &'a V) -> Self {
        Self {
            update_id: var.last_update(),
            var,
        }
    }

    fn poll_impl(&mut self, cx: &mut std::task::Context<'_>) -> Poll<VarUpdateId> {
        let update_id = self.var.last_update();
        if update_id != self.update_id {
            // has changed since init or last poll
            self.update_id = update_id;
            Poll::Ready(update_id)
        } else {
            // has not changed since init or last poll, register hook
            let waker = cx.waker().clone();
            let handle = self.var.hook_any(Box::new(move |_| {
                waker.wake_by_ref();
                false
            }));

            // check if changed in parallel while was registering hook
            let update_id = self.var.last_update();
            if update_id != self.update_id {
                // changed in parallel
                // the hook will be dropped (handle not perm), it may wake in parallel too, but poll checks again.
                self.update_id = update_id;
                Poll::Ready(update_id)
            } else {
                // really not ready yet
                handle.perm();
                Poll::Pending
            }
        }
    }
}
impl<V: AnyVar> Future for WaitUpdateFut<'_, V> {
    type Output = VarUpdateId;

    fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
        self.poll_impl(cx)
    }
}

/// See [`Var::wait_animation`].
pub(crate) struct WaitIsNotAnimatingFut<'a, V: AnyVar> {
    var: &'a V,
    observed_animation_start: bool,
}
impl<'a, V: AnyVar> WaitIsNotAnimatingFut<'a, V> {
    pub(super) fn new(var: &'a V) -> Self {
        Self {
            observed_animation_start: var.is_animating(),
            var,
        }
    }
}
impl<V: AnyVar> Future for WaitIsNotAnimatingFut<'_, V> {
    type Output = ();

    fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<()> {
        if !self.var.capabilities().contains(VarCapability::NEW) {
            // var cannot have new value, ready to avoid deadlock.
            self.observed_animation_start = false;
            return Poll::Ready(());
        }
        if self.observed_animation_start {
            // already observed `is_animating` in a previous poll.

            if self.var.is_animating() {
                // still animating, but received poll so an animation was overridden and stopped.
                // try hook with new animation.

                while self.var.capabilities().contains(VarCapability::NEW) {
                    let waker = cx.waker().clone();
                    let r = self.var.hook_animation_stop(Box::new(move || {
                        waker.wake_by_ref();
                    }));
                    if r.is_err() {
                        // failed to hook with new animation too.
                        if self.var.is_animating() {
                            // but has yet another animation, try again.
                            continue;
                        } else {
                            // observed `is_animating` changing to `false`.
                            self.observed_animation_start = false;
                            return Poll::Ready(());
                        }
                    } else {
                        // new animation hook setup ok, break loop.
                        return Poll::Pending;
                    }
                }

                // var no longer has the `NEW` capability.
                self.observed_animation_start = false;
                Poll::Ready(())
            } else {
                // now observed change to `false`.
                self.observed_animation_start = false;
                Poll::Ready(())
            }
        } else {
            // have not observed `is_animating` yet.

            // hook with normal var updates, `is_animating && is_new` is always `true`.
            let waker = cx.waker().clone();
            let start_hook = self.var.hook_any(Box::new(move |_| {
                waker.wake_by_ref();
                false
            }));

            if self.var.is_animating() {
                // observed `is_animating` already, changed in other thread during the `hook` setup.
                self.observed_animation_start = true;

                while self.var.capabilities().contains(VarCapability::NEW) {
                    // hook with animation stop.
                    let waker = cx.waker().clone();
                    let r = self.var.hook_animation_stop(Box::new(move || {
                        waker.wake_by_ref();
                    }));
                    if r.is_err() {
                        // failed to hook, animation already stopped during hook setup.
                        if self.var.is_animating() {
                            // but var is still animating, reason a new animation replaced the previous one (that stopped).
                            // continue to hook with new animation.
                            continue;
                        } else {
                            // we have observed `is_animating` changing to `false` in one poll call.
                            self.observed_animation_start = false;
                            return Poll::Ready(());
                        }
                    } else {
                        // animation hook setup ok, break loop.
                        return Poll::Pending;
                    }
                }

                // var no longer has the `NEW` capability.
                self.observed_animation_start = false;
                Poll::Ready(())
            } else {
                // updates hook ok.
                start_hook.perm();
                Poll::Pending
            }
        }
    }
}