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}