use core::marker::PhantomData;
use crate::atomic::{AtomicBool, AtomicUsize, Ordering};
use crate::key_voucher::KeyVoucher;
static LOCKSMITH_EXISTS: AtomicBool = AtomicBool::new(false);
pub struct Locksmith {
issued: AtomicUsize,
limit: Option<usize>,
_not_send: PhantomData<*const ()>, }
impl Locksmith {
#[must_use]
pub fn new(limit: usize) -> Option<Self> {
LOCKSMITH_EXISTS
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.ok()
.map(|_| Self {
issued: AtomicUsize::new(0),
limit: Some(limit),
_not_send: PhantomData,
})
}
#[must_use]
pub fn unlimited() -> Option<Self> {
LOCKSMITH_EXISTS
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.ok()
.map(|_| Self {
issued: AtomicUsize::new(0),
limit: None,
_not_send: PhantomData,
})
}
#[must_use]
#[allow(clippy::expect_used)]
pub fn create(limit: usize) -> Self {
Self::new(limit).expect("surelock: a Locksmith already exists")
}
#[must_use]
#[allow(clippy::expect_used)]
pub fn create_unlimited() -> Self {
Self::unlimited().expect("surelock: a Locksmith already exists")
}
#[must_use]
pub fn issue(&self) -> Option<KeyVoucher> {
match self.limit {
None => {
self.issued.fetch_add(1, Ordering::Relaxed);
Some(KeyVoucher::new_internal())
}
Some(limit) => self
.issued
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |n| {
if n < limit { Some(n + 1) } else { None }
})
.ok()
.map(|_| KeyVoucher::new_internal()),
}
}
#[must_use]
pub fn issued(&self) -> usize {
self.issued.load(Ordering::Relaxed)
}
#[must_use]
pub const fn limit(&self) -> Option<usize> {
self.limit
}
#[must_use]
pub fn remaining(&self) -> Option<usize> {
self.limit.map(|l| l.saturating_sub(self.issued()))
}
}
impl Drop for Locksmith {
fn drop(&mut self) {
LOCKSMITH_EXISTS.store(false, Ordering::Release);
}
}
impl core::fmt::Debug for Locksmith {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Locksmith")
.field("issued", &self.issued())
.field("limit", &self.limit)
.finish()
}
}