util: add const fn support for internal LinkedList. (#2805)

This commit is contained in:
mental
2020-09-02 12:37:13 -07:00
committed by GitHub
parent 5cdb6f8fd6
commit c9f5bc2915
8 changed files with 73 additions and 55 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
use crate::park::{Park, Unpark}; use crate::park::{Park, Unpark};
use crate::runtime; use crate::runtime;
use crate::runtime::task::{self, JoinHandle, Schedule, Task}; use crate::runtime::task::{self, JoinHandle, Schedule, Task};
use crate::util::linked_list::LinkedList; use crate::util::linked_list::{Link, LinkedList};
use crate::util::{waker_ref, Wake}; use crate::util::{waker_ref, Wake};
use std::cell::RefCell; use std::cell::RefCell;
@@ -42,7 +42,7 @@ pub(crate) struct Spawner {
struct Tasks { struct Tasks {
/// Collection of all active tasks spawned onto this executor. /// Collection of all active tasks spawned onto this executor.
owned: LinkedList<Task<Arc<Shared>>>, owned: LinkedList<Task<Arc<Shared>>, <Task<Arc<Shared>> as Link>::Target>,
/// Local run queue. /// Local run queue.
/// ///
+2 -2
View File
@@ -1,5 +1,5 @@
use crate::runtime::task::{self, Schedule, Task}; use crate::runtime::task::{self, Schedule, Task};
use crate::util::linked_list::LinkedList; use crate::util::linked_list::{Link, LinkedList};
use crate::util::TryLock; use crate::util::TryLock;
use std::collections::VecDeque; use std::collections::VecDeque;
@@ -72,7 +72,7 @@ struct Inner {
struct Core { struct Core {
queue: VecDeque<task::Notified<Runtime>>, queue: VecDeque<task::Notified<Runtime>>,
tasks: LinkedList<Task<Runtime>>, tasks: LinkedList<Task<Runtime>, <Task<Runtime> as Link>::Target>,
} }
static CURRENT: TryLock<Option<Runtime>> = TryLock::new(None); static CURRENT: TryLock<Option<Runtime>> = TryLock::new(None);
+2 -2
View File
@@ -12,7 +12,7 @@ use crate::runtime;
use crate::runtime::park::{Parker, Unparker}; use crate::runtime::park::{Parker, Unparker};
use crate::runtime::thread_pool::{AtomicCell, Idle}; use crate::runtime::thread_pool::{AtomicCell, Idle};
use crate::runtime::{queue, task}; use crate::runtime::{queue, task};
use crate::util::linked_list::LinkedList; use crate::util::linked_list::{Link, LinkedList};
use crate::util::FastRand; use crate::util::FastRand;
use std::cell::RefCell; use std::cell::RefCell;
@@ -53,7 +53,7 @@ struct Core {
is_shutdown: bool, is_shutdown: bool,
/// Tasks owned by the core /// Tasks owned by the core
tasks: LinkedList<Task>, tasks: LinkedList<Task, <Task as Link>::Target>,
/// Parker /// Parker
/// ///
+1 -1
View File
@@ -36,7 +36,7 @@ pub(crate) struct Semaphore {
} }
struct Waitlist { struct Waitlist {
queue: LinkedList<Waiter>, queue: LinkedList<Waiter, <Waiter as linked_list::Link>::Target>,
closed: bool, closed: bool,
} }
+1 -1
View File
@@ -273,7 +273,7 @@ struct Tail {
closed: bool, closed: bool,
/// Receivers waiting for a value /// Receivers waiting for a value
waiters: LinkedList<Waiter>, waiters: LinkedList<Waiter, <Waiter as linked_list::Link>::Target>,
} }
/// Slot in the buffer /// Slot in the buffer
+4 -2
View File
@@ -10,6 +10,8 @@ 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};
type WaitList = LinkedList<Waiter, <Waiter as linked_list::Link>::Target>;
/// Notify a single task to wake up. /// Notify a single task to wake up.
/// ///
/// `Notify` provides a basic mechanism to notify a single task of an event. /// `Notify` provides a basic mechanism to notify a single task of an event.
@@ -101,7 +103,7 @@ use std::task::{Context, Poll, Waker};
#[derive(Debug)] #[derive(Debug)]
pub struct Notify { pub struct Notify {
state: AtomicU8, state: AtomicU8,
waiters: Mutex<LinkedList<Waiter>>, waiters: Mutex<WaitList>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -285,7 +287,7 @@ impl Default for Notify {
} }
} }
fn notify_locked(waiters: &mut LinkedList<Waiter>, state: &AtomicU8, curr: u8) -> Option<Waker> { fn notify_locked(waiters: &mut WaitList, state: &AtomicU8, curr: u8) -> Option<Waker> {
loop { loop {
match curr { match curr {
EMPTY | NOTIFIED => { EMPTY | NOTIFIED => {
+2 -2
View File
@@ -1,7 +1,7 @@
//! Runs `!Send` futures on the current thread. //! Runs `!Send` futures on the current thread.
use crate::runtime::task::{self, JoinHandle, Task}; use crate::runtime::task::{self, JoinHandle, Task};
use crate::sync::AtomicWaker; use crate::sync::AtomicWaker;
use crate::util::linked_list::LinkedList; use crate::util::linked_list::{Link, LinkedList};
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::collections::VecDeque; use std::collections::VecDeque;
@@ -132,7 +132,7 @@ struct Context {
struct Tasks { struct Tasks {
/// Collection of all active tasks spawned onto this executor. /// Collection of all active tasks spawned onto this executor.
owned: LinkedList<Task<Arc<Shared>>>, owned: LinkedList<Task<Arc<Shared>>, <Task<Arc<Shared>> as Link>::Target>,
/// Local run queue sender and receiver. /// Local run queue sender and receiver.
queue: VecDeque<task::Notified<Arc<Shared>>>, queue: VecDeque<task::Notified<Arc<Shared>>>,
+59 -43
View File
@@ -5,6 +5,7 @@
//! specified node is actually contained by the list. //! specified node is actually contained by the list.
use core::fmt; use core::fmt;
use core::marker::PhantomData;
use core::mem::ManuallyDrop; use core::mem::ManuallyDrop;
use core::ptr::NonNull; use core::ptr::NonNull;
@@ -12,16 +13,19 @@ use core::ptr::NonNull;
/// ///
/// Currently, the list is not emptied on drop. It is the caller's /// Currently, the list is not emptied on drop. It is the caller's
/// responsibility to ensure the list is empty before dropping it. /// responsibility to ensure the list is empty before dropping it.
pub(crate) struct LinkedList<T: Link> { pub(crate) struct LinkedList<L, T> {
/// Linked list head /// Linked list head
head: Option<NonNull<T::Target>>, head: Option<NonNull<T>>,
/// Linked list tail /// Linked list tail
tail: Option<NonNull<T::Target>>, tail: Option<NonNull<T>>,
/// Node type marker.
_marker: PhantomData<*const L>,
} }
unsafe impl<T: Link> Send for LinkedList<T> where T::Target: Send {} unsafe impl<L: Link> Send for LinkedList<L, L::Target> where L::Target: Send {}
unsafe impl<T: Link> Sync for LinkedList<T> where T::Target: Sync {} unsafe impl<L: Link> Sync for LinkedList<L, L::Target> where L::Target: Sync {}
/// Defines how a type is tracked within a linked list. /// Defines how a type is tracked within a linked list.
/// ///
@@ -66,27 +70,31 @@ unsafe impl<T: Sync> Sync for Pointers<T> {}
// ===== impl LinkedList ===== // ===== impl LinkedList =====
impl<T: Link> LinkedList<T> { impl<L, T> LinkedList<L, T> {
/// Creates an empty linked list /// Creates an empty linked list.
pub(crate) fn new() -> LinkedList<T> { #[allow(dead_code)] // NOTE: This will get removed with: https://github.com/tokio-rs/tokio/pull/2790
pub(crate) const fn new() -> LinkedList<L, T> {
LinkedList { LinkedList {
head: None, head: None,
tail: None, tail: None,
_marker: PhantomData,
} }
} }
}
impl<L: Link> LinkedList<L, L::Target> {
/// Adds an element first in the list. /// Adds an element first in the list.
pub(crate) fn push_front(&mut self, val: T::Handle) { pub(crate) fn push_front(&mut self, val: L::Handle) {
// The value should not be dropped, it is being inserted into the list // The value should not be dropped, it is being inserted into the list
let val = ManuallyDrop::new(val); let val = ManuallyDrop::new(val);
let ptr = T::as_raw(&*val); let ptr = L::as_raw(&*val);
assert_ne!(self.head, Some(ptr)); assert_ne!(self.head, Some(ptr));
unsafe { unsafe {
T::pointers(ptr).as_mut().next = self.head; L::pointers(ptr).as_mut().next = self.head;
T::pointers(ptr).as_mut().prev = None; L::pointers(ptr).as_mut().prev = None;
if let Some(head) = self.head { if let Some(head) = self.head {
T::pointers(head).as_mut().prev = Some(ptr); L::pointers(head).as_mut().prev = Some(ptr);
} }
self.head = Some(ptr); self.head = Some(ptr);
@@ -99,21 +107,21 @@ impl<T: Link> LinkedList<T> {
/// Removes the last element from a list and returns it, or None if it is /// Removes the last element from a list and returns it, or None if it is
/// empty. /// empty.
pub(crate) fn pop_back(&mut self) -> Option<T::Handle> { pub(crate) fn pop_back(&mut self) -> Option<L::Handle> {
unsafe { unsafe {
let last = self.tail?; let last = self.tail?;
self.tail = T::pointers(last).as_ref().prev; self.tail = L::pointers(last).as_ref().prev;
if let Some(prev) = T::pointers(last).as_ref().prev { if let Some(prev) = L::pointers(last).as_ref().prev {
T::pointers(prev).as_mut().next = None; L::pointers(prev).as_mut().next = None;
} else { } else {
self.head = None self.head = None
} }
T::pointers(last).as_mut().prev = None; L::pointers(last).as_mut().prev = None;
T::pointers(last).as_mut().next = None; L::pointers(last).as_mut().next = None;
Some(T::from_raw(last)) Some(L::from_raw(last))
} }
} }
@@ -133,38 +141,38 @@ impl<T: Link> LinkedList<T> {
/// ///
/// The caller **must** ensure that `node` is currently contained by /// The caller **must** ensure that `node` is currently contained by
/// `self` or not contained by any other list. /// `self` or not contained by any other list.
pub(crate) unsafe fn remove(&mut self, node: NonNull<T::Target>) -> Option<T::Handle> { pub(crate) unsafe fn remove(&mut self, node: NonNull<L::Target>) -> Option<L::Handle> {
if let Some(prev) = T::pointers(node).as_ref().prev { if let Some(prev) = L::pointers(node).as_ref().prev {
debug_assert_eq!(T::pointers(prev).as_ref().next, Some(node)); debug_assert_eq!(L::pointers(prev).as_ref().next, Some(node));
T::pointers(prev).as_mut().next = T::pointers(node).as_ref().next; L::pointers(prev).as_mut().next = L::pointers(node).as_ref().next;
} else { } else {
if self.head != Some(node) { if self.head != Some(node) {
return None; return None;
} }
self.head = T::pointers(node).as_ref().next; self.head = L::pointers(node).as_ref().next;
} }
if let Some(next) = T::pointers(node).as_ref().next { if let Some(next) = L::pointers(node).as_ref().next {
debug_assert_eq!(T::pointers(next).as_ref().prev, Some(node)); debug_assert_eq!(L::pointers(next).as_ref().prev, Some(node));
T::pointers(next).as_mut().prev = T::pointers(node).as_ref().prev; L::pointers(next).as_mut().prev = L::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(node) { if self.tail != Some(node) {
return None; return None;
} }
self.tail = T::pointers(node).as_ref().prev; self.tail = L::pointers(node).as_ref().prev;
} }
T::pointers(node).as_mut().next = None; L::pointers(node).as_mut().next = None;
T::pointers(node).as_mut().prev = None; L::pointers(node).as_mut().prev = None;
Some(T::from_raw(node)) Some(L::from_raw(node))
} }
} }
impl<T: Link> fmt::Debug for LinkedList<T> { impl<L: Link> fmt::Debug for LinkedList<L, L::Target> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LinkedList") f.debug_struct("LinkedList")
.field("head", &self.head) .field("head", &self.head)
@@ -174,8 +182,8 @@ impl<T: Link> fmt::Debug for LinkedList<T> {
} }
cfg_sync! { cfg_sync! {
impl<T: Link> LinkedList<T> { impl<L: Link> LinkedList<L, L::Target> {
pub(crate) fn last(&self) -> Option<&T::Target> { pub(crate) fn last(&self) -> Option<&L::Target> {
let tail = self.tail.as_ref()?; let tail = self.tail.as_ref()?;
unsafe { unsafe {
Some(&*tail.as_ptr()) Some(&*tail.as_ptr())
@@ -192,8 +200,8 @@ cfg_rt_threaded! {
_p: core::marker::PhantomData<&'a T>, _p: core::marker::PhantomData<&'a T>,
} }
impl<T: Link> LinkedList<T> { impl<L: Link> LinkedList<L, L::Target> {
pub(crate) fn iter(&self) -> Iter<'_, T> { pub(crate) fn iter(&self) -> Iter<'_, L> {
Iter { Iter {
curr: self.head, curr: self.head,
_p: core::marker::PhantomData, _p: core::marker::PhantomData,
@@ -277,7 +285,7 @@ mod tests {
r.as_ref().get_ref().into() r.as_ref().get_ref().into()
} }
fn collect_list(list: &mut LinkedList<&'_ Entry>) -> Vec<i32> { fn collect_list(list: &mut LinkedList<&'_ Entry, <&'_ Entry as Link>::Target>) -> Vec<i32> {
let mut ret = vec![]; let mut ret = vec![];
while let Some(entry) = list.pop_back() { while let Some(entry) = list.pop_back() {
@@ -287,7 +295,10 @@ mod tests {
ret ret
} }
fn push_all<'a>(list: &mut LinkedList<&'a Entry>, entries: &[Pin<&'a Entry>]) { fn push_all<'a>(
list: &mut LinkedList<&'a Entry, <&'_ Entry as Link>::Target>,
entries: &[Pin<&'a Entry>],
) {
for entry in entries.iter() { for entry in entries.iter() {
list.push_front(*entry); list.push_front(*entry);
} }
@@ -307,6 +318,11 @@ mod tests {
}}; }};
} }
#[test]
fn const_new() {
const _: LinkedList<&Entry, <&Entry as Link>::Target> = LinkedList::new();
}
#[test] #[test]
fn push_and_drain() { fn push_and_drain() {
let a = entry(5); let a = entry(5);
@@ -332,7 +348,7 @@ mod tests {
let a = entry(5); let a = entry(5);
let b = entry(7); let b = entry(7);
let mut list = LinkedList::<&Entry>::new(); let mut list = LinkedList::<&Entry, <&Entry as Link>::Target>::new();
list.push_front(a.as_ref()); list.push_front(a.as_ref());
@@ -489,7 +505,7 @@ mod tests {
unsafe { unsafe {
// Remove missing // Remove missing
let mut list = LinkedList::<&Entry>::new(); let mut list = LinkedList::<&Entry, <&Entry as Link>::Target>::new();
list.push_front(b.as_ref()); list.push_front(b.as_ref());
list.push_front(a.as_ref()); list.push_front(a.as_ref());
@@ -503,7 +519,7 @@ mod tests {
let a = entry(5); let a = entry(5);
let b = entry(7); let b = entry(7);
let mut list = LinkedList::<&Entry>::new(); let mut list = LinkedList::<&Entry, <&Entry as Link>::Target>::new();
assert_eq!(0, list.iter().count()); assert_eq!(0, list.iter().count());
@@ -543,7 +559,7 @@ mod tests {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let mut ll = LinkedList::<&Entry>::new(); let mut ll = LinkedList::<&Entry, <&Entry as Link>::Target>::new();
let mut reference = VecDeque::new(); let mut reference = VecDeque::new();
let entries: Vec<_> = (0..ops.len()).map(|i| entry(i as i32)).collect(); let entries: Vec<_> = (0..ops.len()).map(|i| entry(i as i32)).collect();