246 lines
7.9 KiB
Rust
246 lines
7.9 KiB
Rust
//! Doubly non-Arc linked list.
|
|
//!
|
|
//! Doubly as each node points to the next and previous node.
|
|
|
|
use std::{marker::PhantomPinned, mem::transmute, ops::Deref, pin::Pin};
|
|
|
|
use parking_lot::RwLock;
|
|
|
|
use crate::double::node::{BackNodeWriteLock, Node, NodeBackPtr};
|
|
|
|
pub mod node;
|
|
|
|
// # Rules to prevent deadlocks
|
|
//
|
|
// Left locking must be `try_` and if it fails at any point, the way rightwards must be cleared in
|
|
// case the task holding the left lock is moving rightwards.
|
|
// Rightwards locking can be blocking.
|
|
|
|
pub struct NodeHeadInner<'ll, T> {
|
|
start: Option<&'ll node::Node<'ll, T>>,
|
|
}
|
|
|
|
impl<T> Default for NodeHeadInner<'_, T> {
|
|
fn default() -> Self {
|
|
Self { start: None }
|
|
}
|
|
}
|
|
|
|
pub struct NodeHead<'ll, T>(RwLock<NodeHeadInner<'ll, T>>);
|
|
|
|
impl<T> Default for NodeHead<'_, T> {
|
|
#[must_use]
|
|
fn default() -> Self {
|
|
Self(RwLock::new(NodeHeadInner::default()))
|
|
}
|
|
}
|
|
|
|
impl<'ll, T> Deref for NodeHead<'ll, T> {
|
|
type Target = RwLock<NodeHeadInner<'ll, T>>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl<'ll, T> NodeHead<'ll, T> {
|
|
#[must_use]
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// The only context in which it's safe to call this when [`self`] was pinned immediately after
|
|
/// its creation so that it can be guaranteed that the returned reference is valid for all the
|
|
/// [`LinkedList`]'s lifetime.
|
|
///
|
|
/// The issue arises from the [`Drop`] implementation. It takes a `&mut` reference, that means
|
|
/// that all previous immutable references are dropped. But most methods of the linked require
|
|
/// you to promise the borrow of [`self`] is valid for all `'ll` and that's only true if no
|
|
/// destructor runs. This makes [`Drop`] incompatible with the use of methods of the form
|
|
/// `fn(&'myself self)`.
|
|
///
|
|
/// In turn to this, the [`Drop`] implementation must assume it's not the only reference even
|
|
/// thought it's `&mut`. Anyhow it should only be called when the scope of [`self`] is about to
|
|
/// end and the other references would be invalidated after the call to [`Drop`], though in
|
|
/// reality they really have no use before that call but to guarantee the "internal" self
|
|
/// references in the linked list remain valid through the destructor. It's kinda a really edge
|
|
/// case in the language with shared structures that require references with lifetimes as long
|
|
/// as self to guarantee their validity but still have [`Drop`] implementations.
|
|
#[must_use]
|
|
pub unsafe fn get_self_ref(myself: Pin<&Self>) -> &'ll Self {
|
|
unsafe { transmute::<&Self, &'ll Self>(myself.get_ref()) }
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// Must be at the end at the end of its scope, when there's no references into it left.
|
|
pub unsafe fn manual_drop(&'ll self) {
|
|
// SAFETY: this is the drop impl so we can guarantee the reference is valid for the
|
|
// lifetime of the struct itself and external references would be invalidated right after
|
|
// the [`Drop`] of it (this fn). I don't think there's a way to differenciate the lifetimes
|
|
// by a drop implementation so this would be safe as no external references lifetimes would
|
|
// be valid after drop finishes
|
|
while unsafe { self.pop().is_some() } {}
|
|
}
|
|
|
|
pub fn prepend(&'ll self, data: T) {
|
|
let self_lock = self.write();
|
|
let next = self_lock.start;
|
|
let next_lock = next.map(|n| n.write());
|
|
let new_node = Node::new_leaked(data, NodeBackPtr::new_head(self), next);
|
|
// SAFETY: ptrs are surrounding and they've been locked all along
|
|
unsafe { new_node.integrate((BackNodeWriteLock::Head(self_lock), next_lock)) };
|
|
}
|
|
|
|
/// Returns [`None`] if there's no next node.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// There must be no outer references to the first node.
|
|
pub unsafe fn pop(&'ll self) -> Option<()> {
|
|
let self_lock = self.write();
|
|
let pop_node = self_lock.start?;
|
|
let pop_node_lock = pop_node.write();
|
|
let next_node_lock = pop_node_lock.next.map(|n| n.write());
|
|
|
|
// SAFETY: locked all along and consecutive nodes
|
|
unsafe {
|
|
Node::isolate(
|
|
&pop_node_lock,
|
|
(BackNodeWriteLock::Head(self_lock), next_node_lock),
|
|
);
|
|
}
|
|
|
|
drop(pop_node_lock);
|
|
// SAFETY: node has been isolated so no references out
|
|
unsafe { pop_node.wait_free() }
|
|
|
|
Some(())
|
|
}
|
|
|
|
pub fn clone_into_vec(&self) -> Vec<T>
|
|
where
|
|
T: Clone,
|
|
{
|
|
let mut total = Vec::new();
|
|
let mut next_node = self.read().start;
|
|
while let Some(node) = next_node {
|
|
let read = node.read();
|
|
total.push(read.data.clone());
|
|
next_node = read.next;
|
|
}
|
|
total
|
|
}
|
|
}
|
|
|
|
// Can't quite make this work how I want 😭
|
|
|
|
/// Attempt to safe wrap around a [`NodeHead`] to allow a sound API with [`Drop`] implementation.
|
|
///
|
|
/// Please see [`create_ll`] and [`del_ll`]
|
|
pub struct LinkedList<'ll, T> {
|
|
head: NodeHead<'ll, T>,
|
|
_pinned: PhantomPinned,
|
|
}
|
|
|
|
impl<'ll, T> LinkedList<'ll, T> {
|
|
#[must_use]
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// NEVER EVER EVER extend the lifetime of this beyond the end of scope of the linked list
|
|
/// itself, it's all good and safe UNTIL it's dropped.
|
|
///
|
|
/// Allows you to associate the lifetime of this to something else to prevent accidentall
|
|
/// over-extending the lifetime.
|
|
pub unsafe fn extend_for<'scope>(&self, _: &'scope impl std::any::Any) -> &'ll NodeHead<'ll, T>
|
|
where
|
|
'scope: 'll,
|
|
{
|
|
unsafe { transmute(&self.head) }
|
|
}
|
|
}
|
|
|
|
impl<T> Default for LinkedList<'_, T> {
|
|
#[must_use]
|
|
fn default() -> Self {
|
|
Self {
|
|
head: NodeHead::new(),
|
|
_pinned: PhantomPinned,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> Drop for LinkedList<'_, T> {
|
|
fn drop(&mut self) {
|
|
// Extend to 'static so the compiler doesn't cry, we know this covers 'll
|
|
let myself = unsafe { self.extend_for(&()) };
|
|
// And this is `Drop` so there shouldn't be any refs as the end of this function would be
|
|
// where their lifetime ('ll) ends
|
|
unsafe { myself.manual_drop() }
|
|
}
|
|
}
|
|
|
|
// I'm not so sure this is that much bulletproof but behaves sollidly enough
|
|
|
|
/// Unsafe macro that automatically creates a linked list and an extended reference.
|
|
///
|
|
/// Also creates a `scope = ()` variable at the same level as `$val` to mimic its scope and prevent
|
|
/// accidental expansion of the unsafe lifetime beyond the current scope.
|
|
///
|
|
/// For example:
|
|
///
|
|
/// ```
|
|
/// use concurrent_linked_list::double::{create_ll, del_ll, LinkedList};
|
|
///
|
|
/// create_ll!(LinkedList::<String>::new(), ll_val, ll);
|
|
/// ll.prepend("test".to_string());
|
|
/// del_ll!(ll_val, ll);
|
|
/// ```
|
|
///
|
|
/// But trying to use `ll` after the deletion should fail:
|
|
///
|
|
/// ```compile_fail
|
|
/// use concurrent_linked_list::double::{create_ll, del_ll, LinkedList};
|
|
///
|
|
/// create_ll!(LinkedList::<String>::new(), ll_val, ll);
|
|
/// ll.prepend("test".to_string());
|
|
/// del_ll!(ll_val, ll);
|
|
/// ll.prepend("test2".to_string());
|
|
/// ```
|
|
///
|
|
/// Or trying to expand `ll` beyond the scope it was defined in, even if not deleted:
|
|
///
|
|
/// ```compile_fail
|
|
/// use concurrent_linked_list::double::{create_ll, del_ll, LinkedList};
|
|
///
|
|
/// let ll = {
|
|
/// create_ll!(LinkedList::<String>::new(), ll_val, ll);
|
|
/// ll.prepend("test".to_string());
|
|
/// ll
|
|
/// }
|
|
/// ll.prepend("test2".to_string());
|
|
/// ```
|
|
pub macro create_ll($rhs:expr, $val:ident, $ref:ident) {
|
|
let $val = $rhs;
|
|
let scope = ();
|
|
let $ref = unsafe { $val.extend_for(&scope) };
|
|
}
|
|
|
|
/// Macro that attempts to run some higene cleanup on [`create_ll`] to avoid accidental use ot the
|
|
/// reference further too. Other functions could still extend the lifetime beyond acceptable
|
|
/// though.
|
|
pub macro del_ll($val:ident, $ref:ident) {
|
|
#[allow(unused_variables)]
|
|
let $ref = ();
|
|
drop($val);
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests;
|