Compare commits
2 commits
56faa52d0d
...
42443518e1
Author | SHA1 | Date | |
---|---|---|---|
|
42443518e1 | ||
|
f4623d1205 |
249
src/ptr.rs
249
src/ptr.rs
|
@ -1,10 +1,12 @@
|
||||||
use core::{
|
use core::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
fmt, hash,
|
fmt, hash,
|
||||||
marker::Send,
|
marker::{PhantomData, Send},
|
||||||
|
mem,
|
||||||
num::NonZero,
|
num::NonZero,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
ptr::NonNull,
|
ptr::NonNull,
|
||||||
|
sync::atomic::{self, AtomicPtr, AtomicUsize},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
@ -156,3 +158,248 @@ impl<T> SendNonNull<T> {
|
||||||
unsafe { Self(NonNull::new_unchecked(ptr)) }
|
unsafe { Self(NonNull::new_unchecked(ptr)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A tagged atomic pointer that can store a pointer and a tag `BITS` wide in the same space
|
||||||
|
/// as the pointer.
|
||||||
|
/// The pointer must be aligned to `BITS` bits, i.e. `align_of::<T>() >= 2^BITS`.
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TaggedAtomicPtr<T, const BITS: u8> {
|
||||||
|
ptr: AtomicPtr<()>,
|
||||||
|
_pd: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, const BITS: u8> TaggedAtomicPtr<T, BITS> {
|
||||||
|
const fn mask() -> usize {
|
||||||
|
!(!0usize << BITS)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(ptr: *mut T, tag: usize) -> TaggedAtomicPtr<T, BITS> {
|
||||||
|
debug_assert!(mem::align_of::<T>().ilog2() as u8 >= BITS);
|
||||||
|
let mask = Self::mask();
|
||||||
|
Self {
|
||||||
|
ptr: AtomicPtr::new(ptr.with_addr((ptr.addr() & !mask) | (tag & mask)).cast()),
|
||||||
|
_pd: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ptr(&self, order: atomic::Ordering) -> NonNull<T> {
|
||||||
|
unsafe {
|
||||||
|
NonNull::new_unchecked(
|
||||||
|
self.ptr
|
||||||
|
.load(order)
|
||||||
|
.map_addr(|addr| addr & !Self::mask())
|
||||||
|
.cast(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tag(&self, order: atomic::Ordering) -> usize {
|
||||||
|
self.ptr.load(order).addr() & Self::mask()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fetch_or_tag(&self, tag: usize, order: atomic::Ordering) -> usize {
|
||||||
|
let mask = Self::mask();
|
||||||
|
|
||||||
|
// TODO: switch to fetch_or when stable
|
||||||
|
// let old_ptr = self.ptr.fetch_or(tag & mask, order);
|
||||||
|
|
||||||
|
let ptr = unsafe { AtomicUsize::from_ptr(self.ptr.as_ptr() as *mut usize) };
|
||||||
|
let old_ptr = ptr.fetch_or(tag & mask, order);
|
||||||
|
|
||||||
|
old_ptr & mask
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns the tag and clears it
|
||||||
|
pub fn take_tag(&self, order: atomic::Ordering) -> usize {
|
||||||
|
let mask = Self::mask();
|
||||||
|
|
||||||
|
// TODO: switch to fetch_and when stable
|
||||||
|
// let old_ptr = self.ptr.fetch_and(!mask, order);
|
||||||
|
|
||||||
|
let ptr = unsafe { AtomicUsize::from_ptr(self.ptr.as_ptr() as *mut usize) };
|
||||||
|
let old_ptr = ptr.fetch_and(!mask, order);
|
||||||
|
|
||||||
|
old_ptr & mask
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns tag
|
||||||
|
#[inline(always)]
|
||||||
|
fn compare_exchange_tag_inner(
|
||||||
|
&self,
|
||||||
|
old: usize,
|
||||||
|
new: usize,
|
||||||
|
success: atomic::Ordering,
|
||||||
|
failure: atomic::Ordering,
|
||||||
|
cmpxchg: fn(
|
||||||
|
&AtomicPtr<()>,
|
||||||
|
*mut (),
|
||||||
|
*mut (),
|
||||||
|
atomic::Ordering,
|
||||||
|
atomic::Ordering,
|
||||||
|
) -> Result<*mut (), *mut ()>,
|
||||||
|
) -> Result<usize, usize> {
|
||||||
|
let mask = Self::mask();
|
||||||
|
let old_ptr = self.ptr.load(failure);
|
||||||
|
|
||||||
|
let old = old_ptr.map_addr(|addr| (addr & !mask) | (old & mask));
|
||||||
|
let new = old_ptr.map_addr(|addr| (addr & !mask) | (new & mask));
|
||||||
|
|
||||||
|
let result = cmpxchg(&self.ptr, old, new, success, failure);
|
||||||
|
|
||||||
|
result
|
||||||
|
.map(|ptr| ptr.addr() & mask)
|
||||||
|
.map_err(|ptr| ptr.addr() & mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns tag
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn compare_exchange_tag(
|
||||||
|
&self,
|
||||||
|
old: usize,
|
||||||
|
new: usize,
|
||||||
|
success: atomic::Ordering,
|
||||||
|
failure: atomic::Ordering,
|
||||||
|
) -> Result<usize, usize> {
|
||||||
|
self.compare_exchange_tag_inner(
|
||||||
|
old,
|
||||||
|
new,
|
||||||
|
success,
|
||||||
|
failure,
|
||||||
|
AtomicPtr::<()>::compare_exchange,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns tag
|
||||||
|
pub fn compare_exchange_weak_tag(
|
||||||
|
&self,
|
||||||
|
old: usize,
|
||||||
|
new: usize,
|
||||||
|
success: atomic::Ordering,
|
||||||
|
failure: atomic::Ordering,
|
||||||
|
) -> Result<usize, usize> {
|
||||||
|
self.compare_exchange_tag_inner(
|
||||||
|
old,
|
||||||
|
new,
|
||||||
|
success,
|
||||||
|
failure,
|
||||||
|
AtomicPtr::<()>::compare_exchange_weak,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn set_ptr(&self, ptr: *mut T, success: atomic::Ordering, failure: atomic::Ordering) {
|
||||||
|
let mask = Self::mask();
|
||||||
|
let ptr = ptr.cast::<()>();
|
||||||
|
loop {
|
||||||
|
let old = self.ptr.load(failure);
|
||||||
|
let new = ptr.map_addr(|addr| (addr & !mask) | (old.addr() & mask));
|
||||||
|
if self
|
||||||
|
.ptr
|
||||||
|
.compare_exchange_weak(old, new, success, failure)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_tag(&self, tag: usize, success: atomic::Ordering, failure: atomic::Ordering) {
|
||||||
|
let mask = Self::mask();
|
||||||
|
loop {
|
||||||
|
let ptr = self.ptr.load(failure);
|
||||||
|
let new = ptr.map_addr(|addr| (addr & !mask) | (tag & mask));
|
||||||
|
|
||||||
|
if self
|
||||||
|
.ptr
|
||||||
|
.compare_exchange_weak(ptr, new, success, failure)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ptr_and_tag(&self, order: atomic::Ordering) -> (NonNull<T>, usize) {
|
||||||
|
let mask = Self::mask();
|
||||||
|
let ptr = self.ptr.load(order);
|
||||||
|
let tag = ptr.addr() & mask;
|
||||||
|
let ptr = ptr.map_addr(|addr| addr & !mask);
|
||||||
|
let ptr = unsafe { NonNull::new_unchecked(ptr.cast()) };
|
||||||
|
(ptr, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use core::sync::atomic::Ordering;
|
||||||
|
use std::boxed::Box;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tagged_ptr_zero_tag() {
|
||||||
|
let ptr = Box::into_raw(Box::new(42u32));
|
||||||
|
let tagged_ptr = TaggedAtomicPtr::<u32, 2>::new(ptr, 0);
|
||||||
|
assert_eq!(tagged_ptr.tag(Ordering::Relaxed), 0);
|
||||||
|
assert_eq!(tagged_ptr.ptr(Ordering::Relaxed).as_ptr(), ptr);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
_ = Box::from_raw(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tagged_ptr_take_tag() {
|
||||||
|
let ptr = Box::into_raw(Box::new(42u32));
|
||||||
|
let tagged_ptr = TaggedAtomicPtr::<u32, 2>::new(ptr, 0b11);
|
||||||
|
assert_eq!(tagged_ptr.tag(Ordering::Relaxed), 0b11);
|
||||||
|
assert_eq!(tagged_ptr.ptr(Ordering::Relaxed).as_ptr(), ptr);
|
||||||
|
|
||||||
|
assert_eq!(tagged_ptr.take_tag(Ordering::Relaxed), 0b11);
|
||||||
|
assert_eq!(tagged_ptr.tag(Ordering::Relaxed), 0);
|
||||||
|
assert_eq!(tagged_ptr.ptr(Ordering::Relaxed).as_ptr(), ptr);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
_ = Box::from_raw(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tagged_ptr_fetch_or_tag() {
|
||||||
|
let ptr = Box::into_raw(Box::new(42u32));
|
||||||
|
let tagged_ptr = TaggedAtomicPtr::<u32, 2>::new(ptr, 0b11);
|
||||||
|
assert_eq!(tagged_ptr.tag(Ordering::Relaxed), 0b11);
|
||||||
|
assert_eq!(tagged_ptr.ptr(Ordering::Relaxed).as_ptr(), ptr);
|
||||||
|
|
||||||
|
assert_eq!(tagged_ptr.fetch_or_tag(0b10, Ordering::Relaxed), 0b11);
|
||||||
|
assert_eq!(tagged_ptr.tag(Ordering::Relaxed), 0b11 | 0b10);
|
||||||
|
assert_eq!(tagged_ptr.ptr(Ordering::Relaxed).as_ptr(), ptr);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
_ = Box::from_raw(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tagged_ptr_exchange() {
|
||||||
|
let ptr = Box::into_raw(Box::new(42u32));
|
||||||
|
let tagged_ptr = TaggedAtomicPtr::<u32, 2>::new(ptr, 0b11);
|
||||||
|
assert_eq!(tagged_ptr.tag(Ordering::Relaxed), 0b11);
|
||||||
|
assert_eq!(tagged_ptr.ptr(Ordering::Relaxed).as_ptr(), ptr);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
tagged_ptr
|
||||||
|
.compare_exchange_tag(0b11, 0b10, Ordering::Relaxed, Ordering::Relaxed)
|
||||||
|
.unwrap(),
|
||||||
|
0b11
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(tagged_ptr.tag(Ordering::Relaxed), 0b10);
|
||||||
|
assert_eq!(tagged_ptr.ptr(Ordering::Relaxed).as_ptr(), ptr);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
_ = Box::from_raw(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -137,7 +137,8 @@ impl<T> SmallBox<T> {
|
||||||
// the value can be stored inline iff the size of T is equal or
|
// the value can be stored inline iff the size of T is equal or
|
||||||
// smaller than the size of the boxed type and the alignment of the
|
// smaller than the size of the boxed type and the alignment of the
|
||||||
// boxed type is an integer multiple of the alignment of T
|
// boxed type is an integer multiple of the alignment of T
|
||||||
crate::can_transmute::<Box<MaybeUninit<T>>, T>()
|
(size_of::<Box<MaybeUninit<T>>>() >= size_of::<MaybeUninit<T>>())
|
||||||
|
& (align_of::<Box<MaybeUninit<T>>>() >= align_of::<MaybeUninit<T>>())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(value: T) -> Self {
|
pub fn new(value: T) -> Self {
|
||||||
|
|
|
@ -44,5 +44,5 @@ pub const fn can_transmute<A, B>() -> bool {
|
||||||
use core::mem::{align_of, size_of};
|
use core::mem::{align_of, size_of};
|
||||||
// We can transmute `A` to `B` iff `A` and `B` have the same size and the
|
// We can transmute `A` to `B` iff `A` and `B` have the same size and the
|
||||||
// alignment of `A` is greater than or equal to the alignment of `B`.
|
// alignment of `A` is greater than or equal to the alignment of `B`.
|
||||||
size_of::<A>() <= size_of::<B>() && align_of::<A>() >= align_of::<B>()
|
(size_of::<A>() == size_of::<B>()) & (align_of::<A>() >= align_of::<B>())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue