use std::{
fmt,
hash::{Hash, Hasher},
ops::Deref,
ptr::NonNull,
};
#[cfg(not(moka_loom))]
use std::sync::atomic::{self, AtomicU32};
#[cfg(moka_loom)]
use loom::sync::atomic::{self, AtomicU32};
pub(crate) struct MiniArc<T: ?Sized> {
ptr: NonNull<ArcData<T>>,
}
struct ArcData<T: ?Sized> {
ref_count: AtomicU32,
data: T,
}
const MAX_REFCOUNT: u32 = (i32::MAX) as u32;
unsafe impl<T: ?Sized + Send + Sync> Send for MiniArc<T> {}
unsafe impl<T: ?Sized + Send + Sync> Sync for MiniArc<T> {}
impl<T> MiniArc<T> {
pub(crate) fn new(data: T) -> MiniArc<T> {
MiniArc {
ptr: NonNull::from(Box::leak(Box::new(ArcData {
ref_count: AtomicU32::new(1),
data,
}))),
}
}
}
impl<T: ?Sized> MiniArc<T> {
pub(crate) fn count(this: &Self) -> u32 {
use atomic::Ordering::Acquire;
this.data().ref_count.load(Acquire)
}
#[inline]
#[allow(ambiguous_wide_pointer_comparisons)] #[allow(clippy::ptr_eq)] pub(crate) fn ptr_eq(this: &Self, other: &Self) -> bool {
this.ptr.as_ptr() == other.ptr.as_ptr()
}
#[inline]
fn data(&self) -> &ArcData<T> {
unsafe { self.ptr.as_ref() }
}
}
impl<T: ?Sized> Deref for MiniArc<T> {
type Target = T;
fn deref(&self) -> &T {
&self.data().data
}
}
impl<T: ?Sized> Clone for MiniArc<T> {
fn clone(&self) -> Self {
use atomic::Ordering::Relaxed;
if self.data().ref_count.fetch_add(1, Relaxed) > MAX_REFCOUNT {
std::process::abort();
}
MiniArc { ptr: self.ptr }
}
}
impl<T: ?Sized> Drop for MiniArc<T> {
fn drop(&mut self) {
use std::sync::atomic::Ordering::{Acquire, Release};
if self.data().ref_count.fetch_sub(1, Release) == 1 {
atomic::fence(Acquire);
unsafe {
drop(Box::from_raw(self.ptr.as_ptr()));
}
}
}
}
impl<T: Default> Default for MiniArc<T> {
fn default() -> MiniArc<T> {
MiniArc::new(Default::default())
}
}
impl<T: ?Sized + PartialEq> PartialEq for MiniArc<T> {
fn eq(&self, other: &MiniArc<T>) -> bool {
Self::ptr_eq(self, other) || *(*self) == *(*other)
}
#[allow(clippy::partialeq_ne_impl)]
fn ne(&self, other: &MiniArc<T>) -> bool {
!Self::ptr_eq(self, other) && *(*self) != *(*other)
}
}
impl<T: ?Sized + Eq> Eq for MiniArc<T> {}
impl<T: ?Sized + fmt::Display> fmt::Display for MiniArc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
impl<T: ?Sized + fmt::Debug> fmt::Debug for MiniArc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<T: ?Sized> fmt::Pointer for MiniArc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.ptr.as_ptr(), f)
}
}
impl<T: ?Sized + Hash> Hash for MiniArc<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state)
}
}
#[cfg(all(test, not(moka_loom)))]
mod tests {
use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
use super::*;
#[test]
fn test_drop() {
static NUM_DROPS: AtomicUsize = AtomicUsize::new(0);
struct DetectDrop;
impl Drop for DetectDrop {
fn drop(&mut self) {
NUM_DROPS.fetch_add(1, Relaxed);
}
}
let x = MiniArc::new(("hello", DetectDrop));
let y = x.clone();
let t = std::thread::spawn(move || {
assert_eq!(x.0, "hello");
});
assert_eq!(y.0, "hello");
assert!(MiniArc::count(&y) >= 1);
t.join().unwrap();
assert_eq!(NUM_DROPS.load(Relaxed), 0);
assert_eq!(MiniArc::count(&y), 1);
drop(y);
assert_eq!(NUM_DROPS.load(Relaxed), 1);
}
#[test]
fn test_eq() {
let w = MiniArc::new(6502);
let x = w.clone();
let y = MiniArc::new(6502);
let z = MiniArc::new(8086);
assert_eq!(w, x);
assert_eq!(x, w);
assert_eq!(w, y);
assert_eq!(y, w);
assert_ne!(y, z);
assert_ne!(z, y);
}
#[test]
fn test_partial_eq_bug() {
let float = f32::NAN;
assert_ne!(float, float);
let arc = MiniArc::new(f32::NAN);
assert_eq!(arc, arc);
}
#[allow(dead_code)]
const fn is_partial_eq<T: ?Sized + PartialEq>() {}
#[allow(dead_code)]
const fn is_eq<T: ?Sized + Eq>() {}
const _: () = is_partial_eq::<MiniArc<i32>>();
const _: () = is_eq::<MiniArc<i32>>();
}
#[cfg(all(test, moka_loom))]
mod loom_tests {
use super::*;
#[test]
fn test_drop() {
use loom::sync::atomic::{AtomicUsize, Ordering::Relaxed};
struct DetectDrop(loom::sync::Arc<AtomicUsize>);
impl Drop for DetectDrop {
fn drop(&mut self) {
self.0.fetch_add(1, Relaxed);
}
}
loom::model(move || {
let num_drops = loom::sync::Arc::new(AtomicUsize::new(0));
let x = MiniArc::new(("hello", DetectDrop(loom::sync::Arc::clone(&num_drops))));
let y = x.clone();
let t = loom::thread::spawn(move || {
assert_eq!(x.0, "hello");
});
assert_eq!(y.0, "hello");
assert!(MiniArc::count(&y) >= 1);
t.join().unwrap();
assert_eq!(num_drops.load(Relaxed), 0);
assert_eq!(MiniArc::count(&y), 1);
drop(y);
assert_eq!(num_drops.load(Relaxed), 1);
});
}
}