use std::hash::{Hash, Hasher};
use std::pin::Pin;
use std::ptr::{self};
use infinity_pool::{RawBlindPooled, define_pooled_dyn_cast};
use crate::TypeErasedTask;
define_pooled_dyn_cast!(TypeErasedTask);
#[derive(Copy, Clone, Debug)]
pub(crate) struct TaskRef {
inner: RawBlindPooled<dyn TypeErasedTask>,
}
impl TaskRef {
pub(crate) fn new(inner: RawBlindPooled<dyn TypeErasedTask>) -> Self {
Self { inner }
}
#[cfg(test)]
pub(crate) unsafe fn fake() -> Self {
use infinity_pool::RawBlindPool;
use crate::MockTypeErasedTask;
let mut fake_pool = RawBlindPool::new();
let pool_ticket = fake_pool.insert(MockTypeErasedTask::new());
let pool_ticket = unsafe { pool_ticket.cast_type_erased_task() };
Self {
inner: pool_ticket.into_shared(),
}
}
#[must_use]
pub(crate) unsafe fn as_task(&self) -> Pin<&dyn TypeErasedTask> {
let task = unsafe { self.inner.ptr().as_ref() };
unsafe { Pin::new_unchecked(task) }
}
#[must_use]
pub(crate) unsafe fn into_pool_ticket(self) -> RawBlindPooled<dyn TypeErasedTask> {
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.ptr().as_ptr(), other.inner.ptr().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.ptr().hash(state);
}
}
#[cfg(test)]
mod tests {
use std::hash::DefaultHasher;
use infinity_pool::RawBlindPool;
use super::*;
use crate::MockTypeErasedTask;
#[test]
fn same_task_refs_eq() {
let mut pool = RawBlindPool::new();
let ticket1 = pool.insert(MockTypeErasedTask::new());
let ticket1 = unsafe { ticket1.cast_type_erased_task() };
let task_ref1 = TaskRef::new(ticket1.into_shared());
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);
}
#[test]
fn different_task_refs_not_eq() {
let mut pool = RawBlindPool::new();
let ticket1 = pool.insert(MockTypeErasedTask::new());
let ticket2 = pool.insert(MockTypeErasedTask::new());
let ticket1 = unsafe { ticket1.cast_type_erased_task() };
let ticket2 = unsafe { ticket2.cast_type_erased_task() };
let task_ref1 = TaskRef::new(ticket1.into_shared());
let task_ref2 = TaskRef::new(ticket2.into_shared());
assert_ne!(task_ref1, task_ref2);
}
}