use atomic int instead of mutex on the whole id.

This commit is contained in:
Joe Ardent 2023-07-26 08:51:47 -07:00
parent 703eb550e1
commit 8a79e737fc

View file

@ -1,17 +1,20 @@
use core::{fmt, str::FromStr};
use std::{sync::Mutex, time::Duration};
use std::{
sync::atomic::{AtomicU64, Ordering},
time::Duration,
};
use rand::{random, thread_rng, Rng};
use crate::base32::{self, DecodeError};
/// This ID is used to ensure monotonicity for new IDs.
static LAST_ID: Mutex<Julid> = Mutex::new(Julid::alpha());
/// This is used to ensure monotonicity for new IDs.
static LAST_SORTABLE: AtomicU64 = AtomicU64::new(0);
/// The number of bits in a Julid's time portion
pub const TIME_BITS: u8 = 48;
/// The number of bits in the monotonic counter for intra-millisecond IDs
pub const MBITS: u8 = 16;
pub const COUNTER_BITS: u8 = 16;
/// The number of random bits + bits in the monotonic counter
pub const UNIQUE_BITS: u8 = 80;
pub const RANDOM_BITS: u8 = 64;
@ -44,36 +47,36 @@ impl Julid {
pub fn new() -> Self {
let lsb: u64 = random();
loop {
if let Ok(mut guard) = LAST_ID.try_lock() {
let ts = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or(Duration::ZERO)
.as_millis() as u64;
let ots = guard.timestamp();
let last = LAST_SORTABLE.load(Ordering::SeqCst);
let ots = last >> COUNTER_BITS;
if ots < ts {
let new = Julid::new_time(ts, lsb);
*guard = new;
break new;
let msb = ts << COUNTER_BITS;
if LAST_SORTABLE
.compare_exchange(last, msb, Ordering::SeqCst, Ordering::Relaxed)
.is_ok()
{
break (msb, lsb).into();
}
} else {
let counter = guard.counter().saturating_add(1);
let tbits = ots & bitmask!(TIME_BITS);
let msb = (tbits << MBITS) + counter as u64;
let new: Julid = (msb, lsb).into();
*guard = new;
break new;
let counter = ((last & bitmask!(COUNTER_BITS) as u64) as u16).saturating_add(1);
let msb = (ots << COUNTER_BITS) + counter as u64;
if LAST_SORTABLE
.compare_exchange(last, msb, Ordering::SeqCst, Ordering::Relaxed)
.is_ok()
{
break (msb, lsb).into();
}
}
// we didn't update the global counter, try again
let micros = thread_rng().gen_range(10..50);
std::thread::sleep(Duration::from_micros(micros));
}
}
fn new_time(time: u64, lsb: u64) -> Self {
let tbits = time & bitmask!(TIME_BITS);
let msb = tbits << MBITS;
(msb, lsb).into()
}
/// Creates a Julid from a Crockford Base32 encoded string
///
/// An DecodeError will be returned when the given string is not formated
@ -103,6 +106,10 @@ impl Julid {
Julid(0)
}
/// The 'Omega Julid'.
///
/// The Omega Julid is special form of Julid that is specified to have
/// all 128 bits set to one.
pub const fn omega() -> Self {
Julid(u128::MAX)
}
@ -113,12 +120,12 @@ impl Julid {
}
pub const fn counter(&self) -> u16 {
let mask = bitmask!(MBITS);
let mask = bitmask!(COUNTER_BITS);
((self.0 >> RANDOM_BITS) & mask) as u16
}
pub const fn sortable(&self) -> u64 {
let mask = bitmask!(TIME_BITS + MBITS);
let mask = bitmask!(TIME_BITS + COUNTER_BITS);
((self.0 >> RANDOM_BITS) & mask) as u64
}