only_every/
lib.rs

1//! # only_every
2//!
3//! There are lots of rate-limiting crates that do lots of things, but sometimes
4//! you just want to evaluate an expression once every whatever interval, for
5//! example rate-limited logging.  This crate exposes a macro:
6//!
7//! ```
8//! only_every!(Duration::from_millis(100), expensive_thing)
9//! ```
10//!
11//! This macro will evaluate the given expression at most every duration rounded
12//! up to the next ms.  The limiter is global: if you use 20 threads, it's still
13//! only going to happen once every duration.  The expression is not inside a
14//! closure and is consequently somewhat more transparent to the borrow checker
15//! (This probably doesn't matter to you; you can read it as "there aren't edge
16//! cases").
17//!
18//! Enable the `quanta` feature for faster time-keeping at the cost of using the
19//! quanta crate, which requires a calibration period when executing the first
20//! rate-limited expression and an O(1) heap allocation.  If you use quanta for
21//! other things, whoever gets there first handles the calibration.
22//!
23//! If you need a bit more, e.g. storing these in structs instead of using the
24//! macro, there is also a `OnlyEvery` type.  I suggest something like
25//! [governor](https://docs.rs/governor/latest/governor/) if you need more than
26//! "execute this once every x".
27//!
28//! For completeness, internally we hold times in an i64 as ms since the process
29//! started.  Behavior is undefined if your process runs for long enough that
30//! `pt + interval > i64::MAX` where `pt` is the uptime of the process and units
31//! are in ms.  In other words, let me know if you have a billion years of
32//! continuous uptime and I'll fix it for you.
33#![allow(dead_code)]
34mod only_every;
35#[cfg(quanta)]
36mod quanta_time_source;
37#[cfg(not(quanta))]
38mod std_time_source;
39
40pub use crate::only_every::OnlyEvery;
41
42#[macro_export]
43macro_rules! only_every {
44    ($interval: expr, $expression: expr) => {
45        // open a scope so that our statics are unique.
46        {
47            use std::mem::MaybeUninit;
48            use std::sync::Once;
49            use $crate::OnlyEvery;
50
51            static mut OE: MaybeUninit<OnlyEvery> = MaybeUninit::uninit();
52            static OE_ONCE: Once = Once::new();
53            OE_ONCE.call_once(|| unsafe {
54                OE = MaybeUninit::new(OnlyEvery::new());
55            });
56
57            if unsafe { (*OE.as_ptr()).check($interval) } {
58                $expression;
59            }
60        }
61    };
62}