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}