zng_app/
timer.rs

1//! App timers, deadlines and timeouts.
2//!
3//! The primary `struct` of this module is [`TIMERS`]. You can use it to
4//! create UI bound timers that run using only the main thread and can awake the app event loop
5//! to notify updates.
6
7use crate::Deadline;
8use parking_lot::Mutex;
9use std::{
10    fmt, mem,
11    pin::Pin,
12    sync::{
13        Arc,
14        atomic::{AtomicBool, AtomicUsize, Ordering},
15    },
16    task::Waker,
17    time::Duration,
18};
19use zng_app_context::app_local;
20use zng_handle::{Handle, HandleOwner, WeakHandle};
21use zng_time::{DInstant, INSTANT, INSTANT_APP};
22use zng_var::{Var, WeakVar, var};
23
24use crate::{
25    LoopTimer,
26    handler::{AppHandler, AppHandlerArgs, AppWeakHandle},
27    update::UPDATES,
28};
29
30struct DeadlineHandlerEntry {
31    handle: HandleOwner<DeadlineState>,
32    handler: Mutex<Box<dyn FnMut(&dyn AppWeakHandle) + Send>>, // not actually locked, just makes this Sync
33    pending: bool,
34}
35
36struct TimerHandlerEntry {
37    handle: HandleOwner<TimerState>,
38    handler: Mutex<Box<dyn FnMut(&TimerArgs, &dyn AppWeakHandle) + Send>>, // not actually locked, just makes this Sync
39    pending: Option<Deadline>,                                             // the last expected deadline
40}
41
42struct WaitDeadline {
43    deadline: Deadline,
44    wakers: Mutex<Vec<Waker>>,
45}
46struct WaitDeadlineFut(Arc<WaitDeadline>);
47impl Future for WaitDeadlineFut {
48    type Output = ();
49
50    fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> {
51        if self.0.deadline.has_elapsed() {
52            std::task::Poll::Ready(())
53        } else {
54            let waker = cx.waker().clone();
55            self.0.wakers.lock().push(waker);
56            std::task::Poll::Pending
57        }
58    }
59}
60
61struct TimerVarEntry {
62    handle: HandleOwner<TimerState>,
63    weak_var: WeakVar<Timer>,
64}
65
66app_local! {
67    pub(crate) static TIMERS_SV: TimersService = const { TimersService::new() };
68}
69
70pub(crate) struct TimersService {
71    deadlines: Vec<WeakVar<Deadline>>,
72    wait_deadlines: Vec<std::sync::Weak<WaitDeadline>>,
73    timers: Vec<TimerVarEntry>,
74    deadline_handlers: Vec<DeadlineHandlerEntry>,
75    timer_handlers: Vec<TimerHandlerEntry>,
76    has_pending_handlers: bool,
77}
78impl TimersService {
79    const fn new() -> Self {
80        Self {
81            deadlines: vec![],
82            wait_deadlines: vec![],
83            timers: vec![],
84            deadline_handlers: vec![],
85            timer_handlers: vec![],
86            has_pending_handlers: false,
87        }
88    }
89
90    fn deadline(&mut self, deadline: Deadline) -> DeadlineVar {
91        let timer = var(deadline);
92        self.deadlines.push(timer.downgrade());
93        UPDATES.send_awake();
94        timer.read_only()
95    }
96
97    fn wait_deadline(&mut self, deadline: Deadline) -> impl Future<Output = ()> + Send + Sync + use<> {
98        let deadline = Arc::new(WaitDeadline {
99            deadline,
100            wakers: Mutex::new(vec![]),
101        });
102        self.wait_deadlines.push(Arc::downgrade(&deadline));
103        UPDATES.send_awake();
104        WaitDeadlineFut(deadline)
105    }
106
107    fn interval(&mut self, interval: Duration, paused: bool) -> TimerVar {
108        let (owner, handle) = TimerHandle::new(interval, paused);
109        let timer = var(Timer(handle));
110        self.timers.push(TimerVarEntry {
111            handle: owner,
112            weak_var: timer.downgrade(),
113        });
114        UPDATES.send_awake();
115        timer.read_only()
116    }
117
118    fn on_deadline<H>(&mut self, deadline: Deadline, mut handler: H) -> DeadlineHandle
119    where
120        H: AppHandler<DeadlineArgs>,
121    {
122        let (handle_owner, handle) = DeadlineHandle::new(deadline);
123        self.deadline_handlers.push(DeadlineHandlerEntry {
124            handle: handle_owner,
125            handler: Mutex::new(Box::new(move |handle| {
126                handler.event(
127                    &DeadlineArgs {
128                        timestamp: INSTANT.now(),
129                        deadline,
130                    },
131                    &AppHandlerArgs { handle, is_preview: true },
132                )
133            })),
134            pending: false,
135        });
136        UPDATES.send_awake();
137        handle
138    }
139
140    fn on_interval<H>(&mut self, interval: Duration, paused: bool, mut handler: H) -> TimerHandle
141    where
142        H: AppHandler<TimerArgs>,
143    {
144        let (owner, handle) = TimerHandle::new(interval, paused);
145
146        self.timer_handlers.push(TimerHandlerEntry {
147            handle: owner,
148            handler: Mutex::new(Box::new(move |args, handle| {
149                handler.event(args, &AppHandlerArgs { handle, is_preview: true });
150            })),
151            pending: None,
152        });
153        UPDATES.send_awake();
154        handle
155    }
156
157    pub(crate) fn next_deadline(&self, timer: &mut LoopTimer) {
158        for wk in &self.deadlines {
159            if let Some(var) = wk.upgrade() {
160                timer.register(var.get());
161            }
162        }
163
164        for wk in &self.wait_deadlines {
165            if let Some(e) = wk.upgrade() {
166                timer.register(e.deadline);
167            }
168        }
169
170        for t in &self.timers {
171            if let Some(var) = t.weak_var.upgrade()
172                && !t.handle.is_dropped()
173                && !t.handle.data().paused.load(Ordering::Relaxed)
174            {
175                // not dropped and not paused
176                var.with(|t| {
177                    let deadline = t.0.0.data().deadline.lock();
178                    timer.register(deadline.current_deadline());
179                });
180            }
181        }
182
183        for e in &self.deadline_handlers {
184            if !e.handle.is_dropped() {
185                let deadline = e.handle.data().deadline;
186                timer.register(deadline);
187            }
188        }
189
190        for t in &self.timer_handlers {
191            if !t.handle.is_dropped() {
192                let state = t.handle.data();
193                if !state.paused.load(Ordering::Relaxed) {
194                    let deadline = state.deadline.lock();
195                    timer.register(deadline.current_deadline());
196                }
197            }
198        }
199    }
200
201    /// if the last `apply_updates` observed elapsed timers.
202    pub(crate) fn has_pending_updates(&self) -> bool {
203        self.has_pending_handlers
204    }
205
206    /// Update timer vars, flag handlers to be called in [`Self::notify`], returns new app wake time.
207    pub(crate) fn apply_updates(&mut self, timer: &mut LoopTimer) {
208        let now = INSTANT.now();
209
210        // update `deadline` vars
211        self.deadlines.retain(|wk| {
212            if let Some(var) = wk.upgrade() {
213                if !timer.elapsed(var.get()) {
214                    return true; // retain
215                }
216
217                var.update();
218            }
219            false // don't retain
220        });
221
222        // update `wait_deadline` vars
223        self.wait_deadlines.retain(|wk| {
224            if let Some(e) = wk.upgrade() {
225                if !e.deadline.has_elapsed() {
226                    return true; // retain
227                }
228                for w in mem::take(&mut *e.wakers.lock()) {
229                    w.wake();
230                }
231            }
232            false // don't retain
233        });
234
235        // update `interval` vars
236        self.timers.retain(|t| {
237            if let Some(var) = t.weak_var.upgrade()
238                && !t.handle.is_dropped()
239            {
240                if !t.handle.data().paused.load(Ordering::Relaxed) {
241                    var.with(|t| {
242                        let mut deadline = t.0.0.data().deadline.lock();
243
244                        if timer.elapsed(deadline.current_deadline()) {
245                            t.0.0.data().count.fetch_add(1, Ordering::Relaxed);
246                            var.update();
247
248                            deadline.last = now;
249                            timer.register(deadline.current_deadline());
250                        }
251                    })
252                }
253
254                return true; // retain, var is alive and did not call stop.
255            }
256            false // don't retain.
257        });
258
259        // flag `on_deadline` handlers that need to run.
260        self.deadline_handlers.retain_mut(|e| {
261            if e.handle.is_dropped() {
262                return false; // cancel
263            }
264
265            let deadline = e.handle.data().deadline;
266            e.pending = timer.elapsed(deadline);
267
268            self.has_pending_handlers |= e.pending;
269
270            true // retain if not canceled, elapsed deadlines will be dropped in [`Self::notify`].
271        });
272
273        // flag `on_interval` handlers that need to run.
274        self.timer_handlers.retain_mut(|e| {
275            if e.handle.is_dropped() {
276                return false; // stop
277            }
278
279            let state = e.handle.data();
280            if !state.paused.load(Ordering::Relaxed) {
281                let mut deadline = state.deadline.lock();
282
283                if timer.elapsed(deadline.current_deadline()) {
284                    state.count.fetch_add(1, Ordering::Relaxed);
285                    e.pending = Some(deadline.current_deadline());
286                    self.has_pending_handlers = true;
287
288                    deadline.last = now;
289                    timer.register(deadline.current_deadline());
290                }
291            }
292
293            true // retain if stop was not called
294        });
295    }
296
297    /// does on_* notifications.
298    pub(crate) fn notify() {
299        let _s = tracing::trace_span!("TIMERS").entered();
300
301        let _t = INSTANT_APP.pause_for_update();
302
303        // we need to detach the handlers, so we can pass the context for then
304        // so we `mem::take` for the duration of the call. But new timers can be registered inside
305        // the handlers, so we add those handlers using `extend`.
306
307        let mut timers = TIMERS_SV.write();
308
309        if !mem::take(&mut timers.has_pending_handlers) {
310            return;
311        }
312
313        // call `on_deadline` handlers.
314        let mut handlers = mem::take(&mut timers.deadline_handlers);
315        drop(timers);
316        handlers.retain_mut(|h| {
317            if h.pending {
318                (h.handler.get_mut())(&h.handle.weak_handle());
319                h.handle.data().executed.store(true, Ordering::Relaxed);
320            }
321            !h.pending // drop if just called, deadline handlers are *once*.
322        });
323        let mut timers = TIMERS_SV.write();
324        handlers.append(&mut timers.deadline_handlers);
325        timers.deadline_handlers = handlers;
326
327        // call `on_interval` handlers.
328        let mut handlers = mem::take(&mut timers.timer_handlers);
329        drop(timers);
330        handlers.retain_mut(|h| {
331            if let Some(deadline) = h.pending.take() {
332                let args = TimerArgs {
333                    timestamp: INSTANT.now(),
334                    deadline,
335                    wk_handle: h.handle.weak_handle(),
336                };
337                (h.handler.get_mut())(&args, &h.handle.weak_handle());
338            }
339
340            !h.handle.is_dropped() // drop if called stop inside the handler.
341        });
342        let mut timers = TIMERS_SV.write();
343        handlers.append(&mut timers.timer_handlers);
344        timers.timer_handlers = handlers;
345    }
346}
347
348/// App timers, deadlines and timeouts.
349///
350/// You can use this service to create UI bound timers, these timers run using only the app loop and awake the app
351/// to notify updates.
352///
353/// Timer updates can be observed using variables that update when the timer elapses, or you can register
354/// handlers to be called directly when the time elapses. Timers can be *one-time*, updating only once when
355/// a [`deadline`] is reached; or they can update every time on a set [`interval`].
356///
357/// Note that you can also use the [`task::deadline`](zng_task::deadline) function to `.await` deadlines, in app
358/// threads this function uses the `TIMERS` service too.
359///
360/// # Precision
361///
362/// Timers elapse at the specified time or a little later, depending on how busy the app main loop is. High frequency
363/// timers can also have an effective lower frequency of updates because timers only elapse once per frame cycle.
364///
365/// [variable]: Var
366/// [`deadline`]: TIMERS::deadline
367/// [`interval`]: TIMERS::interval
368/// [`async_app_hn!`]: crate::handler::async_app_hn!
369/// [`async_app_hn_once!`]: crate::handler::async_app_hn_once!
370pub struct TIMERS;
371impl TIMERS {
372    /// Returns a [`DeadlineVar`] that will update once when the `deadline` is reached.
373    ///
374    /// If the `deadline` is in the past the variable will still update once in the next app update.
375    /// Drop all clones of the variable to cancel the timer.
376    ///
377    /// ```
378    /// # use zng_app::timer::*;
379    /// # use zng_app::handler::*;
380    /// # use zng_layout::unit::*;
381    /// # use zng_app::var::*;
382    /// # use std::time::Instant;
383    /// # fn foo() {
384    /// let deadline = TIMERS.deadline(20.secs());
385    ///
386    /// # let
387    /// text = deadline.map(|d| if d.has_elapsed() { "20 seconds have passed" } else { "..." });
388    /// # }
389    /// ```
390    ///
391    /// In the example above the deadline variable will update 20 seconds later when the deadline [`has_elapsed`]. The variable
392    /// is read-only and will only update once.
393    ///
394    /// [`has_elapsed`]: Deadline::has_elapsed
395    #[must_use]
396    pub fn deadline(&self, deadline: impl Into<Deadline>) -> DeadlineVar {
397        TIMERS_SV.write().deadline(deadline.into())
398    }
399
400    /// Returns a [`TimerVar`] that will update every time the `interval` elapses.
401    ///
402    /// The timer can be controlled using methods in the variable value. The timer starts
403    /// running immediately if `paused` is `false`.
404    ///
405    /// ```
406    /// # use zng_app::timer::*;
407    /// # use zng_app::handler::*;
408    /// # use zng_layout::unit::*;
409    /// # use zng_app::var::*;
410    /// # use zng_txt::*;
411    /// # use std::time::Instant;
412    /// # fn foo() {
413    /// let timer = TIMERS.interval(1.secs(), false);
414    ///
415    /// # let
416    /// text = timer.map(|t| match t.count() {
417    ///     0 => formatx!(""),
418    ///     1 => formatx!("1 second elapsed"),
419    ///     c => formatx!("{c} seconds elapsed"),
420    /// });
421    /// # }
422    /// ```
423    ///
424    /// In the example above the timer variable will update every second, the variable keeps a [`count`](Timer::count)
425    /// of times the time elapsed, that is incremented every update. The variable is read-only but the value can
426    /// be used to control the timer to some extent, see [`TimerVar`] for details.
427    #[must_use]
428    pub fn interval(&self, interval: Duration, paused: bool) -> TimerVar {
429        TIMERS_SV.write().interval(interval, paused)
430    }
431
432    /// Register a `handler` that will be called once when the `deadline` is reached.
433    ///
434    /// If the `deadline` is in the past the `handler` will be called in the next app update.
435    ///
436    /// ```
437    /// # use zng_app::timer::*;
438    /// # use zng_app::handler::*;
439    /// # use zng_layout::unit::*;
440    /// # use std::time::Instant;
441    /// # fn foo() {
442    /// let handle = TIMERS.on_deadline(
443    ///     20.secs(),
444    ///     app_hn_once!(|_| {
445    ///         println!("20 seconds have passed");
446    ///     }),
447    /// );
448    /// # }
449    /// ```
450    ///
451    /// # Handler
452    ///
453    /// The `handler` can be any of the *once* [`AppHandler`] implementers. You can use the macros
454    /// [`app_hn_once!`](crate::handler::app_hn_once!) or [`async_hn_once!`](crate::handler::async_app_hn_once!)
455    /// to declare a handler closure.
456    ///
457    /// Async handlers execute up to the first `.await` immediately when the `deadline` is reached, subsequent awakes
458    /// are scheduled like an async *preview* event handler.
459    ///
460    /// # Handle
461    ///
462    /// Returns a [`DeadlineHandle`] that can be used to cancel the timer, either by dropping the handle or by
463    /// calling [`cancel`](DeadlineHandle::cancel). You can also call [`perm`](DeadlineHandle::perm)
464    /// to drop the handle without cancelling.
465    pub fn on_deadline<H>(&self, deadline: impl Into<Deadline>, handler: H) -> DeadlineHandle
466    where
467        H: AppHandler<DeadlineArgs>,
468    {
469        TIMERS_SV.write().on_deadline(deadline.into(), handler)
470    }
471
472    /// Register a `handler` that will be called every time the `interval` elapses.
473    ///
474    /// The timer starts running immediately if `paused` is `false`.
475    pub fn on_interval<H>(&self, interval: Duration, paused: bool, handler: H) -> TimerHandle
476    where
477        H: AppHandler<TimerArgs>,
478    {
479        TIMERS_SV.write().on_interval(interval, paused, handler)
480    }
481}
482
483impl TIMERS {
484    /// Implementation of the [`task::deadline`] function when called from app threads.
485    ///
486    /// [`task::deadline`]: zng_task::deadline
487    pub fn wait_deadline(&self, deadline: impl Into<Deadline>) -> impl Future<Output = ()> + Send + Sync + 'static {
488        TIMERS_SV.write().wait_deadline(deadline.into())
489    }
490}
491
492/// A [`deadline`](TIMERS::deadline) timer.
493///
494/// This is a read-only variable of type [`Deadline`], it will update once when the timer elapses.
495///
496/// Drop all clones of this variable to cancel the timer.
497///
498/// ```
499/// # use zng_app::timer::*;
500/// # use zng_app::handler::*;
501/// # use zng_layout::unit::*;
502/// # use zng_app::var::*;
503/// # use std::time::Instant;
504/// # fn foo() {
505/// let deadline: DeadlineVar = TIMERS.deadline(20.secs());
506///
507/// # let
508/// text = deadline.map(|d| if d.has_elapsed() { "20 seconds have passed" } else { "..." });
509/// # }
510/// ```
511///
512/// In the example above the variable is mapped to a text, there are many other things you can do with variables,
513/// including `.await` for the update in UI bound async tasks. See [`Var<T>`] for details.
514///
515/// [`Var<T>`]: zng_var::Var
516pub type DeadlineVar = Var<Deadline>;
517
518/// Represents a [`on_deadline`](TIMERS::on_deadline) handler.
519///
520/// Drop all clones of this handle to cancel the timer, or call [`perm`](Self::perm) to drop the handle
521/// without cancelling the timer.
522#[derive(Clone, PartialEq, Eq, Hash)]
523#[repr(transparent)]
524#[must_use = "the timer is canceled if the handler is dropped"]
525pub struct DeadlineHandle(Handle<DeadlineState>);
526struct DeadlineState {
527    deadline: Deadline,
528    executed: AtomicBool,
529}
530impl DeadlineHandle {
531    /// Create a handle to nothing, the handle always in the *canceled* state.
532    ///
533    /// Note that `Option<DeadlineHandle>` takes up the same space as `DeadlineHandle` and avoids an allocation.
534    pub fn dummy() -> DeadlineHandle {
535        DeadlineHandle(Handle::dummy(DeadlineState {
536            deadline: Deadline(DInstant::EPOCH),
537            executed: AtomicBool::new(false),
538        }))
539    }
540
541    fn new(deadline: Deadline) -> (HandleOwner<DeadlineState>, Self) {
542        let (owner, handle) = Handle::new(DeadlineState {
543            deadline,
544            executed: AtomicBool::new(false),
545        });
546        (owner, DeadlineHandle(handle))
547    }
548
549    /// Drops the handle but does **not** drop the handler closure.
550    ///
551    /// The handler closure will be dropped after it is executed or when the app exits.
552    pub fn perm(self) {
553        self.0.perm();
554    }
555
556    /// If [`perm`](Self::perm) was called in another handle.
557    ///
558    /// If `true` the closure will be dropped when it executes, when the app exits or if [`cancel`](Self::cancel) is called.
559    pub fn is_permanent(&self) -> bool {
560        self.0.is_permanent()
561    }
562
563    /// Drops the handle and forces the handler to drop.
564    ///
565    /// If the deadline has not been reached the handler will not be called, and will drop in the next app update.
566    pub fn cancel(self) {
567        self.0.force_drop();
568    }
569
570    /// The timeout deadline.
571    ///
572    /// The handler is called once when this deadline is reached.
573    pub fn deadline(&self) -> Deadline {
574        self.0.data().deadline
575    }
576
577    /// If the handler has executed. The handler executes once when the deadline is reached.
578    pub fn has_executed(&self) -> bool {
579        self.0.data().executed.load(Ordering::Relaxed)
580    }
581
582    /// If the timeout handler will never execute. Returns `true` if [`cancel`](Self::cancel) was called
583    /// before the handler could execute.
584    pub fn is_canceled(&self) -> bool {
585        !self.has_executed() && self.0.is_dropped()
586    }
587
588    /// Create a weak handle to the deadline.
589    pub fn downgrade(&self) -> WeakDeadlineHandle {
590        WeakDeadlineHandle(self.0.downgrade())
591    }
592}
593impl fmt::Debug for DeadlineHandle {
594    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
595        f.debug_struct("DeadlineHandle")
596            .field("deadline", &self.deadline())
597            .field("handle", &self.0)
598            .field(
599                "state",
600                &if self.has_executed() {
601                    "has_executed"
602                } else if self.is_canceled() {
603                    "is_canceled"
604                } else {
605                    "awaiting"
606                },
607            )
608            .finish()
609    }
610}
611
612/// Weak [`DeadlineHandle`]
613#[derive(Clone, PartialEq, Eq, Hash, Default, Debug)]
614pub struct WeakDeadlineHandle(WeakHandle<DeadlineState>);
615impl WeakDeadlineHandle {
616    /// New weak handle that does not upgrade.
617    pub fn new() -> Self {
618        Self(WeakHandle::new())
619    }
620
621    /// Get the strong handle is still waiting the deadline.
622    pub fn upgrade(&self) -> Option<DeadlineHandle> {
623        self.0.upgrade().map(DeadlineHandle)
624    }
625}
626
627/// Arguments for the handler of [`on_deadline`](TIMERS::on_deadline).
628#[derive(Clone, Debug)]
629#[non_exhaustive]
630pub struct DeadlineArgs {
631    /// When the handler was called.
632    pub timestamp: DInstant,
633    /// Timer deadline, is less-or-equal to the [`timestamp`](Self::timestamp).
634    pub deadline: Deadline,
635}
636
637/// Represents a [`on_interval`](TIMERS::on_interval) handler.
638///
639/// Drop all clones of this handler to stop the timer, or call [`perm`](Self::perm) to drop the handler
640/// without cancelling the timer.
641#[derive(Clone, PartialEq, Eq, Hash)]
642#[repr(transparent)]
643#[must_use = "the timer is stopped if the handler is dropped"]
644pub struct TimerHandle(Handle<TimerState>);
645struct TimerState {
646    paused: AtomicBool,
647    deadline: Mutex<TimerDeadline>,
648    count: AtomicUsize,
649}
650struct TimerDeadline {
651    interval: Duration,
652    last: DInstant,
653}
654impl TimerDeadline {
655    fn current_deadline(&self) -> Deadline {
656        Deadline(self.last + self.interval)
657    }
658}
659impl TimerHandle {
660    fn new(interval: Duration, paused: bool) -> (HandleOwner<TimerState>, TimerHandle) {
661        let (owner, handle) = Handle::new(TimerState {
662            paused: AtomicBool::new(paused),
663            deadline: Mutex::new(TimerDeadline {
664                interval,
665                last: INSTANT.now(),
666            }),
667            count: AtomicUsize::new(0),
668        });
669        (owner, TimerHandle(handle))
670    }
671
672    /// Create a handle to nothing, the handle is always in the *stopped* state.
673    ///
674    /// Note that `Option<TimerHandle>` takes up the same space as `TimerHandle` and avoids an allocation.
675    pub fn dummy() -> TimerHandle {
676        TimerHandle(Handle::dummy(TimerState {
677            paused: AtomicBool::new(true),
678            deadline: Mutex::new(TimerDeadline {
679                interval: Duration::MAX,
680                last: DInstant::EPOCH,
681            }),
682            count: AtomicUsize::new(0),
683        }))
684    }
685
686    /// Drops the handle but does **not** drop the handler closure.
687    ///
688    /// The handler closure will be dropped when the app exits or if it is stopped from the inside or using another handle.
689    pub fn perm(self) {
690        self.0.perm();
691    }
692
693    /// If [`perm`](Self::perm) was called in another handle.
694    ///
695    /// If `true` the closure will keep being called until the app exits or the timer is stopped from the inside or using
696    /// another handle.
697    pub fn is_permanent(&self) -> bool {
698        self.0.is_permanent()
699    }
700
701    /// Drops the handle and forces the handler to drop.
702    ///
703    /// The handler will no longer be called and will drop in the next app update.
704    pub fn stop(self) {
705        self.0.force_drop();
706    }
707
708    /// If the timer was stopped. The timer can be stopped from the inside, from another handle calling [`stop`](Self::stop)
709    /// or from the app shutting down.
710    pub fn is_stopped(&self) -> bool {
711        self.0.is_dropped()
712    }
713
714    /// The timer interval. Enabled handlers are called every time this interval elapses.
715    pub fn interval(&self) -> Duration {
716        self.0.data().deadline.lock().interval
717    }
718
719    /// Sets the [`interval`](Self::interval).
720    ///
721    /// Note that this method does not awake the app, so if this is called from outside the app
722    /// thread it will only apply on the next app update.
723    pub fn set_interval(&self, new_interval: Duration) {
724        self.0.data().deadline.lock().interval = new_interval;
725    }
726
727    /// Last elapsed time, or the start time if the timer has not elapsed yet.
728    pub fn timestamp(&self) -> DInstant {
729        self.0.data().deadline.lock().last
730    }
731
732    /// The next deadline.
733    ///
734    /// This is the [`timestamp`](Self::timestamp) plus the [`interval`](Self::interval).
735    pub fn deadline(&self) -> Deadline {
736        self.0.data().deadline.lock().current_deadline()
737    }
738
739    /// If the timer is not ticking, but can be started again.
740    pub fn is_paused(&self) -> bool {
741        self.0.data().paused.load(Ordering::Relaxed)
742    }
743
744    /// Disable the timer, this causes the timer to stop ticking until [`play`] is called.
745    ///
746    /// [`play`]: Self::play
747    pub fn pause(&self) {
748        self.0.data().paused.store(true, Ordering::Relaxed);
749    }
750
751    /// If the timer is ticking.
752    pub fn is_playing(&self) -> bool {
753        !self.is_paused() && !self.is_stopped()
754    }
755
756    /// Enable the timer, this causes it to start ticking again.
757    ///
758    /// If `reset` is `true` the last [`timestamp`] is set to now.
759    ///
760    /// Note that this method does not wake the app, so if this is called from outside the app
761    /// the timer will only start ticking in next app update.
762    ///
763    /// [`timestamp`]: Self::timestamp
764    pub fn play(&self, reset: bool) {
765        self.0.data().paused.store(false, Ordering::Relaxed);
766        if reset {
767            self.0.data().deadline.lock().last = INSTANT.now();
768        }
769    }
770
771    /// Count incremented by one every time the timer elapses.
772    pub fn count(&self) -> usize {
773        self.0.data().count.load(Ordering::Relaxed)
774    }
775
776    /// Resets the [`count`](Self::count).
777    pub fn set_count(&self, count: usize) {
778        self.0.data().count.store(count, Ordering::Relaxed)
779    }
780
781    /// Create a weak handle to the timer.
782    pub fn downgrade(&self) -> WeakTimerHandle {
783        WeakTimerHandle(self.0.downgrade())
784    }
785}
786impl fmt::Debug for TimerHandle {
787    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
788        f.debug_struct("TimerHandle")
789            .field("interval", &self.interval())
790            .field("count", &self.count())
791            .field("timestamp", &self.timestamp())
792            .field("handle", &self.0)
793            .field(
794                "state",
795                &if self.is_stopped() {
796                    "is_stopped"
797                } else if self.is_paused() {
798                    "is_paused"
799                } else {
800                    "playing"
801                },
802            )
803            .finish()
804    }
805}
806
807/// Weak [`TimerHandle`].
808#[derive(Clone, PartialEq, Eq, Hash, Default, Debug)]
809pub struct WeakTimerHandle(WeakHandle<TimerState>);
810impl WeakTimerHandle {
811    /// New weak handle that does not upgrade.
812    pub fn new() -> Self {
813        Self(WeakHandle::new())
814    }
815
816    /// Get the strong handle if the timer has not stopped.
817    pub fn upgrade(&self) -> Option<TimerHandle> {
818        self.0.upgrade().map(TimerHandle)
819    }
820}
821
822/// An [`interval`](TIMERS::interval) timer.
823///
824/// This is a variable of type [`Timer`], it will update every time the timer elapses.
825///
826/// Drop all clones of this variable to stop the timer, you can also control the timer
827/// with methods in the [`Timer`] value even though the variable is read-only.
828///
829/// ```
830/// # use zng_app::timer::*;
831/// # use zng_app::handler::*;
832/// # use zng_app::var::*;
833/// # use zng_txt::*;
834/// # use zng_layout::unit::*;
835/// # use std::time::Instant;
836/// # fn foo() {
837/// let timer: TimerVar = TIMERS.interval(1.secs(), false);
838///
839/// # let
840/// text = timer.map(|d| match 20 - d.count() {
841///     0 => {
842///         d.stop();
843///         formatx!("Done!")
844///     }
845///     1 => formatx!("1 second left"),
846///     s => formatx!("{s} seconds left"),
847/// });
848/// # }
849/// ```
850///
851/// In the example above the variable updates every second and stops after 20 seconds have elapsed. The variable
852/// is mapped to a text and controls the timer from inside the mapping closure. See [`Var<T>`] for other things you
853/// can do with variables, including `.await` for updates. Also see [`Timer`] for more timer control methods.
854///
855/// [`Var<T>`]: zng_var::Var
856pub type TimerVar = Var<Timer>;
857
858/// Represents a timer state in a [`TimerVar`] or interval handler.
859///
860/// This type uses interior mutability to communicate with the timer, the values provided by the methods
861/// can be changed anytime by the [`TimerVar`] owners without the variable updating.
862#[derive(Clone, PartialEq)]
863pub struct Timer(TimerHandle);
864impl fmt::Debug for Timer {
865    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
866        f.debug_struct("Timer")
867            .field("interval", &self.interval())
868            .field("count", &self.count())
869            .field("is_paused", &self.is_paused())
870            .field("is_stopped", &self.is_stopped())
871            .finish_non_exhaustive()
872    }
873}
874impl Timer {
875    /// Permanently stops the timer.
876    pub fn stop(&self) {
877        self.0.clone().stop();
878    }
879
880    /// If the timer was stopped.
881    ///
882    /// If `true` the timer var will not update again, this is permanent.
883    pub fn is_stopped(&self) -> bool {
884        self.0.is_stopped()
885    }
886
887    /// The timer interval. Enabled variables update every time this interval elapses.
888    pub fn interval(&self) -> Duration {
889        self.0.interval()
890    }
891
892    /// Sets the [`interval`](Self::interval).
893    ///
894    /// Note that this method does not awake the app, so if this is called from outside the app
895    /// thread it will only apply on the next app update.
896    pub fn set_interval(&self, new_interval: Duration) {
897        self.0.set_interval(new_interval)
898    }
899
900    /// Last update time, or the start time if the timer has not updated yet.
901    pub fn timestamp(&self) -> DInstant {
902        self.0.timestamp()
903    }
904
905    /// The next deadline.
906    ///
907    /// This is the [`timestamp`](Self::timestamp) plus the [`interval`](Self::interval).
908    pub fn deadline(&self) -> Deadline {
909        self.0.deadline()
910    }
911
912    /// If the timer is not ticking, but can be started again.
913    pub fn is_paused(&self) -> bool {
914        self.0.is_paused()
915    }
916
917    /// If the timer is ticking.
918    pub fn is_playing(&self) -> bool {
919        self.0.is_playing()
920    }
921
922    /// Disable the timer, this causes the timer to stop ticking until [`play`] is called.
923    ///
924    /// [`play`]: Self::play
925    pub fn pause(&self) {
926        self.0.pause();
927    }
928
929    /// Enable the timer, this causes it to start ticking again.
930    ///
931    /// If `reset` is `true` the last [`timestamp`] is set to now.
932    ///
933    /// [`timestamp`]: Self::timestamp
934    pub fn play(&self, reset: bool) {
935        self.0.play(reset);
936    }
937
938    /// Count incremented by one every time the timer elapses.
939    pub fn count(&self) -> usize {
940        self.0.count()
941    }
942
943    /// Resets the [`count`](Self::count).
944    pub fn set_count(&self, count: usize) {
945        self.0.set_count(count)
946    }
947}
948
949/// Arguments for an [`on_interval`](TIMERS::on_interval) handler.
950///
951/// Note the timer can be stopped using the handlers [`unsubscribe`](crate::handler::AppWeakHandle::unsubscribe),
952/// and *once* handlers stop the timer automatically.
953///
954/// The field values are about the specific call to handler that received the args, the methods on the other hand
955/// are **connected** with the timer by a weak reference and always show the up-to-date state of the timer.
956/// For synchronous handlers this does not matter, but for async handlers this means that the values can be
957/// different after each `.await`. This can be useful to for example, disable the timer until the async task finishes
958/// but it can also be surprising.
959#[derive(Clone)]
960pub struct TimerArgs {
961    /// When the handler was called.
962    pub timestamp: DInstant,
963
964    /// Expected deadline, is less-or-equal to the [`timestamp`](Self::timestamp).
965    pub deadline: Deadline,
966
967    wk_handle: WeakHandle<TimerState>,
968}
969
970impl TimerArgs {
971    fn handle(&self) -> Option<TimerHandle> {
972        self.wk_handle.upgrade().map(TimerHandle)
973    }
974
975    /// The timer interval. Enabled handlers are called every time this interval elapses.
976    pub fn interval(&self) -> Duration {
977        self.handle().map(|h| h.interval()).unwrap_or_default()
978    }
979
980    /// Set the [`interval`](Self::interval).
981    ///
982    /// Note that this method does not awake the app, so if this is called from outside the app
983    /// thread it will only apply on the next app update.
984    pub fn set_interval(&self, new_interval: Duration) {
985        if let Some(h) = self.handle() {
986            h.set_interval(new_interval)
987        }
988    }
989
990    /// If the timer is not ticking, but can be started again.
991    pub fn is_paused(&self) -> bool {
992        self.handle().map(|h| h.is_paused()).unwrap_or(true)
993    }
994
995    /// If the timer is ticking.
996    pub fn is_playing(&self) -> bool {
997        self.handle().map(|h| h.is_playing()).unwrap_or(false)
998    }
999
1000    /// Disable the timer, this causes the timer to stop ticking until [`play`] is called.
1001    ///
1002    /// [`play`]: Self::play
1003    pub fn pause(&self) {
1004        if let Some(h) = self.handle() {
1005            h.pause();
1006        }
1007    }
1008
1009    /// Enable the timer, this causes it to start ticking again.
1010    ///
1011    /// If `reset` is `true` the last [`timestamp`] is set to now.
1012    ///
1013    /// [`timestamp`]: Self::timestamp
1014    pub fn play(&self, reset: bool) {
1015        if let Some(h) = self.handle() {
1016            h.play(reset);
1017        }
1018    }
1019
1020    /// Count incremented by one every time the timer elapses.
1021    pub fn count(&self) -> usize {
1022        self.handle().map(|h| h.count()).unwrap_or(0)
1023    }
1024
1025    /// Resets the [`count`](Self::count).
1026    pub fn set_count(&self, count: usize) {
1027        if let Some(h) = self.handle() {
1028            h.set_count(count)
1029        }
1030    }
1031
1032    /// The timestamp of the last update. This can be different from [`timestamp`](Self::timestamp)
1033    /// after the first `.await` in async handlers of if called from a different thread.
1034    pub fn last_timestamp(&self) -> DInstant {
1035        self.handle().map(|h| h.timestamp()).unwrap_or(self.timestamp)
1036    }
1037
1038    /// The next timer deadline.
1039    ///
1040    /// This is [`last_timestamp`](Self::last_timestamp) plus [`interval`](Self::interval).
1041    pub fn next_deadline(&self) -> Deadline {
1042        self.handle().map(|h| h.deadline()).unwrap_or(self.deadline)
1043    }
1044
1045    /// If the timer was stopped while the handler was running after it started handling.
1046    ///
1047    /// Note the timer can be stopped from the inside of the handler using the handlers
1048    /// [`unsubscribe`], and once handlers stop the timer automatically.
1049    ///
1050    /// Outside of the handler the [`TimerHandle`] can be used to stop the timer at any time, even from another thread.
1051    ///
1052    /// [`unsubscribe`]: crate::handler::AppWeakHandle::unsubscribe
1053    pub fn is_stopped(&self) -> bool {
1054        self.handle().is_none()
1055    }
1056}
1057
1058pub(crate) fn deadline_service(deadline: Deadline) -> Pin<Box<dyn Future<Output = ()> + Send + Sync>> {
1059    Box::pin(TIMERS.wait_deadline(deadline))
1060}