use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::ptr;
use std::sync::atomic::AtomicPtr;
use std::sync::atomic::Ordering;
pub struct AtomicOptionBox<T> {
ptr: AtomicPtr<T>,
phantom: PhantomData<Box<T>>,
}
unsafe impl<T> Sync for AtomicOptionBox<T> where T: Send {}
fn into_ptr<T>(value: Option<Box<T>>) -> *mut T {
match value {
Some(box_value) => Box::into_raw(box_value),
None => ptr::null_mut(),
}
}
unsafe fn from_ptr<T>(ptr: *mut T) -> Option<Box<T>> {
if ptr.is_null() {
None
} else {
Some(unsafe { Box::from_raw(ptr) })
}
}
impl<T> AtomicOptionBox<T> {
pub fn new(value: Option<Box<T>>) -> AtomicOptionBox<T> {
AtomicOptionBox {
ptr: AtomicPtr::new(into_ptr(value)),
phantom: PhantomData,
}
}
pub const fn none() -> Self {
Self {
ptr: AtomicPtr::new(ptr::null_mut()),
phantom: PhantomData,
}
}
pub fn swap(&self, other: Option<Box<T>>) -> Option<Box<T>> {
let order = match other {
Some(_) => Ordering::AcqRel,
None => Ordering::Acquire,
};
let new_ptr = into_ptr(other);
let old_ptr = self.ptr.swap(new_ptr, order);
unsafe { from_ptr(old_ptr) }
}
pub fn store(&self, other: Option<Box<T>>) {
self.swap(other);
}
pub fn take(&self) -> Option<Box<T>> {
self.swap(None)
}
pub fn swap_mut(&self, other: &mut Option<Box<T>>) {
let previous = self.swap(other.take());
*other = previous;
}
pub fn into_inner(mut self) -> Option<Box<T>> {
let result = unsafe { from_ptr(*self.ptr.get_mut()) };
mem::forget(self);
result
}
pub fn get_mut(&mut self) -> Option<&mut T> {
unsafe { self.ptr.get_mut().as_mut() }
}
}
impl<T> Drop for AtomicOptionBox<T> {
fn drop(&mut self) {
let ptr = *self.ptr.get_mut();
unsafe { drop(from_ptr(ptr)) }
}
}
impl<T> Default for AtomicOptionBox<T> {
fn default() -> AtomicOptionBox<T> {
AtomicOptionBox::new(None)
}
}
impl<T> fmt::Debug for AtomicOptionBox<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let p = self.ptr.load(Ordering::Relaxed);
f.write_str("AtomicOptionBox(")?;
if p.is_null() {
f.write_str("None")?;
} else {
fmt::Pointer::fmt(&p, f)?;
}
f.write_str(")")?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use core::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use super::*;
#[test]
fn atomic_option_box_swap_works() {
let b = AtomicOptionBox::new(Some(Box::new("hello world")));
let bis = Box::new("bis");
assert_eq!(b.swap(None), Some(Box::new("hello world")));
assert_eq!(b.swap(Some(bis)), None);
assert_eq!(b.swap(None), Some(Box::new("bis")));
}
#[test]
fn atomic_option_box_store_works() {
let b = AtomicOptionBox::new(Some(Box::new("hello world")));
b.store(None);
assert_eq!(b.into_inner(), None);
let b = AtomicOptionBox::new(Some(Box::new("hello world")));
let bis = Box::new("bis");
b.store(Some(bis));
assert_eq!(b.into_inner(), Some(Box::new("bis")));
}
#[test]
fn atomic_option_box_swap_mut_works() {
let b = AtomicOptionBox::new(Some(Box::new("hello world")));
let mut bis = None;
b.swap_mut(&mut bis);
assert_eq!(bis, Some(Box::new("hello world")));
bis = Some(Box::new("bis"));
b.swap_mut(&mut bis);
assert_eq!(bis, None);
b.swap_mut(&mut bis);
assert_eq!(bis, Some(Box::new("bis")));
}
#[test]
fn atomic_option_box_pointer_identity() {
let box1 = Box::new(1);
let p1 = &*box1 as *const i32;
let atom = AtomicOptionBox::new(Some(box1));
let box2 = Box::new(2);
let p2 = &*box2 as *const i32;
assert_ne!(p2, p1);
let box3 = atom.swap(Some(box2)).unwrap(); let p3 = &*box3 as *const i32;
assert_eq!(p3, p1);
let box4 = atom.swap(None).unwrap(); let p4 = &*box4 as *const i32;
assert_eq!(p4, p2); }
#[test]
fn atomic_box_drops() {
struct K(Arc<AtomicUsize>, usize);
impl Drop for K {
fn drop(&mut self) {
self.0.fetch_add(self.1, Ordering::Relaxed);
}
}
let n = Arc::new(AtomicUsize::new(0));
{
let ab = AtomicOptionBox::new(Some(Box::new(K(n.clone(), 5))));
assert_eq!(n.load(Ordering::Relaxed), 0);
let first = ab.swap(None);
assert_eq!(n.load(Ordering::Relaxed), 0);
drop(first);
assert_eq!(n.load(Ordering::Relaxed), 5);
let second = ab.swap(Some(Box::new(K(n.clone(), 13))));
assert!(second.is_none());
assert_eq!(n.load(Ordering::Relaxed), 5);
}
assert_eq!(n.load(Ordering::Relaxed), 5 + 13);
}
#[test]
fn debug_fmt() {
let my_box = Box::new(32);
let expected = format!("AtomicOptionBox({my_box:p})");
assert_eq!(
format!("{:?}", AtomicOptionBox::new(Some(my_box))),
expected
);
assert_eq!(
format!("{:?}", AtomicOptionBox::<String>::new(None)),
"AtomicOptionBox(None)"
);
}
}