use std::marker::PhantomData;
use std::rc::Rc;
use crate::{TaskHandle, TaskPtr};
#[derive(Default)]
pub(crate) struct TaskList {
head: Option<TaskPtr>,
tail: Option<TaskPtr>,
_owner_thread: PhantomData<Rc<()>>,
}
impl TaskList {
pub(crate) fn insert(&mut self, task_handle: TaskHandle) -> TaskPtr {
let pointer = task_handle.as_ptr();
let links = unsafe { pointer.as_task() }.get_ref().links();
assert!(!links.linked.replace(true), "a task may be linked only once");
links.previous.set(self.tail);
links.next.set(None);
if let Some(tail) = self.tail {
unsafe { tail.as_task() }.get_ref().links().next.set(Some(pointer));
} else {
self.head = Some(pointer);
}
self.tail = Some(pointer);
let owned = task_handle.into_raw();
debug_assert_eq!(owned, pointer);
pointer
}
pub(crate) unsafe fn remove(&mut self, pointer: TaskPtr) -> Option<TaskHandle> {
let links = unsafe { pointer.as_task() }.get_ref().links();
if !links.linked.replace(false) {
return None;
}
let previous = links.previous.replace(None);
let next = links.next.replace(None);
if let Some(previous) = previous {
unsafe { previous.as_task() }.get_ref().links().next.set(next);
} else {
self.head = next;
}
if let Some(next) = next {
unsafe { next.as_task() }.get_ref().links().previous.set(previous);
} else {
self.tail = previous;
}
Some(unsafe { pointer.into_handle() })
}
pub(crate) fn pop_front(&mut self) -> Option<TaskHandle> {
self.head.and_then(|head| unsafe { self.remove(head) })
}
pub(crate) fn is_empty(&self) -> bool {
self.head.is_none()
}
}
impl std::fmt::Debug for TaskList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TaskList").finish_non_exhaustive()
}
}
impl Drop for TaskList {
fn drop(&mut self) {
while self.pop_front().is_some() {}
}
}
#[cfg(test)]
mod tests {
use plurality::MultiPool;
use static_assertions::assert_not_impl_any;
use super::*;
#[test]
fn task_list_is_thread_affine() {
assert_not_impl_any!(TaskList: Send, Sync);
}
#[test]
fn insertion_removal_and_drop_preserve_ownership() {
let pool = MultiPool::new();
let first = crate::allocate_test_task_in(&pool);
let first_pointer = first.as_ptr();
let second = crate::allocate_test_task_in(&pool);
let second_pointer = second.as_ptr();
let mut list = TaskList::default();
assert_eq!(list.insert(first), first_pointer);
assert_eq!(list.insert(second), second_pointer);
assert_eq!(format!("{list:?}"), "TaskList { .. }");
drop(unsafe { list.remove(first_pointer) }.unwrap());
assert_eq!(list.pop_front().unwrap().as_ptr(), second_pointer);
assert!(list.is_empty());
_ = list.insert(crate::allocate_test_task_in(&pool));
drop(list);
assert!(pool.is_empty());
}
}