use alloc::sync::Arc;
use core::cmp::Ordering;
#[cfg(feature = "tracing")]
use tracing::instrument;
use crate::{
generator::{Error, Mutex, Poll, Result, SnowflakeGenerator},
id::SnowflakeId,
time::TimeSource,
};
pub struct LockSnowflakeGenerator<ID, T>
where
ID: SnowflakeId,
T: TimeSource<ID::Ty>,
{
#[cfg(feature = "cache-padded")]
pub(crate) state: Arc<crossbeam_utils::CachePadded<Mutex<ID>>>,
#[cfg(not(feature = "cache-padded"))]
pub(crate) state: Arc<Mutex<ID>>,
pub(crate) time: T,
}
impl<ID, T> LockSnowflakeGenerator<ID, T>
where
ID: SnowflakeId,
T: TimeSource<ID::Ty>,
{
pub fn new(machine_id: ID::Ty, time: T) -> Self {
Self::from_components(ID::ZERO, machine_id, ID::ZERO, time)
}
pub fn from_components(
timestamp: ID::Ty,
machine_id: ID::Ty,
sequence: ID::Ty,
time: T,
) -> Self {
let id = ID::from_components(timestamp, machine_id, sequence);
Self {
#[cfg(feature = "cache-padded")]
state: Arc::new(crossbeam_utils::CachePadded::new(Mutex::new(id))),
#[cfg(not(feature = "cache-padded"))]
state: Arc::new(Mutex::new(id)),
time,
}
}
#[cfg(feature = "parking-lot")]
pub fn next_id(&self, f: impl FnMut(ID::Ty)) -> ID {
match self.try_next_id(f) {
Ok(id) => id,
Err(e) =>
{
#[allow(unreachable_code)]
match e {}
}
}
}
#[cfg_attr(feature = "tracing", instrument(level = "trace", skip(self, f)))]
pub fn try_next_id(&self, mut f: impl FnMut(ID::Ty)) -> Result<ID, Error> {
loop {
match self.try_poll_id()? {
Poll::Ready { id } => break Ok(id),
Poll::Pending { yield_for } => f(yield_for),
}
}
}
#[cfg(feature = "parking-lot")]
pub fn poll_id(&self) -> Poll<ID>
where
Error: Into<core::convert::Infallible>,
{
match self.try_poll_id() {
Ok(id) => id,
Err(e) => {
#[allow(unreachable_code)]
match Into::<core::convert::Infallible>::into(e) {}
}
}
}
#[cfg_attr(feature = "tracing", instrument(level = "trace", skip(self)))]
pub fn try_poll_id(&self) -> Result<Poll<ID>, Error> {
let now = self.time.current_millis();
let mut id = {
#[cfg(feature = "parking-lot")]
{
self.state.lock()
}
#[cfg(not(feature = "parking-lot"))]
{
self.state.lock()?
}
};
let current_ts = id.timestamp();
match now.cmp(¤t_ts) {
Ordering::Equal => {
if id.has_sequence_room() {
*id = id.increment_sequence();
Ok(Poll::Ready { id: *id })
} else {
Ok(Poll::Pending { yield_for: ID::ONE })
}
}
Ordering::Greater => {
*id = id.rollover_to_timestamp(now);
Ok(Poll::Ready { id: *id })
}
Ordering::Less => Ok(Self::cold_clock_behind(now, current_ts)),
}
}
#[cold]
#[inline(never)]
fn cold_clock_behind(now: ID::Ty, current_ts: ID::Ty) -> Poll<ID> {
let yield_for = current_ts - now;
debug_assert!(yield_for >= ID::ZERO);
Poll::Pending { yield_for }
}
}
impl<ID, T> SnowflakeGenerator<ID, T> for LockSnowflakeGenerator<ID, T>
where
ID: SnowflakeId,
T: TimeSource<ID::Ty>,
{
type Err = Error;
fn new(machine_id: ID::Ty, time: T) -> Self {
Self::new(machine_id, time)
}
fn try_next_id(&self, f: impl FnMut(ID::Ty)) -> Result<ID, Self::Err> {
self.try_next_id(f)
}
fn try_poll_id(&self) -> Result<Poll<ID>, Self::Err> {
self.try_poll_id()
}
}