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