use atomic int instead of mutex on the whole id.
This commit is contained in:
parent
703eb550e1
commit
8a79e737fc
1 changed files with 36 additions and 29 deletions
65
src/julid.rs
65
src/julid.rs
|
@ -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();
|
||||
if ots < ts {
|
||||
let new = Julid::new_time(ts, lsb);
|
||||
*guard = new;
|
||||
break new;
|
||||
} 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 ts = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap_or(Duration::ZERO)
|
||||
.as_millis() as u64;
|
||||
let last = LAST_SORTABLE.load(Ordering::SeqCst);
|
||||
let ots = last >> COUNTER_BITS;
|
||||
if ots < ts {
|
||||
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 = ((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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue