use crossbeam_epoch::{Atomic, Guard, Owned, Shared};
use std::fmt;
use std::ops::Deref;
use std::sync::atomic::Ordering;
pub const ORDERING: Ordering = Ordering::SeqCst;
pub struct NotNull<'t, T: 't>(Shared<'t, T>);
impl<'t, T> NotNull<'t, T> {
pub fn as_maybe_null(&self) -> MaybeNull<'t, T> {
MaybeNull(self.0)
}
pub fn as_shared(&self) -> Shared<'t, T> {
self.0
}
pub fn deref<'f>(&'f self) -> &'t T {
debug_assert!(!self.0.is_null());
unsafe { self.0.deref() }
}
pub unsafe fn drop(self) {
let shared = self.0;
debug_assert!(!shared.is_null());
drop(shared.into_owned());
}
}
impl<'t, T: fmt::Debug> fmt::Debug for NotNull<'t, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.as_maybe_null())
}
}
impl<'t, T> Clone for NotNull<'t, T> {
fn clone<'b>(&'b self) -> Self {
NotNull(self.0)
}
}
impl<'t, T> Copy for NotNull<'t, T> { }
impl<'t, T> Deref for NotNull<'t, T> {
type Target = T;
fn deref(&self) -> &T {
self.deref()
}
}
pub struct NotNullOwned<T>(Owned<T>);
impl<T> NotNullOwned<T> {
pub fn new(value: T) -> Self {
NotNullOwned(Owned::new(value))
}
pub fn into_owned(self) -> Owned<T> {
self.0
}
}
impl<T: fmt::Debug> fmt::Debug for NotNullOwned<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.deref())
}
}
impl<T> Deref for NotNullOwned<T> {
type Target = T;
fn deref(&self) -> &T {
self.0.deref()
}
}
pub struct MaybeNull<'t, T: 't>(Shared<'t, T>);
impl<'t, T> Clone for MaybeNull<'t, T> {
fn clone<'b>(&'b self) -> Self {
MaybeNull(self.0)
}
}
impl<'t, T> Copy for MaybeNull<'t, T> { }
impl<'t, T> MaybeNull<'t, T> {
pub fn from_shared(shared: Shared<'t, T>) -> Self {
MaybeNull(shared)
}
pub fn as_shared(&self) -> Shared<T> {
self.0
}
pub fn as_option(&self) -> Option<NotNull<'t, T>> {
match self.0.is_null() {
true => None,
false => Some(NotNull(self.0)),
}
}
pub unsafe fn try_defer_drop(self, guard: &Guard) {
if !self.as_shared().is_null() {
guard.defer(move || {
self.as_shared().into_owned();
})
}
}
}
impl<'t, T: fmt::Debug> fmt::Debug for MaybeNull<'t, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.as_option() {
Some(not_null) => write!(f, "{:?}", not_null.deref()),
None => write!(f, "(Null)"),
}
}
}
pub struct AtomicPtr<T>(Atomic<T>);
impl<T> AtomicPtr<T> {
pub fn new(value: Option<T>) -> Self {
match value {
Some(t) => AtomicPtr(Atomic::new(t)),
None => AtomicPtr(Atomic::null()),
}
}
pub fn load<'g>(&self, guard: &'g Guard) -> MaybeNull<'g, T> {
MaybeNull(self.0.load(ORDERING, guard))
}
pub fn relaxed_exists<'g>(&self, guard: &'g Guard) -> bool {
!self.0.load(Ordering::Relaxed, guard).is_null()
}
pub unsafe fn take<'g>(&self, guard: &'g Guard) -> Option<Box<T>> {
let shared = self.0.swap(Shared::null(), ORDERING, guard);
match shared.is_null() {
true => None,
false => Some(shared.into_owned().into_box())
}
}
pub fn compare_and_set<'g>(&self, compare: MaybeNull<T>, set: NotNull<'g, T>, guard: &'g Guard)
-> Result<NotNull<'g, T>, (MaybeNull<'g, T>, NotNull<'g, T>)>
{
self.0.compare_and_set(compare.as_shared(), set.as_shared(), ORDERING, guard)
.map(|set| NotNull(set))
.map_err(|e| (MaybeNull(e.current), NotNull(e.new)))
}
pub fn compare_null_and_set<'g>(
&self,
set: NotNull<'g, T>,
guard: &'g Guard
) -> Result<NotNull<'g, T>, (NotNull<'g, T>, NotNull<'g, T>)>
{
self.0.compare_and_set(Shared::null(), set.as_shared(), ORDERING, guard)
.map(|set| NotNull(set))
.map_err(|e| (NotNull(e.current), NotNull(e.new)))
}
pub fn compare_null_and_set_owned<'g>(
&self,
set: NotNullOwned<T>,
guard: &'g Guard
) -> Result<NotNull<'g, T>, (NotNull<'g, T>, NotNullOwned<T>)>
{
self.0.compare_and_set(Shared::null(), set.into_owned(), ORDERING, guard)
.map(|set| NotNull(set))
.map_err(|e| (NotNull(e.current), NotNullOwned(e.new)))
}
pub fn compare_and_set_owned<'g>(
&self,
compare: MaybeNull<T>,
set: NotNullOwned<T>,
guard: &'g Guard
) -> Result<NotNull<'g, T>, (MaybeNull<'g, T>, NotNullOwned<T>)>
{
self.0.compare_and_set(compare.as_shared(), set.into_owned(), ORDERING, guard)
.map(|set| NotNull(set))
.map_err(|e| (MaybeNull(e.current), NotNullOwned(e.new)))
}
pub fn compare_and_set_owned_weak<'g>(
&self,
compare: MaybeNull<T>,
set: NotNullOwned<T>,
guard: &'g Guard
) -> Result<NotNull<'g, T>, (MaybeNull<'g, T>, NotNullOwned<T>)>
{
self.0.compare_and_set_weak(compare.as_shared(), set.into_owned(), ORDERING, guard)
.map(|set| NotNull(set))
.map_err(|e| (MaybeNull(e.current), NotNullOwned(e.new)))
}
pub fn tag<'g>(&self, guard: &'g Guard) {
self.0.fetch_or(1, ORDERING, guard);
}
pub fn is_tagged<'g>(&self, guard: &'g Guard) -> bool {
self.0.fetch_or(0, ORDERING, guard).tag() == 1
}
pub unsafe fn try_drop(&mut self, guard: &Guard) {
let inner = self.0.swap(Shared::null(), ORDERING, &guard);
if !inner.is_null() {
inner.into_owned();
}
}
}
impl<T: fmt::Debug> fmt::Debug for AtomicPtr<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let guard = ::pin();
write!(f, "{:?}", self.load(&guard))
}
}
pub struct AtomicBox<T>(Atomic<T>);
impl<T> AtomicBox<T> {
pub fn new(value: T) -> Self {
AtomicBox(Atomic::new(value))
}
pub fn load<'g>(&self, guard: &'g Guard) -> NotNull<'g, T> {
NotNull(self.0.load(ORDERING, guard))
}
pub fn compare_and_set_shared<'g>(
&'g self,
compare: NotNull<T>,
set: NotNull<'g, T>,
guard: &'g Guard
)
-> Result<NotNull<'g, T>, (NotNull<'g, T>, NotNull<T>)>
{
self.0.compare_and_set(compare.0, set.0, ORDERING, guard)
.map(|set| NotNull(set))
.map_err(|e| (NotNull(e.current), NotNull(e.new)))
}
}
impl<T: fmt::Debug> fmt::Debug for AtomicBox<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let guard = ::pin();
write!(f, "{:?}", self.load(&guard).as_maybe_null())
}
}
impl<T> Drop for AtomicBox<T> {
fn drop(&mut self) {
let guard = ::pin();
let inner = self.0.swap(Shared::null(), ORDERING, &guard);
debug_assert!(!inner.is_null());
if !inner.is_null() {
unsafe { inner.into_owned(); }
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_atomic_ptr() {
let ptr = AtomicPtr::new(None);
let guard = ::pin();
assert!(!ptr.relaxed_exists(&guard));
assert!(ptr.load(&guard).as_option().is_none());
let _ok = ptr.compare_null_and_set_owned(NotNullOwned::new(String::from("first")), &guard);
let err = ptr.compare_null_and_set_owned(NotNullOwned::new(String::from("second")), &guard)
.unwrap_err();
let current = err.0;
let tried_to_insert = err.1;
assert_eq!(&*tried_to_insert, "second");
assert_eq!(&*current, "first");
let _ok = ptr.compare_and_set_owned(
current.as_maybe_null(), NotNullOwned::new(String::from("third")), &guard
).unwrap();
unsafe { guard.defer(move || current.drop()); }
assert_eq!(*_ok, "third");
let inner = unsafe { ptr.take(&guard).unwrap() };
assert_eq!(*inner, "third");
drop(ptr);
}
}