use alloc::boxed::Box;
use core::fmt::{self, Debug, Formatter};
use core::marker::PhantomData;
use core::mem::forget;
use core::ptr;
use core::sync::atomic::{AtomicPtr, Ordering};
pub struct AtomicBox<T> {
ptr: AtomicPtr<T>,
phantom: PhantomData<Box<T>>,
}
unsafe impl<T> Sync for AtomicBox<T> where T: Send {}
impl<T> AtomicBox<T> {
pub fn new(value: Box<T>) -> AtomicBox<T> {
AtomicBox {
ptr: AtomicPtr::new(Box::into_raw(value)),
phantom: PhantomData,
}
}
pub fn swap(&self, other: Box<T>, order: Ordering) -> Box<T> {
let mut result = other;
self.swap_mut(&mut result, order);
result
}
pub fn store(&self, other: Box<T>, order: Ordering) {
self.swap(other, order);
}
pub fn swap_mut(&self, other: &mut Box<T>, order: Ordering) {
match order {
Ordering::AcqRel | Ordering::SeqCst => {}
_ => panic!("invalid ordering for atomic swap"),
}
let other_ptr = Box::into_raw(unsafe { ptr::read(other) });
let ptr = self.ptr.swap(other_ptr, order);
unsafe {
ptr::write(other, Box::from_raw(ptr));
}
}
pub fn into_inner(self) -> Box<T> {
let last_ptr = self.ptr.load(Ordering::Acquire);
forget(self);
unsafe { Box::from_raw(last_ptr) }
}
pub fn get_mut(&mut self) -> &mut T {
let ptr = self.ptr.load(Ordering::Relaxed);
unsafe { &mut *ptr }
}
}
impl<T> Drop for AtomicBox<T> {
fn drop(&mut self) {
let last_ptr = self.ptr.load(Ordering::Acquire);
unsafe {
Box::from_raw(last_ptr);
}
}
}
impl<T> Default for AtomicBox<T>
where
Box<T>: Default,
{
fn default() -> AtomicBox<T> {
AtomicBox::new(Default::default())
}
}
impl<T> Debug for AtomicBox<T> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
let p = self.ptr.load(Ordering::Relaxed);
f.write_str("AtomicBox(")?;
fmt::Pointer::fmt(&p, f)?;
f.write_str(")")?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::sync::atomic::Ordering;
use std::sync::{Arc, Barrier};
use std::thread::spawn;
#[test]
fn atomic_box_swap_works() {
let b = AtomicBox::new(Box::new("hello world"));
let bis = Box::new("bis");
assert_eq!(b.swap(bis, Ordering::AcqRel), Box::new("hello world"));
assert_eq!(b.swap(Box::new(""), Ordering::AcqRel), Box::new("bis"));
}
#[test]
fn atomic_box_store_works() {
let b = AtomicBox::new(Box::new("hello world"));
let bis = Box::new("bis");
b.store(bis, Ordering::AcqRel);
assert_eq!(b.into_inner(), Box::new("bis"));
}
#[test]
fn atomic_box_swap_mut_works() {
let b = AtomicBox::new(Box::new("hello world"));
let mut bis = Box::new("bis");
b.swap_mut(&mut bis, Ordering::AcqRel);
assert_eq!(bis, Box::new("hello world"));
b.swap_mut(&mut bis, Ordering::AcqRel);
assert_eq!(bis, Box::new("bis"));
}
#[test]
fn atomic_box_pointer_identity() {
let box1 = Box::new(1);
let p1 = format!("{:p}", box1);
let atom = AtomicBox::new(box1);
let box2 = Box::new(2);
let p2 = format!("{:p}", box2);
assert!(p2 != p1);
let box3 = atom.swap(box2, Ordering::AcqRel); let p3 = format!("{:p}", box3);
assert_eq!(p3, p1);
let box4 = atom.swap(Box::new(5), Ordering::AcqRel); let p4 = format!("{:p}", box4);
assert_eq!(p4, p2); }
#[test]
fn atomic_box_drops() {
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
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 = AtomicBox::new(Box::new(K(n.clone(), 5)));
assert_eq!(n.load(Ordering::Relaxed), 0);
let first = ab.swap(Box::new(K(n.clone(), 13)), Ordering::AcqRel);
assert_eq!(n.load(Ordering::Relaxed), 0);
drop(first);
assert_eq!(n.load(Ordering::Relaxed), 5);
}
assert_eq!(n.load(Ordering::Relaxed), 5 + 13);
}
#[test]
fn atomic_threads() {
const NTHREADS: usize = 9;
let gate = Arc::new(Barrier::new(NTHREADS));
let abox: Arc<AtomicBox<Vec<u8>>> = Arc::new(Default::default());
let handles: Vec<_> = (0..NTHREADS as u8)
.map(|t| {
let my_gate = gate.clone();
let my_box = abox.clone();
spawn(move || {
my_gate.wait();
let mut my_vec = Box::new(vec![]);
for _ in 0..100 {
my_vec = my_box.swap(my_vec, Ordering::AcqRel);
my_vec.push(t);
}
my_vec
})
})
.collect();
let mut counts = [0usize; NTHREADS];
for h in handles {
for val in *h.join().unwrap() {
counts[val as usize] += 1;
}
}
for val in *abox.swap(Box::new(vec![]), Ordering::AcqRel) {
counts[val as usize] += 1;
}
println!("{:?}", counts);
for count in counts {
assert_eq!(count, 100);
}
}
#[test]
#[should_panic(expected = "invalid ordering for atomic swap")]
fn cant_use_foolish_swap_ordering_type() {
let atom = AtomicBox::new(Box::new(0));
atom.swap(Box::new(44), Ordering::Release); }
#[test]
fn debug_fmt() {
let my_box = Box::new(32);
let expected = format!("AtomicBox({:p})", my_box);
assert_eq!(format!("{:?}", AtomicBox::new(my_box)), expected);
}
}