fix Value<T> type

This commit is contained in:
Janis 2025-02-20 17:08:31 +01:00
parent a4992e7dc7
commit 36c32c4dd6
2 changed files with 208 additions and 76 deletions

View file

@ -207,47 +207,52 @@ mod job {
#[derive(Debug, Default, Clone, Copy)]
struct Size2([usize; 2]);
struct Value<T>(pub MaybeUninit<Box<MaybeUninit<T>>>);
pub struct Value<T>(pub MaybeUninit<Box<MaybeUninit<T>>>);
impl<T> Value<T> {
/// must only be called once. takes a reference so this can be called in
/// drop()
unsafe fn get_unchecked(&self, inline: bool) -> T {
if inline {
unsafe { mem::transmute_copy(&self.0) }
unsafe { mem::transmute_copy::<MaybeUninit<Box<MaybeUninit<T>>>, T>(&self.0) }
} else {
unsafe { (*self.0.assume_init_read()).assume_init() }
unsafe {
let inner = *self.0.assume_init_read();
inner.assume_init()
}
}
}
fn get(self) -> T {
pub fn get(self) -> T {
let this = ManuallyDrop::new(self);
let inline = Self::is_inline();
// SAFETY: inline is correctly calculated and this function
// consumes `self`
unsafe { self.get_unchecked(inline) }
unsafe { this.get_unchecked(inline) }
}
fn is_inline() -> bool {
pub fn is_inline() -> bool {
// 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
// boxed type is an integer multiple of the alignment of T
mem::size_of::<T>() > mem::size_of::<Box<MaybeUninit<T>>>()
|| mem::align_of::<Box<MaybeUninit<T>>>() % mem::align_of::<T>() != 0
mem::size_of::<T>() < mem::size_of::<Box<MaybeUninit<T>>>()
&& mem::align_of::<Box<MaybeUninit<T>>>() % mem::align_of::<T>() == 0
}
fn new(value: T) -> Self {
pub fn new(value: T) -> Self {
let inline = Self::is_inline();
// SAFETY: we know the box is allocated if state was `Pending`.
if inline {
Self(MaybeUninit::new(Box::new(MaybeUninit::new(value))))
} else {
let mut this = Self(MaybeUninit::uninit());
unsafe {
*mem::transmute::<_, &mut T>(&mut this.0) = value;
*mem::transmute::<&mut MaybeUninit<Box<MaybeUninit<T>>>, &mut T>(&mut this.0) =
value;
}
this
} else {
Self(MaybeUninit::new(Box::new(MaybeUninit::new(value))))
}
}
}
@ -298,15 +303,13 @@ mod job {
// head and tail point at themselves
unsafe {
(&mut *head.err_or_link.get()).link.next =
NonNull::new_unchecked((&raw const *head).cast_mut());
(&mut *head.err_or_link.get()).link.next = None;
(&mut *head.err_or_link.get()).link.prev =
NonNull::new_unchecked((&raw const *tail).cast_mut());
Some(NonNull::new_unchecked((&raw const *tail).cast_mut()));
(&mut *tail.err_or_link.get()).link.next =
NonNull::new_unchecked((&raw const *head).cast_mut());
(&mut *tail.err_or_link.get()).link.prev =
NonNull::new_unchecked((&raw const *tail).cast_mut());
Some(NonNull::new_unchecked((&raw const *head).cast_mut()));
(&mut *tail.err_or_link.get()).link.prev = None;
}
Self { head, tail }
@ -329,52 +332,50 @@ mod job {
pub unsafe fn push_front<T>(&mut self, elem: Pin<&Job<T>>) {
let head_link = unsafe { self.head.link_mut() };
let prev = head_link.prev;
// SAFETY: head will always have a previous element.
let prev = head_link.prev.unwrap();
let prev_link = unsafe { prev.as_ref().link_mut() };
let elem_ptr = unsafe { NonNull::new_unchecked(&*elem as *const Job<T> as *mut Job) };
head_link.prev = elem_ptr;
prev_link.next = elem_ptr;
head_link.prev = Some(elem_ptr);
prev_link.next = Some(elem_ptr);
let elem_link = unsafe { elem.link_mut() };
elem_link.prev = prev;
elem_link.next = self.head();
elem_link.prev = Some(prev);
elem_link.next = Some(self.head());
}
/// elem must be valid until it is popped.
pub unsafe fn push_back<T>(&mut self, elem: Pin<&Job<T>>) {
let tail_link = unsafe { self.tail.link_mut() };
let next = tail_link.next;
// SAFETY: tail will always have a previous element.
let next = tail_link.next.unwrap();
let next_link = unsafe { next.as_ref().link_mut() };
let elem_ptr = unsafe { NonNull::new_unchecked(&*elem as *const Job<T> as *mut Job) };
tail_link.next = elem_ptr;
next_link.prev = elem_ptr;
tail_link.next = Some(elem_ptr);
next_link.prev = Some(elem_ptr);
let elem_link = unsafe { elem.link_mut() };
elem_link.next = next;
elem_link.prev = self.tail();
elem_link.next = Some(next);
elem_link.prev = Some(self.tail());
}
pub fn pop_front(&mut self) -> Option<NonNull<Job>> {
let head_link = unsafe { self.head.link_mut() };
// SAFETY: head will always have a previous element.
let elem = head_link.prev;
let elem = head_link.prev.unwrap();
let elem_link = unsafe { elem.as_ref().link_mut() };
let prev = elem_link.prev.as_ptr();
head_link.prev = unsafe { NonNull::new_unchecked(prev) };
let prev = elem_link.prev?.as_ptr();
head_link.prev = unsafe { Some(NonNull::new_unchecked(prev)) };
let prev_link = unsafe { (&*prev).link_mut() };
prev_link.next = self.head();
prev_link.next = Some(self.head());
if elem == self.tail() {
None
} else {
Some(elem)
}
Some(elem)
}
pub fn pop_back(&mut self) -> Option<NonNull<Job>> {
@ -382,20 +383,16 @@ mod job {
let tail_link = unsafe { self.tail.link_mut() };
// SAFETY: head will always have a previous element.
let elem = tail_link.next;
let elem = tail_link.next.unwrap();
let elem_link = unsafe { elem.as_ref().link_mut() };
let next = elem_link.next.as_ptr();
tail_link.next = unsafe { NonNull::new_unchecked(next) };
let next = elem_link.next?.as_ptr();
tail_link.next = unsafe { Some(NonNull::new_unchecked(next)) };
let next_link = unsafe { (&*next).link_mut() };
next_link.prev = self.tail();
next_link.prev = Some(self.tail());
if elem == self.head() {
None
} else {
Some(elem)
}
Some(elem)
}
}
@ -407,8 +404,8 @@ mod job {
#[derive(Debug, PartialEq, Eq)]
struct Link<T> {
prev: NonNull<T>,
next: NonNull<T>,
prev: Option<NonNull<T>>,
next: Option<NonNull<T>>,
}
impl<T> Clone for Link<T> {
@ -500,8 +497,8 @@ mod job {
val_or_this: UnsafeCell::new(ValueOrThis { this }),
err_or_link: UnsafeCell::new(LinkOrError {
link: Link {
prev: NonNull::dangling(),
next: NonNull::dangling(),
prev: None,
next: None,
},
}),
phantom: PhantomPinned,
@ -509,14 +506,17 @@ mod job {
}
pub fn empty() -> Job<T> {
Self {
harness_and_state: TaggedAtomicPtr::new(ptr::dangling_mut(), 0),
harness_and_state: TaggedAtomicPtr::new(
ptr::dangling_mut(),
JobState::Empty as usize,
),
val_or_this: UnsafeCell::new(ValueOrThis {
this: NonNull::dangling(),
}),
err_or_link: UnsafeCell::new(LinkOrError {
link: Link {
prev: NonNull::dangling(),
next: NonNull::dangling(),
prev: None,
next: None,
},
}),
phantom: PhantomPinned,
@ -528,17 +528,19 @@ mod job {
}
/// assumes job is in joblist
pub unsafe fn unlink(&self) {
pub unsafe fn unlink(&self) -> Option<()> {
unsafe {
let link = self.link_mut();
link.prev.as_ref().link_mut().next = link.next;
link.next.as_ref().link_mut().prev = link.prev;
link.prev?.as_ref().link_mut().next = link.next;
link.next?.as_ref().link_mut().prev = link.prev;
}
Some(())
}
pub fn state(&self) -> u8 {
self.harness_and_state.tag(Ordering::Relaxed) as u8
}
pub fn wait(&self) -> std::thread::Result<T> {
let mut spin = SpinWait::new();
loop {
@ -549,7 +551,8 @@ mod job {
Ordering::Relaxed,
) {
// if still pending, sleep until completed
Ok(_) => {
Ok(state) => {
assert_eq!(state, JobState::Pending as usize);
unsafe {
*(&mut *self.err_or_link.get()).waker = Some(std::thread::current());
}
@ -568,6 +571,8 @@ mod job {
continue;
}
Err(state) => {
assert_ne!(state, JobState::Pending as usize);
if state == JobState::Finished as usize {
let err = unsafe { (&mut *self.err_or_link.get()).error.take() };
@ -606,14 +611,17 @@ mod job {
Ordering::Acquire,
Ordering::Relaxed,
) {
Ok(_) => {
Ok(state) => {
assert_eq!(state, JobState::Empty as usize);
// set waker to None
unsafe {
(&mut *self.err_or_link.get()).waker = ManuallyDrop::new(None);
}
return;
}
Err(_) => {
Err(state) => {
assert_ne!(state, JobState::Empty as usize);
eprintln!("######## what the sigma?");
spin.spin();
}
@ -624,8 +632,10 @@ mod job {
pub fn execute(&self) {
// SAFETY: self is non-null
unsafe {
let harness: unsafe fn(*const (), *const Self) =
mem::transmute(self.harness_and_state.ptr(Ordering::Relaxed).as_ptr());
let (ptr, state) = self.harness_and_state.ptr_and_tag(Ordering::Relaxed);
assert_eq!(state, JobState::Pending as usize);
let harness: unsafe fn(*const (), *const Self) = mem::transmute(ptr.as_ptr());
let this = (*self.val_or_this.get()).this;
eprintln!("{harness:?}({this:?}, {:?})", self as *const Self);
@ -643,14 +653,17 @@ mod job {
Ordering::Acquire,
Ordering::Relaxed,
) {
Ok(_) => {
Ok(state) => {
assert_eq!(state, JobState::Pending as usize);
break;
}
Err(tag) => {
eprintln!(
"complete(): spin waiting for lock to complete: ({:?})",
JobState::from_u8(tag as u8).unwrap()
);
Err(state) => {
assert_ne!(state, JobState::Pending as usize);
// eprintln!(
// "complete(): spin waiting for lock to complete with {:?}: ({:?})",
// result.as_ref().map(|_| ()),
// JobState::from_u8(state as u8).unwrap()
// );
spin.spin();
}
}
@ -953,8 +966,8 @@ impl Scope {
if let Some(job) = self.pop_back() {
unsafe {
job.as_ref().set_pending();
eprintln!("sharing {job:?} {:#?}", job.as_ref());
}
eprintln!("sharing {job:?}");
guard.jobs.insert(self.index, job);
self.context.shared_job.notify_one();
}
@ -979,14 +992,22 @@ impl Scope {
// }
while job.state() != JobState::Finished as u8 {
let Some(job) = self.pop_front().or_else(|| {
self.context
.shared
.lock()
.jobs
.pop_first()
.map(|(_, job)| job)
}) else {
let Some(job) =
// self
// .pop_front()
// .inspect(|job| unsafe {
// job.as_ref().set_pending();
// })
None
.or_else(|| {
self.context
.shared
.lock()
.jobs
.pop_first()
.map(|(_, job)| job)
})
else {
// no more jobs, sleep instead
break;
};

View file

@ -170,3 +170,114 @@ fn tagged_ptr_exchange_failure() {
assert_eq!(ptr.tag(Ordering::Relaxed), 1);
assert_eq!(ptr.ptr(Ordering::Relaxed).as_ptr(), boxed);
}
#[test]
fn value_inline() {
let val = Value::new(3usize);
let inner = val.get();
assert_eq!(inner, 3usize);
}
#[test]
fn value_inline_struct() {
#[derive(Default, PartialEq, Debug)]
struct Small {
c: f32,
}
let val = Value::new(Small { c: 3.2 });
let inner = val.get();
assert_eq!(inner.c, 3.2);
}
#[test]
fn value_boxed() {
#[derive(Default, PartialEq, Debug)]
struct Big {
a: usize,
b: f32,
c: u32,
}
let val = Value::new(Big {
a: 42,
b: 2.25,
c: 7,
});
let inner = val.get();
assert_eq!(
inner,
Big {
a: 42,
b: 2.25,
c: 7
}
);
}
#[test]
fn value_inline_drop() {
#[derive(PartialEq, Debug)]
#[repr(transparent)]
struct Small<'a> {
inner: &'a mut usize,
}
impl Drop for Small<'_> {
fn drop(&mut self) {
*self.inner += 1;
}
}
let mut dropped = 0;
{
let inner = {
let val = Value::new(Small {
inner: &mut dropped,
});
val.get()
};
assert_eq!(*inner.inner, 0);
}
assert_eq!(dropped, 1);
{
let _val = Value::new(Small {
inner: &mut dropped,
});
}
assert_eq!(dropped, 2);
}
#[test]
fn value_boxed_drop() {
#[derive(PartialEq, Debug)]
struct Big<'a> {
inner: &'a mut usize,
pad: [usize; 3],
}
impl Drop for Big<'_> {
fn drop(&mut self) {
*self.inner += 1;
}
}
let mut dropped = 0;
{
let inner = {
let val = Value::new(Big {
inner: &mut dropped,
pad: [0; 3],
});
val.get()
};
assert_eq!(*inner.inner, 0);
}
assert_eq!(dropped, 1);
{
let _val = Value::new(Big {
inner: &mut dropped,
pad: [0; 3],
});
}
assert_eq!(dropped, 2);
}