#![cfg(feature = "global_gen")]
use std::sync;
use reseeding_rng::{RngExt as _, StdReseedingRng};
use crate::{Generator, Id, generator::RandSource};
pub fn new() -> Id {
static G: sync::LazyLock<sync::Mutex<GlobalGenInner>> = sync::LazyLock::new(Default::default);
G.lock()
.expect("scru128: could not lock global generator")
.get_mut()
.generate()
}
pub fn new_string() -> String {
new().into()
}
#[derive(Debug, Default)]
struct GlobalGenInner {
guard: forkguard::Guard,
generator: Generator<GlobalGenRng>,
}
impl GlobalGenInner {
fn get_mut(&mut self) -> &mut Generator<GlobalGenRng> {
if self.guard.detected_fork() {
self.reset_generator();
}
&mut self.generator
}
#[cold]
fn reset_generator(&mut self) {
self.generator.reset_state();
let _ = self.generator.rand_source_mut().0.try_reseed();
}
}
#[derive(Debug, Default)]
struct GlobalGenRng(StdReseedingRng);
impl RandSource for GlobalGenRng {
fn next_u32(&mut self) -> u32 {
self.0.random()
}
}
#[cfg(test)]
mod tests {
#[test]
fn generates_no_ids_sharing_same_timestamp_and_counters_under_multithreading()
-> Result<(), Box<dyn std::error::Error>> {
use std::{collections::HashSet, sync::mpsc, thread};
let (tx, rx) = mpsc::channel();
for _ in 0..4 {
let tx = tx.clone();
thread::Builder::new()
.spawn(move || {
for _ in 0..10_000 {
tx.send(super::new()).unwrap();
}
})
.map_err(|err| format!("failed to spawn thread: {:?}", err))?;
}
drop(tx);
let mut s = HashSet::new();
while let Ok(e) = rx.recv() {
s.insert((e.timestamp(), e.counter_hi(), e.counter_lo()));
}
assert_eq!(s.len(), 4 * 10_000);
Ok(())
}
}