use alloc::boxed::Box;
use core::alloc::Layout;
use core::borrow::{Borrow, BorrowMut};
use core::cmp;
use core::fmt;
use core::marker::PhantomData;
use core::mem::{self, MaybeUninit};
use core::ops::{Deref, DerefMut};
use core::ptr;
use core::slice;
use crate::guard::Guard;
use crate::primitive::sync::atomic::{AtomicUsize, Ordering};
use crossbeam_utils::atomic::AtomicConsume;
#[inline]
fn strongest_failure_ordering(ord: Ordering) -> Ordering {
use self::Ordering::*;
match ord {
Relaxed | Release => Relaxed,
Acquire | AcqRel => Acquire,
_ => SeqCst,
}
}
#[deprecated(note = "Use `CompareExchangeError` instead")]
pub type CompareAndSetError<'g, T, P> = CompareExchangeError<'g, T, P>;
pub struct CompareExchangeError<'g, T: ?Sized + Pointable, P: Pointer<T>> {
pub current: Shared<'g, T>,
pub new: P,
}
impl<T, P: Pointer<T> + fmt::Debug> fmt::Debug for CompareExchangeError<'_, T, P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CompareExchangeError")
.field("current", &self.current)
.field("new", &self.new)
.finish()
}
}
#[deprecated(
note = "`compare_and_set` and `compare_and_set_weak` that use this trait are deprecated, \
use `compare_exchange` or `compare_exchange_weak instead`"
)]
pub trait CompareAndSetOrdering {
fn success(&self) -> Ordering;
fn failure(&self) -> Ordering;
}
#[allow(deprecated)]
impl CompareAndSetOrdering for Ordering {
#[inline]
fn success(&self) -> Ordering {
*self
}
#[inline]
fn failure(&self) -> Ordering {
strongest_failure_ordering(*self)
}
}
#[allow(deprecated)]
impl CompareAndSetOrdering for (Ordering, Ordering) {
#[inline]
fn success(&self) -> Ordering {
self.0
}
#[inline]
fn failure(&self) -> Ordering {
self.1
}
}
#[inline]
fn low_bits<T: ?Sized + Pointable>() -> usize {
(1 << T::ALIGN.trailing_zeros()) - 1
}
#[inline]
fn ensure_aligned<T: ?Sized + Pointable>(raw: usize) {
assert_eq!(raw & low_bits::<T>(), 0, "unaligned pointer");
}
#[inline]
fn compose_tag<T: ?Sized + Pointable>(data: usize, tag: usize) -> usize {
(data & !low_bits::<T>()) | (tag & low_bits::<T>())
}
#[inline]
fn decompose_tag<T: ?Sized + Pointable>(data: usize) -> (usize, usize) {
(data & !low_bits::<T>(), data & low_bits::<T>())
}
pub trait Pointable {
const ALIGN: usize;
type Init;
unsafe fn init(init: Self::Init) -> usize;
unsafe fn deref<'a>(ptr: usize) -> &'a Self;
unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut Self;
unsafe fn drop(ptr: usize);
}
impl<T> Pointable for T {
const ALIGN: usize = mem::align_of::<T>();
type Init = T;
unsafe fn init(init: Self::Init) -> usize {
Box::into_raw(Box::new(init)) as usize
}
unsafe fn deref<'a>(ptr: usize) -> &'a Self {
&*(ptr as *const T)
}
unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut Self {
&mut *(ptr as *mut T)
}
unsafe fn drop(ptr: usize) {
drop(Box::from_raw(ptr as *mut T));
}
}
#[repr(C)]
struct Array<T> {
len: usize,
elements: [MaybeUninit<T>; 0],
}
impl<T> Array<T> {
fn layout(len: usize) -> Layout {
Layout::new::<Self>()
.extend(Layout::array::<MaybeUninit<T>>(len).unwrap())
.unwrap()
.0
.pad_to_align()
}
}
impl<T> Pointable for [MaybeUninit<T>] {
const ALIGN: usize = mem::align_of::<Array<T>>();
type Init = usize;
unsafe fn init(len: Self::Init) -> usize {
let layout = Array::<T>::layout(len);
let ptr = alloc::alloc::alloc(layout).cast::<Array<T>>();
if ptr.is_null() {
alloc::alloc::handle_alloc_error(layout);
}
ptr::addr_of_mut!((*ptr).len).write(len);
ptr as usize
}
unsafe fn deref<'a>(ptr: usize) -> &'a Self {
let array = &*(ptr as *const Array<T>);
slice::from_raw_parts(array.elements.as_ptr() as *const _, array.len)
}
unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut Self {
let array = &*(ptr as *mut Array<T>);
slice::from_raw_parts_mut(array.elements.as_ptr() as *mut _, array.len)
}
unsafe fn drop(ptr: usize) {
let len = (*(ptr as *mut Array<T>)).len;
let layout = Array::<T>::layout(len);
alloc::alloc::dealloc(ptr as *mut u8, layout);
}
}
pub struct Atomic<T: ?Sized + Pointable> {
data: AtomicUsize,
_marker: PhantomData<*mut T>,
}
unsafe impl<T: ?Sized + Pointable + Send + Sync> Send for Atomic<T> {}
unsafe impl<T: ?Sized + Pointable + Send + Sync> Sync for Atomic<T> {}
impl<T> Atomic<T> {
pub fn new(init: T) -> Atomic<T> {
Self::init(init)
}
}
impl<T: ?Sized + Pointable> Atomic<T> {
pub fn init(init: T::Init) -> Atomic<T> {
Self::from(Owned::init(init))
}
fn from_usize(data: usize) -> Self {
Self {
data: AtomicUsize::new(data),
_marker: PhantomData,
}
}
#[cfg(not(crossbeam_loom))]
pub const fn null() -> Atomic<T> {
Self {
data: AtomicUsize::new(0),
_marker: PhantomData,
}
}
#[cfg(crossbeam_loom)]
pub fn null() -> Atomic<T> {
Self {
data: AtomicUsize::new(0),
_marker: PhantomData,
}
}
pub fn load<'g>(&self, ord: Ordering, _: &'g Guard) -> Shared<'g, T> {
unsafe { Shared::from_usize(self.data.load(ord)) }
}
pub fn load_consume<'g>(&self, _: &'g Guard) -> Shared<'g, T> {
unsafe { Shared::from_usize(self.data.load_consume()) }
}
pub fn store<P: Pointer<T>>(&self, new: P, ord: Ordering) {
self.data.store(new.into_usize(), ord);
}
pub fn swap<'g, P: Pointer<T>>(&self, new: P, ord: Ordering, _: &'g Guard) -> Shared<'g, T> {
unsafe { Shared::from_usize(self.data.swap(new.into_usize(), ord)) }
}
pub fn compare_exchange<'g, P>(
&self,
current: Shared<'_, T>,
new: P,
success: Ordering,
failure: Ordering,
_: &'g Guard,
) -> Result<Shared<'g, T>, CompareExchangeError<'g, T, P>>
where
P: Pointer<T>,
{
let new = new.into_usize();
self.data
.compare_exchange(current.into_usize(), new, success, failure)
.map(|_| unsafe { Shared::from_usize(new) })
.map_err(|current| unsafe {
CompareExchangeError {
current: Shared::from_usize(current),
new: P::from_usize(new),
}
})
}
pub fn compare_exchange_weak<'g, P>(
&self,
current: Shared<'_, T>,
new: P,
success: Ordering,
failure: Ordering,
_: &'g Guard,
) -> Result<Shared<'g, T>, CompareExchangeError<'g, T, P>>
where
P: Pointer<T>,
{
let new = new.into_usize();
self.data
.compare_exchange_weak(current.into_usize(), new, success, failure)
.map(|_| unsafe { Shared::from_usize(new) })
.map_err(|current| unsafe {
CompareExchangeError {
current: Shared::from_usize(current),
new: P::from_usize(new),
}
})
}
pub fn fetch_update<'g, F>(
&self,
set_order: Ordering,
fail_order: Ordering,
guard: &'g Guard,
mut func: F,
) -> Result<Shared<'g, T>, Shared<'g, T>>
where
F: FnMut(Shared<'g, T>) -> Option<Shared<'g, T>>,
{
let mut prev = self.load(fail_order, guard);
while let Some(next) = func(prev) {
match self.compare_exchange_weak(prev, next, set_order, fail_order, guard) {
Ok(shared) => return Ok(shared),
Err(next_prev) => prev = next_prev.current,
}
}
Err(prev)
}
#[allow(deprecated)]
#[deprecated(note = "Use `compare_exchange` instead")]
pub fn compare_and_set<'g, O, P>(
&self,
current: Shared<'_, T>,
new: P,
ord: O,
guard: &'g Guard,
) -> Result<Shared<'g, T>, CompareAndSetError<'g, T, P>>
where
O: CompareAndSetOrdering,
P: Pointer<T>,
{
self.compare_exchange(current, new, ord.success(), ord.failure(), guard)
}
#[allow(deprecated)]
#[deprecated(note = "Use `compare_exchange_weak` instead")]
pub fn compare_and_set_weak<'g, O, P>(
&self,
current: Shared<'_, T>,
new: P,
ord: O,
guard: &'g Guard,
) -> Result<Shared<'g, T>, CompareAndSetError<'g, T, P>>
where
O: CompareAndSetOrdering,
P: Pointer<T>,
{
self.compare_exchange_weak(current, new, ord.success(), ord.failure(), guard)
}
pub fn fetch_and<'g>(&self, val: usize, ord: Ordering, _: &'g Guard) -> Shared<'g, T> {
unsafe { Shared::from_usize(self.data.fetch_and(val | !low_bits::<T>(), ord)) }
}
pub fn fetch_or<'g>(&self, val: usize, ord: Ordering, _: &'g Guard) -> Shared<'g, T> {
unsafe { Shared::from_usize(self.data.fetch_or(val & low_bits::<T>(), ord)) }
}
pub fn fetch_xor<'g>(&self, val: usize, ord: Ordering, _: &'g Guard) -> Shared<'g, T> {
unsafe { Shared::from_usize(self.data.fetch_xor(val & low_bits::<T>(), ord)) }
}
pub unsafe fn into_owned(self) -> Owned<T> {
Owned::from_usize(self.data.into_inner())
}
pub unsafe fn try_into_owned(self) -> Option<Owned<T>> {
let data = self.data.into_inner();
if decompose_tag::<T>(data).0 == 0 {
None
} else {
Some(Owned::from_usize(data))
}
}
}
impl<T: ?Sized + Pointable> fmt::Debug for Atomic<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let data = self.data.load(Ordering::SeqCst);
let (raw, tag) = decompose_tag::<T>(data);
f.debug_struct("Atomic")
.field("raw", &raw)
.field("tag", &tag)
.finish()
}
}
impl<T: ?Sized + Pointable> fmt::Pointer for Atomic<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let data = self.data.load(Ordering::SeqCst);
let (raw, _) = decompose_tag::<T>(data);
fmt::Pointer::fmt(&(unsafe { T::deref(raw) as *const _ }), f)
}
}
impl<T: ?Sized + Pointable> Clone for Atomic<T> {
fn clone(&self) -> Self {
let data = self.data.load(Ordering::Relaxed);
Atomic::from_usize(data)
}
}
impl<T: ?Sized + Pointable> Default for Atomic<T> {
fn default() -> Self {
Atomic::null()
}
}
impl<T: ?Sized + Pointable> From<Owned<T>> for Atomic<T> {
fn from(owned: Owned<T>) -> Self {
let data = owned.data;
mem::forget(owned);
Self::from_usize(data)
}
}
impl<T> From<Box<T>> for Atomic<T> {
fn from(b: Box<T>) -> Self {
Self::from(Owned::from(b))
}
}
impl<T> From<T> for Atomic<T> {
fn from(t: T) -> Self {
Self::new(t)
}
}
impl<'g, T: ?Sized + Pointable> From<Shared<'g, T>> for Atomic<T> {
fn from(ptr: Shared<'g, T>) -> Self {
Self::from_usize(ptr.data)
}
}
impl<T> From<*const T> for Atomic<T> {
fn from(raw: *const T) -> Self {
Self::from_usize(raw as usize)
}
}
pub trait Pointer<T: ?Sized + Pointable> {
fn into_usize(self) -> usize;
unsafe fn from_usize(data: usize) -> Self;
}
pub struct Owned<T: ?Sized + Pointable> {
data: usize,
_marker: PhantomData<Box<T>>,
}
impl<T: ?Sized + Pointable> Pointer<T> for Owned<T> {
#[inline]
fn into_usize(self) -> usize {
let data = self.data;
mem::forget(self);
data
}
#[inline]
unsafe fn from_usize(data: usize) -> Self {
debug_assert!(data != 0, "converting zero into `Owned`");
Owned {
data,
_marker: PhantomData,
}
}
}
impl<T> Owned<T> {
pub unsafe fn from_raw(raw: *mut T) -> Owned<T> {
let raw = raw as usize;
ensure_aligned::<T>(raw);
Self::from_usize(raw)
}
pub fn into_box(self) -> Box<T> {
let (raw, _) = decompose_tag::<T>(self.data);
mem::forget(self);
unsafe { Box::from_raw(raw as *mut _) }
}
pub fn new(init: T) -> Owned<T> {
Self::init(init)
}
}
impl<T: ?Sized + Pointable> Owned<T> {
pub fn init(init: T::Init) -> Owned<T> {
unsafe { Self::from_usize(T::init(init)) }
}
#[allow(clippy::needless_lifetimes)]
pub fn into_shared<'g>(self, _: &'g Guard) -> Shared<'g, T> {
unsafe { Shared::from_usize(self.into_usize()) }
}
pub fn tag(&self) -> usize {
let (_, tag) = decompose_tag::<T>(self.data);
tag
}
pub fn with_tag(self, tag: usize) -> Owned<T> {
let data = self.into_usize();
unsafe { Self::from_usize(compose_tag::<T>(data, tag)) }
}
}
impl<T: ?Sized + Pointable> Drop for Owned<T> {
fn drop(&mut self) {
let (raw, _) = decompose_tag::<T>(self.data);
unsafe {
T::drop(raw);
}
}
}
impl<T: ?Sized + Pointable> fmt::Debug for Owned<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (raw, tag) = decompose_tag::<T>(self.data);
f.debug_struct("Owned")
.field("raw", &raw)
.field("tag", &tag)
.finish()
}
}
impl<T: Clone> Clone for Owned<T> {
fn clone(&self) -> Self {
Owned::new((**self).clone()).with_tag(self.tag())
}
}
impl<T: ?Sized + Pointable> Deref for Owned<T> {
type Target = T;
fn deref(&self) -> &T {
let (raw, _) = decompose_tag::<T>(self.data);
unsafe { T::deref(raw) }
}
}
impl<T: ?Sized + Pointable> DerefMut for Owned<T> {
fn deref_mut(&mut self) -> &mut T {
let (raw, _) = decompose_tag::<T>(self.data);
unsafe { T::deref_mut(raw) }
}
}
impl<T> From<T> for Owned<T> {
fn from(t: T) -> Self {
Owned::new(t)
}
}
impl<T> From<Box<T>> for Owned<T> {
fn from(b: Box<T>) -> Self {
unsafe { Self::from_raw(Box::into_raw(b)) }
}
}
impl<T: ?Sized + Pointable> Borrow<T> for Owned<T> {
fn borrow(&self) -> &T {
self.deref()
}
}
impl<T: ?Sized + Pointable> BorrowMut<T> for Owned<T> {
fn borrow_mut(&mut self) -> &mut T {
self.deref_mut()
}
}
impl<T: ?Sized + Pointable> AsRef<T> for Owned<T> {
fn as_ref(&self) -> &T {
self.deref()
}
}
impl<T: ?Sized + Pointable> AsMut<T> for Owned<T> {
fn as_mut(&mut self) -> &mut T {
self.deref_mut()
}
}
pub struct Shared<'g, T: 'g + ?Sized + Pointable> {
data: usize,
_marker: PhantomData<(&'g (), *const T)>,
}
impl<T: ?Sized + Pointable> Clone for Shared<'_, T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized + Pointable> Copy for Shared<'_, T> {}
impl<T: ?Sized + Pointable> Pointer<T> for Shared<'_, T> {
#[inline]
fn into_usize(self) -> usize {
self.data
}
#[inline]
unsafe fn from_usize(data: usize) -> Self {
Shared {
data,
_marker: PhantomData,
}
}
}
impl<'g, T> Shared<'g, T> {
pub fn as_raw(&self) -> *const T {
let (raw, _) = decompose_tag::<T>(self.data);
raw as *const _
}
}
impl<'g, T: ?Sized + Pointable> Shared<'g, T> {
pub fn null() -> Shared<'g, T> {
Shared {
data: 0,
_marker: PhantomData,
}
}
pub fn is_null(&self) -> bool {
let (raw, _) = decompose_tag::<T>(self.data);
raw == 0
}
pub unsafe fn deref(&self) -> &'g T {
let (raw, _) = decompose_tag::<T>(self.data);
T::deref(raw)
}
pub unsafe fn deref_mut(&mut self) -> &'g mut T {
let (raw, _) = decompose_tag::<T>(self.data);
T::deref_mut(raw)
}
pub unsafe fn as_ref(&self) -> Option<&'g T> {
let (raw, _) = decompose_tag::<T>(self.data);
if raw == 0 {
None
} else {
Some(T::deref(raw))
}
}
pub unsafe fn into_owned(self) -> Owned<T> {
debug_assert!(!self.is_null(), "converting a null `Shared` into `Owned`");
Owned::from_usize(self.data)
}
pub unsafe fn try_into_owned(self) -> Option<Owned<T>> {
if self.is_null() {
None
} else {
Some(Owned::from_usize(self.data))
}
}
pub fn tag(&self) -> usize {
let (_, tag) = decompose_tag::<T>(self.data);
tag
}
pub fn with_tag(&self, tag: usize) -> Shared<'g, T> {
unsafe { Self::from_usize(compose_tag::<T>(self.data, tag)) }
}
}
impl<T> From<*const T> for Shared<'_, T> {
fn from(raw: *const T) -> Self {
let raw = raw as usize;
ensure_aligned::<T>(raw);
unsafe { Self::from_usize(raw) }
}
}
impl<'g, T: ?Sized + Pointable> PartialEq<Shared<'g, T>> for Shared<'g, T> {
fn eq(&self, other: &Self) -> bool {
self.data == other.data
}
}
impl<T: ?Sized + Pointable> Eq for Shared<'_, T> {}
impl<'g, T: ?Sized + Pointable> PartialOrd<Shared<'g, T>> for Shared<'g, T> {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
self.data.partial_cmp(&other.data)
}
}
impl<T: ?Sized + Pointable> Ord for Shared<'_, T> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.data.cmp(&other.data)
}
}
impl<T: ?Sized + Pointable> fmt::Debug for Shared<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (raw, tag) = decompose_tag::<T>(self.data);
f.debug_struct("Shared")
.field("raw", &raw)
.field("tag", &tag)
.finish()
}
}
impl<T: ?Sized + Pointable> fmt::Pointer for Shared<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&(unsafe { self.deref() as *const _ }), f)
}
}
impl<T: ?Sized + Pointable> Default for Shared<'_, T> {
fn default() -> Self {
Shared::null()
}
}
#[cfg(all(test, not(crossbeam_loom)))]
mod tests {
use super::{Owned, Shared};
use std::mem::MaybeUninit;
#[test]
fn valid_tag_i8() {
Shared::<i8>::null().with_tag(0);
}
#[test]
fn valid_tag_i64() {
Shared::<i64>::null().with_tag(7);
}
#[test]
fn const_atomic_null() {
use super::Atomic;
static _U: Atomic<u8> = Atomic::<u8>::null();
}
#[test]
fn array_init() {
let owned = Owned::<[MaybeUninit<usize>]>::init(10);
let arr: &[MaybeUninit<usize>] = &owned;
assert_eq!(arr.len(), 10);
}
}