mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-08 00:00:13 +02:00
sync: refactor intrusive linked list (#2279)
Allow storing the intrusive linked-list pointers in an arbitrary location in the node. This is in preparation for using the linked list in the scheduler. In order to make using the intrusive linked list more flexible, a trait is introduced to abstract mapping an entry to raw pointers and the next / prev pointers. This also pushes more unsafety onto the user.
This commit is contained in:
+52
-20
@@ -2,8 +2,11 @@ use crate::loom::sync::atomic::AtomicU8;
|
|||||||
use crate::loom::sync::Mutex;
|
use crate::loom::sync::Mutex;
|
||||||
use crate::util::linked_list::{self, LinkedList};
|
use crate::util::linked_list::{self, LinkedList};
|
||||||
|
|
||||||
|
use std::cell::UnsafeCell;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
use std::marker::PhantomPinned;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
use std::ptr::NonNull;
|
||||||
use std::sync::atomic::Ordering::SeqCst;
|
use std::sync::atomic::Ordering::SeqCst;
|
||||||
use std::task::{Context, Poll, Waker};
|
use std::task::{Context, Poll, Waker};
|
||||||
|
|
||||||
@@ -103,11 +106,17 @@ pub struct Notify {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Waiter {
|
struct Waiter {
|
||||||
|
/// Intrusive linked-list pointers
|
||||||
|
pointers: linked_list::Pointers<Waiter>,
|
||||||
|
|
||||||
/// Waiting task's waker
|
/// Waiting task's waker
|
||||||
waker: Option<Waker>,
|
waker: Option<Waker>,
|
||||||
|
|
||||||
/// `true` if the notification has been assigned to this waiter.
|
/// `true` if the notification has been assigned to this waiter.
|
||||||
notified: bool,
|
notified: bool,
|
||||||
|
|
||||||
|
/// Should not be `Unpin`.
|
||||||
|
_p: PhantomPinned,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Future returned from `notified()`
|
/// Future returned from `notified()`
|
||||||
@@ -120,9 +129,12 @@ struct Notified<'a> {
|
|||||||
state: State,
|
state: State,
|
||||||
|
|
||||||
/// Entry in the waiter `LinkedList`.
|
/// Entry in the waiter `LinkedList`.
|
||||||
waiter: linked_list::Entry<Waiter>,
|
waiter: UnsafeCell<Waiter>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe impl<'a> Send for Notified<'a> {}
|
||||||
|
unsafe impl<'a> Sync for Notified<'a> {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum State {
|
enum State {
|
||||||
Init,
|
Init,
|
||||||
@@ -189,9 +201,11 @@ impl Notify {
|
|||||||
Notified {
|
Notified {
|
||||||
notify: self,
|
notify: self,
|
||||||
state: State::Init,
|
state: State::Init,
|
||||||
waiter: linked_list::Entry::new(Waiter {
|
waiter: UnsafeCell::new(Waiter {
|
||||||
|
pointers: linked_list::Pointers::new(),
|
||||||
waker: None,
|
waker: None,
|
||||||
notified: false,
|
notified: false,
|
||||||
|
_p: PhantomPinned,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
.await
|
.await
|
||||||
@@ -292,7 +306,10 @@ fn notify_locked(waiters: &mut LinkedList<Waiter>, state: &AtomicU8, curr: u8) -
|
|||||||
// transition **out** of `WAITING`.
|
// transition **out** of `WAITING`.
|
||||||
//
|
//
|
||||||
// Get a pending waiter
|
// Get a pending waiter
|
||||||
let mut waiter = waiters.pop_back().unwrap();
|
let waiter = waiters.pop_back().unwrap();
|
||||||
|
|
||||||
|
// Safety: `waiters` lock is still held.
|
||||||
|
let waiter = unsafe { &mut *waiter };
|
||||||
|
|
||||||
assert!(!waiter.notified);
|
assert!(!waiter.notified);
|
||||||
|
|
||||||
@@ -319,9 +336,7 @@ fn notify_locked(waiters: &mut LinkedList<Waiter>, state: &AtomicU8, curr: u8) -
|
|||||||
impl Notified<'_> {
|
impl Notified<'_> {
|
||||||
/// A custom `project` implementation is used in place of `pin-project-lite`
|
/// A custom `project` implementation is used in place of `pin-project-lite`
|
||||||
/// as a custom drop implementation is needed.
|
/// as a custom drop implementation is needed.
|
||||||
fn project(
|
fn project(self: Pin<&mut Self>) -> (&Notify, &mut State, &UnsafeCell<Waiter>) {
|
||||||
self: Pin<&mut Self>,
|
|
||||||
) -> (&Notify, &mut State, Pin<&mut linked_list::Entry<Waiter>>) {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// Safety: both `notify` and `state` are `Unpin`.
|
// Safety: both `notify` and `state` are `Unpin`.
|
||||||
|
|
||||||
@@ -329,11 +344,7 @@ impl Notified<'_> {
|
|||||||
is_unpin::<AtomicU8>();
|
is_unpin::<AtomicU8>();
|
||||||
|
|
||||||
let me = self.get_unchecked_mut();
|
let me = self.get_unchecked_mut();
|
||||||
(
|
(&me.notify, &mut me.state, &me.waiter)
|
||||||
&me.notify,
|
|
||||||
&mut me.state,
|
|
||||||
Pin::new_unchecked(&mut me.waiter),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -344,7 +355,7 @@ impl Future for Notified<'_> {
|
|||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
|
||||||
use State::*;
|
use State::*;
|
||||||
|
|
||||||
let (notify, state, mut waiter) = self.project();
|
let (notify, state, waiter) = self.project();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match *state {
|
match *state {
|
||||||
@@ -408,12 +419,12 @@ impl Future for Notified<'_> {
|
|||||||
|
|
||||||
// Safety: called while locked.
|
// Safety: called while locked.
|
||||||
unsafe {
|
unsafe {
|
||||||
(*waiter.as_mut().get()).waker = Some(cx.waker().clone());
|
(*waiter.get()).waker = Some(cx.waker().clone());
|
||||||
|
|
||||||
// Insert the waiter into the linked list
|
|
||||||
waiters.push_front(waiter.as_mut());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Insert the waiter into the linked list
|
||||||
|
waiters.push_front(waiter.get());
|
||||||
|
|
||||||
*state = Waiting;
|
*state = Waiting;
|
||||||
}
|
}
|
||||||
Waiting => {
|
Waiting => {
|
||||||
@@ -425,7 +436,7 @@ impl Future for Notified<'_> {
|
|||||||
let waiters = notify.waiters.lock().unwrap();
|
let waiters = notify.waiters.lock().unwrap();
|
||||||
|
|
||||||
// Safety: called while locked
|
// Safety: called while locked
|
||||||
let w = unsafe { &mut *waiter.as_mut().get() };
|
let w = unsafe { &mut *waiter.get() };
|
||||||
|
|
||||||
if w.notified {
|
if w.notified {
|
||||||
// Our waker has been notified. Reset the fields and
|
// Our waker has been notified. Reset the fields and
|
||||||
@@ -463,7 +474,7 @@ impl Drop for Notified<'_> {
|
|||||||
use State::*;
|
use State::*;
|
||||||
|
|
||||||
// Safety: The type only transitions to a "Waiting" state when pinned.
|
// Safety: The type only transitions to a "Waiting" state when pinned.
|
||||||
let (notify, state, mut waiter) = unsafe { Pin::new_unchecked(self).project() };
|
let (notify, state, waiter) = unsafe { Pin::new_unchecked(self).project() };
|
||||||
|
|
||||||
// This is where we ensure safety. The `Notified` value is being
|
// This is where we ensure safety. The `Notified` value is being
|
||||||
// dropped, which means we must ensure that the waiter entry is no
|
// dropped, which means we must ensure that the waiter entry is no
|
||||||
@@ -490,7 +501,7 @@ impl Drop for Notified<'_> {
|
|||||||
//
|
//
|
||||||
// safety: the waiter is only added to `waiters` by virtue of it
|
// safety: the waiter is only added to `waiters` by virtue of it
|
||||||
// being the only `LinkedList` available to the type.
|
// being the only `LinkedList` available to the type.
|
||||||
unsafe { waiters.remove(waiter.as_mut()) };
|
unsafe { waiters.remove(NonNull::new_unchecked(waiter.get())) };
|
||||||
|
|
||||||
if waiters.is_empty() {
|
if waiters.is_empty() {
|
||||||
notify_state = EMPTY;
|
notify_state = EMPTY;
|
||||||
@@ -508,7 +519,7 @@ impl Drop for Notified<'_> {
|
|||||||
//
|
//
|
||||||
// Safety: with the entry removed from the linked list, there can be
|
// Safety: with the entry removed from the linked list, there can be
|
||||||
// no concurrent access to the entry
|
// no concurrent access to the entry
|
||||||
let notified = unsafe { (*waiter.as_mut().get()).notified };
|
let notified = unsafe { (*waiter.get()).notified };
|
||||||
|
|
||||||
if notified {
|
if notified {
|
||||||
if let Some(waker) = notify_locked(&mut waiters, ¬ify.state, notify_state) {
|
if let Some(waker) = notify_locked(&mut waiters, ¬ify.state, notify_state) {
|
||||||
@@ -520,4 +531,25 @@ impl Drop for Notified<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `Waiter` is forced to be !Unpin.
|
||||||
|
unsafe impl linked_list::Link for Waiter {
|
||||||
|
type Handle = *mut Waiter;
|
||||||
|
type Target = Waiter;
|
||||||
|
|
||||||
|
fn to_raw(handle: *mut Waiter) -> NonNull<Waiter> {
|
||||||
|
debug_assert!(!handle.is_null());
|
||||||
|
unsafe { NonNull::new_unchecked(handle) }
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn from_raw(ptr: NonNull<Waiter>) -> *mut Waiter {
|
||||||
|
ptr.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn pointers(mut target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
|
||||||
|
NonNull::from(&mut target.as_mut().pointers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn is_unpin<T: Unpin>() {}
|
fn is_unpin<T: Unpin>() {}
|
||||||
|
|||||||
+204
-177
@@ -4,95 +4,116 @@
|
|||||||
//! structure's APIs are `unsafe` as they require the caller to ensure the
|
//! structure's APIs are `unsafe` as they require the caller to ensure the
|
||||||
//! specified node is actually contained by the list.
|
//! specified node is actually contained by the list.
|
||||||
|
|
||||||
use core::cell::UnsafeCell;
|
|
||||||
use core::marker::PhantomPinned;
|
|
||||||
use core::pin::Pin;
|
|
||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
|
|
||||||
/// An intrusive linked list of nodes, where each node carries associated data
|
/// An intrusive linked list.
|
||||||
/// of type `T`.
|
///
|
||||||
|
/// Currently, the list is not emptied on drop. It is the caller's
|
||||||
|
/// responsibility to ensure the list is empty before dropping it.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct LinkedList<T> {
|
pub(crate) struct LinkedList<T: Link> {
|
||||||
head: Option<NonNull<Entry<T>>>,
|
/// Linked list head
|
||||||
tail: Option<NonNull<Entry<T>>>,
|
head: Option<NonNull<T::Target>>,
|
||||||
|
|
||||||
|
/// Linked list tail
|
||||||
|
tail: Option<NonNull<T::Target>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T: Send> Send for LinkedList<T> {}
|
unsafe impl<T: Link> Send for LinkedList<T> where T::Target: Send {}
|
||||||
unsafe impl<T: Sync> Sync for LinkedList<T> {}
|
unsafe impl<T: Link> Sync for LinkedList<T> where T::Target: Sync {}
|
||||||
|
|
||||||
/// A node which carries data of type `T` and is stored in an intrusive list.
|
/// 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) unsafe trait 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
|
||||||
|
///
|
||||||
|
/// Consumes ownership of the handle.
|
||||||
|
fn to_raw(handle: Self::Handle) -> NonNull<Self::Target>;
|
||||||
|
|
||||||
|
/// Convert the raw pointer to a handle
|
||||||
|
unsafe fn from_raw(ptr: NonNull<Self::Target>) -> Self::Handle;
|
||||||
|
|
||||||
|
/// Return the pointers for a node
|
||||||
|
unsafe fn pointers(target: NonNull<Self::Target>) -> NonNull<Pointers<Self::Target>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Previous / next pointers
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Entry<T> {
|
pub(crate) struct Pointers<T> {
|
||||||
/// The previous node in the list. null if there is no previous node.
|
/// The previous node in the list. null if there is no previous node.
|
||||||
prev: Option<NonNull<Entry<T>>>,
|
prev: Option<NonNull<T>>,
|
||||||
|
|
||||||
/// The next node in the list. null if there is no previous node.
|
/// The next node in the list. null if there is no previous node.
|
||||||
next: Option<NonNull<Entry<T>>>,
|
next: Option<NonNull<T>>,
|
||||||
|
|
||||||
/// The data which is associated to this list item
|
|
||||||
data: UnsafeCell<T>,
|
|
||||||
|
|
||||||
/// Prevents `Entry`s from being `Unpin`. They may never be moved, since
|
|
||||||
/// the list semantics require addresses to be stable.
|
|
||||||
_pin: PhantomPinned,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T: Send> Send for Entry<T> {}
|
unsafe impl<T: Send> Send for Pointers<T> {}
|
||||||
unsafe impl<T: Sync> Sync for Entry<T> {}
|
unsafe impl<T: Sync> Sync for Pointers<T> {}
|
||||||
|
|
||||||
impl<T> LinkedList<T> {
|
// ===== impl LinkedList =====
|
||||||
|
|
||||||
|
impl<T: Link> LinkedList<T> {
|
||||||
/// Creates an empty linked list
|
/// Creates an empty linked list
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> LinkedList<T> {
|
||||||
LinkedList {
|
LinkedList {
|
||||||
head: None,
|
head: None,
|
||||||
tail: None,
|
tail: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds an item to the back of the linked list.
|
/// Adds an element first in the list.
|
||||||
///
|
pub(crate) fn push_front(&mut self, val: T::Handle) {
|
||||||
/// # Safety
|
let ptr = T::to_raw(val);
|
||||||
///
|
|
||||||
/// The function is only safe as long as valid pointers are stored inside
|
|
||||||
/// the linked list.
|
|
||||||
pub(crate) unsafe fn push_front(&mut self, entry: Pin<&mut Entry<T>>) {
|
|
||||||
let mut entry: NonNull<Entry<T>> = entry.get_unchecked_mut().into();
|
|
||||||
|
|
||||||
entry.as_mut().next = self.head;
|
unsafe {
|
||||||
entry.as_mut().prev = None;
|
T::pointers(ptr).as_mut().next = self.head;
|
||||||
|
T::pointers(ptr).as_mut().prev = None;
|
||||||
|
|
||||||
if let Some(head) = &mut self.head {
|
if let Some(head) = self.head {
|
||||||
head.as_mut().prev = Some(entry);
|
T::pointers(head).as_mut().prev = Some(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.head = Some(entry);
|
self.head = Some(ptr);
|
||||||
|
|
||||||
if self.tail.is_none() {
|
if self.tail.is_none() {
|
||||||
self.tail = Some(entry);
|
self.tail = Some(ptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes the first element and returns it, or `None` if the list is empty.
|
/// Removes the last element from a list and returns it, or None if it is
|
||||||
///
|
/// empty.
|
||||||
/// The function is safe as the lifetime of the entry is bound to `&mut
|
pub(crate) fn pop_back(&mut self) -> Option<T::Handle> {
|
||||||
/// self`.
|
|
||||||
pub(crate) fn pop_back(&mut self) -> Option<Pin<&mut T>> {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut last = self.tail?;
|
let last = self.tail?;
|
||||||
self.tail = last.as_ref().prev;
|
self.tail = T::pointers(last).as_ref().prev;
|
||||||
|
|
||||||
if let Some(mut prev) = last.as_mut().prev {
|
if let Some(prev) = T::pointers(last).as_ref().prev {
|
||||||
prev.as_mut().next = None;
|
T::pointers(prev).as_mut().next = None;
|
||||||
} else {
|
} else {
|
||||||
self.head = None
|
self.head = None
|
||||||
}
|
}
|
||||||
|
|
||||||
last.as_mut().prev = None;
|
T::pointers(last).as_mut().prev = None;
|
||||||
last.as_mut().next = None;
|
T::pointers(last).as_mut().next = None;
|
||||||
|
|
||||||
let val = &mut *last.as_mut().data.get();
|
Some(T::from_raw(last))
|
||||||
|
|
||||||
Some(Pin::new_unchecked(val))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,60 +127,53 @@ impl<T> LinkedList<T> {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes the given item from the linked list.
|
/// Removes the specified node from the list
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// The caller **must** ensure that `entry` is currently contained by
|
/// The caller **must** ensure that `node` is currently contained by
|
||||||
/// `self`.
|
/// `self` or not contained by any other list.
|
||||||
pub(crate) unsafe fn remove(&mut self, entry: Pin<&mut Entry<T>>) -> bool {
|
pub(crate) unsafe fn remove(&mut self, node: NonNull<T::Target>) -> bool {
|
||||||
let mut entry = NonNull::from(entry.get_unchecked_mut());
|
if let Some(prev) = T::pointers(node).as_ref().prev {
|
||||||
|
debug_assert_eq!(T::pointers(prev).as_ref().next, Some(node));
|
||||||
if let Some(mut prev) = entry.as_mut().prev {
|
T::pointers(prev).as_mut().next = T::pointers(node).as_ref().next;
|
||||||
debug_assert_eq!(prev.as_ref().next, Some(entry));
|
|
||||||
prev.as_mut().next = entry.as_ref().next;
|
|
||||||
} else {
|
} else {
|
||||||
if self.head != Some(entry) {
|
if self.head != Some(node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.head = entry.as_ref().next;
|
self.head = T::pointers(node).as_ref().next;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(mut next) = entry.as_mut().next {
|
if let Some(next) = T::pointers(node).as_ref().next {
|
||||||
debug_assert_eq!(next.as_ref().prev, Some(entry));
|
debug_assert_eq!(T::pointers(next).as_ref().prev, Some(node));
|
||||||
next.as_mut().prev = entry.as_ref().prev;
|
T::pointers(next).as_mut().prev = T::pointers(node).as_ref().prev;
|
||||||
} else {
|
} else {
|
||||||
// This might be the last item in the list
|
// This might be the last item in the list
|
||||||
if self.tail != Some(entry) {
|
if self.tail != Some(node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tail = entry.as_ref().prev;
|
self.tail = T::pointers(node).as_ref().prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.as_mut().next = None;
|
T::pointers(node).as_mut().next = None;
|
||||||
entry.as_mut().prev = None;
|
T::pointers(node).as_mut().prev = None;
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Entry<T> {
|
// ===== impl Pointers =====
|
||||||
/// Creates a new node with the associated data
|
|
||||||
pub(crate) fn new(data: T) -> Entry<T> {
|
impl<T> Pointers<T> {
|
||||||
Entry {
|
/// Create a new set of empty pointers
|
||||||
|
pub(crate) fn new() -> Pointers<T> {
|
||||||
|
Pointers {
|
||||||
prev: None,
|
prev: None,
|
||||||
next: None,
|
next: None,
|
||||||
data: UnsafeCell::new(data),
|
|
||||||
_pin: PhantomPinned,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a raw pointer to the inner data
|
|
||||||
pub(crate) fn get(&self) -> *mut T {
|
|
||||||
self.data.get()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -167,53 +181,84 @@ impl<T> Entry<T> {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn collect_list<T: Copy>(list: &mut LinkedList<T>) -> Vec<T> {
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
struct Entry {
|
||||||
|
pointers: Pointers<Entry>,
|
||||||
|
val: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<'a> Link for &'a Entry {
|
||||||
|
type Handle = Pin<&'a Entry>;
|
||||||
|
type Target = Entry;
|
||||||
|
|
||||||
|
fn to_raw(handle: Pin<&'_ Entry>) -> NonNull<Entry> {
|
||||||
|
NonNull::from(handle.get_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn from_raw(ptr: NonNull<Entry>) -> Pin<&'a Entry> {
|
||||||
|
Pin::new(&*ptr.as_ptr())
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn pointers(mut target: NonNull<Entry>) -> NonNull<Pointers<Entry>> {
|
||||||
|
NonNull::from(&mut target.as_mut().pointers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn entry(val: i32) -> Pin<Box<Entry>> {
|
||||||
|
Box::pin(Entry {
|
||||||
|
pointers: Pointers::new(),
|
||||||
|
val,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ptr(r: &Pin<Box<Entry>>) -> NonNull<Entry> {
|
||||||
|
r.as_ref().get_ref().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_list(list: &mut LinkedList<&'_ Entry>) -> Vec<i32> {
|
||||||
let mut ret = vec![];
|
let mut ret = vec![];
|
||||||
|
|
||||||
while let Some(v) = list.pop_back() {
|
while let Some(entry) = list.pop_back() {
|
||||||
ret.push(*v);
|
ret.push(entry.val);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn push_all(list: &mut LinkedList<i32>, entries: &mut [Pin<&mut Entry<i32>>]) {
|
fn push_all<'a>(list: &mut LinkedList<&'a Entry>, entries: &[Pin<&'a Entry>]) {
|
||||||
for entry in entries.iter_mut() {
|
for entry in entries.iter() {
|
||||||
list.push_front(entry.as_mut());
|
list.push_front(*entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! assert_clean {
|
macro_rules! assert_clean {
|
||||||
($e:ident) => {{
|
($e:ident) => {{
|
||||||
assert!($e.next.is_none());
|
assert!($e.pointers.next.is_none());
|
||||||
assert!($e.prev.is_none());
|
assert!($e.pointers.prev.is_none());
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! assert_ptr_eq {
|
macro_rules! assert_ptr_eq {
|
||||||
($a:expr, $b:expr) => {{
|
($a:expr, $b:expr) => {{
|
||||||
// Deal with mapping a Pin<&mut T> -> Option<NonNull<T>>
|
// Deal with mapping a Pin<&mut T> -> Option<NonNull<T>>
|
||||||
assert_eq!(Some($a.as_mut().get_unchecked_mut().into()), $b)
|
assert_eq!(Some($a.as_ref().get_ref().into()), $b)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn push_and_drain() {
|
fn push_and_drain() {
|
||||||
pin! {
|
let a = entry(5);
|
||||||
let a = Entry::new(5);
|
let b = entry(7);
|
||||||
let b = Entry::new(7);
|
let c = entry(31);
|
||||||
let c = Entry::new(31);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut list = LinkedList::new();
|
let mut list = LinkedList::new();
|
||||||
assert!(list.is_empty());
|
assert!(list.is_empty());
|
||||||
|
|
||||||
unsafe {
|
list.push_front(a.as_ref());
|
||||||
list.push_front(a);
|
assert!(!list.is_empty());
|
||||||
assert!(!list.is_empty());
|
list.push_front(b.as_ref());
|
||||||
list.push_front(b);
|
list.push_front(c.as_ref());
|
||||||
list.push_front(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
let items: Vec<i32> = collect_list(&mut list);
|
let items: Vec<i32> = collect_list(&mut list);
|
||||||
assert_eq!([5, 7, 31].to_vec(), items);
|
assert_eq!([5, 7, 31].to_vec(), items);
|
||||||
@@ -223,27 +268,21 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn push_pop_push_pop() {
|
fn push_pop_push_pop() {
|
||||||
pin! {
|
let a = entry(5);
|
||||||
let a = Entry::new(5);
|
let b = entry(7);
|
||||||
let b = Entry::new(7);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut list = LinkedList::new();
|
let mut list = LinkedList::<&Entry>::new();
|
||||||
|
|
||||||
unsafe {
|
list.push_front(a.as_ref());
|
||||||
list.push_front(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
let v = list.pop_back().unwrap();
|
let entry = list.pop_back().unwrap();
|
||||||
assert_eq!(5, *v);
|
assert_eq!(5, entry.val);
|
||||||
assert!(list.is_empty());
|
assert!(list.is_empty());
|
||||||
|
|
||||||
unsafe {
|
list.push_front(b.as_ref());
|
||||||
list.push_front(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
let v = list.pop_back().unwrap();
|
let entry = list.pop_back().unwrap();
|
||||||
assert_eq!(7, *v);
|
assert_eq!(7, entry.val);
|
||||||
|
|
||||||
assert!(list.is_empty());
|
assert!(list.is_empty());
|
||||||
assert!(list.pop_back().is_none());
|
assert!(list.pop_back().is_none());
|
||||||
@@ -251,33 +290,31 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_by_address() {
|
fn remove_by_address() {
|
||||||
pin! {
|
let a = entry(5);
|
||||||
let a = Entry::new(5);
|
let b = entry(7);
|
||||||
let b = Entry::new(7);
|
let c = entry(31);
|
||||||
let c = Entry::new(31);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// Remove first
|
// Remove first
|
||||||
let mut list = LinkedList::new();
|
let mut list = LinkedList::new();
|
||||||
|
|
||||||
push_all(&mut list, &mut [c.as_mut(), b.as_mut(), a.as_mut()]);
|
push_all(&mut list, &[c.as_ref(), b.as_ref(), a.as_ref()]);
|
||||||
assert!(list.remove(a.as_mut()));
|
assert!(list.remove(ptr(&a)));
|
||||||
assert_clean!(a);
|
assert_clean!(a);
|
||||||
// `a` should be no longer there and can't be removed twice
|
// `a` should be no longer there and can't be removed twice
|
||||||
assert!(!list.remove(a.as_mut()));
|
assert!(!list.remove(ptr(&a)));
|
||||||
assert!(!list.is_empty());
|
assert!(!list.is_empty());
|
||||||
|
|
||||||
assert!(list.remove(b.as_mut()));
|
assert!(list.remove(ptr(&b)));
|
||||||
assert_clean!(b);
|
assert_clean!(b);
|
||||||
// `b` should be no longer there and can't be removed twice
|
// `b` should be no longer there and can't be removed twice
|
||||||
assert!(!list.remove(b.as_mut()));
|
assert!(!list.remove(ptr(&b)));
|
||||||
assert!(!list.is_empty());
|
assert!(!list.is_empty());
|
||||||
|
|
||||||
assert!(list.remove(c.as_mut()));
|
assert!(list.remove(ptr(&c)));
|
||||||
assert_clean!(c);
|
assert_clean!(c);
|
||||||
// `b` should be no longer there and can't be removed twice
|
// `b` should be no longer there and can't be removed twice
|
||||||
assert!(!list.remove(c.as_mut()));
|
assert!(!list.remove(ptr(&c)));
|
||||||
assert!(list.is_empty());
|
assert!(list.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,14 +322,14 @@ mod tests {
|
|||||||
// Remove middle
|
// Remove middle
|
||||||
let mut list = LinkedList::new();
|
let mut list = LinkedList::new();
|
||||||
|
|
||||||
push_all(&mut list, &mut [c.as_mut(), b.as_mut(), a.as_mut()]);
|
push_all(&mut list, &[c.as_ref(), b.as_ref(), a.as_ref()]);
|
||||||
|
|
||||||
assert!(list.remove(a.as_mut()));
|
assert!(list.remove(ptr(&a)));
|
||||||
assert_clean!(a);
|
assert_clean!(a);
|
||||||
|
|
||||||
assert_ptr_eq!(b, list.head);
|
assert_ptr_eq!(b, list.head);
|
||||||
assert_ptr_eq!(c, b.next);
|
assert_ptr_eq!(c, b.pointers.next);
|
||||||
assert_ptr_eq!(b, c.prev);
|
assert_ptr_eq!(b, c.pointers.prev);
|
||||||
|
|
||||||
let items = collect_list(&mut list);
|
let items = collect_list(&mut list);
|
||||||
assert_eq!([31, 7].to_vec(), items);
|
assert_eq!([31, 7].to_vec(), items);
|
||||||
@@ -302,13 +339,13 @@ mod tests {
|
|||||||
// Remove middle
|
// Remove middle
|
||||||
let mut list = LinkedList::new();
|
let mut list = LinkedList::new();
|
||||||
|
|
||||||
push_all(&mut list, &mut [c.as_mut(), b.as_mut(), a.as_mut()]);
|
push_all(&mut list, &[c.as_ref(), b.as_ref(), a.as_ref()]);
|
||||||
|
|
||||||
assert!(list.remove(b.as_mut()));
|
assert!(list.remove(ptr(&b)));
|
||||||
assert_clean!(b);
|
assert_clean!(b);
|
||||||
|
|
||||||
assert_ptr_eq!(c, a.next);
|
assert_ptr_eq!(c, a.pointers.next);
|
||||||
assert_ptr_eq!(a, c.prev);
|
assert_ptr_eq!(a, c.pointers.prev);
|
||||||
|
|
||||||
let items = collect_list(&mut list);
|
let items = collect_list(&mut list);
|
||||||
assert_eq!([31, 5].to_vec(), items);
|
assert_eq!([31, 5].to_vec(), items);
|
||||||
@@ -319,12 +356,12 @@ mod tests {
|
|||||||
// Remove middle
|
// Remove middle
|
||||||
let mut list = LinkedList::new();
|
let mut list = LinkedList::new();
|
||||||
|
|
||||||
push_all(&mut list, &mut [c.as_mut(), b.as_mut(), a.as_mut()]);
|
push_all(&mut list, &[c.as_ref(), b.as_ref(), a.as_ref()]);
|
||||||
|
|
||||||
assert!(list.remove(c.as_mut()));
|
assert!(list.remove(ptr(&c)));
|
||||||
assert_clean!(c);
|
assert_clean!(c);
|
||||||
|
|
||||||
assert!(b.next.is_none());
|
assert!(b.pointers.next.is_none());
|
||||||
assert_ptr_eq!(b, list.tail);
|
assert_ptr_eq!(b, list.tail);
|
||||||
|
|
||||||
let items = collect_list(&mut list);
|
let items = collect_list(&mut list);
|
||||||
@@ -335,20 +372,20 @@ mod tests {
|
|||||||
// Remove first of two
|
// Remove first of two
|
||||||
let mut list = LinkedList::new();
|
let mut list = LinkedList::new();
|
||||||
|
|
||||||
push_all(&mut list, &mut [b.as_mut(), a.as_mut()]);
|
push_all(&mut list, &[b.as_ref(), a.as_ref()]);
|
||||||
|
|
||||||
assert!(list.remove(a.as_mut()));
|
assert!(list.remove(ptr(&a)));
|
||||||
|
|
||||||
assert_clean!(a);
|
assert_clean!(a);
|
||||||
|
|
||||||
// a should be no longer there and can't be removed twice
|
// a should be no longer there and can't be removed twice
|
||||||
assert!(!list.remove(a.as_mut()));
|
assert!(!list.remove(ptr(&a)));
|
||||||
|
|
||||||
assert_ptr_eq!(b, list.head);
|
assert_ptr_eq!(b, list.head);
|
||||||
assert_ptr_eq!(b, list.tail);
|
assert_ptr_eq!(b, list.tail);
|
||||||
|
|
||||||
assert!(b.next.is_none());
|
assert!(b.pointers.next.is_none());
|
||||||
assert!(b.prev.is_none());
|
assert!(b.pointers.prev.is_none());
|
||||||
|
|
||||||
let items = collect_list(&mut list);
|
let items = collect_list(&mut list);
|
||||||
assert_eq!([7].to_vec(), items);
|
assert_eq!([7].to_vec(), items);
|
||||||
@@ -358,17 +395,17 @@ mod tests {
|
|||||||
// Remove last of two
|
// Remove last of two
|
||||||
let mut list = LinkedList::new();
|
let mut list = LinkedList::new();
|
||||||
|
|
||||||
push_all(&mut list, &mut [b.as_mut(), a.as_mut()]);
|
push_all(&mut list, &[b.as_ref(), a.as_ref()]);
|
||||||
|
|
||||||
assert!(list.remove(b.as_mut()));
|
assert!(list.remove(ptr(&b)));
|
||||||
|
|
||||||
assert_clean!(b);
|
assert_clean!(b);
|
||||||
|
|
||||||
assert_ptr_eq!(a, list.head);
|
assert_ptr_eq!(a, list.head);
|
||||||
assert_ptr_eq!(a, list.tail);
|
assert_ptr_eq!(a, list.tail);
|
||||||
|
|
||||||
assert!(a.next.is_none());
|
assert!(a.pointers.next.is_none());
|
||||||
assert!(a.prev.is_none());
|
assert!(a.pointers.prev.is_none());
|
||||||
|
|
||||||
let items = collect_list(&mut list);
|
let items = collect_list(&mut list);
|
||||||
assert_eq!([5].to_vec(), items);
|
assert_eq!([5].to_vec(), items);
|
||||||
@@ -378,9 +415,9 @@ mod tests {
|
|||||||
// Remove last item
|
// Remove last item
|
||||||
let mut list = LinkedList::new();
|
let mut list = LinkedList::new();
|
||||||
|
|
||||||
push_all(&mut list, &mut [a.as_mut()]);
|
push_all(&mut list, &[a.as_ref()]);
|
||||||
|
|
||||||
assert!(list.remove(a.as_mut()));
|
assert!(list.remove(ptr(&a)));
|
||||||
assert_clean!(a);
|
assert_clean!(a);
|
||||||
|
|
||||||
assert!(list.head.is_none());
|
assert!(list.head.is_none());
|
||||||
@@ -391,12 +428,12 @@ mod tests {
|
|||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// Remove missing
|
// Remove missing
|
||||||
let mut list = LinkedList::new();
|
let mut list = LinkedList::<&Entry>::new();
|
||||||
|
|
||||||
list.push_front(b.as_mut());
|
list.push_front(b.as_ref());
|
||||||
list.push_front(a.as_mut());
|
list.push_front(a.as_ref());
|
||||||
|
|
||||||
assert!(!list.remove(c.as_mut()));
|
assert!(!list.remove(ptr(&c)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,23 +464,18 @@ mod tests {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut next = 0;
|
let mut ll = LinkedList::<&Entry>::new();
|
||||||
let mut ll = LinkedList::new();
|
|
||||||
let mut entries = VecDeque::new();
|
|
||||||
let mut reference = VecDeque::new();
|
let mut reference = VecDeque::new();
|
||||||
|
|
||||||
for op in ops {
|
let entries: Vec<_> = (0..ops.len()).map(|i| entry(i as i32)).collect();
|
||||||
|
|
||||||
|
for (i, op) in ops.iter().enumerate() {
|
||||||
match op {
|
match op {
|
||||||
Op::Push => {
|
Op::Push => {
|
||||||
let v = next;
|
reference.push_front(i as i32);
|
||||||
next += 1;
|
assert_eq!(entries[i].val, i as i32);
|
||||||
|
|
||||||
reference.push_front(v);
|
ll.push_front(entries[i].as_ref());
|
||||||
entries.push_front(Box::pin(Entry::new(v)));
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
ll.push_front(entries.front_mut().unwrap().as_mut());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Op::Pop => {
|
Op::Pop => {
|
||||||
if reference.is_empty() {
|
if reference.is_empty() {
|
||||||
@@ -452,8 +484,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let v = reference.pop_back();
|
let v = reference.pop_back();
|
||||||
assert_eq!(v, ll.pop_back().map(|v| *v));
|
assert_eq!(v, ll.pop_back().map(|v| v.val));
|
||||||
entries.pop_back();
|
|
||||||
}
|
}
|
||||||
Op::Remove(n) => {
|
Op::Remove(n) => {
|
||||||
if reference.is_empty() {
|
if reference.is_empty() {
|
||||||
@@ -462,15 +493,11 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let idx = n % reference.len();
|
let idx = n % reference.len();
|
||||||
|
let v = reference.remove(idx).unwrap();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
assert!(ll.remove(entries[idx].as_mut()));
|
assert!(ll.remove(ptr(&entries[v as usize])));
|
||||||
}
|
}
|
||||||
|
|
||||||
let v = reference.remove(idx).unwrap();
|
|
||||||
assert_eq!(v, unsafe { *entries[idx].get() });
|
|
||||||
|
|
||||||
entries.remove(idx);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user