| //! A doubly-linked list where callers are in charge of memory allocation |
| //! of the nodes in the list. |
| //! |
| //! # Safety |
| //! |
| //! `UnsafeList` itself does not synchronize any of its memory accesses, so |
| //! callers must serialize all operations on a list, e.g. with a lock. |
| //! |
| //! While an entry passed to `push` is in the list, it must not be invalidated, |
| //! with one exception explained below. Invalidation of the entry, by creating a new |
| //! exclusive reference to it, would invalidate the pointers to the entry stored |
| //! in the list. The entry goes through one of two flows (see also each operation's |
| //! safety documentation): |
| //! |
| //! * `push` -> `pop`, usually with `pop` on another thread: the entry pointer |
| //! stored in the list keeps its `push`-time provenance. As mentioned, for it to |
| //! still be valid to dereference in `pop`, the pushing caller must not access |
| //! the entry in between. After `pop`, the references into `value` returned by |
| //! `push` and `pop` are held concurrently, possibly by two threads. This is |
| //! valid as they are shared references, but mutating `value` requires interior |
| //! mutability and synchronization. That synchronization must also ensure the |
| //! entry is only deallocated after the popping thread's last access to it. |
| //! * `push` -> `remove`, on the thread that pushed: the caller reclaims a pushed |
| //! entry by passing a reference to the entry to `remove`. The entry must still |
| //! be in the list. The caller of `remove` must create a new exclusive reference |
| //! to the entry, which invalidates the pointers to the entry stored in the list. |
| //! This is fine in this case because `remove` only overwrites those pointers, |
| //! and never dereferences them. |
| |
| // # Aliasing |
| // |
| // The list is self-referential: it stores pointers to its own `head_tail` |
| // field in the list entries' links. `UnsafePinned` is used to ensure pointer |
| // validity. |
| // |
| // Pointers to the other entries are derived from the exclusive reference passed |
| // to `push` and stay valid while the entry is in the list (see the safety |
| // requirements in the module documentation). Multiple immutable references may |
| // exist to values of entries in the list, while the links in the list may be |
| // mutated simultaneously. Creating mutable references to entries to update the |
| // links would invalidate any outstanding shared references. As such, all links |
| // are updated via raw-pointer place expressions instead, keeping the value |
| // references valid. |
| // |
| // # Pointer dereferencing |
| // |
| // All pointers stored in the list are valid to dereference: |
| // |
| // 1. The head/tail pointer is derived from `head_tail`'s `UnsafePinned` |
| // wherever it is needed. Because of the `UnsafePinned` wrapper, no |
| // exclusive reference to the list (or a structure containing it) makes an |
| // aliasing claim on `head_tail`, so every derived pointer and every copy |
| // of it stored in the links stay valid for the list's lifetime. |
| // 2. Pointers to other entries, stored in the links, are derived from the |
| // exclusive reference passed to `push` and stay valid while the entry is |
| // in the list, as ensured by the safety requirements in the module |
| // documentation. |
| // |
| // Both points rely on this code never creating references to entries, as |
| // those would make their own aliasing claims on the entries. |
| // |
| // # Pinning |
| // |
| // Once initialized, the list is self-referential, so it must not be moved. |
| // `UnsafeList` is `!Unpin` and the operations take `Pin<&mut Self>`, letting |
| // the compiler enforce this. Dropping the list while entries are still |
| // linked would leave those entries dangling; `Drop` checks this, and `Pin`'s |
| // drop guarantee ensures every path that invalidates the list's storage |
| // (including in-place replacement with `Pin::set`) runs the check. |
| |
| #[cfg(test)] |
| mod tests; |
| |
| use crate::pin::{Pin, UnsafePinned}; |
| use crate::ptr::{self, NonNull}; |
| |
| /// A caller-allocated list entry. |
| /// |
| /// While the entry is in a list, the list holds a pointer derived from the |
| /// exclusive reference passed to `UnsafeList::push`, so the caller must not |
| /// access the entry until it is removed from the list. `UnsafeList::push` |
| /// returns a reference borrowing the entry, and `UnsafeList::remove` |
| /// reborrows it exclusively, so the borrow checker enforces this for safe |
| /// accesses. |
| pub(crate) struct UnsafeListEntry<T> { |
| next: NonNull<UnsafeListEntry<T>>, |
| prev: NonNull<UnsafeListEntry<T>>, |
| value: Option<T>, |
| } |
| |
| impl<T> UnsafeListEntry<T> { |
| const fn dummy() -> Self { |
| UnsafeListEntry { next: NonNull::dangling(), prev: NonNull::dangling(), value: None } |
| } |
| |
| pub(crate) fn new(value: T) -> Self { |
| UnsafeListEntry { value: Some(value), ..Self::dummy() } |
| } |
| } |
| |
| // WARNING: self-referential struct! Must not be moved once initialized, see |
| // the `Pinning` explanation at the top of the file. |
| pub(crate) struct UnsafeList<T> { |
| // UnsafePinned isn't required to implement this code, but it makes it a lot |
| // simpler. Without UnsafePinned, the provenance of each entry link pointer |
| // would need to be re-established prior to dereferencing, whenever it points |
| // to `head_tail`. |
| head_tail: UnsafePinned<UnsafeListEntry<T>>, |
| } |
| |
| impl<T> UnsafeList<T> { |
| /// Creates a new list. |
| /// |
| /// Before use, the list must be placed in its final location and |
| /// initialized with `init`, making it self-referential; from then on |
| /// it must not be moved and can only be operated on through |
| /// `Pin<&mut Self>` (see the `Pinning` explanation at the top of the |
| /// file). `WaitVariable::new` performs this sequence. |
| /// |
| /// # Safety |
| /// |
| /// The caller must initialize the list with `init` before any other use, |
| /// including dropping it. |
| pub(crate) const unsafe fn new() -> Self { |
| UnsafeList { head_tail: UnsafePinned::new(UnsafeListEntry::dummy()) } |
| } |
| |
| fn head_tail(&mut self) -> NonNull<UnsafeListEntry<T>> { |
| // SAFETY: `get_mut_unchecked` returns the address of `head_tail`, |
| // which is non-null. |
| unsafe { NonNull::new_unchecked(self.head_tail.get_mut_unchecked()) } |
| } |
| |
| /// Makes the list self-referential: the list must be in its final |
| /// location and must never be moved afterwards. Called exactly once per |
| /// list, during construction (`WaitVariable::new`), so lists |
| /// are always initialized before use. |
| pub(crate) fn init(&mut self) { |
| let head_tail = self.head_tail(); |
| // SAFETY: `head_tail` is valid to dereference (see point 1 of the |
| // `Pointer dereferencing` explanation at the top of the file). |
| unsafe { (*head_tail.as_ptr()).next = head_tail }; |
| unsafe { (*head_tail.as_ptr()).prev = head_tail }; |
| } |
| |
| pub(crate) fn is_empty(&self) -> bool { |
| // SAFETY: `get` returns the address of `head_tail`, which is |
| // non-null. |
| let head_tail = unsafe { NonNull::new_unchecked(self.head_tail.get()) }; |
| // SAFETY: `head_tail` is valid to dereference (see point 1 |
| // of the `Pointer dereferencing` explanation at the top of the |
| // file). |
| let first = unsafe { (*head_tail.as_ptr()).next }; |
| if first == head_tail { |
| // ,-------> /---------\ next ---, |
| // | |head_tail| | |
| // `--- prev \---------/ <-------` |
| // SAFETY: `head_tail` is valid to dereference. |
| unsafe { rtassert!((*head_tail.as_ptr()).prev == first) }; |
| true |
| } else { |
| false |
| } |
| } |
| |
| /// Pushes an entry onto the back of the list. |
| /// |
| /// # Safety |
| /// |
| /// The entry must remain allocated until the entry is removed from the |
| /// list AND the caller who popped is done using the entry. Special |
| /// care must be taken in the caller of `push` to ensure unwinding does |
| /// not destroy the stack frame containing the entry. While the entry is |
| /// in the list, it must not be accessed except through the reference |
| /// returned here or by passing the entry to `remove`. |
| pub(crate) unsafe fn push<'a>( |
| self: Pin<&mut Self>, |
| entry: &'a mut UnsafeListEntry<T>, |
| ) -> &'a T { |
| // SAFETY: the list is not moved out of the pinned reference. |
| let this = unsafe { self.get_unchecked_mut() }; |
| |
| // BEFORE: |
| // /---------\ next ---> /---------\ |
| // ... |prev_tail| |head_tail| ... |
| // \---------/ <--- prev \---------/ |
| // |
| // AFTER: |
| // /---------\ next ---> /-----\ next ---> /---------\ |
| // ... |prev_tail| |entry| |head_tail| ... |
| // \---------/ <--- prev \-----/ <--- prev \---------/ |
| let entry = unsafe { NonNull::new_unchecked(entry) }; |
| let head_tail = this.head_tail(); |
| // SAFETY: `head_tail` is valid to dereference (see point 1 |
| // of the `Pointer dereferencing` explanation at the top of the |
| // file). |
| let prev_tail = unsafe { ptr::replace(&raw mut (*head_tail.as_ptr()).prev, entry) }; |
| // SAFETY: `entry` is valid to dereference: it was derived from an |
| // exclusive reference above. |
| unsafe { (*entry.as_ptr()).prev = prev_tail }; |
| unsafe { (*entry.as_ptr()).next = head_tail }; |
| // SAFETY: `prev_tail` was loaded from the list's links, so it is |
| // valid to dereference (see points 1 and 2 of the |
| // `Pointer dereferencing` explanation at the top of the file). |
| unsafe { (*prev_tail.as_ptr()).next = entry }; |
| // unwrap ok: always `Some` on non-dummy entries |
| unsafe { (*entry.as_ptr()).value.as_ref() }.unwrap() |
| } |
| |
| /// Pops an entry from the front of the list. |
| /// |
| /// # Safety |
| /// |
| /// The caller must make sure to synchronize ending the borrow of the |
| /// return value and deallocation of the containing entry. |
| pub(crate) unsafe fn pop<'a>(self: Pin<&mut Self>) -> Option<&'a T> { |
| if self.is_empty() { |
| None |
| } else { |
| // SAFETY: the list is not moved out of the pinned reference. |
| let this = unsafe { self.get_unchecked_mut() }; |
| |
| // BEFORE: |
| // /---------\ next ---> /-----\ next ---> /------\ |
| // ... |head_tail| |first| |second| ... |
| // \---------/ <--- prev \-----/ <--- prev \------/ |
| // |
| // AFTER: |
| // /---------\ next ---> /------\ |
| // ... |head_tail| |second| ... |
| // \---------/ <--- prev \------/ |
| |
| let head_tail = this.head_tail(); |
| // SAFETY: `head_tail` is valid to dereference (see point 1 |
| // of the `Pointer dereferencing` explanation at the top of the |
| // file). |
| let first = unsafe { (*head_tail.as_ptr()).next }; |
| // SAFETY: `first` was loaded from the list's links, so it is |
| // valid to dereference (see point 2 of the |
| // `Pointer dereferencing` explanation at the top of the file). |
| let second = unsafe { (*first.as_ptr()).next }; |
| unsafe { (*head_tail.as_ptr()).next = second }; |
| // SAFETY: `second` was loaded from the list's links, so it is |
| // valid to dereference (see points 1 and 2 of the |
| // `Pointer dereferencing` explanation at the top of the file). |
| unsafe { (*second.as_ptr()).prev = head_tail }; |
| unsafe { (*first.as_ptr()).next = NonNull::dangling() }; |
| unsafe { (*first.as_ptr()).prev = NonNull::dangling() }; |
| // unwrap ok: always `Some` on non-dummy entries |
| Some(unsafe { (*first.as_ptr()).value.as_ref() }.unwrap()) |
| } |
| } |
| |
| /// Removes an entry from the list. |
| /// |
| /// # Safety |
| /// |
| /// The caller must ensure that `entry` has been pushed onto `self` |
| /// prior to this call, has not been removed from the list since then |
| /// (by `pop` or `remove`), and has not moved since it was pushed. |
| pub(crate) unsafe fn remove(self: Pin<&mut Self>, entry: &mut UnsafeListEntry<T>) { |
| rtassert!(!self.is_empty()); |
| |
| // BEFORE: |
| // /----\ next ---> /-----\ next ---> /----\ |
| // ... |prev| |entry| |next| ... |
| // \----/ <--- prev \-----/ <--- prev \----/ |
| // |
| // AFTER: |
| // /----\ next ---> /----\ |
| // ... |prev| |next| ... |
| // \----/ <--- prev \----/ |
| |
| // The exclusive reference `entry`, created by the caller, has |
| // invalidated the pointers to `entry` stored in its neighbors (see |
| // the module documentation); those are only overwritten below, |
| // never dereferenced. |
| let prev = entry.prev; |
| let next = entry.next; |
| // SAFETY: `prev` and `next` were loaded from `entry`'s links, so |
| // they are valid to dereference (see points 1 and 2 of the |
| // `Pointer dereferencing` explanation at the top of the file). |
| unsafe { (*prev.as_ptr()).next = next }; |
| unsafe { (*next.as_ptr()).prev = prev }; |
| entry.next = NonNull::dangling(); |
| entry.prev = NonNull::dangling(); |
| } |
| } |
| |
| impl<T> Drop for UnsafeList<T> { |
| fn drop(&mut self) { |
| // A non-empty list would leave its entries with dangling links. |
| // `Pin`'s drop guarantee routes every path that invalidates the |
| // list's storage (including in-place replacement via `Pin::set`) |
| // through this check. |
| rtassert!(self.is_empty()); |
| } |
| } |