tokio_js_set_interval/lib.rs
1/*
2MIT License
3
4Copyright (c) 2021 Philipp Schuster
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25//! The crate `tokio-js-set-interval` allows you to use `setInterval(callback, ms)` and
26//! `setTimeout(callback, ms)` as in Javascript inside a `tokio` runtime (<https://tokio.rs/>).
27//! The library provides the macros:
28//! - `set_interval!(callback, ms)`,
29//! - `set_interval_async!(future, ms)`,
30//! - `set_timeout!(callback, ms)`,
31//! - and `set_timeout_async!(async_callback, ms)`.
32//!
33//! ## Restrictions
34//! They behave similar to their Javascript counterparts, with a few exceptions:
35//!
36//! * They only get executed if the `tokio` runtime lives long enough.
37//! * on order to compile, the callback must return the union type, i.e. `()` \
38//! => all actions must be done via side effects
39//! * ⚠ again, there is **NO GUARANTEE** that the tasks will get executed \
40//! (=> however useful and convenient for low priority background tasks and for the learning effect of course) ⚠
41//!
42//! ## Trivia
43//! The functionality behind is rather simple. However, it took me some time to figure out what kind of
44//! input the macros should accept and how the generic arguments of the functions behind the macros
45//! need to be structured. Especially the `*_async!()` versions of the macros were quite complicated
46//! during the development.
47//!
48//! ## Compatibility
49//! Version 1.2.0 is developed with:
50//! * `tokio` @ 1.6.0 (but should also work with 1.0.0)
51//! * `rustc` @ 1.52.1 (but should also work with 1.45.2)
52
53use std::collections::BTreeSet;
54use std::future::Future;
55use std::sync::atomic::{AtomicU64, Ordering};
56use std::sync::Mutex;
57use tokio::time::{Duration, Interval};
58
59static INTERVAL_MANAGER: IntervalManager = IntervalManager::new();
60
61/// **INTERNAL** Use macro [`set_timeout`] instead!
62///
63/// Creates a future that glues the tokio sleep function (timeout) together with
64/// the provided callback.
65pub async fn _set_timeout(f: impl Fn(), ms: u64) {
66 tokio::time::sleep(Duration::from_millis(ms)).await;
67 f();
68}
69
70/// **INTERNAL** Use macro [`set_timeout`] instead!
71///
72/// Async version of [_set_timeout]. Instead of a closure, it consumes a future.
73pub async fn _set_timeout_async<T, F: Future<Output = T>>(f: F, ms: u64) {
74 tokio::time::sleep(Duration::from_millis(ms)).await;
75 f.await;
76}
77
78/// **INTERNAL** Used to manage intervals created by macro [`set_interval`]!
79/// Helps to assign unique IDs to intervals and stop them.
80pub struct IntervalManager {
81 /// Monotonic incrementing counter from 0 to max value used for interval IDs.
82 pub counter: AtomicU64,
83 /// Contains only the IDs of dispatched and not cleared intervals.
84 pub running_intervals: Mutex<BTreeSet<u64>>,
85}
86
87impl IntervalManager {
88 const fn new() -> Self {
89 Self {
90 counter: AtomicU64::new(0),
91 running_intervals: Mutex::new(BTreeSet::new()),
92 }
93 }
94}
95
96/// Common code for [_set_interval] and [_set_interval_async]. Adds the new interval to the
97/// interval manager.
98async fn _set_interval_common(ms: u64, id: u64) -> Interval {
99 // dedicated scope -> early drop lock
100 {
101 let mut map = INTERVAL_MANAGER.running_intervals.lock().unwrap();
102 map.insert(id);
103 }
104
105 let mut int = tokio::time::interval(Duration::from_millis(ms));
106
107 // the first tick in tokios interval returns immediately. Because we want behaviour
108 // similar to Javascript in this library, we work around this.
109 int.tick().await;
110
111 int
112}
113
114/// Creates a future that glues the tokio interval function together with
115/// the provided callback. Helper function for [`_set_interval_spawn`].
116async fn _set_interval(f: impl Fn() + Send + 'static, ms: u64, id: u64) {
117 let mut int = _set_interval_common(ms, id).await;
118
119 // this looks like it runs for ever, but this is not how tokio works
120 // at least with tokio 1.6.0 and Rust 1.52 this stops executing
121 // when the tokio runtime gets dropped.
122 loop {
123 int.tick().await;
124
125 // - breaks the loop if the interval was cleared.
126 // - dedicated block to drop lock early
127 {
128 let map = INTERVAL_MANAGER.running_intervals.lock().unwrap();
129 if !map.contains(&id) {
130 break;
131 }
132 }
133
134 f();
135 }
136}
137
138/// Async version of [_set_interval].
139async fn _set_interval_async<T, Func, Fut>(f: Func, ms: u64, id: u64)
140where
141 Func: (Fn() -> Fut) + Send + 'static + Sync,
142 Fut: Future<Output = T> + Send + Sync,
143{
144 let mut int = _set_interval_common(ms, id).await;
145
146 // this looks like it runs for ever, but this is not how tokio works
147 // at least with tokio 1.6.0 and Rust 1.52 this stops executing
148 // when the tokio runtime gets dropped.
149 loop {
150 int.tick().await;
151
152 // - breaks the loop if the interval was cleared.
153 // - dedicated block to drop lock early
154 {
155 let map = INTERVAL_MANAGER.running_intervals.lock().unwrap();
156 if !map.contains(&id) {
157 break;
158 }
159 }
160
161 f().await;
162 }
163}
164
165/// **INTERNAL** Use macro [`set_interval`] instead!
166///
167/// Acquires a new ID, creates an interval future and returns the ID. The future gets
168/// dispatched as tokio task.
169pub fn _set_interval_spawn(f: impl Fn() + Send + 'static, ms: u64) -> u64 {
170 // assign next ID to interval
171 let id = INTERVAL_MANAGER.counter.fetch_add(1, Ordering::SeqCst);
172 tokio::spawn(_set_interval(f, ms, id));
173 id
174}
175
176/// **INTERNAL** Use macro [`set_interval_async`] instead!
177///
178/// Async version of [_set_interval_spawn].
179pub fn _set_interval_spawn_async<T: 'static, Func, Fut>(f: Func, ms: u64) -> u64
180where
181 Func: (Fn() -> Fut) + Send + 'static + Sync,
182 Fut: Future<Output = T> + Send + 'static + Sync,
183{
184 // assign next ID to interval
185 let id = INTERVAL_MANAGER.counter.fetch_add(1, Ordering::SeqCst);
186 tokio::spawn(_set_interval_async(f, ms, id));
187 id
188}
189
190/// Clears an interval that was created via [`set_interval!`]. You have to pass
191/// the unique numeric ID as parameter. Throws no error, if the ID is unknown.
192pub fn clear_interval(id: u64) {
193 let mut set = INTERVAL_MANAGER.running_intervals.lock().unwrap();
194 if set.contains(&id) {
195 set.remove(&id);
196 }
197}
198
199/// Creates a timeout that behaves similar to `setTimeout(callback, ms)` in Javascript
200/// for the `tokio` runtime. Unlike in Javascript, it will only be executed, if after the
201/// specified time passed, the `tokio` runtime still lives, i.e. didn't get dropped.
202///
203/// As in Javascript, a timeout may only have side effects and no return type.
204/// You don't get a handle to manually wait for it, you must ensure, that the tokio
205/// runtime lives long enough.
206///
207/// # Parameters
208/// * `#1` expression, closure-expression, block or identifier (which points to a closure).
209/// The code that represents the callback function.
210/// * `#2` time delay in milliseconds
211///
212/// # Example
213/// ```rust
214/// use tokio::time::Duration;
215/// use tokio_js_set_interval::set_timeout;
216///
217/// #[tokio::main]
218/// async fn main() {
219/// set_timeout!(println!("hello1"), 0);
220/// println!("hello2");
221/// // prevent that tokios runtime gets dropped too early
222/// // order of output should be
223/// // "hello2"
224/// // "hello1"
225/// tokio::time::sleep(Duration::from_millis(1)).await;
226/// }
227/// ```
228#[macro_export]
229macro_rules! set_timeout {
230 // match for identifier, i.e. a closure, that is behind a variable
231 ($cb:ident, $ms:literal) => {
232 tokio::spawn($crate::_set_timeout($cb, $ms));
233 };
234 // match for direct closure expression
235 (|| $cb:expr, $ms:literal) => {
236 tokio::spawn($crate::_set_timeout(|| $cb, $ms));
237 };
238 // match for direct move closure expression
239 (move || $cb:expr, $ms:literal) => {
240 tokio::spawn($crate::_set_timeout(move || $cb, $ms));
241 };
242 // match for expr, like `set_timeout!(println!())`
243 ($cb:expr, $ms:literal) => {
244 $crate::set_timeout!(|| $cb, $ms);
245 };
246 // match for block
247 ($cb:block, $ms:literal) => {
248 $crate::set_timeout!(|| $cb, $ms);
249 };
250
251 // match for identifier, i.e. a closure, that is behind a variable
252 ($cb:ident, $ms:ident) => {
253 tokio::spawn($crate::_set_timeout($cb, $ms));
254 };
255 // match for direct closure expression
256 (|| $cb:expr, $ms:ident) => {
257 tokio::spawn($crate::_set_timeout(|| $cb, $ms));
258 };
259 // match for direct move closure expression
260 (move || $cb:expr, $ms:ident) => {
261 tokio::spawn($crate::_set_timeout(move || $cb, $ms));
262 };
263 // match for expr, like `set_timeout!(println!())`
264 ($cb:expr, $ms:ident) => {
265 $crate::set_timeout!(|| $cb, $ms);
266 };
267 // match for block
268 ($cb:block, $ms:ident) => {
269 $crate::set_timeout!(|| $cb, $ms);
270 };
271}
272
273/// Async version of [set_timeout]. Instead of a closure, this macro accepts a future.
274///
275/// You can pass:
276/// - an identifier that points a future
277/// - an expression that returns a future
278///
279/// # Example
280/// ```rust
281/// use tokio::time::Duration;
282/// use tokio_js_set_interval::set_timeout_async;
283///
284/// async fn async_foo() {
285/// println!("hello1");
286/// }
287///
288/// #[tokio::main]
289/// async fn main() {
290/// let future = async_foo();
291/// set_timeout_async!(future, 0);
292/// println!("hello2");
293/// // prevent that tokios runtime gets dropped too early
294/// // order of output should be
295/// // "hello2"
296/// // "hello1"
297/// tokio::time::sleep(Duration::from_millis(1)).await;
298/// }
299/// ```
300#[macro_export]
301macro_rules! set_timeout_async {
302 // match for identifier, i.e. a future, that is behind a variable
303 ($future:ident, $ms:literal) => {
304 tokio::spawn($crate::_set_timeout_async($future, $ms));
305 };
306 ($future:expr, $ms:literal) => {
307 tokio::spawn($crate::_set_timeout_async($future, $ms));
308 };
309 ($future:block, $ms:literal) => {
310 tokio::spawn($crate::_set_timeout_async($future, $ms));
311 };
312
313 ($future:ident, $ms:ident) => {
314 tokio::spawn($crate::_set_timeout_async($future, $ms));
315 };
316 ($future:expr, $ms:ident) => {
317 tokio::spawn($crate::_set_timeout_async($future, $ms));
318 };
319 ($future:block, $ms:ident) => {
320 tokio::spawn($crate::_set_timeout_async($future, $ms));
321 };
322}
323
324/// Creates a timeout that behaves similar to `setInterval(callback, ms)` in Javascript
325/// for the `tokio` runtime. Unlike in Javascript, it will only be executed, if after the
326/// specified time passed, the `tokio` runtime still lives, i.e. didn't got dropped.
327///
328/// As in Javascript, an interval may only have side effects and no return type.
329/// You don't get a handle to manually wait for it, you must ensure, that the tokio
330/// runtime lives long enough.
331///
332/// The macro returns an numeric ID. Similar to Javascript, you can use thie ID to
333/// clear/stop intervals with [`clear_interval`].
334///
335/// # Parameters
336/// * `#1` expression, closure-expression, block or identifier (which points to a closure).
337/// The code that represents the callback function.
338/// * `#2` time delay in milliseconds
339///
340/// # Example
341/// ```rust
342/// use tokio::time::Duration;
343/// use tokio_js_set_interval::set_interval;
344///
345/// #[tokio::main]
346/// async fn main() {
347/// let interval = set_interval!(println!("hello1"), 50);
348/// // If you want to clear the interval later: save the ID
349/// // let id = set_interval!(println!("hello1"), 50);
350/// println!("hello2");
351/// // prevent that tokios runtime gets dropped too early
352/// // "hello1" should get printed 2 times (50*2 == 100 < 120)
353/// tokio::time::sleep(Duration::from_millis(120)).await;
354/// }
355/// ```
356#[macro_export]
357macro_rules! set_interval {
358 // match for identifier, i.e. a closure, that is behind a variable
359 ($cb:ident, $ms:literal) => {
360 // not so nice, need to wrap the identifier in another closure
361 $crate::set_interval!(move || $cb(), $ms)
362 };
363 // match for direct closure expression
364 (|| $cb:expr, $ms:literal) => {
365 $crate::_set_interval_spawn(|| $cb, $ms)
366 };
367 // match for direct move closure expression
368 (move || $cb:expr, $ms:literal) => {
369 $crate::_set_interval_spawn(move || $cb, $ms)
370 };
371 // match for expr, like `set_interval!(println!())`
372 ($cb:expr, $ms:literal) => {
373 $crate::set_interval!(|| $cb, $ms)
374 };
375 // match for block
376 ($cb:block, $ms:literal) => {
377 $crate::set_interval!(|| $cb, $ms)
378 };
379
380 // match for identifier, i.e. a closure, that is behind a variable
381 ($cb:ident, $ms:ident) => {
382 // not so nice, need to wrap the identifier in another closure
383 $crate::set_interval!(move || $cb(), $ms)
384 };
385 // match for direct closure expression
386 (|| $cb:expr, $ms:ident) => {
387 $crate::_set_interval_spawn(|| $cb, $ms)
388 };
389 // match for direct move closure expression
390 (move || $cb:expr, $ms:ident) => {
391 $crate::_set_interval_spawn(move || $cb, $ms)
392 };
393 // match for expr, like `set_interval!(println!())`
394 ($cb:expr, $ms:ident) => {
395 $crate::set_interval!(|| $cb, $ms)
396 };
397 // match for block
398 ($cb:block, $ms:ident) => {
399 $crate::set_interval!(|| $cb, $ms)
400 };
401}
402
403/// Async version of [set_interval]. Instead of a closure, this macro accepts a non-async closure
404/// that produces futures.
405///
406/// You can pass:
407/// - an identifier that points to a function that returns a future
408/// - a block that returns a future
409/// - a closure that returns a future
410///
411/// # Example
412/// ```rust
413/// use tokio::time::Duration;
414/// use tokio_js_set_interval::set_interval_async;
415///
416/// async fn future_producer() {
417/// println!("hello1")
418/// }
419///
420/// #[tokio::main]
421/// async fn main() {
422///
423///
424/// set_interval_async!(future_producer, 50);
425/// // If you want to clear the interval later: save the ID
426/// // let id = set_interval_async!(future_producer, 50);
427/// println!("hello2");
428/// // prevent that tokios runtime gets dropped too early
429/// // "hello1" should get printed 2 times (50*2 == 100 < 120)
430/// tokio::time::sleep(Duration::from_millis(120)).await;
431/// }
432/// ```
433#[macro_export]
434macro_rules! set_interval_async {
435 // match for identifier, i.e. a future, that is behind a variable
436 ($future_producer:ident, $ms:literal) => {
437 $crate::_set_interval_spawn_async($future_producer, $ms)
438 };
439 // match for closure that produces futures
440 (|| $cb:expr, $ms:literal) => {
441 $crate::_set_interval_spawn_async(|| $cb, $ms)
442 };
443 // match for move closure that produces futures
444 (move || $cb:block, $ms:literal) => {
445 $crate::_set_interval_spawn_async(move || $cb, $ms)
446 };
447 // match for block expression that produces futures
448 ($cb:block, $ms:literal) => {
449 $crate::set_interval_async!(move || $cb, $ms)
450 };
451
452 // match for identifier, i.e. a future, that is behind a variable
453 ($future_producer:ident, $ms:ident) => {
454 $crate::_set_interval_spawn_async($future_producer, $ms)
455 };
456 // match for closure that produces futures
457 (|| $cb:expr, $ms:ident) => {
458 $crate::_set_interval_spawn_async(|| $cb, $ms)
459 };
460 // match for move closure that produces futures
461 (move || $cb:block, $ms:ident) => {
462 $crate::_set_interval_spawn_async(move || $cb, $ms)
463 };
464 // match for block expression that produces futures
465 ($cb:block, $ms:ident) => {
466 $crate::set_interval_async!(move || $cb, $ms)
467 };
468}
469
470#[cfg(test)]
471mod tests {
472 use std::sync::atomic::AtomicU64;
473 use std::sync::Arc;
474
475 use super::*;
476
477 // Compile-time tests: Macros accept the corresponding kind of parameters
478 mod compile_time_tests {
479 use super::*;
480
481 #[tokio::test]
482 async fn test_set_timeout_macro_all_argument_variants_builds() {
483 // macro takes expression
484 set_timeout!(println!("hello1"), 4);
485 // macro takes block
486 set_timeout!({ println!("hello2") }, 3);
487 // macro takes direct closure expressions
488 set_timeout!(|| println!("hello3"), 2);
489 // macro takes direct move closure expressions
490 set_timeout!(move || println!("hello4"), 2);
491 // macro takes identifiers (which must point to closures)
492 let closure = || println!("hello5");
493 set_timeout!(closure, 1);
494
495 // now with the period as identifier
496
497 let period = 42;
498
499 // macro takes expression
500 set_timeout!(println!("hello1"), period);
501 // macro takes block
502 set_timeout!({ println!("hello2") }, period);
503 // macro takes direct closure expressions
504 set_timeout!(|| println!("hello3"), period);
505 // macro takes direct move closure expressions
506 set_timeout!(move || println!("hello4"), period);
507 // macro takes identifiers (which must point to closures)
508 let closure = || println!("hello5");
509 set_timeout!(closure, period);
510 }
511
512 #[tokio::test]
513 async fn test_set_timeout_async_macro_all_argument_variants_builds() {
514 async fn async_foo() {
515 println!("hello1");
516 }
517
518 let future = async_foo();
519 // macro takes future by identifier
520 set_timeout_async!(future, 1);
521 // macro takes an expression that returns a future
522 set_timeout_async!(async_foo(), 1);
523 // macro takes block
524 set_timeout_async!(async { println!("hello2") }, 1);
525
526 // now with the period as identifier
527
528 let period = 42;
529
530 let future = async_foo();
531 // macro takes future by identifier
532 set_timeout_async!(future, period);
533 // macro takes an expression that returns a future
534 set_timeout_async!(async_foo(), period);
535 // macro takes block
536 set_timeout_async!(async { println!("hello2") }, period);
537 }
538
539 #[tokio::test]
540 async fn test_set_interval_macro_all_argument_variants_builds() {
541 // macro takes expression
542 set_interval!(println!("hello1"), 42);
543 // macro takes block
544 set_interval!({ println!("hello2") }, 42);
545 // macro takes direct closure expressions
546 set_interval!(|| println!("hello3"), 42);
547 // macro takes direct move closure expressions
548 set_interval!(move || println!("hello4"), 42);
549 // macro takes identifiers (which must point to closures)
550 let closure = || println!("hello5");
551 set_interval!(closure, 42);
552
553 // now with the period as identifier
554
555 let period = 42;
556
557 // macro takes expression
558 set_interval!(println!("hello1"), period);
559 // macro takes block
560 set_interval!({ println!("hello2") }, period);
561 // macro takes direct closure expressions
562 set_interval!(|| println!("hello3"), period);
563 // macro takes direct move closure expressions
564 set_interval!(move || println!("hello4"), period);
565 // macro takes identifiers (which must point to closures)
566 let closure = || println!("hello5");
567 set_interval!(closure, period);
568 }
569
570 #[tokio::test]
571 async fn test_set_interval_async_macro_all_argument_variants_builds() {
572 async fn async_foo() {
573 println!("hello1");
574 }
575
576 // macro takes identifier (that must point to a future-producing function)
577 set_interval_async!(async_foo, 42);
578 // macro takes block with async inner block
579 set_interval_async!(
580 {
581 async {
582 println!("hello2");
583 }
584 },
585 42
586 );
587 // macro takes a closure that produces a future
588 set_interval_async!(
589 || {
590 async {
591 println!("hello3");
592 }
593 },
594 42
595 );
596 // macro takes a move closure that produces a future
597 set_interval_async!(
598 move || {
599 async move {
600 println!("hello4");
601 }
602 },
603 42
604 );
605
606 // now with the period as identifier
607
608 let period = 42;
609
610 // macro takes identifier (that must point to a future-producing function)
611 set_interval_async!(async_foo, period);
612 // macro takes block with async inner block
613 set_interval_async!(
614 {
615 async {
616 println!("hello2");
617 }
618 },
619 period
620 );
621 // macro takes a closure that produces a future
622 set_interval_async!(
623 || {
624 async {
625 println!("hello3");
626 }
627 },
628 period
629 );
630 // macro takes a move closure that produces a future
631 set_interval_async!(
632 move || {
633 async move {
634 println!("hello4");
635 }
636 },
637 period
638 );
639 }
640 }
641
642 /// Test can't been automated because the test is correct
643 /// if stdout is correct. Its just a visual test which can
644 /// be executed manually.
645 /// Output should be
646 /// ```text
647 /// hello1
648 /// hello3
649 /// hello5
650 /// hello2
651 /// hello4
652 /// ```
653 #[tokio::test]
654 async fn test_set_timeout_visual() {
655 println!("hello1");
656 set_timeout!(println!("hello2"), 0);
657 println!("hello3");
658 set_timeout!(println!("hello4"), 0);
659 println!("hello5");
660
661 // give enough execution time, before tokio gets dropped
662 // two tokio sleeps: timeouts will usually get scheduled in between
663 tokio::time::sleep(Duration::from_millis(1)).await;
664 tokio::time::sleep(Duration::from_millis(1)).await;
665 }
666
667 /// Test can't been automated because the test is correct
668 /// if stdout is correct. Its just a visual test which can
669 /// be executed manually.
670 #[tokio::test]
671 async fn test_set_interval_visual() {
672 let id = set_interval!(println!("should be printed 3 times"), 50);
673 // give the timeout enough execution time
674 tokio::time::sleep(Duration::from_millis(151)).await;
675 tokio::time::sleep(Duration::from_millis(0)).await;
676 println!("interval id is: {}", id);
677 }
678
679 #[tokio::test]
680 async fn test_set_timeout() {
681 let counter = Arc::new(AtomicU64::new(0));
682 {
683 let counter = counter.clone();
684 // the block is required to change the return type to "()"
685 set_timeout!(
686 move || {
687 counter.fetch_add(1, Ordering::SeqCst);
688 },
689 50
690 );
691 }
692 {
693 let counter = counter.clone();
694 // the block is required to change the return type to "()"
695 set_timeout!(
696 move || {
697 counter.fetch_add(1, Ordering::SeqCst);
698 },
699 50
700 );
701 }
702
703 // give the tokio runtime enough execution time
704 tokio::time::sleep(Duration::from_millis(110)).await;
705 assert_eq!(counter.load(Ordering::SeqCst), 2);
706 }
707
708 #[tokio::test]
709 async fn test_set_timeout_async() {
710 let counter = Arc::new(AtomicU64::new(0));
711 let counter_closure = counter.clone();
712 let future_producer = move || {
713 let counter_inner = counter_closure.clone();
714 async move {
715 counter_inner.fetch_add(1, Ordering::SeqCst);
716 }
717 };
718
719 set_timeout_async!(future_producer(), 50);
720 set_timeout_async!(future_producer(), 50);
721
722 // give the tokio runtime enough execution time
723 tokio::time::sleep(Duration::from_millis(110)).await;
724 assert_eq!(counter.load(Ordering::SeqCst), 2);
725 }
726
727 #[tokio::test]
728 async fn test_set_interval() {
729 let counter = Arc::new(AtomicU64::new(0));
730 {
731 let counter = counter.clone();
732 // the block is required to change the return type to "()"
733 set_interval!(
734 move || {
735 counter.fetch_add(1, Ordering::SeqCst);
736 },
737 50
738 );
739 }
740
741 // give the tokio runtime enough execution time to execute the interval 3 times
742 tokio::time::sleep(Duration::from_millis(180)).await;
743 assert_eq!(counter.load(Ordering::SeqCst), 3);
744 }
745
746 #[tokio::test]
747 async fn test_set_interval_async() {
748 let counter = Arc::new(AtomicU64::new(0));
749 {
750 let counter_closure = counter.clone();
751 let future_producer = move || {
752 let counter_inner = counter_closure.clone();
753 async move {
754 counter_inner.fetch_add(1, Ordering::SeqCst);
755 }
756 };
757 set_interval_async!(future_producer, 50);
758 }
759
760 // give the tokio runtime enough execution time to execute the interval 3 times
761 tokio::time::sleep(Duration::from_millis(180)).await;
762 assert_eq!(counter.load(Ordering::SeqCst), 3);
763 }
764
765 #[tokio::test]
766 async fn test_clear_interval() {
767 let counter = Arc::new(AtomicU64::new(0));
768 let interval_id;
769 {
770 let counter = counter.clone();
771 // the block is required to change the return type to "()"
772 interval_id = set_interval!(
773 move || {
774 counter.fetch_add(1, Ordering::SeqCst);
775 },
776 50
777 );
778 }
779
780 // give the tokio runtime enough execution time to execute the interval 3 times
781 tokio::time::sleep(Duration::from_millis(180)).await;
782 assert_eq!(counter.load(Ordering::SeqCst), 3);
783 clear_interval(interval_id);
784 tokio::time::sleep(Duration::from_millis(200)).await;
785 assert_eq!(counter.load(Ordering::SeqCst), 3);
786 }
787}