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}