blob: 78828d83130467b130575aeb9c4cec78f35f9939 [file] [log] [blame]
// Copyright 2015 Colin Sherratt
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::fmt::{self, Debug, Formatter};
use std::marker::PhantomData;
use std::mem;
use std::ops::Deref;
use std::ptr;
use std::sync::atomic::AtomicPtr;
use std::sync::atomic::Ordering;
use std::sync::Arc;
/// An Atom wraps an AtomicPtr, it allows for safe mutation of an atomic
/// into common Rust Types.
pub struct Atom<P>
where
P: IntoRawPtr + FromRawPtr,
{
inner: AtomicPtr<()>,
data: PhantomData<P>,
}
impl<P> Debug for Atom<P>
where
P: IntoRawPtr + FromRawPtr,
{
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "atom({:?})", self.inner.load(Ordering::Relaxed))
}
}
impl<P> Atom<P>
where
P: IntoRawPtr + FromRawPtr,
{
/// Create a empty Atom
pub fn empty() -> Atom<P> {
Atom {
inner: AtomicPtr::new(ptr::null_mut()),
data: PhantomData,
}
}
/// Create a new Atomic from Pointer P
pub fn new(value: P) -> Atom<P> {
Atom {
inner: AtomicPtr::new(unsafe { value.into_raw() }),
data: PhantomData,
}
}
/// Swap a new value into the Atom, This will try multiple
/// times until it succeeds. The old value will be returned.
pub fn swap(&self, v: P) -> Option<P> {
let new = unsafe { v.into_raw() };
let old = self.inner.swap(new, Ordering::AcqRel);
if !old.is_null() {
Some(unsafe { FromRawPtr::from_raw(old) })
} else {
None
}
}
/// Take the value of the Atom replacing it with null pointer
/// Returning the contents. If the contents was a `null` pointer the
/// result will be `None`.
pub fn take(&self) -> Option<P> {
let old = self.inner.swap(ptr::null_mut(), Ordering::Acquire);
if !old.is_null() {
Some(unsafe { FromRawPtr::from_raw(old) })
} else {
None
}
}
/// This will do a `CAS` setting the value only if it is NULL
/// this will return `None` if the value was written,
/// otherwise a `Some(v)` will be returned, where the value was
/// the same value that you passed into this function
pub fn set_if_none(&self, v: P) -> Option<P> {
let new = unsafe { v.into_raw() };
let old = self.inner
.compare_and_swap(ptr::null_mut(), new, Ordering::Release);
if !old.is_null() {
Some(unsafe { FromRawPtr::from_raw(new) })
} else {
None
}
}
/// Take the current content, write it into P then do a CAS to extent this
/// Atom with the previous contents. This can be used to create a LIFO
///
/// Returns true if this set this migrated the Atom from null.
pub fn replace_and_set_next(&self, mut value: P) -> bool
where
P: GetNextMut<NextPtr = Option<P>>,
{
unsafe {
let next = value.get_next() as *mut Option<P>;
let raw = value.into_raw();
// Iff next was set to Some(P) we want to
// assert that it was droppeds
drop(ptr::read(next));
loop {
let pcurrent = self.inner.load(Ordering::Relaxed);
let current = if pcurrent.is_null() {
None
} else {
Some(FromRawPtr::from_raw(pcurrent))
};
ptr::write(next, current);
let last = self.inner.compare_and_swap(pcurrent, raw, Ordering::AcqRel);
if last == pcurrent {
return last.is_null();
}
}
}
}
/// Check to see if an atom is None
///
/// This only means that the contents was None when it was measured
pub fn is_none(&self) -> bool {
self.inner.load(Ordering::Relaxed).is_null()
}
}
impl<P> Drop for Atom<P>
where
P: IntoRawPtr + FromRawPtr,
{
fn drop(&mut self) {
unsafe {
let ptr = self.inner.load(Ordering::Relaxed);
if !ptr.is_null() {
let _: P = FromRawPtr::from_raw(ptr);
}
}
}
}
unsafe impl<P> Send for Atom<P>
where
P: IntoRawPtr + FromRawPtr,
{
}
unsafe impl<P> Sync for Atom<P>
where
P: IntoRawPtr + FromRawPtr,
{
}
/// Convert from into a raw pointer
pub trait IntoRawPtr {
unsafe fn into_raw(self) -> *mut ();
}
/// Convert from a raw ptr into a pointer
pub trait FromRawPtr {
unsafe fn from_raw(ptr: *mut ()) -> Self;
}
impl<T> IntoRawPtr for Box<T> {
#[inline]
unsafe fn into_raw(self) -> *mut () {
Box::into_raw(self) as *mut ()
}
}
impl<T> FromRawPtr for Box<T> {
#[inline]
unsafe fn from_raw(ptr: *mut ()) -> Box<T> {
Box::from_raw(ptr as *mut T)
}
}
impl<T> IntoRawPtr for Arc<T> {
#[inline]
unsafe fn into_raw(self) -> *mut () {
Arc::into_raw(self) as *mut T as *mut ()
}
}
impl<T> FromRawPtr for Arc<T> {
#[inline]
unsafe fn from_raw(ptr: *mut ()) -> Arc<T> {
Arc::from_raw(ptr as *const () as *const T)
}
}
/// Transforms lifetime of the second pointer to match the first.
#[inline]
unsafe fn copy_lifetime<'a, S: ?Sized, T: ?Sized + 'a>(_ptr: &'a S, ptr: &T) -> &'a T {
mem::transmute(ptr)
}
/// Transforms lifetime of the second pointer to match the first.
#[inline]
unsafe fn copy_mut_lifetime<'a, S: ?Sized, T: ?Sized + 'a>(_ptr: &'a S, ptr: &mut T) -> &'a mut T {
mem::transmute(ptr)
}
/// This is a restricted version of the Atom. It allows for only
/// `set_if_none` to be called.
///
/// `swap` and `take` can be used only with a mutable reference. Meaning
/// that AtomSetOnce is not usable as a
#[derive(Debug)]
pub struct AtomSetOnce<P>
where
P: IntoRawPtr + FromRawPtr,
{
inner: Atom<P>,
}
impl<P> AtomSetOnce<P>
where
P: IntoRawPtr + FromRawPtr,
{
/// Create an empty `AtomSetOnce`
pub fn empty() -> AtomSetOnce<P> {
AtomSetOnce {
inner: Atom::empty(),
}
}
/// Create a new `AtomSetOnce` from Pointer P
pub fn new(value: P) -> AtomSetOnce<P> {
AtomSetOnce {
inner: Atom::new(value),
}
}
/// This will do a `CAS` setting the value only if it is NULL
/// this will return `OK(())` if the value was written,
/// otherwise a `Err(P)` will be returned, where the value was
/// the same value that you passed into this function
pub fn set_if_none(&self, v: P) -> Option<P> {
self.inner.set_if_none(v)
}
/// Convert an `AtomSetOnce` into an `Atom`
pub fn into_atom(self) -> Atom<P> {
self.inner
}
/// Allow access to the atom if exclusive access is granted
pub fn atom(&mut self) -> &mut Atom<P> {
&mut self.inner
}
/// Check to see if an atom is None
///
/// This only means that the contents was None when it was measured
pub fn is_none(&self) -> bool {
self.inner.is_none()
}
}
impl<T, P> AtomSetOnce<P>
where
P: IntoRawPtr + FromRawPtr + Deref<Target = T>,
{
/// If the Atom is set, get the value
pub fn get<'a>(&'a self) -> Option<&'a T> {
let ptr = self.inner.inner.load(Ordering::Acquire);
if ptr.is_null() {
None
} else {
unsafe {
// This is safe since ptr cannot be changed once it is set
// which means that this is now a Arc or a Box.
let v: P = FromRawPtr::from_raw(ptr);
let out = copy_lifetime(self, &*v);
mem::forget(v);
Some(out)
}
}
}
}
impl<T> AtomSetOnce<Box<T>> {
/// If the Atom is set, get the value
pub fn get_mut<'a>(&'a mut self) -> Option<&'a mut T> {
let ptr = self.inner.inner.load(Ordering::Acquire);
if ptr.is_null() {
None
} else {
unsafe {
// This is safe since ptr cannot be changed once it is set
// which means that this is now a Arc or a Box.
let mut v: Box<T> = FromRawPtr::from_raw(ptr);
let out = copy_mut_lifetime(self, &mut *v);
mem::forget(v);
Some(out)
}
}
}
}
impl<T> AtomSetOnce<T>
where
T: Clone + IntoRawPtr + FromRawPtr,
{
/// Duplicate the inner pointer if it is set
pub fn dup<'a>(&self) -> Option<T> {
let ptr = self.inner.inner.load(Ordering::Acquire);
if ptr.is_null() {
None
} else {
unsafe {
// This is safe since ptr cannot be changed once it is set
// which means that this is now a Arc or a Box.
let v: T = FromRawPtr::from_raw(ptr);
let out = v.clone();
mem::forget(v);
Some(out)
}
}
}
}
/// This is a utility Trait that fetches the next ptr from
/// an object.
pub trait GetNextMut {
type NextPtr;
fn get_next(&mut self) -> &mut Self::NextPtr;
}