use std::ptr;
use std::sync::{
atomic::{AtomicPtr, Ordering},
Arc, Weak,
};
pub struct ArcCell<T> {
ptr: AtomicPtr<T>,
}
impl<T> Drop for ArcCell<T> {
#[inline]
fn drop(&mut self) {
self.clear();
}
}
unsafe impl<T> Send for ArcCell<T> {}
unsafe impl<T> Sync for ArcCell<T> {}
impl<T> ArcCell<T> {
#[inline(always)]
pub fn new() -> Self {
Self { ptr: AtomicPtr::new(ptr::null_mut()) }
}
#[inline(always)]
pub fn exists(&self) -> bool {
!self.ptr.load(Ordering::Acquire).is_null()
}
#[inline(always)]
pub fn pop(&self) -> Option<Arc<T>> {
let ptr = self.ptr.swap(ptr::null_mut(), Ordering::SeqCst);
if !ptr.is_null() {
Some(unsafe { Arc::from_raw(ptr) })
} else {
None
}
}
#[allow(dead_code)]
#[inline(always)]
pub fn clear(&self) {
let ptr = self.ptr.swap(ptr::null_mut(), Ordering::SeqCst);
if !ptr.is_null() {
let _ = unsafe { Arc::from_raw(ptr) };
}
}
#[inline(always)]
pub fn try_put(&self, item: Arc<T>) {
let item_ptr = Arc::into_raw(item) as *mut T;
match self.ptr.compare_exchange(
ptr::null_mut(),
item_ptr,
Ordering::SeqCst,
Ordering::Relaxed,
) {
Ok(_) => {}
Err(_) => {
let _ = unsafe { Arc::from_raw(item_ptr) };
}
}
}
}
#[allow(dead_code)]
pub struct WeakCell<T> {
ptr: AtomicPtr<T>,
}
unsafe impl<T> Send for WeakCell<T> {}
unsafe impl<T> Sync for WeakCell<T> {}
impl<T> Drop for WeakCell<T> {
#[inline]
fn drop(&mut self) {
self.clear();
}
}
impl<T> WeakCell<T> {
#[inline(always)]
pub fn new() -> Self {
Self { ptr: AtomicPtr::new(ptr::null_mut()) }
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.ptr.load(Ordering::SeqCst).is_null()
}
#[inline(always)]
pub fn pop(&self) -> Option<Arc<T>> {
let mut v = self.ptr.load(Ordering::SeqCst);
if v.is_null() {
return None;
}
loop {
match self.ptr.compare_exchange(v, ptr::null_mut(), Ordering::SeqCst, Ordering::Acquire)
{
Ok(_) => return unsafe { Weak::from_raw(v) }.upgrade(),
Err(_v) => {
if _v.is_null() {
return None;
}
v = _v;
}
}
}
}
#[inline(always)]
pub fn clear(&self) -> bool {
let v = self.ptr.load(Ordering::Acquire);
if v.is_null() {
return false;
}
match self.ptr.compare_exchange(v, ptr::null_mut(), Ordering::Release, Ordering::Relaxed) {
Ok(_) => {
let _ = unsafe { Weak::from_raw(v) };
true
}
Err(_v) => {
false
}
}
}
#[inline(always)]
pub fn replace(&self, item: Weak<T>) {
let old_ptr = self.ptr.swap(item.into_raw() as *mut T, Ordering::SeqCst);
if !old_ptr.is_null() {
let _ = unsafe { Weak::from_raw(old_ptr) };
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_weak_cell() {
use super::*;
use std::sync::Arc;
let cell = WeakCell::new();
assert!(cell.is_empty());
let item = Arc::new(1);
cell.replace(Arc::downgrade(&item));
assert!(!cell.is_empty());
let _item = cell.pop().unwrap();
assert!(cell.is_empty());
assert!(Arc::ptr_eq(&item, &_item));
cell.replace(Arc::downgrade(&item));
assert!(!cell.is_empty());
println!("clear");
while !cell.clear() {
assert!(!cell.is_empty());
println!("try clear again");
}
assert!(cell.is_empty());
drop(_item);
assert_eq!(Arc::strong_count(&item), 1);
assert_eq!(Arc::weak_count(&item), 0);
}
}