use parking_lot::{lock_api::RawMutex as _, RawMutex};
use std::{
borrow::Borrow,
cell::UnsafeCell,
fmt,
hash::{Hash, Hasher},
ops,
ptr::{self, NonNull},
slice,
sync::Arc,
};
pub struct Interned<'a, T: ?Sized>(pub &'a T, Prv);
impl<'a, T: ?Sized> Interned<'a, T> {
pub fn raw(&self) -> RawInterned<T> {
let ptr = unsafe { NonNull::new_unchecked(self.0 as *const T as *mut T) };
RawInterned(ptr)
}
pub fn erased_raw(&self) -> RawInterned {
let ptr = unsafe { NonNull::new_unchecked(self.0 as *const T as *mut T) };
RawInterned(ptr.cast::<Prv>())
}
pub(crate) fn unique(value: &'a T) -> Self {
Self(value, Prv)
}
}
impl<'a, T: ?Sized> Interned<'a, T> {
pub unsafe fn from_raw(raw: RawInterned<T>) -> Self {
let ref_ = unsafe { raw.0.as_ref() };
Self(ref_, Prv)
}
}
impl<'a, T> Interned<'a, T> {
pub unsafe fn from_erased_raw(raw: RawInterned) -> Self {
let ref_ = unsafe { raw.0.cast::<T>().as_ref() };
Self(ref_, Prv)
}
}
impl<T: ?Sized> PartialEq for Interned<'_, T> {
fn eq(&self, other: &Self) -> bool {
(self.0 as *const T as *const ()) == (other.0 as *const T as *const ())
}
}
impl<T: ?Sized> Eq for Interned<'_, T> {}
impl<T: PartialOrd + ?Sized> PartialOrd for Interned<'_, T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.0.partial_cmp(other.0)
}
}
impl<T: Ord + ?Sized> Ord for Interned<'_, T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.cmp(other.0)
}
}
impl<T: Hash + ?Sized> Hash for Interned<'_, T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state)
}
}
impl<T: ?Sized> Borrow<T> for Interned<'_, T> {
fn borrow(&self) -> &T {
self.0
}
}
impl<T: ?Sized> AsRef<T> for Interned<'_, T> {
fn as_ref(&self) -> &T {
self.0
}
}
impl<'a, T: ?Sized> ops::Deref for Interned<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0
}
}
impl<T: ?Sized> Clone for Interned<'_, T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized> Copy for Interned<'_, T> {}
impl<T: fmt::Debug + ?Sized> fmt::Debug for Interned<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl<T: fmt::Display + ?Sized> fmt::Display for Interned<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
pub struct RawInterned<T: ?Sized = Prv>(pub(crate) NonNull<T>);
impl<T: ?Sized> RawInterned<T> {
#[inline]
pub fn cast<U>(self) -> RawInterned<U> {
RawInterned(self.0.cast())
}
#[inline]
pub fn erase(self) -> RawInterned {
RawInterned(self.0.cast())
}
}
impl<T: ?Sized> PartialEq for RawInterned<T> {
fn eq(&self, other: &Self) -> bool {
(self.as_ptr() as *mut ()) == (other.as_ptr() as *mut ())
}
}
impl<T: ?Sized> Eq for RawInterned<T> {}
impl<T: ?Sized> PartialOrd for RawInterned<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<T: ?Sized> Ord for RawInterned<T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0
.as_ptr()
.cast::<()>()
.cmp(&other.0.as_ptr().cast::<()>())
}
}
impl<T: ?Sized> Hash for RawInterned<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state)
}
}
impl<T: ?Sized> Borrow<NonNull<T>> for RawInterned<T> {
fn borrow(&self) -> &NonNull<T> {
&self.0
}
}
impl<T: ?Sized> ops::Deref for RawInterned<T> {
type Target = NonNull<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: ?Sized> Clone for RawInterned<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized> Copy for RawInterned<T> {}
impl<T: ?Sized> fmt::Debug for RawInterned<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct Prv;
#[derive(Clone)]
pub struct UnsafeLock<T: ?Sized> {
inner: Arc<ManualMutex<T>>,
}
unsafe impl<T: ?Sized> Send for UnsafeLock<T> {}
unsafe impl<T: ?Sized> Sync for UnsafeLock<T> {}
impl<T> UnsafeLock<T> {
pub unsafe fn new(value: T) -> Self {
Self {
inner: Arc::new(ManualMutex {
mutex: RawMutex::INIT,
data: UnsafeCell::new(value),
}),
}
}
}
impl<T: ?Sized> UnsafeLock<T> {
pub unsafe fn lock(&self) -> NonNull<T> {
self.inner.mutex.lock();
unsafe { NonNull::new_unchecked(self.inner.data.get()) }
}
pub unsafe fn unlock(&self) {
self.inner.mutex.unlock();
}
}
impl<T: fmt::Debug> fmt::Debug for UnsafeLock<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe {
let t = self.lock().as_ref();
let ret = fmt::Debug::fmt(t, f);
self.unlock();
ret
}
}
}
struct ManualMutex<T: ?Sized> {
mutex: RawMutex,
data: UnsafeCell<T>,
}
unsafe impl<T: Send + ?Sized> Send for ManualMutex<T> {}
unsafe impl<T: Send + ?Sized> Sync for ManualMutex<T> {}
pub(crate) unsafe fn cast_then_drop_slice<T>(ptr: *mut u8, num_elems: usize) {
unsafe {
let slice = slice::from_raw_parts_mut(ptr.cast::<T>(), num_elems);
ptr::drop_in_place(slice);
}
}
#[cfg(test)]
pub(crate) fn assert_group_addr_eq(groups: &[&[RawInterned]]) {
for i in 0..groups.len() {
for w in groups[i].windows(2) {
assert_eq!(w[0], w[1]);
}
let a = groups[i][0];
for group in groups.iter().skip(i + 1) {
let b = group[0];
assert_ne!(a, b);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::{cell::Cell, rc::Rc, thread};
#[test]
#[allow(unused_mut)]
fn test_unsafelock() {
let Ok(num_cpus) = thread::available_parallelism() else {
return;
};
let mut num_threads = num_cpus.get();
let val = Rc::new(Cell::new(0_usize));
let lock = unsafe { UnsafeLock::new(val.clone()) };
const N: usize = 10_000;
let mut handles = Vec::new();
for _ in 0..num_threads {
let c_lock = lock.clone();
let handle = thread::spawn(move || {
for _ in 0..N {
unsafe {
let val = c_lock.lock().as_mut();
val.set(val.get() + 1);
c_lock.unlock();
}
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
unsafe {
let val = lock.lock().as_ref();
assert_eq!(val.get(), N * num_threads);
lock.unlock();
}
}
}