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