use std::hash::{Hash, Hasher};
use std::pin::Pin;
use std::ptr::{self, NonNull};
use plurality::{Box as PoolBox, coerce};
use crate::TypeErasedTask;
#[derive(Copy, Clone, Debug)]
pub(crate) struct TaskRef {
inner: NonNull<dyn TypeErasedTask>,
}
impl TaskRef {
pub(crate) fn new<T: TypeErasedTask + 'static>(inner: PoolBox<T>) -> Self {
let inner: PoolBox<dyn TypeErasedTask> = PoolBox::unsize(inner, coerce!(dyn TypeErasedTask));
Self {
inner: PoolBox::into_raw(inner),
}
}
#[cfg(test)]
pub(crate) unsafe fn fake() -> Self {
use crate::MockTypeErasedTask;
let inner = NonNull::<MockTypeErasedTask>::dangling().as_ptr();
let inner: *mut dyn TypeErasedTask = inner;
let inner = unsafe { NonNull::new_unchecked(inner) };
Self { inner }
}
#[must_use]
pub(crate) unsafe fn as_task(&self) -> Pin<&dyn TypeErasedTask> {
let task = unsafe { self.inner.as_ref() };
unsafe { Pin::new_unchecked(task) }
}
pub(crate) unsafe fn release(self) {
drop(unsafe { PoolBox::<dyn TypeErasedTask>::from_raw(self.inner) });
}
}
unsafe impl Send for TaskRef {}
impl PartialEq for TaskRef {
#[cfg_attr(test, mutants::skip)] fn eq(&self, other: &Self) -> bool {
ptr::addr_eq(self.inner.as_ptr(), other.inner.as_ptr())
}
}
impl Eq for TaskRef {}
impl Hash for TaskRef {
#[cfg_attr(test, mutants::skip)] fn hash<H: Hasher>(&self, state: &mut H) {
self.inner.as_ptr().cast::<()>().hash(state);
}
}
#[cfg(test)]
mod tests {
use std::hash::DefaultHasher;
use plurality::MultiPool;
use super::*;
use crate::MockTypeErasedTask;
#[test]
fn same_task_refs_eq() {
let pool = MultiPool::new();
let task_ref1 = TaskRef::new(pool.alloc_box(MockTypeErasedTask::new()));
let task_ref2 = task_ref1;
assert_eq!(task_ref1, task_ref2);
let mut hasher1 = DefaultHasher::new();
task_ref1.hash(&mut hasher1);
let hash1 = hasher1.finish();
let mut hasher2 = DefaultHasher::new();
task_ref2.hash(&mut hasher2);
let hash2 = hasher2.finish();
assert_eq!(hash1, hash2);
unsafe { task_ref1.release() };
}
#[test]
fn different_task_refs_not_eq() {
let pool = MultiPool::new();
let task_ref1 = TaskRef::new(pool.alloc_box(MockTypeErasedTask::new()));
let task_ref2 = TaskRef::new(pool.alloc_box(MockTypeErasedTask::new()));
assert_ne!(task_ref1, task_ref2);
unsafe { task_ref1.release() };
unsafe { task_ref2.release() };
}
}