use std::marker::PhantomData;
use std::marker;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::ptr;
use std::sync::atomic::{self, Ordering};
mod participant;
mod participants;
mod global;
mod local;
mod garbage;
pub struct Owned<T> {
data: Box<T>,
}
impl<T> Owned<T> {
pub fn new(t: T) -> Owned<T> {
Owned { data: Box::new(t) }
}
fn as_raw(&self) -> *mut T {
self.deref() as *const _ as *mut _
}
pub fn into_inner(self) -> T {
*self.data
}
}
impl<T> Deref for Owned<T> {
type Target = T;
fn deref(&self) -> &T {
&self.data
}
}
impl<T> DerefMut for Owned<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.data
}
}
#[derive(PartialEq, Eq)]
pub struct Shared<'a, T: 'a> {
data: &'a T,
}
impl<'a, T> Copy for Shared<'a, T> {}
impl<'a, T> Clone for Shared<'a, T> {
fn clone(&self) -> Shared<'a, T> {
Shared { data: self.data }
}
}
impl<'a, T> Deref for Shared<'a, T> {
type Target = &'a T;
fn deref(&self) -> &&'a T {
&self.data
}
}
impl<'a, T> Shared<'a, T> {
unsafe fn from_raw(raw: *mut T) -> Option<Shared<'a, T>> {
if raw == ptr::null_mut() { None }
else {
Some(Shared {
data: mem::transmute::<*mut T, &T>(raw)
})
}
}
unsafe fn from_ref(r: &T) -> Shared<'a, T> {
Shared { data: mem::transmute(r) }
}
unsafe fn from_owned(owned: Owned<T>) -> Shared<'a, T> {
let ret = Shared::from_ref(owned.deref());
mem::forget(owned);
ret
}
pub fn as_raw(&self) -> *mut T {
self.data as *const _ as *mut _
}
}
pub struct Atomic<T> {
ptr: atomic::AtomicPtr<T>,
_marker: PhantomData<*const ()>,
}
unsafe impl<T: Sync> Send for Atomic<T> {}
unsafe impl<T: Sync> Sync for Atomic<T> {}
fn opt_shared_into_raw<T>(val: Option<Shared<T>>) -> *mut T {
val.map(|p| p.as_raw()).unwrap_or(ptr::null_mut())
}
fn opt_owned_as_raw<T>(val: &Option<Owned<T>>) -> *mut T {
val.as_ref().map(Owned::as_raw).unwrap_or(ptr::null_mut())
}
fn opt_owned_into_raw<T>(val: Option<Owned<T>>) -> *mut T {
let ptr = val.as_ref().map(Owned::as_raw).unwrap_or(ptr::null_mut());
mem::forget(val);
ptr
}
impl<T> Atomic<T> {
#[cfg(feature = "nightly")]
pub const fn null() -> Atomic<T> {
Atomic {
ptr: atomic::AtomicPtr::new(0 as *mut _),
_marker: PhantomData
}
}
#[cfg(not(feature = "nightly"))]
pub fn null() -> Atomic<T> {
Atomic {
ptr: atomic::AtomicPtr::new(0 as *mut _),
_marker: PhantomData
}
}
pub fn new(data: T) -> Atomic<T> {
Atomic {
ptr: atomic::AtomicPtr::new(Box::into_raw(Box::new(data))),
_marker: PhantomData
}
}
pub fn load<'a>(&self, ord: Ordering, _: &'a Guard) -> Option<Shared<'a, T>> {
unsafe { Shared::from_raw(self.ptr.load(ord)) }
}
pub fn store(&self, val: Option<Owned<T>>, ord: Ordering) {
self.ptr.store(opt_owned_into_raw(val), ord)
}
pub fn store_and_ref<'a>(&self, val: Owned<T>, ord: Ordering, _: &'a Guard)
-> Shared<'a, T>
{
unsafe {
let shared = Shared::from_owned(val);
self.store_shared(Some(shared), ord);
shared
}
}
pub fn store_shared(&self, val: Option<Shared<T>>, ord: Ordering) {
self.ptr.store(opt_shared_into_raw(val), ord)
}
pub fn cas(&self, old: Option<Shared<T>>, new: Option<Owned<T>>, ord: Ordering)
-> Result<(), Option<Owned<T>>>
{
if self.ptr.compare_and_swap(opt_shared_into_raw(old),
opt_owned_as_raw(&new),
ord) == opt_shared_into_raw(old)
{
mem::forget(new);
Ok(())
} else {
Err(new)
}
}
pub fn cas_and_ref<'a>(&self, old: Option<Shared<T>>, new: Owned<T>,
ord: Ordering, _: &'a Guard)
-> Result<Shared<'a, T>, Owned<T>>
{
if self.ptr.compare_and_swap(opt_shared_into_raw(old), new.as_raw(), ord)
== opt_shared_into_raw(old)
{
Ok(unsafe { Shared::from_owned(new) })
} else {
Err(new)
}
}
pub fn cas_shared(&self, old: Option<Shared<T>>, new: Option<Shared<T>>, ord: Ordering)
-> bool
{
self.ptr.compare_and_swap(opt_shared_into_raw(old),
opt_shared_into_raw(new),
ord) == opt_shared_into_raw(old)
}
pub fn swap<'a>(&self, new: Option<Owned<T>>, ord: Ordering, _: &'a Guard)
-> Option<Shared<'a, T>> {
unsafe { Shared::from_raw(self.ptr.swap(opt_owned_into_raw(new), ord)) }
}
pub fn swap_shared<'a>(&self, new: Option<Shared<T>>, ord: Ordering, _: &'a Guard)
-> Option<Shared<'a, T>> {
unsafe { Shared::from_raw(self.ptr.swap(opt_shared_into_raw(new), ord)) }
}
}
#[must_use]
pub struct Guard {
_marker: marker::PhantomData<*mut ()>, }
pub fn pin() -> Guard {
local::with_participant(|p| {
p.enter();
let g = Guard {
_marker: marker::PhantomData,
};
if p.should_gc() {
p.try_collect(&g);
}
g
})
}
impl Guard {
pub unsafe fn unlinked<T>(&self, val: Shared<T>) {
local::with_participant(|p| p.reclaim(val.as_raw()))
}
}
impl Drop for Guard {
fn drop(&mut self) {
local::with_participant(|p| p.exit());
}
}
#[cfg(test)]
mod test {
use std::sync::atomic::Ordering;
use super::*;
use mem::epoch;
#[test]
fn test_no_drop() {
static mut DROPS: i32 = 0;
struct Test;
impl Drop for Test {
fn drop(&mut self) {
unsafe {
DROPS += 1;
}
}
}
let g = pin();
let x = Atomic::null();
x.store(Some(Owned::new(Test)), Ordering::Relaxed);
x.store_and_ref(Owned::new(Test), Ordering::Relaxed, &g);
let y = x.load(Ordering::Relaxed, &g);
let z = x.cas_and_ref(y, Owned::new(Test), Ordering::Relaxed, &g).ok();
let _ = x.cas(z, Some(Owned::new(Test)), Ordering::Relaxed);
x.swap(Some(Owned::new(Test)), Ordering::Relaxed, &g);
unsafe {
assert_eq!(DROPS, 0);
}
}
#[test]
fn test_new() {
let guard = epoch::pin();
let my_atomic = Atomic::new(42);
assert_eq!(**my_atomic.load(Ordering::Relaxed, &guard).unwrap(), 42);
}
}