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