//! An intrusive double linked list of data. //! //! The data structure supports tracking pinned nodes. Most of the data //! structure's APIs are `unsafe` as they require the caller to ensure the //! specified node is actually contained by the list.
use core::cell::UnsafeCell; use core::fmt; use core::marker::{PhantomData, PhantomPinned}; use core::mem::ManuallyDrop; use core::ptr::{self, NonNull};
/// An intrusive linked list. /// /// Currently, the list is not emptied on drop. It is the caller's /// responsibility to ensure the list is empty before dropping it. pub(crate) struct LinkedList<L, T> { /// Linked list head
head: Option<NonNull<T>>,
/// Linked list tail
tail: Option<NonNull<T>>,
/// Node type marker.
_marker: PhantomData<*const L>,
}
unsafeimpl<L: Link> Send for LinkedList<L, L::Target> where L::Target: Send {} unsafeimpl<L: Link> Sync for LinkedList<L, L::Target> where L::Target: Sync {}
/// Defines how a type is tracked within a linked list. /// /// In order to support storing a single type within multiple lists, accessing /// the list pointers is decoupled from the entry type. /// /// # Safety /// /// Implementations must guarantee that `Target` types are pinned in memory. In /// other words, when a node is inserted, the value will not be moved as long as /// it is stored in the list. pub(crate) unsafetrait Link { /// Handle to the list entry. /// /// This is usually a pointer-ish type. type Handle;
/// Node type. type Target;
/// Convert the handle to a raw pointer without consuming the handle. #[allow(clippy::wrong_self_convention)] fn as_raw(handle: &Self::Handle) -> NonNull<Self::Target>;
/// Convert the raw pointer to a handle unsafefn from_raw(ptr: NonNull<Self::Target>) -> Self::Handle;
/// Return the pointers for a node /// /// # Safety /// /// The resulting pointer should have the same tag in the stacked-borrows /// stack as the argument. In particular, the method may not create an /// intermediate reference in the process of creating the resulting raw /// pointer. unsafefn pointers(target: NonNull<Self::Target>) -> NonNull<Pointers<Self::Target>>;
}
/// Previous / next pointers. pub(crate) struct Pointers<T> {
inner: UnsafeCell<PointersInner<T>>,
} /// We do not want the compiler to put the `noalias` attribute on mutable /// references to this type, so the type has been made `!Unpin` with a /// `PhantomPinned` field. /// /// Additionally, we never access the `prev` or `next` fields directly, as any /// such access would implicitly involve the creation of a reference to the /// field, which we want to avoid since the fields are not `!Unpin`, and would /// hence be given the `noalias` attribute if we were to do such an access. As /// an alternative to accessing the fields directly, the `Pointers` type /// provides getters and setters for the two fields, and those are implemented /// using `ptr`-specific methods which avoids the creation of intermediate /// references. /// /// See this link for more information: /// <https://github.com/rust-lang/rust/pull/82834> struct PointersInner<T> { /// The previous node in the list. null if there is no previous node.
prev: Option<NonNull<T>>,
/// The next node in the list. null if there is no previous node.
next: Option<NonNull<T>>,
impl<L: Link> LinkedList<L, L::Target> { /// Adds an element first in the list. pub(crate) fn push_front(&mutself, val: L::Handle) { // The value should not be dropped, it is being inserted into the list let val = ManuallyDrop::new(val); let ptr = L::as_raw(&val);
assert_ne!(self.head, Some(ptr)); unsafe {
L::pointers(ptr).as_mut().set_next(self.head);
L::pointers(ptr).as_mut().set_prev(None);
/// Removes the first element from a list and returns it, or None if it is /// empty. pub(crate) fn pop_front(&mutself) -> Option<L::Handle> { unsafe { let head = self.head?; self.head = L::pointers(head).as_ref().get_next();
/// Removes the last element from a list and returns it, or None if it is /// empty. pub(crate) fn pop_back(&mutself) -> Option<L::Handle> { unsafe { let last = self.tail?; self.tail = L::pointers(last).as_ref().get_prev();
/// Returns whether the linked list does not contain any node pub(crate) fn is_empty(&self) -> bool { ifself.head.is_some() { returnfalse;
}
assert!(self.tail.is_none()); true
}
/// Removes the specified node from the list /// /// # Safety /// /// The caller **must** ensure that exactly one of the following is true: /// - `node` is currently contained by `self`, /// - `node` is not contained by any list, /// - `node` is currently contained by some other `GuardedLinkedList` **and** /// the caller has an exclusive access to that list. This condition is /// used by the linked list in `sync::Notify`. pub(crate) unsafefn remove(&mutself, node: NonNull<L::Target>) -> Option<L::Handle> { iflet Some(prev) = L::pointers(node).as_ref().get_prev() {
debug_assert_eq!(L::pointers(prev).as_ref().get_next(), Some(node));
L::pointers(prev)
.as_mut()
.set_next(L::pointers(node).as_ref().get_next());
} else { ifself.head != Some(node) { return None;
}
iflet Some(next) = L::pointers(node).as_ref().get_next() {
debug_assert_eq!(L::pointers(next).as_ref().get_prev(), Some(node));
L::pointers(next)
.as_mut()
.set_prev(L::pointers(node).as_ref().get_prev());
} else { // This might be the last item in the list ifself.tail != Some(node) { return None;
}
impl<'a, T, F> Iterator for DrainFilter<'a, T, F> where
T: Link,
F: FnMut(&T::Target) -> bool,
{ type Item = T::Handle;
fn next(&mutself) -> Option<Self::Item> { whilelet Some(curr) = self.curr { // safety: the pointer references data contained by the list self.curr = unsafe { T::pointers(curr).as_ref() }.get_next();
// safety: the value is still owned by the linked list. if (self.filter)(unsafe { &mut *curr.as_ptr() }) { returnunsafe { self.list.remove(curr) };
}
}
/// An intrusive linked list, but instead of keeping pointers to the head /// and tail nodes, it uses a special guard node linked with those nodes. /// It means that the list is circular and every pointer of a node from /// the list is not `None`, including pointers from the guard node. /// /// If a list is empty, then both pointers of the guard node are pointing /// at the guard node itself. pub(crate) struct GuardedLinkedList<L, T> { /// Pointer to the guard node.
guard: NonNull<T>,
/// Node type marker.
_marker: PhantomData<*const L>,
}
impl<L: Link> LinkedList<L, L::Target> { /// Turns a linked list into the guarded version by linking the guard node /// with the head and tail nodes. Like with other nodes, you should guarantee /// that the guard node is pinned in memory. pub(crate) fn into_guarded(self, guard_handle: L::Handle) -> GuardedLinkedList<L, L::Target> { // `guard_handle` is a NonNull pointer, we don't have to care about dropping it. let guard = L::as_raw(&guard_handle);
// The list is not empty, so the tail cannot be `None`. let tail = self.tail.unwrap();
debug_assert!(L::pointers(tail).as_ref().get_next().is_none());
L::pointers(tail).as_mut().set_next(Some(guard));
L::pointers(guard).as_mut().set_prev(Some(tail));
} else { // The list is empty.
L::pointers(guard).as_mut().set_prev(Some(guard));
L::pointers(guard).as_mut().set_next(Some(guard));
}
}
// Compare the tail pointer with the address of the guard node itself. // If the guard points at itself, then there are no other nodes and // the list is considered empty. if tail_ptr != self.guard {
Some(tail_ptr)
} else {
None
}
}
/// Removes the last element from a list and returns it, or None if it is /// empty. pub(crate) fn pop_back(&mutself) -> Option<L::Handle> { unsafe { let last = self.tail()?; let before_last = L::pointers(last).as_ref().get_prev().unwrap();
impl<T> Pointers<T> { /// Create a new set of empty pointers pub(crate) fn new() -> Pointers<T> {
Pointers {
inner: UnsafeCell::new(PointersInner {
prev: None,
next: None,
_pin: PhantomPinned,
}),
}
}
pub(crate) fn get_prev(&self) -> Option<NonNull<T>> { // SAFETY: Field is accessed immutably through a reference. unsafe { ptr::addr_of!((*self.inner.get()).prev).read() }
} pub(crate) fn get_next(&self) -> Option<NonNull<T>> { // SAFETY: Field is accessed immutably through a reference. unsafe { ptr::addr_of!((*self.inner.get()).next).read() }
}
fn set_prev(&mutself, value: Option<NonNull<T>>) { // SAFETY: Field is accessed mutably through a mutable reference. unsafe {
ptr::addr_of_mut!((*self.inner.get()).prev).write(value);
}
} fn set_next(&mutself, value: Option<NonNull<T>>) { // SAFETY: Field is accessed mutably through a mutable reference. unsafe {
ptr::addr_of_mut!((*self.inner.get()).next).write(value);
}
}
}
impl<T> fmt::Debug for Pointers<T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let prev = self.get_prev(); let next = self.get_next();
f.debug_struct("Pointers")
.field("prev", &prev)
.field("next", &next)
.finish()
}
}
#[cfg(any(test, fuzzing))] #[cfg(not(loom))] pub(crate) mod tests { usesuper::*;
#[test] fn remove_by_address() { let a = entry(5); let b = entry(7); let c = entry(31);
unsafe { // Remove first letmut list = LinkedList::new();
push_all(&mut list, &[c.as_ref(), b.as_ref(), a.as_ref()]);
assert!(list.remove(ptr(&a)).is_some());
assert_clean!(a); // `a` should be no longer there and can't be removed twice
assert!(list.remove(ptr(&a)).is_none());
assert!(!list.is_empty());
assert!(list.remove(ptr(&b)).is_some());
assert_clean!(b); // `b` should be no longer there and can't be removed twice
assert!(list.remove(ptr(&b)).is_none());
assert!(!list.is_empty());
assert!(list.remove(ptr(&c)).is_some());
assert_clean!(c); // `b` should be no longer there and can't be removed twice
assert!(list.remove(ptr(&c)).is_none());
assert!(list.is_empty());
}
unsafe { // Remove middle letmut list = LinkedList::new();
/// This is a fuzz test. You run it by entering `cargo fuzz run fuzz_linked_list` in CLI in `/tokio/` module. #[cfg(fuzzing)] pubfn fuzz_linked_list(ops: &[u8]) { enum Op {
Push,
Pop,
Remove(usize),
} use std::collections::VecDeque;
let ops = ops
.iter()
.map(|i| match i % 3u8 { 0 => Op::Push, 1 => Op::Pop, 2 => Op::Remove((i / 3u8) as usize),
_ => unreachable!(),
})
.collect::<Vec<_>>();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.