#![no_std]
#![allow(clippy::ptr_eq)]
use arrayvec::ArrayVec;
use core::cell::UnsafeCell;
use core::marker::PhantomPinned;
use core::mem::offset_of;
use core::mem::MaybeUninit;
use core::pin::Pin;
use core::ptr;
use core::ptr::addr_of;
use core::ptr::addr_of_mut;
use core::ptr::NonNull;
use core::sync::atomic::AtomicPtr;
use core::sync::atomic::Ordering;
use core::task::Waker;
#[cfg(doc)]
use core::future::Future;
const EXTRACT_CAPACITY: usize = 7;
#[derive(Copy, Clone, Debug)]
struct Pointers {
next: *mut Pointers,
prev: *mut Pointers,
_pinned: PhantomPinned,
}
impl Default for Pointers {
fn default() -> Self {
Self {
next: ptr::null_mut(),
prev: ptr::null_mut(),
_pinned: PhantomPinned,
}
}
}
impl Pointers {
unsafe fn ensure_cyclic(node: *mut Pointers) {
unsafe {
let nextp = addr_of_mut!((*node).next);
if nextp.read().is_null() {
Self::make_cyclic(node, nextp);
}
}
}
#[cold]
unsafe fn make_cyclic(node: *mut Pointers, nextp: *mut *mut Pointers) {
let prevp = addr_of_mut!((*node).prev);
assert!(
prevp.read().is_null(),
"either both are null or neither are"
);
nextp.write(node);
prevp.write(node);
}
fn is_empty(&self) -> bool {
self.next.is_null() || (self as *const Pointers == self.next)
}
unsafe fn unlink(node: *mut Pointers) {
unsafe {
let nextp = addr_of_mut!((*node).next);
let prevp = addr_of_mut!((*node).prev);
let next = nextp.read();
let prev = prevp.read();
assert!(!next.is_null());
assert!(!prev.is_null());
addr_of_mut!((*prev).next).write(next);
addr_of_mut!((*next).prev).write(prev);
nextp.write(ptr::null_mut());
prevp.write(ptr::null_mut());
}
}
unsafe fn link_back(list: *mut Pointers, node: *mut Pointers) {
unsafe {
Pointers::ensure_cyclic(list);
let nodenextp = addr_of_mut!((*node).next);
let nodeprevp = addr_of_mut!((*node).prev);
debug_assert!(nodenextp.read().is_null());
debug_assert!(nodeprevp.read().is_null());
nodenextp.write(list);
nodeprevp.write(addr_of_mut!((*list).prev).read());
addr_of_mut!((*(*list).prev).next).write(node);
addr_of_mut!((*list).prev).write(node);
}
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd)]
#[repr(transparent)]
struct Generation(u64);
impl Generation {
fn bump(&mut self) {
self.0 += 1;
}
}
#[derive(Copy, Clone, Debug)]
#[repr(transparent)]
pub struct ExtractionRound(Generation);
#[derive(Debug, Default)]
pub struct WakerList {
pointers: Pointers,
generation: Generation,
}
unsafe impl Send for WakerList {}
#[allow(non_snake_case)]
#[inline(never)]
#[cold]
extern "C" fn MUST_UNLINK_ALL_WakerSlots_BEFORE_DROPPING_LIST() -> ! {
panic!("Must unlink all WakerSlots before dropping list")
}
impl Drop for WakerList {
fn drop(&mut self) {
if !self.pointers.is_empty() {
MUST_UNLINK_ALL_WakerSlots_BEFORE_DROPPING_LIST();
}
}
}
#[inline(always)]
#[cold]
fn cold() {}
impl WakerList {
pub fn new() -> Self {
Default::default()
}
pub fn is_empty(self: Pin<&mut Self>) -> bool {
self.pointers.is_empty()
}
pub fn link(
mut self: Pin<&mut Self>,
slot: Pin<&mut WakerSlot>,
waker: Waker,
) {
unsafe {
let generation = self.as_mut().get_unchecked_mut().generation;
let selfp = self.get_unchecked_mut() as *mut Self;
let slotp = slot.get_unchecked_mut() as *mut WakerSlot;
if let Some(slot_list) = (*slotp).is_linked_locked() {
cold();
if selfp != slot_list.as_ptr() {
#[inline(never)]
#[cold]
fn relink_panic() {
panic!("relinking a WakerSlot must use the same list");
}
relink_panic();
}
Pointers::unlink(UnsafeCell::raw_get(addr_of!(
(*slotp).pointers
)));
(*slotp).waker.assume_init_drop();
}
Pointers::link_back(
addr_of_mut!((*selfp).pointers),
UnsafeCell::raw_get(addr_of!((*slotp).pointers)),
);
addr_of_mut!((*slotp).waker).write(MaybeUninit::new(
UnsafeCell::new(SlotStorage { waker, generation }),
));
(*slotp).list.store(selfp, Ordering::Release);
}
}
pub fn unlink(self: Pin<&mut Self>, mut slot: Pin<&mut WakerSlot>) {
let slot_list = slot.list.load(Ordering::Relaxed);
if slot_list.is_null() {
return;
}
unsafe {
let list = self.get_unchecked_mut() as *mut _;
assert_eq!(list, slot_list, "slot must be unlinked from same list");
let slotp = slot.as_mut().get_unchecked_mut() as *mut WakerSlot;
Pointers::unlink(UnsafeCell::raw_get(addr_of!((*slotp).pointers)));
let slot = slot.get_unchecked_mut();
slot.waker.assume_init_drop();
slot.list.store(ptr::null_mut(), Ordering::Release);
}
}
pub fn begin_extraction(mut self: Pin<&mut Self>) -> ExtractionRound {
let current = self.as_mut().generation;
unsafe { self.get_unchecked_mut() }.generation.bump();
ExtractionRound(current)
}
pub fn extract_some_wakers(
self: Pin<&mut Self>,
round: ExtractionRound,
wakers: &mut ExtractedWakers,
) -> bool {
assert!(wakers.wakers.is_empty());
let wakers = &mut wakers.wakers;
let generation = round.0;
let mut more = false;
unsafe {
let selfp = self.get_unchecked_mut() as *mut Self;
let listp = addr_of_mut!((*selfp).pointers);
let mut p = addr_of_mut!((*listp).next).read();
if p.is_null() {
debug_assert!(addr_of_mut!((*listp).prev).read().is_null());
return false;
}
loop {
if p == listp {
debug_assert_eq!(addr_of_mut!((*listp).next).read(), listp);
debug_assert_eq!(addr_of_mut!((*listp).prev).read(), listp);
break;
}
let slot = p
.byte_sub(offset_of!(WakerSlot, pointers))
.cast::<WakerSlot>();
let slot_generation = ptr::addr_of!((*slot).waker)
.read()
.assume_init_mut()
.get_mut()
.generation;
if generation < slot_generation {
break;
}
if wakers.is_full() {
more = true;
break;
}
let waker = ptr::addr_of!((*slot).waker)
.read()
.assume_init_read()
.into_inner();
wakers.push(waker.waker);
let next = addr_of_mut!((*p).next).read();
let prev = addr_of_mut!((*p).prev).read();
addr_of_mut!((*next).prev).write(prev);
addr_of_mut!((*prev).next).write(next);
addr_of_mut!((*p).next).write(ptr::null_mut());
addr_of_mut!((*p).prev).write(ptr::null_mut());
(*slot).list.store(ptr::null_mut(), Ordering::Release);
p = next;
}
};
more
}
}
#[derive(Debug, Default)]
pub struct ExtractedWakers {
wakers: ArrayVec<Waker, EXTRACT_CAPACITY>,
}
impl ExtractedWakers {
#[inline(always)]
pub fn new() -> Self {
Self::default()
}
pub fn is_empty(&self) -> bool {
self.wakers.is_empty()
}
pub fn wake_all(&mut self) {
for waker in self.wakers.drain(..) {
waker.wake();
}
}
}
#[derive(Debug)]
pub struct WakerSlot {
list: AtomicPtr<WakerList>,
pointers: UnsafeCell<Pointers>,
waker: MaybeUninit<UnsafeCell<SlotStorage>>,
}
struct SlotStorage {
waker: Waker,
generation: Generation,
}
unsafe impl Send for WakerSlot {}
impl Default for WakerSlot {
fn default() -> Self {
Self {
list: AtomicPtr::new(ptr::null_mut()),
pointers: UnsafeCell::new(Pointers::default()),
waker: MaybeUninit::uninit(),
}
}
}
#[allow(non_snake_case)]
#[inline(never)]
#[cold]
extern "C" fn MUST_UNLINK_WakerSlot_BEFORE_DROP() -> ! {
panic!("Must unlink WakerSlot before drop")
}
impl Drop for WakerSlot {
fn drop(&mut self) {
if self.is_linked() {
MUST_UNLINK_WakerSlot_BEFORE_DROP()
}
}
}
impl WakerSlot {
pub fn new() -> WakerSlot {
Default::default()
}
pub fn is_linked(&self) -> bool {
!self.list.load(Ordering::Acquire).is_null()
}
fn is_linked_locked(&self) -> Option<NonNull<WakerList>> {
NonNull::new(self.list.load(Ordering::Relaxed))
}
}