Skip to main content

intuituive/
task.rs

1//! Background work: run something slow off the app's thread, and write
2//! signals with its result when it is done.
3//!
4//! - [`spawn`] runs a closure on its own thread;
5//! - [`spawn_future`] drives a future on its own thread, for async code
6//!   that does not need a particular runtime (a tokio app keeps its own
7//!   runtime and sends results back with a [`Proxy`](crate::Proxy));
8//! - [`resource`] is a value that loads in the background, with
9//!   [`Load::Loading`] until it arrives, and reloads on demand;
10//!   [`resource_for`] loads again whenever a key changes;
11//! - [`latest`] and [`latest_every`] are the newest value a thread of
12//!   their own makes.
13//!
14//! The `done` closure runs back on the app's thread, between frames, with a
15//! [`Ctx`]: it can write signals, open a screen, or quit. A cancelled
16//! [`Task`] still finishes its work (a thread cannot be stopped from
17//! outside), but its result is dropped.
18
19use std::cell::Cell;
20use std::collections::HashMap;
21use std::fmt::Display;
22use std::future::Future;
23use std::panic::Location;
24use std::rc::Rc;
25use std::sync::atomic::{AtomicBool, Ordering};
26use std::sync::{Arc, Condvar, Mutex, MutexGuard};
27#[cfg(not(target_arch = "wasm32"))]
28use std::task::Wake;
29use std::task::{Context, Poll, Waker};
30use std::time::Duration;
31
32use crate::app::Ctx;
33use crate::reactive::{Runtime, Signal, signal, watch};
34
35/// A running piece of background work.
36#[derive(Clone, Debug)]
37pub struct Task {
38    cancelled: Arc<AtomicBool>,
39    finished: Arc<AtomicBool>,
40}
41
42impl Task {
43    /// Drop the result when it arrives: `done` will not run.
44    pub fn cancel(&self) {
45        self.cancelled.store(true, Ordering::SeqCst);
46    }
47
48    /// Whether the work has finished (its result may still be on its way
49    /// to the app's thread).
50    pub fn is_finished(&self) -> bool {
51        self.finished.load(Ordering::SeqCst)
52    }
53}
54
55/// Run `work` on its own thread; when it returns, run `done` with the
56/// result on the app's thread. Call it while the app is built or running
57/// (in `App::new`'s closure, a handler, or a node's closure).
58///
59/// ```
60/// use intuituive::prelude::*;
61///
62/// let app = App::new(|| {
63///     let answer = signal(String::from("thinking…"));
64///     spawn(|| 6 * 7, move |n, _| answer.set(n.to_string()));
65///     text!("{answer}").on_key("q", |cx| cx.quit())
66/// })
67/// .wait_for_tasks(true);
68/// let screen = app.render_with(&["q"], 20, 1).unwrap();
69/// assert_eq!(screen[0].trim_end(), "42");
70/// ```
71///
72/// # In a browser
73/// On `wasm32-unknown-unknown` there are no threads: `work` runs at once,
74/// on the app's thread, and its result arrives on the next turn.
75///
76/// # Panics
77/// Outside a running or building app.
78#[track_caller]
79pub fn spawn<T: Send + 'static>(
80    work: impl FnOnce() -> T + Send + 'static,
81    done: impl FnOnce(T, &mut Ctx) + Send + 'static,
82) -> Task {
83    spawn_at(Location::caller(), work, done)
84}
85
86/// [`spawn`], saying in the inspector's trace that it was started at
87/// `site`.
88fn spawn_at<T: Send + 'static>(
89    site: &'static Location<'static>,
90    work: impl FnOnce() -> T + Send + 'static,
91    done: impl FnOnce(T, &mut Ctx) + Send + 'static,
92) -> Task {
93    let runtime = Runtime::current_for("spawn()");
94    let proxy = runtime.proxy();
95    let tasks = runtime.tasks.clone();
96    let task = Task {
97        cancelled: Arc::new(AtomicBool::new(false)),
98        finished: Arc::new(AtomicBool::new(false)),
99    };
100    let handle = task.clone();
101    tasks.fetch_add(1, Ordering::SeqCst);
102    crate::clock::off_thread(move || {
103        // Count the task delivered however the work ends, a panic included.
104        struct Delivered(Arc<std::sync::atomic::AtomicUsize>);
105        impl Drop for Delivered {
106            fn drop(&mut self) {
107                self.0.fetch_sub(1, Ordering::SeqCst);
108            }
109        }
110        let _delivered = Delivered(tasks);
111        let value = work();
112        handle.finished.store(true, Ordering::SeqCst);
113        let cancelled = handle.cancelled;
114        proxy.send(Box::new(move |cx| {
115            let dropped = cancelled.load(Ordering::SeqCst);
116            crate::reactive::traced(|| crate::inspect::Traced::Task { at: site, dropped });
117            if !dropped {
118                done(value, cx);
119            }
120        }));
121    });
122    task
123}
124
125/// [`spawn`] whose `done` need not be [`Send`]: it stays on the app's
126/// thread while `work` runs, so it can hold an `Rc`, a node's handle, or
127/// anything else that cannot cross threads. Only the value travels.
128///
129/// ```
130/// use intuituive::prelude::*;
131/// use intuituive::spawn_then;
132/// use std::rc::Rc;
133///
134/// let app = App::new(|| {
135///     let shown = signal(String::new());
136///     let prefix = Rc::new(String::from("answer: "));
137///     spawn_then(|| 6 * 7, move |n, _| shown.set(format!("{prefix}{n}")));
138///     text!("{shown}")
139/// })
140/// .wait_for_tasks(true);
141/// let mut driver = app.driver(12, 1);
142/// driver.settle();
143/// assert_eq!(driver.screen().plain()[0].trim_end(), "answer: 42");
144/// ```
145///
146/// # Panics
147/// Outside a running or building app.
148#[track_caller]
149pub fn spawn_then<T: Send + 'static>(
150    work: impl FnOnce() -> T + Send + 'static,
151    done: impl FnOnce(T, &mut Ctx) + 'static,
152) -> Task {
153    let runtime = Runtime::current_for("spawn_then()");
154    let id = runtime.next_local.get();
155    runtime.next_local.set(id + 1);
156    let done: Then<T> = Box::new(done);
157    runtime.locals.borrow_mut().insert(id, Box::new(done));
158    spawn_at(Location::caller(), work, move |value, cx| {
159        // The continuation waited on this thread, under its number.
160        let done = Runtime::current().locals.borrow_mut().remove(&id);
161        if let Some(done) = done.and_then(|d| d.downcast::<Then<T>>().ok()) {
162            done(value, cx);
163        }
164    })
165}
166
167/// Run `work` on a thread of its own with a [`Proxy`](crate::Proxy) to send
168/// results back as it goes (lines of a log, entries of a directory walk),
169/// counted as a [task](Task) until it returns: an app that
170/// [waits for its tasks](crate::App::wait_for_tasks) waits for the whole
171/// stream each turn, so a test sees all of it. The [`Task`] says when it
172/// has finished; [`cancel`](Task::cancel) sets a flag `work` can read from
173/// the [`Stream`] it is given, to stop early.
174///
175/// ```
176/// use intuituive::prelude::*;
177/// use intuituive::spawn_stream;
178///
179/// let app = App::new(|| {
180///     let lines = signal(Vec::<String>::new());
181///     spawn_stream(move |stream| {
182///         for n in 1..=3 {
183///             if stream.cancelled() {
184///                 break;
185///             }
186///             stream.proxy().run(move || lines.update(|l| l.push(format!("line {n}"))));
187///         }
188///     });
189///     text(move || lines.get().join(","))
190/// })
191/// .wait_for_tasks(true);
192/// let mut driver = app.driver(30, 1);
193/// driver.settle();
194/// assert_eq!(driver.screen().plain()[0].trim_end(), "line 1,line 2,line 3");
195/// ```
196///
197/// # In a browser
198/// On `wasm32-unknown-unknown` there are no threads: `work` runs at once,
199/// on the app's thread, and what it sends arrives on the next turn.
200///
201/// # Panics
202/// Outside a running or building app.
203#[track_caller]
204pub fn spawn_stream(work: impl FnOnce(Stream) + Send + 'static) -> Task {
205    let runtime = Runtime::current_for("spawn_stream()");
206    let stream = Stream {
207        proxy: runtime.proxy(),
208        cancelled: Arc::new(AtomicBool::new(false)),
209    };
210    let task = Task {
211        cancelled: stream.cancelled.clone(),
212        finished: Arc::new(AtomicBool::new(false)),
213    };
214    let tasks = runtime.tasks.clone();
215    let finished = task.finished.clone();
216    tasks.fetch_add(1, Ordering::SeqCst);
217    crate::clock::off_thread(move || {
218        // Done however the work ends, a panic included. The count goes
219        // down after the last result is queued, so a waiting app takes
220        // them all.
221        struct Done(Arc<std::sync::atomic::AtomicUsize>, Arc<AtomicBool>);
222        impl Drop for Done {
223            fn drop(&mut self) {
224                self.1.store(true, Ordering::SeqCst);
225                self.0.fetch_sub(1, Ordering::SeqCst);
226            }
227        }
228        let _done = Done(tasks, finished);
229        work(stream);
230    });
231    task
232}
233
234/// What a [`spawn_stream`] thread sends its results with.
235#[derive(Clone)]
236pub struct Stream {
237    proxy: crate::reactive::Proxy,
238    cancelled: Arc<AtomicBool>,
239}
240
241impl Stream {
242    /// The proxy to the app: [`run`](crate::Proxy::run) a closure on the
243    /// app's thread with each result.
244    pub fn proxy(&self) -> &crate::reactive::Proxy {
245        &self.proxy
246    }
247
248    /// Whether the stream's [`Task`] was cancelled: stop then.
249    pub fn cancelled(&self) -> bool {
250        self.cancelled.load(Ordering::SeqCst)
251    }
252}
253
254/// A [`spawn_then`] continuation, kept on the app's thread.
255type Then<T> = Box<dyn FnOnce(T, &mut Ctx)>;
256
257/// Drive `future` to completion on its own thread, then run `done` with its
258/// output on the app's thread. The future is polled with a waker that
259/// parks and unparks that thread, so it suits futures that do not need a
260/// specific async runtime.
261#[track_caller]
262pub fn spawn_future<F>(future: F, done: impl FnOnce(F::Output, &mut Ctx) + Send + 'static) -> Task
263where
264    F: Future + Send + 'static,
265    F::Output: Send + 'static,
266{
267    spawn_at(Location::caller(), move || block_on(future), done)
268}
269
270/// Poll `future` on this thread until it is ready.
271#[cfg(not(target_arch = "wasm32"))]
272fn block_on<F: Future>(future: F) -> F::Output {
273    struct Unpark(std::thread::Thread);
274    impl Wake for Unpark {
275        fn wake(self: Arc<Self>) {
276            self.0.unpark();
277        }
278    }
279    let waker = Waker::from(Arc::new(Unpark(std::thread::current())));
280    let mut context = Context::from_waker(&waker);
281    let mut future = std::pin::pin!(future);
282    loop {
283        match future.as_mut().poll(&mut context) {
284            Poll::Ready(output) => return output,
285            Poll::Pending => std::thread::park(),
286        }
287    }
288}
289
290/// Poll `future` until it is ready. On wasm32 there is no other thread to
291/// wake it, so it is polled again at once: a future that waits on I/O or a
292/// timer never finishes there.
293#[cfg(target_arch = "wasm32")]
294fn block_on<F: Future>(future: F) -> F::Output {
295    let mut context = Context::from_waker(Waker::noop());
296    let mut future = std::pin::pin!(future);
297    loop {
298        if let Poll::Ready(output) = future.as_mut().poll(&mut context) {
299            return output;
300        }
301    }
302}
303
304/// The state of a [`Resource`].
305#[derive(Clone, Debug, PartialEq, Eq)]
306pub enum Load<T> {
307    /// Not arrived yet.
308    Loading,
309    Ready(T),
310    /// The fetch failed, with its error.
311    Failed(String),
312}
313
314impl<T> Load<T> {
315    pub fn is_loading(&self) -> bool {
316        matches!(self, Load::Loading)
317    }
318
319    /// The value, if it arrived.
320    pub fn ready(&self) -> Option<&T> {
321        match self {
322            Load::Ready(value) => Some(value),
323            _ => None,
324        }
325    }
326}
327
328/// A value fetched in the background. `Copy`, like a signal: read it in a
329/// node and the node redraws when it arrives.
330pub struct Resource<T: 'static> {
331    pub(crate) state: Signal<Load<T>>,
332    start: Signal<Rc<dyn Fn()>>,
333    /// How long the last value stays while the next loads
334    /// ([`stale_after`](Resource::stale_after)).
335    stale: Signal<Option<Duration>>,
336}
337
338impl<T> Clone for Resource<T> {
339    fn clone(&self) -> Self {
340        *self
341    }
342}
343
344impl<T> Copy for Resource<T> {}
345
346impl<T: 'static> Resource<T> {
347    /// Read the state through `f`, subscribing the reader.
348    pub fn with<R>(&self, f: impl FnOnce(&Load<T>) -> R) -> R {
349        self.state.with(f)
350    }
351
352    /// Fetch again: the state goes back to [`Load::Loading`], and a result
353    /// still on its way from an earlier fetch is dropped.
354    pub fn reload(&self) {
355        let start = self.start.with_untracked(Rc::clone);
356        start();
357    }
358
359    /// When it fetches again (a [`reload`](Self::reload), or a new key of
360    /// [`resource_for`]), keep the last value (or error) for up to `wait`
361    /// rather than going back to [`Load::Loading`] at once: a preview pane
362    /// that does not blink between two quick loads, and clears once a slow
363    /// one has kept the old value on show for `wait`. The wait is on the
364    /// app's clock ([`after`](crate::after)), so a test steps through it
365    /// with [`Driver::advance`](crate::Driver::advance). `None` goes
366    /// back to loading at once, as without it.
367    #[must_use]
368    pub fn stale_after(self, wait: impl Into<Option<Duration>>) -> Resource<T> {
369        self.stale.set(wait.into());
370        self
371    }
372}
373
374impl<T: Clone + 'static> Resource<T> {
375    /// The state (a clone), subscribing the reader.
376    pub fn get(&self) -> Load<T> {
377        self.state.get()
378    }
379}
380
381/// Fetch a value in the background with `fetch` (on its own thread), now
382/// and on every [`Resource::reload`].
383///
384/// ```
385/// use intuituive::prelude::*;
386///
387/// let app = App::new(|| {
388///     let user = resource(|| Ok::<_, String>("Ada"));
389///     text(move || match user.get() {
390///         Load::Loading => "loading…".into(),
391///         Load::Ready(name) => format!("Hello, {name}"),
392///         Load::Failed(error) => format!("[red]{error}"),
393///     })
394///     .on_key("q", |cx| cx.quit())
395/// })
396/// .wait_for_tasks(true);
397/// let screen = app.render_with(&["q"], 20, 1).unwrap();
398/// assert_eq!(screen[0].trim_end(), "Hello, Ada");
399/// ```
400#[track_caller]
401pub fn resource<T, E>(fetch: impl Fn() -> Result<T, E> + Send + Sync + 'static) -> Resource<T>
402where
403    T: Send + 'static,
404    E: Display,
405{
406    let site = Location::caller();
407    let (state, generation, stale) = (signal(Load::<T>::Loading), signal(0u64), signal(None));
408    let fetch = Arc::new(fetch);
409    let start: Rc<dyn Fn()> = Rc::new(move || {
410        let fetch = fetch.clone();
411        load(site, state, generation, stale, move || {
412            fetch().map_err(|error| error.to_string())
413        });
414    });
415    start();
416    Resource {
417        state,
418        start: signal(start),
419        stale,
420    }
421}
422
423/// A [`resource`] that fetches for a key: `key` reads signals (the
424/// hovered file, the pane's width), and whenever what it returns changes,
425/// `fetch` runs for the new key on its own thread. A result for a key
426/// already left behind is dropped. With
427/// [`stale_after`](Resource::stale_after), the last key's value stays on
428/// show while the next one loads, for a while at most. The first fetch
429/// starts on the app's first turn; [`reload`](Resource::reload) fetches
430/// the current key again.
431///
432/// ```
433/// use intuituive::prelude::*;
434/// use intuituive::resource_for;
435///
436/// let app = App::new(|| {
437///     let id = signal(1);
438///     let user = resource_for(move || id.get(), |id| Ok::<_, String>(format!("user {id}")));
439///     text(move || match user.get() {
440///         Load::Ready(name) => name,
441///         _ => "…".into(),
442///     })
443///     .on_key("n", move |_| id.update(|i| *i += 1))
444/// })
445/// .wait_for_tasks(true);
446/// let mut driver = app.driver(10, 1);
447/// driver.press("n");
448/// assert_eq!(driver.screen().plain()[0].trim_end(), "user 2");
449/// ```
450///
451/// # Panics
452/// Outside a running or building app.
453#[track_caller]
454pub fn resource_for<K, T, E>(
455    key: impl Fn() -> K + 'static,
456    fetch: impl Fn(K) -> Result<T, E> + Send + Sync + 'static,
457) -> Resource<T>
458where
459    K: PartialEq + Clone + Send + 'static,
460    T: Send + 'static,
461    E: Display,
462{
463    let site = Location::caller();
464    let (state, generation, stale) = (signal(Load::<T>::Loading), signal(0u64), signal(None));
465    let current: Signal<Option<K>> = signal(None);
466    let fetch = Arc::new(fetch);
467    let start: Rc<dyn Fn()> = Rc::new(move || {
468        let Some(key) = current.get_untracked() else {
469            return;
470        };
471        let fetch = fetch.clone();
472        load(site, state, generation, stale, move || {
473            fetch(key).map_err(|error| error.to_string())
474        });
475    });
476    let start = signal(start);
477    watch(key, move |key, _| {
478        current.replace(Some(key));
479        start.with_untracked(Rc::clone)();
480    });
481    Resource {
482        state,
483        start,
484        stale,
485    }
486}
487
488/// Start a fetch of `state` with `work`: a result for an earlier one is
489/// dropped from now on. The state goes back to loading at once, or after
490/// `stale`'s wait if the fetch is still under way then.
491fn load<T: Send + 'static>(
492    site: &'static Location<'static>,
493    state: Signal<Load<T>>,
494    generation: Signal<u64>,
495    stale: Signal<Option<Duration>>,
496    work: impl FnOnce() -> Result<T, String> + Send + 'static,
497) {
498    generation.update(|g| *g += 1);
499    let mine = generation.get_untracked();
500    let arrived = Arc::new(AtomicBool::new(false));
501    match stale.get_untracked() {
502        Some(wait) if !state.with_untracked(Load::is_loading) => {
503            let arrived = arrived.clone();
504            // On the app's clock, so a test steps through the wait.
505            crate::app::after(wait, move |_| {
506                // Still waiting for this fetch: the old value goes.
507                if generation.get_untracked() == mine && !arrived.load(Ordering::SeqCst) {
508                    state.update(|s| *s = Load::Loading);
509                }
510            });
511        }
512        _ => state.update(|s| *s = Load::Loading),
513    }
514    spawn_at(site, work, move |result, _| {
515        // A later fetch started: this result is stale.
516        if generation.get_untracked() == mine {
517            arrived.store(true, Ordering::SeqCst);
518            state.update(|s| {
519                *s = match result {
520                    Ok(value) => Load::Ready(value),
521                    Err(error) => Load::Failed(error),
522                }
523            });
524        }
525    });
526}
527
528/// The sending end of a [`latest`] signal, for its thread.
529pub struct Latest<T> {
530    /// The newest value not yet delivered.
531    slot: Arc<std::sync::Mutex<Option<T>>>,
532    /// A delivery is on its way to the app's thread; it takes the newest.
533    queued: Arc<AtomicBool>,
534    /// The app has gone: a delivery was dropped unrun.
535    closed: Arc<AtomicBool>,
536    proxy: crate::reactive::Proxy,
537    signal: Signal<Option<T>>,
538    /// The clock [`sleep`](Latest::sleep) keeps to, and the time of the
539    /// last beat it slept to.
540    clock: Arc<ThreadClock>,
541    due: Cell<Option<Duration>>,
542}
543
544/// Marks the app gone if the delivery it rides with is dropped unrun.
545struct Delivery {
546    /// `None` once it ran.
547    closed: Option<Arc<AtomicBool>>,
548}
549
550impl Delivery {
551    fn ran(&mut self) {
552        self.closed = None;
553    }
554}
555
556impl Drop for Delivery {
557    fn drop(&mut self) {
558        if let Some(closed) = &self.closed {
559            closed.store(true, Ordering::SeqCst);
560        }
561    }
562}
563
564impl<T: Send + 'static> Latest<T> {
565    /// Make `value` the signal's value: on the app's thread, between
566    /// frames, unless a newer one is sent before it gets there (nothing
567    /// queues: the newest wins). Returns `false` once the app has finished,
568    /// so the thread knows to stop.
569    pub fn send(&self, value: T) -> bool {
570        if self.closed.load(Ordering::SeqCst) {
571            return false;
572        }
573        *self.slot.lock().unwrap_or_else(|e| e.into_inner()) = Some(value);
574        if self.queued.swap(true, Ordering::SeqCst) {
575            // The delivery on its way takes this value.
576            return true;
577        }
578        let (slot, queued, signal) = (self.slot.clone(), self.queued.clone(), self.signal);
579        let mut delivery = Delivery {
580            closed: Some(self.closed.clone()),
581        };
582        let sent = self.proxy.send(Box::new(move |_: &mut Ctx| {
583            delivery.ran();
584            queued.store(false, Ordering::SeqCst);
585            let value = slot.lock().unwrap_or_else(|e| e.into_inner()).take();
586            if let Some(value) = value {
587                signal.update(|slot| *slot = Some(value));
588            }
589        }));
590        if !sent {
591            self.closed.store(true, Ordering::SeqCst);
592        }
593        sent
594    }
595
596    /// Wait until `period` after the last beat (after now, the first
597    /// time): a thread that makes a value every `period` keeps to its beat
598    /// however long each takes to make. One that fell behind does not
599    /// hurry through the beats it missed: as a timer made with
600    /// [`every`](crate::every), it goes on at once and then keeps to its
601    /// step. It waits on the wall clock; in an app
602    /// that [waits for its tasks](crate::App::wait_for_tasks), as a test
603    /// does, on the app's clock instead, so a headless script's
604    /// `wait(..)` moves the thread on as far as it moves the app. Returns
605    /// `false` once the app has finished, as [`send`](Self::send) does.
606    ///
607    /// ```
608    /// use intuituive::interact::headless::{Headless, Script};
609    /// use intuituive::prelude::*;
610    /// use std::time::Duration;
611    ///
612    /// let app = App::new(|| {
613    ///     let tick = latest(|tx| {
614    ///         let mut n = 0;
615    ///         while tx.send(n) && tx.sleep(Duration::from_millis(100)) {
616    ///             n += 1;
617    ///         }
618    ///     });
619    ///     text!("{}", tick.get().unwrap_or(0))
620    /// })
621    /// .wait_for_tasks(true);
622    /// let script = Script::new().wait(Duration::from_millis(350)).keys("ctrl+c");
623    /// let mut backend = Headless::new(script, 10, 1);
624    /// let record = backend.record();
625    /// app.run_on(&mut backend).unwrap();
626    /// // Exactly: the thread kept to the app's clock.
627    /// assert_eq!(record.borrow().last_frame().trim_end(), "3");
628    /// ```
629    pub fn sleep(&self, period: Duration) -> bool {
630        if self.closed.load(Ordering::SeqCst) {
631            return false;
632        }
633        let now = self.clock.now();
634        // The shortest beat is a millisecond, as a timer's.
635        let period = period.max(Duration::from_millis(1));
636        let next = self.due.get().unwrap_or(now) + period;
637        let due = if next >= now {
638            next
639        } else {
640            let behind = (now - next).as_nanos() % period.as_nanos();
641            now + Duration::from_nanos((period.as_nanos() - behind) as u64)
642        };
643        self.due.set(Some(due));
644        self.clock.sleep_until(due) && !self.closed.load(Ordering::SeqCst)
645    }
646
647    /// Wait until `ready` is true, or `timeout` has passed (`None`: no
648    /// limit), looking again each time a [`Notifier`] from
649    /// [`notifier`](Self::notifier) is notified: a device's buffer filled,
650    /// a message in a channel. Whether `ready` was true; `false` on the
651    /// timeout, or once the app has finished.
652    ///
653    /// Waiting this way, the thread counts as asleep for an app that
654    /// [waits for its tasks](crate::App::wait_for_tasks), as a test does:
655    /// its driver does not hold each turn for it (as it would for a
656    /// thread blocked on its own), and the timeout is on the app's clock.
657    /// Once notified it counts as running until it sleeps or waits again,
658    /// so what it sends then is in the same turn on every run.
659    ///
660    /// ```
661    /// use intuituive::prelude::*;
662    /// use std::sync::atomic::{AtomicU32, Ordering};
663    /// use std::sync::Arc;
664    /// use std::time::Duration;
665    ///
666    /// let filled = Arc::new(AtomicU32::new(0));
667    /// let device = filled.clone();
668    /// let app = App::new(move || {
669    ///     let filled = filled.clone();
670    ///     let level = latest(move |tx| {
671    ///         let notifier = tx.notifier();
672    ///         // A device's own thread fills a buffer every 50 ms.
673    ///         std::thread::spawn(move || {
674    ///             for _ in 0..3 {
675    ///                 std::thread::sleep(Duration::from_millis(50));
676    ///                 device.fetch_add(1, Ordering::SeqCst);
677    ///                 notifier.notify();
678    ///             }
679    ///         });
680    ///         let mut seen = 0;
681    ///         while seen < 3 {
682    ///             if !tx.wait_for(|| filled.load(Ordering::SeqCst) > seen, None) {
683    ///                 return;
684    ///             }
685    ///             seen = filled.load(Ordering::SeqCst);
686    ///             tx.send(seen);
687    ///         }
688    ///     });
689    ///     text!("{}", level.get().unwrap_or(0))
690    /// })
691    /// .wait_for_tasks(true);
692    /// let mut driver = app.driver(5, 1);
693    /// let start = std::time::Instant::now();
694    /// while driver.screen().plain()[0].trim_end() != "3" {
695    ///     driver.advance(Duration::from_millis(10));
696    /// }
697    /// // No turn waited the five seconds a busy thread would hold it.
698    /// assert!(start.elapsed() < Duration::from_secs(4));
699    /// ```
700    pub fn wait_for(
701        &self,
702        mut ready: impl FnMut() -> bool,
703        timeout: impl Into<Option<Duration>>,
704    ) -> bool {
705        let timeout = timeout.into();
706        let due = timeout.map(|t| self.clock.now() + t);
707        self.clock.wait_for(&mut ready, due) && !self.closed.load(Ordering::SeqCst)
708    }
709
710    /// A handle any thread can [`notify`](Notifier::notify) a
711    /// [`wait_for`](Self::wait_for) with: `Clone` and `Send`, for the
712    /// device callback or the channel's sender.
713    pub fn notifier(&self) -> Notifier {
714        Notifier {
715            clock: self.clock.clone(),
716        }
717    }
718
719    /// Run `work`, which blocks on something else (a channel's `recv`, a
720    /// read from a pipe), with the thread counted as asleep for an app on
721    /// its own clock, as [`wait_for`](Self::wait_for) is: the driver's
722    /// turns do not wait for it. What it sends after is picked up when it
723    /// comes, not in a set turn; to keep a test exact, wait with
724    /// [`wait_for`](Self::wait_for) and a [`Notifier`] instead.
725    pub fn blocking<R>(&self, work: impl FnOnce() -> R) -> R {
726        self.clock.running(false);
727        let out = work();
728        self.clock.running(true);
729        out
730    }
731}
732
733/// Wakes a [`Latest::wait_for`] to look again: made by
734/// [`Latest::notifier`], sent to whatever thread makes the condition
735/// true.
736#[derive(Clone)]
737pub struct Notifier {
738    clock: Arc<ThreadClock>,
739}
740
741impl Notifier {
742    /// Wake every thread of the app waiting in
743    /// [`wait_for`](Latest::wait_for), to look at its condition again.
744    pub fn notify(&self) {
745        self.clock.notify();
746    }
747}
748
749/// The clock [`latest`] threads keep to with [`Latest::sleep`]: the wall
750/// clock, or, in an app that waits for its tasks, the app's own, which its
751/// driver moves on. Then the driver also waits, each turn, until every
752/// such thread is asleep for a later time or finished, so what they send
753/// is the same on every run.
754pub(crate) struct ThreadClock {
755    state: Mutex<ClockState>,
756    changed: Condvar,
757    /// Where the wall clock's time starts.
758    start: WallStart,
759}
760
761/// `Instant::now()` at the clock's start. On `wasm32-unknown-unknown` std
762/// has no clock (`Instant::now` panics) and no threads to keep to it, so
763/// the wall's time there stays at zero.
764#[cfg(not(target_arch = "wasm32"))]
765struct WallStart(std::time::Instant);
766#[cfg(target_arch = "wasm32")]
767struct WallStart;
768
769impl WallStart {
770    #[cfg(not(target_arch = "wasm32"))]
771    fn now() -> WallStart {
772        WallStart(std::time::Instant::now())
773    }
774    #[cfg(target_arch = "wasm32")]
775    fn now() -> WallStart {
776        WallStart
777    }
778    #[cfg(not(target_arch = "wasm32"))]
779    fn elapsed(&self) -> Duration {
780        self.0.elapsed()
781    }
782    #[cfg(target_arch = "wasm32")]
783    fn elapsed(&self) -> Duration {
784        Duration::ZERO
785    }
786}
787
788#[derive(Default)]
789struct ClockState {
790    /// Threads keep to the app's clock, not the wall's.
791    manual: bool,
792    /// The app's time, as its driver last set it.
793    now: Duration,
794    /// Threads started and neither asleep on the app's clock nor finished.
795    running: usize,
796    /// Threads asleep on the app's clock, by ticket, with when each wakes.
797    asleep: HashMap<u64, Duration>,
798    /// Threads in [`Latest::wait_for`], by ticket (with a timeout, also
799    /// in `asleep`), until notified or due.
800    waiting: std::collections::HashSet<u64>,
801    /// Bumped by each notify: a waiter that saw it change looks again.
802    notified: u64,
803    next: u64,
804    /// The app has finished: sleepers wake, and stop.
805    closed: bool,
806}
807
808impl ThreadClock {
809    pub(crate) fn new() -> Arc<ThreadClock> {
810        Arc::new(ThreadClock {
811            state: Mutex::new(ClockState::default()),
812            changed: Condvar::new(),
813            start: WallStart::now(),
814        })
815    }
816
817    fn lock(&self) -> MutexGuard<'_, ClockState> {
818        self.state.lock().unwrap_or_else(|e| e.into_inner())
819    }
820
821    /// Keep threads to the app's clock from now on (`manual`), or to the
822    /// wall's.
823    pub(crate) fn set_manual(&self, manual: bool) {
824        self.lock().manual = manual;
825    }
826
827    fn now(&self) -> Duration {
828        let state = self.lock();
829        if state.manual {
830            state.now
831        } else {
832            self.start.elapsed()
833        }
834    }
835
836    /// A thread starts (or ends, if not `started`).
837    fn running(&self, started: bool) {
838        let mut state = self.lock();
839        if started {
840            state.running += 1;
841        } else {
842            state.running = state.running.saturating_sub(1);
843            self.changed.notify_all();
844        }
845    }
846
847    /// Sleep until `due`; `false` once the app has finished.
848    fn sleep_until(&self, due: Duration) -> bool {
849        let mut state = self.lock();
850        if !state.manual {
851            drop(state);
852            let wait = due.saturating_sub(self.start.elapsed());
853            if !wait.is_zero() {
854                std::thread::sleep(wait);
855            }
856            return !self.lock().closed;
857        }
858        if due <= state.now || state.closed {
859            return !state.closed;
860        }
861        let ticket = state.next;
862        state.next += 1;
863        state.asleep.insert(ticket, due);
864        state.running = state.running.saturating_sub(1);
865        self.changed.notify_all();
866        // Woken by `advance`, which counts it running again, or the end.
867        while state.asleep.contains_key(&ticket) && !state.closed {
868            state = self.changed.wait(state).unwrap_or_else(|e| e.into_inner());
869        }
870        !state.closed
871    }
872
873    /// Wait until `ready`, or the app's time `due`, waking to look
874    /// again on each [`notify`](Self::notify); whether `ready` was true.
875    /// The thread counts as asleep meanwhile.
876    fn wait_for(&self, ready: &mut dyn FnMut() -> bool, due: Option<Duration>) -> bool {
877        loop {
878            let seen = self.lock().notified;
879            if ready() {
880                return true;
881            }
882            let mut state = self.lock();
883            if state.closed {
884                return false;
885            }
886            if state.notified != seen {
887                // Notified while looking: look again.
888                continue;
889            }
890            if !state.manual {
891                // The wall clock: a wait on the notifications, cut at `due`.
892                let left = due.map(|due| due.saturating_sub(self.start.elapsed()));
893                if left.is_some_and(|left| left.is_zero()) {
894                    return false;
895                }
896                let wait = left.unwrap_or(Duration::from_secs(3600));
897                drop(
898                    self.changed
899                        .wait_timeout_while(state, wait, |s| s.notified == seen && !s.closed),
900                );
901                continue;
902            }
903            if due.is_some_and(|due| due <= state.now) {
904                return false;
905            }
906            let ticket = state.next;
907            state.next += 1;
908            state.waiting.insert(ticket);
909            if let Some(due) = due {
910                state.asleep.insert(ticket, due);
911            }
912            state.running = state.running.saturating_sub(1);
913            self.changed.notify_all();
914            // Woken by a notify or `advance`, which count it running again,
915            // or the end.
916            while state.waiting.contains(&ticket) && !state.closed {
917                state = self.changed.wait(state).unwrap_or_else(|e| e.into_inner());
918            }
919        }
920    }
921
922    /// Wake every [`wait_for`](Self::wait_for) to look again.
923    fn notify(&self) {
924        let mut state = self.lock();
925        state.notified += 1;
926        let woken: Vec<u64> = state.waiting.drain().collect();
927        for ticket in &woken {
928            state.asleep.remove(ticket);
929        }
930        state.running += woken.len();
931        self.changed.notify_all();
932    }
933
934    /// The app's clock is at `now`: wake the threads that are due.
935    pub(crate) fn advance(&self, now: Duration) {
936        let mut state = self.lock();
937        state.now = now;
938        let due: Vec<u64> = state
939            .asleep
940            .iter()
941            .filter(|(_, at)| **at <= now)
942            .map(|(ticket, _)| *ticket)
943            .collect();
944        for ticket in &due {
945            state.asleep.remove(ticket);
946            state.waiting.remove(ticket);
947        }
948        state.running += due.len();
949        self.changed.notify_all();
950    }
951
952    /// Wait (a while at most) until every thread is asleep on the app's
953    /// clock or finished. Only on the app's clock.
954    pub(crate) fn settle(&self) {
955        let deadline = crate::clock::Instant::now() + THREAD_SETTLE;
956        let mut state = self.lock();
957        while state.manual && state.running > 0 && !state.closed {
958            let left = deadline.saturating_duration_since(crate::clock::Instant::now());
959            if left.is_zero() {
960                return;
961            }
962            state = self
963                .changed
964                .wait_timeout(state, left)
965                .unwrap_or_else(|e| e.into_inner())
966                .0;
967        }
968    }
969
970    /// When the next thread asleep on the app's clock wakes; `None` on the
971    /// wall clock, or with none asleep.
972    pub(crate) fn next_due(&self) -> Option<Duration> {
973        let state = self.lock();
974        if !state.manual {
975            return None;
976        }
977        state.asleep.values().min().copied()
978    }
979
980    /// The app has finished.
981    pub(crate) fn close(&self) {
982        self.lock().closed = true;
983        self.changed.notify_all();
984    }
985}
986
987/// How long a turn of an app on its own clock waits, at most, for its
988/// `latest` threads to go to sleep: one that waits on something else (a
989/// pipe, a device) holds each turn up this long.
990const THREAD_SETTLE: Duration = Duration::from_secs(5);
991
992/// A signal holding the newest value a thread of its own produces: `run`
993/// gets the [`Latest`] to send them with, and runs until it returns.
994/// `None` until the first value arrives. Values are not queued: when the
995/// app is busy, the ones it had no time for are skipped, and the next
996/// frame shows the newest (an audio buffer, a sensor, a log's tail). Each
997/// delivery wakes the app's loop, which picks it up on its next turn.
998/// `send` returns `false` once the app has finished; stop then. The thread
999/// is not a [task](spawn): [`App::wait_for_tasks`](crate::App::wait_for_tasks)
1000/// does not wait for it; a thread that keeps to a beat with
1001/// [`Latest::sleep`] keeps to the app's clock under it. The thread starts
1002/// when the app starts running (or at once, when the app already is).
1003///
1004/// ```
1005/// use intuituive::prelude::*;
1006///
1007/// let app = App::new(|| {
1008///     let reading = latest(|tx| {
1009///         for sample in 0..=1000 {
1010///             if !tx.send(sample) {
1011///                 return;
1012///             }
1013///         }
1014///     });
1015///     text(move || match reading.get() {
1016///         Some(n) => format!("last {n}"),
1017///         None => "waiting".into(),
1018///     })
1019/// });
1020/// let mut driver = app.driver(12, 1);
1021/// while driver.screen().plain()[0].trim_end() != "last 1000" {
1022///     std::thread::sleep(std::time::Duration::from_millis(1));
1023///     driver.settle();
1024/// }
1025/// ```
1026///
1027/// # In a browser
1028/// On `wasm32-unknown-unknown` there are no threads: `run` never starts
1029/// and the signal stays `None`. [`latest_every`] works there.
1030///
1031/// # Panics
1032/// Outside a running or building app.
1033#[track_caller]
1034pub fn latest<T: Send + 'static>(
1035    run: impl FnOnce(Latest<T>) + Send + 'static,
1036) -> Signal<Option<T>> {
1037    let runtime = Runtime::current_for("latest()");
1038    let value = signal(None);
1039    let clock = runtime.clock.clone();
1040    let sender = Latest {
1041        slot: Arc::new(std::sync::Mutex::new(None)),
1042        queued: Arc::new(AtomicBool::new(false)),
1043        closed: Arc::new(AtomicBool::new(false)),
1044        proxy: runtime.proxy(),
1045        signal: value,
1046        clock: clock.clone(),
1047        due: Cell::new(None),
1048    };
1049    // No threads on wasm32 (a browser tab): `run` never starts, and the
1050    // signal stays `None`. `latest_every` keeps to the app's timers there.
1051    #[cfg(target_arch = "wasm32")]
1052    let _ = (clock, sender, run);
1053    #[cfg(not(target_arch = "wasm32"))]
1054    runtime.start_thread(Box::new(move || {
1055        clock.running(true);
1056        std::thread::spawn(move || {
1057            // Counted finished however `run` ends, a panic included.
1058            struct Finished(Arc<ThreadClock>);
1059            impl Drop for Finished {
1060                fn drop(&mut self) {
1061                    self.0.running(false);
1062                }
1063            }
1064            let _finished = Finished(clock);
1065            run(sender)
1066        });
1067    }));
1068    value
1069}
1070
1071/// A [`latest`] signal fed by `next`, called on a thread of its own once at
1072/// the start and then every `period`, keeping to the beat as
1073/// [`Latest::sleep`] does: on the app's clock in an app that
1074/// [waits for its tasks](crate::App::wait_for_tasks), so a test sees the
1075/// same values at the same times on every run; on the wall clock
1076/// otherwise. A generator, a sensor polled, a demo's signal.
1077///
1078/// ```
1079/// use intuituive::prelude::*;
1080/// use intuituive::latest_every;
1081/// use std::time::Duration;
1082///
1083/// let app = App::new(|| {
1084///     let mut n = 0;
1085///     let count = latest_every(Duration::from_millis(10), move || {
1086///         n += 1;
1087///         n
1088///     });
1089///     text!("{}", count.get().unwrap_or(0))
1090/// })
1091/// .wait_for_tasks(true);
1092/// let mut driver = app.driver(10, 1);
1093/// driver.advance(Duration::from_millis(45));
1094/// // Made at 0, 10, 20, 30 and 40 ms: each beat is a step of the driver.
1095/// assert_eq!(driver.screen().plain()[0].trim_end(), "5");
1096/// ```
1097///
1098/// Each value is delivered: [`Driver::advance`](crate::Driver::advance)
1099/// and a headless script's `wait` step to each beat, and in a terminal a
1100/// value wakes the loop at once, so a [`rate`](crate::rate) counts every
1101/// one (100 a second at 10 ms), however fast the beat.
1102///
1103/// # In a browser
1104/// On `wasm32-unknown-unknown` there are no threads: `next` runs on the
1105/// app's thread, once at the start and then on an [`every`](crate::every)
1106/// timer.
1107///
1108/// # Panics
1109/// Outside a running or building app.
1110#[track_caller]
1111pub fn latest_every<T: Send + 'static>(
1112    period: Duration,
1113    mut next: impl FnMut() -> T + Send + 'static,
1114) -> Signal<Option<T>> {
1115    #[cfg(not(target_arch = "wasm32"))]
1116    return latest(move |tx| while tx.send(next()) && tx.sleep(period) {});
1117    // No threads on wasm32 (a browser tab): `next` runs on the app's thread,
1118    // at the start and on an [`every`](crate::every) timer.
1119    #[cfg(target_arch = "wasm32")]
1120    {
1121        let value = signal(Some(next()));
1122        crate::every(period.max(Duration::from_millis(1)), move |_| {
1123            value.replace(Some(next()));
1124        });
1125        value
1126    }
1127}