#![cfg(feature = "global_gen")]
use crate::{Generator, Id};
pub fn new() -> Id {
use std::sync::{LazyLock, Mutex};
static G: LazyLock<Mutex<GlobalGenInner>> = LazyLock::new(|| {
Mutex::new(GlobalGenInner {
guard: forkguard::new(),
#[allow(deprecated)]
generator: Generator::with_rand_and_time_sources(
GlobalGenRng::try_new().expect("scru128: could not initialize global generator"),
Default::default(),
),
})
});
G.lock()
.expect("scru128: could not lock global generator")
.get_mut()
.generate()
}
pub fn new_string() -> String {
new().into()
}
#[allow(deprecated)]
use crate::generator::DefaultRng as GlobalGenRng;
#[derive(Debug)]
struct GlobalGenInner {
guard: forkguard::Guard,
#[allow(deprecated)]
generator: Generator<GlobalGenRng>,
}
impl GlobalGenInner {
#[allow(deprecated)]
fn get_mut(&mut self) -> &mut Generator<GlobalGenRng> {
if self.guard.detected_fork() {
self.generator.reset_state();
if let Ok(rng) = GlobalGenRng::try_new() {
*self.generator.rand_source_mut() = rng;
}
}
&mut self.generator
}
}
#[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(())
}
}