use std::sync::atomic::Ordering;
use super::SnowID;
use super::state::State;
use super::wait::next_backoff;
impl SnowID {
#[inline(always)]
pub fn generate(&self) -> u64 {
let now = self.now_ms();
let current = State::from_raw(self.state.load(Ordering::Acquire));
if let Some(id) = self.try_generate_once(now, current) {
return id;
}
self.generate_slow_path()
}
#[inline(always)]
fn try_generate_once(&self, now: u64, current: State) -> Option<u64> {
let ts = current.timestamp();
if now > ts {
let new_state = State::new(now, 0);
if self.cas_state(current, new_state) {
return Some(self.assemble_id(now, 0));
}
} else {
let seq = current.sequence();
if seq < self.max_seq && self.cas_state(current, State::from_raw(current.raw() + 1)) {
return Some(self.assemble_id(ts, seq + 1));
}
}
None
}
#[inline(always)]
#[allow(dead_code)]
pub(crate) fn try_claim_millisecond(&self, current: State, new_ts: u64) -> Option<u64> {
let new_state = State::new(new_ts, 0);
self.cas_state(current, new_state).then(|| self.assemble_id(new_ts, 0))
}
#[inline(always)]
#[allow(dead_code)]
pub(crate) fn try_increment_sequence(&self, current: State) -> Option<u64> {
let seq = current.sequence();
if seq < self.max_seq {
let new_state = State::from_raw(current.raw() + 1);
self.cas_state(current, new_state).then(|| self.assemble_id(current.timestamp(), seq + 1))
} else {
None
}
}
#[inline(always)]
pub(crate) fn cas_state(&self, expected: State, new: State) -> bool {
self.state
.compare_exchange_weak(expected.raw(), new.raw(), Ordering::AcqRel, Ordering::Acquire)
.is_ok()
}
#[cold]
#[inline(never)]
fn generate_slow_path(&self) -> u64 {
let mut backoff_ms = 1u64;
loop {
let now = self.now_ms();
let current = State::from_raw(self.state.load(Ordering::Acquire));
let timestamp = current.timestamp();
if let Some(id) = self.try_generate_once(now, current) {
return id;
}
if now > timestamp || current.sequence() < self.max_seq {
continue;
}
self.wait_next_millis(timestamp, backoff_ms);
backoff_ms = next_backoff(backoff_ms);
}
}
}