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
//! UI-thread bound tasks.

use std::{
    fmt,
    future::Future,
    pin::Pin,
    task::{Poll, Waker},
};

enum UiTaskState<R> {
    Pending {
        future: Pin<Box<dyn Future<Output = R> + Send>>,
        event_loop_waker: Waker,
        #[cfg(debug_assertions)]
        last_update: Option<zng_var::VarUpdateId>,
    },
    Ready(R),
}
impl<R: fmt::Debug> fmt::Debug for UiTaskState<R> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Pending { .. } => write!(f, "Pending"),
            Self::Ready(arg0) => f.debug_tuple("Ready").field(arg0).finish(),
        }
    }
}

/// Represents a [`Future`] running in sync with the UI.
///
/// The future [`Waker`], wakes the app event loop and causes an update, in an update handler
/// of the task owner [`update`] is called, if this task waked the app the future is polled once.
///
/// [`Waker`]: std::task::Waker
/// [`update`]: UiTask::update
#[derive(Debug)]
pub struct UiTask<R>(UiTaskState<R>);
impl<R> UiTask<R> {
    /// New task with already build event-loop waker.
    ///
    /// App crate provides an integrated `UiTaskWidget::new` that creates the waker for widgets.
    pub fn new_raw<F>(event_loop_waker: Waker, task: F) -> Self
    where
        F: Future<Output = R> + Send + 'static,
    {
        UiTask(UiTaskState::Pending {
            future: Box::pin(task),
            event_loop_waker,
            #[cfg(debug_assertions)]
            last_update: None,
        })
    }

    /// Polls the future if needed, returns a reference to the result if the task is done.
    ///
    /// This does not poll the future if the task is done.
    ///
    /// # App Update
    ///
    /// This method must be called only once per app update, if it is called more than once it will cause **execution bugs**,
    /// futures like [`task::yield_now`] will not work correctly, variables will have old values when a new one
    /// is expected and any other number of hard to debug issues will crop-up.
    ///
    /// In debug builds this is validated and an error message is logged if incorrect updates are detected.
    ///
    /// [`task::yield_now`]: crate::yield_now
    pub fn update(&mut self) -> Option<&R> {
        if let UiTaskState::Pending {
            future,
            event_loop_waker,
            #[cfg(debug_assertions)]
            last_update,
            ..
        } = &mut self.0
        {
            #[cfg(debug_assertions)]
            {
                let update = Some(zng_var::VARS.update_id());
                if *last_update == update {
                    tracing::error!("UiTask::update called twice in the same update");
                }
                *last_update = update;
            }

            if let Poll::Ready(r) = future.as_mut().poll(&mut std::task::Context::from_waker(event_loop_waker)) {
                self.0 = UiTaskState::Ready(r);
            }
        }

        if let UiTaskState::Ready(r) = &self.0 {
            Some(r)
        } else {
            None
        }
    }

    /// Returns `true` if the task is done.
    ///
    /// This does not poll the future.
    pub fn is_ready(&self) -> bool {
        matches!(&self.0, UiTaskState::Ready(_))
    }

    /// Returns the result if the task is completed.
    ///
    /// This does not poll the future, you must call [`update`] to poll until a result is available,
    /// then call this method to take ownership of the result.
    ///
    /// [`update`]: Self::update
    pub fn into_result(self) -> Result<R, Self> {
        match self.0 {
            UiTaskState::Ready(r) => Ok(r),
            p @ UiTaskState::Pending { .. } => Err(Self(p)),
        }
    }
}