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
}
}
}
}