#![expect(
clippy::redundant_pub_crate,
reason = "reemphasize that these are all internals",
)]
use std::num::NonZeroU64;
#[cfg(target_has_atomic = "64")]
use std::sync::atomic::{AtomicU64, Ordering};
#[cfg(not(target_has_atomic = "64"))]
use std::sync::Mutex;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct MutexID(NonZeroU64);
const MAX_MUTEXES_PER_PROCESS: u64 = 1 << 63;
pub(crate) fn next_id() -> MutexID {
let counter = next_counter();
assert!(
counter < MAX_MUTEXES_PER_PROCESS,
"Only 2^63 thread-checked mutexes may be created in one process",
);
#[expect(clippy::unwrap_used, reason = "panics cannot occur here, only above")]
let id = NonZeroU64::new(counter + 1).unwrap();
MutexID(id)
}
#[cfg(target_has_atomic = "64")]
#[inline]
fn next_counter() -> u64 {
static ID_COUNTER: AtomicU64 = AtomicU64::new(0);
ID_COUNTER.fetch_add(1, Ordering::Relaxed)
}
#[cfg(not(target_has_atomic = "64"))]
#[inline]
fn next_counter() -> u64 {
static ID_COUNTER: Mutex<u64> = Mutex::new(0);
#[expect(
clippy::unwrap_used,
reason = "Mutex can only be poisoned if the following three lines can panic",
)]
let mut counter_guard = ID_COUNTER.lock().unwrap();
let counter: u64 = *counter_guard;
*counter_guard = counter.wrapping_add(1);
counter
}
#[cfg(test)]
pub(crate) use self::tests::run_this_before_each_test_that_creates_a_mutex_id;
#[cfg(test)]
mod tests {
use std::sync::Once;
use super::*;
const fn first_id() -> MutexID {
#[expect(clippy::unwrap_used, reason = "1 is nonzero")]
MutexID(NonZeroU64::new(1).unwrap())
}
pub(crate) fn run_this_before_each_test_that_creates_a_mutex_id() {
#[inline(never)]
fn test_first_mutex_id() {
assert_eq!(next_id(), first_id());
}
static ONCE: Once = Once::new();
ONCE.call_once(test_first_mutex_id);
}
#[test]
fn check_start_and_uniqueness() {
run_this_before_each_test_that_creates_a_mutex_id();
assert_ne!(next_id(), first_id());
}
}