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