use core::cell::{Cell, UnsafeCell};
use core::future::Future;
use core::mem::ManuallyDrop;
use core::pin::Pin;
use core::ptr::NonNull;
use core::task::{Poll, Waker};
use pin_project::{pin_project, pinned_drop};
use crate::util::{NotSendMarker, Captures};
struct WakerCell(UnsafeCell<Waker>);
impl WakerCell {
fn new(waker: Waker) -> Self {
Self(UnsafeCell::new(waker))
}
fn update(&self, waker: Waker) {
*unsafe { &mut *self.0.get() } = waker;
}
fn wake_by_ref(&self) {
unsafe { &*self.0.get() }.wake_by_ref()
}
}
#[pin_project(PinnedDrop)]
pub struct Node<T, M = ()> {
prev: Cell<NonNull<Self>>,
next: Cell<NonNull<Self>>,
waker: WakerCell,
contents: T,
meta: M,
_marker: NotSendMarker,
}
impl<T> Node<T> {
#[inline(always)]
pub unsafe fn new(contents: T, waker: Waker) -> ManuallyDrop<Self> {
unsafe {
Self::new_with_meta(contents, (), waker)
}
}
}
impl<T, M> Node<T, M> {
pub unsafe fn new_with_meta(contents: T, meta: M, waker: Waker) -> ManuallyDrop<Self> {
ManuallyDrop::new(Node {
prev: Cell::new(NonNull::dangling()),
next: Cell::new(NonNull::dangling()),
waker: WakerCell::new(waker),
contents,
meta,
_marker: NotSendMarker::default(),
})
}
pub unsafe fn finish_init(node: Pin<&mut Self>) {
let nnn = NonNull::from(&*node);
node.next.set(nnn);
node.prev.set(nnn);
}
pub fn detach(self: Pin<&Self>) {
unsafe {
self.prev.get().as_ref().next.set(self.next.get());
self.next.get().as_ref().prev.set(self.prev.get());
}
self.prev.set(NonNull::from(&*self));
self.next.set(NonNull::from(&*self));
}
pub fn is_detached(&self) -> bool {
self.prev.get() == NonNull::from(self)
}
pub fn meta(&self) -> &M {
&self.meta
}
}
#[pinned_drop]
impl<T, M> PinnedDrop for Node<T, M> {
fn drop(self: Pin<&mut Self>) {
self.as_ref().detach();
}
}
fn debug_link<'a, T>(parent: &T, cell: &'a Cell<NonNull<T>>) -> &'a dyn core::fmt::Debug {
#[derive(Debug)]
struct Uninitialized;
#[derive(Debug)]
struct SelfLinked;
let ptr = cell.get();
let rawptr: *const _ = ptr.as_ptr();
if ptr == NonNull::dangling() {
&Uninitialized
} else if rawptr == parent {
&SelfLinked
} else {
cell
}
}
impl<T: core::fmt::Debug, M: core::fmt::Debug> core::fmt::Debug for Node<T, M> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Node")
.field("prev", debug_link(self, &self.prev))
.field("next", debug_link(self, &self.next))
.field("waker", &"...")
.field("contents", &self.contents)
.field("meta", &self.meta)
.finish()
}
}
#[pin_project(PinnedDrop)]
pub struct List<T, M = ()> {
#[pin]
root: Node<T, M>,
_marker: NotSendMarker,
}
impl<T: Default> List<T> {
pub unsafe fn new() -> ManuallyDrop<List<T>> {
unsafe {
Self::new_with_meta(())
}
}
}
impl<T: Default, M> List<T, M> {
pub unsafe fn new_with_meta(meta: M) -> ManuallyDrop<List<T, M>> {
let node = unsafe {
Node::new_with_meta(
T::default(),
meta,
#[allow(deprecated)]
crate::exec::noop_waker(),
)
};
ManuallyDrop::new(List {
root: ManuallyDrop::into_inner(node),
_marker: NotSendMarker::default(),
})
}
}
impl<T, M> List<T, M> {
pub unsafe fn finish_init(list: Pin<&mut Self>) {
unsafe {
Node::finish_init(list.project().root);
}
}
pub fn is_empty(&self) -> bool {
self.root.is_detached()
}
}
impl<T: PartialOrd, M> List<T, M> {
pub fn insert_and_wait<'list, 'node>(
self: Pin<&'list Self>,
node: Pin<&'node mut Node<T, M>>,
) -> impl Future<Output = ()> + Captures<(&'list Self, &'node mut Node<T>)> {
let node = node.into_ref();
WaitForDetach {
node,
list: self,
state: Cell::new(WaitState::NotYetAttached),
}
}
pub fn insert_and_wait_with_cleanup<'list, 'node, F: 'node + FnOnce()>(
self: Pin<&'list Self>,
node: Pin<&'node mut Node<T, M>>,
cleanup: F,
) -> impl Future<Output = ()> + Captures<(&'list Self, &'node mut Node<T>)> {
let node = node.into_ref();
WaitWithCleanup {
inner: WaitForDetach {
node,
list: self,
state: Cell::new(WaitState::NotYetAttached),
},
cleanup: Some(cleanup),
}
}
pub fn wake_thru(self: Pin<&Self>, threshold: T) {
self.wake_while(|n| n.contents <= threshold);
}
pub fn wake_less_than(self: Pin<&Self>, threshold: T) {
self.wake_thru(threshold)
}
pub fn wake_while(self: Pin<&Self>, mut pred: impl FnMut(Pin<&Node<T, M>>) -> bool) -> bool {
let mut changes = false;
let mut candidate = self.root.next.get();
while candidate != NonNull::from(&self.root) {
let cref = unsafe { Pin::new_unchecked(candidate.as_ref()) };
if !pred(cref) {
break;
}
let next = cref.next.get();
cref.detach();
cref.waker.wake_by_ref();
changes = true;
candidate = next;
}
changes
}
pub fn wake_one_if(self: Pin<&Self>, pred: impl FnOnce(Pin<&Node<T, M>>) -> bool) -> bool {
let candidate = self.root.next.get();
if candidate != NonNull::from(&self.root) {
let cref = unsafe { Pin::new_unchecked(candidate.as_ref()) };
if pred(cref) {
cref.detach();
cref.waker.wake_by_ref();
return true;
}
}
false
}
}
impl<M> List<(), M> {
pub fn wake_all(self: Pin<&Self>) {
self.wake_thru(())
}
pub fn wake_one(self: Pin<&Self>) -> bool {
self.wake_one_if(|_| true)
}
}
#[pinned_drop]
impl<T, M> PinnedDrop for List<T, M> {
fn drop(self: Pin<&mut Self>) {
#[cfg(debug_assertions)]
cheap_assert!(self.is_empty());
}
}
impl<T: core::fmt::Debug, M: core::fmt::Debug> core::fmt::Debug for List<T, M> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("List")
.field("last", debug_link(&self.root, &self.root.prev))
.field("first", debug_link(&self.root, &self.root.next))
.finish()
}
}
struct WaitForDetach<'node, 'list, T, M> {
node: Pin<&'node Node<T, M>>,
list: Pin<&'list List<T, M>>,
state: Cell<WaitState>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum WaitState {
NotYetAttached,
Attached,
DetachedAndPolled,
}
impl<T: PartialOrd, M> Future for WaitForDetach<'_, '_, T, M> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut core::task::Context<'_>)
-> Poll<Self::Output>
{
match self.state.get() {
WaitState::NotYetAttached => {
let node = self.node;
let nnn = NonNull::from(&*node);
{
debug_assert!(node.prev.get() == nnn);
debug_assert!(node.next.get() == nnn);
let mut candidate = self.list.root.prev.get();
while candidate != NonNull::from(&self.list.root) {
let cref = unsafe { candidate.as_ref() };
if cref.contents <= node.contents {
break;
}
candidate = cref.prev.get();
}
node.prev.set(candidate);
let cref = unsafe { candidate.as_ref() };
node.next.set(cref.next.get());
unsafe {
cref.next.get().as_ref().prev.set(nnn);
}
cref.next.set(nnn);
}
self.state.set(WaitState::Attached);
self.node.waker.update(cx.waker().clone());
Poll::Pending
}
WaitState::Attached => {
if self.node.is_detached() {
self.state.set(WaitState::DetachedAndPolled);
Poll::Ready(())
} else {
self.node.waker.update(cx.waker().clone());
Poll::Pending
}
}
WaitState::DetachedAndPolled => Poll::Ready(()),
}
}
}
impl<T, M> Drop for WaitForDetach<'_, '_, T, M> {
fn drop(&mut self) {
if self.state.get() == WaitState::Attached {
self.node.detach();
}
}
}
#[pin_project(PinnedDrop)]
struct WaitWithCleanup<'node, 'list, T, M, F: FnOnce()> {
#[pin]
inner: WaitForDetach<'node, 'list, T, M>,
cleanup: Option<F>,
}
impl<T: PartialOrd, M, F: FnOnce()> Future for WaitWithCleanup<'_, '_, T, M, F> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut core::task::Context<'_>)
-> Poll<Self::Output>
{
self.project().inner.poll(cx)
}
}
#[pinned_drop]
impl<T, M, F: FnOnce()> PinnedDrop for WaitWithCleanup<'_, '_, T, M, F> {
fn drop(self: Pin<&mut Self>) {
if self.inner.state.get() == WaitState::Attached && self.inner.node.is_detached() {
if let Some(cleanup) = self.project().cleanup.take() {
cleanup();
}
}
}
}
#[macro_export]
macro_rules! create_list {
($var:ident) => {
#[allow(unused_unsafe)]
let mut $var = core::pin::pin!(unsafe {
core::mem::ManuallyDrop::into_inner($crate::list::List::new())
});
#[allow(unused_unsafe)]
unsafe {
$crate::list::List::finish_init($var.as_mut());
}
};
}
#[macro_export]
macro_rules! create_list_with_meta {
($var:ident, $met:expr) => {
let $var = $met;
#[allow(unused_unsafe)]
let mut $var = core::pin::pin!(unsafe {
core::mem::ManuallyDrop::into_inner($crate::list::List::new_with_meta($var))
});
#[allow(unused_unsafe)]
unsafe {
$crate::list::List::finish_init($var.as_mut());
}
};
($var:ident) => { $crate::create_list_with_meta!($var, core::default::Default::default()) };
}
#[macro_export]
macro_rules! create_node {
($var:ident, $dl:expr, $w: expr) => {
let mut $var = core::pin::pin!(unsafe {
core::mem::ManuallyDrop::into_inner($crate::list::Node::new(
$dl, $w,
))
});
unsafe {
$crate::list::Node::finish_init($var.as_mut());
}
};
($var:ident, $dl:expr) => {
#[allow(deprecated)]
$crate::create_node!($var, $dl, $crate::exec::noop_waker())
};
}
#[macro_export]
macro_rules! create_node_with_meta {
($var:ident, $dl:expr, $meta:expr, $w: expr) => {
let $var = ($dl, $meta, $w);
let mut $var = core::pin::pin!(unsafe {
core::mem::ManuallyDrop::into_inner($crate::list::Node::new_with_meta(
$var.0, $var.1, $var.2,
))
});
unsafe {
$crate::list::Node::finish_init($var.as_mut());
}
};
($var:ident, $dl:expr, $meta:expr) => {
$crate::create_node_with_meta!($var, $dl, $meta, $crate::exec::noop_waker())
}
}