use core::task::{Poll, Waker};
use crate::buf::PoolBuf;
use crate::sync::atomic_waker::{AtomicWaker, Registered};
use crate::sync::internal::{AtomicPtr, AtomicU8, AtomicUsize, Ordering, UnsafeCell};
const BUSY: u8 = 1;
const READY: u8 = 2;
const WAITING: u8 = 4;
const DEFER: u8 = 8;
const REQ: u8 = 16;
const SIDE_SLOTS: usize = 4;
pub(crate) struct Side<S> {
state: AtomicU8,
deferred: AtomicU8,
contended: AtomicPtr<WakerSet<SIDE_SLOTS>>,
inner: UnsafeCell<S>,
}
unsafe impl<S: Send> Send for Side<S> {}
unsafe impl<S: Send> Sync for Side<S> {}
pub(crate) trait Hooks<S> {
fn taken(&self, _st: &mut S, _after: &mut After) {}
fn serve(&self, st: &mut S, after: &mut After);
fn deferred(&self, _st: &mut S, _bits: u8, _after: &mut After) {}
fn requests(&self, _st: &mut S, _after: &mut After) {}
fn released(&self, _saw_requests: bool, _after: &mut After) {}
fn releasing(&self, _st: &mut S) {}
}
pub(crate) struct Held<'a, S> {
side: &'a Side<S>,
expect: u8,
own_ready: bool,
}
impl<S> Side<S> {
pub(crate) fn new(inner: S) -> Self {
Self {
state: AtomicU8::new(0),
deferred: AtomicU8::new(0),
contended: AtomicPtr::new(core::ptr::null_mut()),
inner: UnsafeCell::new(inner),
}
}
fn take(&self, extra: u8) -> Option<Held<'_, S>> {
let prev = self.state.fetch_or(BUSY | extra, Ordering::AcqRel);
(prev & BUSY == 0).then(|| Held {
side: self,
expect: BUSY | (extra & READY),
own_ready: extra & READY != 0,
})
}
#[cfg(any(test, ring))]
pub(crate) fn try_hold(&self) -> Option<Held<'_, S>> {
self.take(0)
}
#[cfg(test)]
pub(crate) fn words(&self) -> (u8, u8) {
(
self.state.load(Ordering::SeqCst),
self.deferred.load(Ordering::SeqCst),
)
}
pub(crate) fn take_for_request(&self) -> Option<Held<'_, S>> {
self.take(REQ)
}
pub(crate) fn poll<R>(
&self,
after: &mut After,
waker: &Waker,
hooks: &impl Hooks<S>,
mut step: impl FnMut(&mut S, &mut After) -> Poll<R>,
) -> Poll<R> {
let Some(mut held) = self.take(0).or_else(|| self.contend(waker)) else {
return Poll::Pending;
};
hooks.taken(held.get(), after);
let mut out = step(held.get(), after);
held.release_with(after, hooks, |st, after| {
if out.is_pending() {
out = step(st, after);
}
});
out
}
fn contend(&self, waker: &Waker) -> Option<Held<'_, S>> {
if !self.contended().register(waker) {
return None;
}
if self.state.fetch_or(WAITING, Ordering::AcqRel) & BUSY != 0 {
return None;
}
self.take(0)
}
#[cfg(readiness)]
pub(crate) fn drive(&self, after: &mut After, hooks: &impl Hooks<S>, defer: u8) {
if defer != 0 {
self.deferred.fetch_or(defer, Ordering::Release);
}
let extra = if defer != 0 { READY | DEFER } else { READY };
let Some(mut held) = self.take(extra) else {
return;
};
hooks.taken(held.get(), after);
held.run_deferred(hooks, after);
hooks.serve(held.get(), after);
held.release(after, hooks);
}
pub(crate) fn defer(&self, bits: u8, after: &mut After, hooks: &impl Hooks<S>) {
self.deferred.fetch_or(bits, Ordering::Release);
let Some(mut held) = self.take(DEFER) else {
return;
};
hooks.taken(held.get(), after);
held.run_deferred(hooks, after);
held.release(after, hooks);
}
pub(crate) fn get_mut(&mut self) -> &mut S {
self.inner.with_mut(|p| {
unsafe { &mut *p }
})
}
fn contended(&self) -> &WakerSet<SIDE_SLOTS> {
let mut set = self.contended.load(Ordering::Acquire);
if set.is_null() {
let made = Box::into_raw(Box::new(WakerSet::new()));
set = match self.contended.compare_exchange(
core::ptr::null_mut(),
made,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => made,
Err(theirs) => {
drop(unsafe { Box::from_raw(made) });
theirs
}
};
}
unsafe { &*set }
}
fn wake_contended(&self, after: &mut After) {
let set = self.contended.load(Ordering::Acquire);
if let Some(set) = unsafe { set.as_ref() } {
set.take_all(after);
}
}
}
impl<S> Drop for Side<S> {
fn drop(&mut self) {
let set = crate::sync::internal::take_ptr(&mut self.contended);
if !set.is_null() {
drop(unsafe { Box::from_raw(set) });
}
}
}
impl<S> Held<'_, S> {
pub(crate) fn get(&mut self) -> &mut S {
self.side.inner.with_mut(|p| {
unsafe { &mut *p }
})
}
fn run_deferred(&mut self, hooks: &impl Hooks<S>, after: &mut After) {
let bits = self.side.deferred.swap(0, Ordering::Acquire);
if bits != 0 {
hooks.deferred(self.get(), bits, after);
}
}
pub(crate) fn release(self, after: &mut After, hooks: &impl Hooks<S>) {
self.release_with(after, hooks, |_, _| {});
}
fn release_with(
mut self,
after: &mut After,
hooks: &impl Hooks<S>,
mut again: impl FnMut(&mut S, &mut After),
) {
let mut saw_requests = false;
loop {
hooks.releasing(self.get());
if self
.side
.state
.compare_exchange(self.expect, 0, Ordering::AcqRel, Ordering::Acquire)
.is_ok()
{
break;
}
let mut flags = self.side.state.swap(BUSY, Ordering::AcqRel) & !BUSY;
if core::mem::take(&mut self.own_ready) {
flags &= !READY;
}
self.expect = BUSY;
if flags & DEFER != 0 {
self.run_deferred(hooks, after);
}
if flags & READY != 0 {
hooks.serve(self.get(), after);
again(self.get(), after);
}
if flags & WAITING != 0 {
self.side.wake_contended(after);
}
if flags & REQ != 0 {
saw_requests = true;
hooks.requests(self.get(), after);
}
}
core::mem::forget(self);
hooks.released(saw_requests, after);
}
}
impl<S> Drop for Held<'_, S> {
fn drop(&mut self) {
let flags = self.side.state.swap(0, Ordering::AcqRel);
if flags & WAITING != 0 {
let mut after = After::default();
self.side.wake_contended(&mut after);
after.run();
}
}
}
pub(crate) struct WakerSet<const N: usize> {
tags: [AtomicUsize; N],
wakers: [AtomicWaker; N],
}
impl<const N: usize> WakerSet<N> {
pub(crate) fn new() -> Self {
Self {
tags: core::array::from_fn(|_| AtomicUsize::new(0)),
wakers: core::array::from_fn(|_| AtomicWaker::new()),
}
}
pub(crate) fn register(&self, waker: &Waker) -> bool {
let tag = waker.data() as usize | 1;
let own = (0..N).filter(|&i| self.tags[i].load(Ordering::Acquire) == tag);
let free = (0..N).filter(|&i| {
self.tags[i]
.compare_exchange(0, tag, Ordering::AcqRel, Ordering::Acquire)
.is_ok()
});
for i in own.chain(free) {
match self.wakers[i].try_register_displacing(waker) {
Registered::Kept(_) => return true,
Registered::WokenNow => return false,
Registered::InUse => {}
}
}
self.wakers[N - 1].register_displacing(waker)
}
pub(crate) fn take_all(&self, after: &mut After) {
for (tag, waker) in self.tags.iter().zip(&self.wakers) {
if let Some(w) = waker.take() {
after.wake_one(w);
}
tag.store(0, Ordering::Release);
}
}
}
pub(crate) fn whole_too_large(len: usize) -> Option<std::io::Error> {
crate::buf::with_pool(|pool| {
pool.filter(|p| !p.could_hold_whole(len)).map(|p| {
std::io::Error::new(
std::io::ErrorKind::OutOfMemory,
format!(
"a datagram of {len} bytes needs more IO buffer memory than the runtime allows ({} bytes); raise `Builder::io_buffer_cap`",
p.cap()
),
)
})
})
}
#[derive(Default)]
pub(crate) struct After {
wake: Wakers,
forget: Wakers,
drop_one: Option<PoolBuf>,
drop_more: Vec<PoolBuf>,
pub(crate) starved: bool,
pub(crate) register: bool,
#[cfg(ring)]
pub(crate) kick: Option<std::sync::Arc<crate::rt::shard::Shared>>,
}
impl After {
pub(crate) fn wake(&mut self, wakers: &mut Wakers) {
self.wake.append(wakers);
}
pub(crate) fn forget(&mut self, wakers: &mut Wakers) {
self.forget.append(wakers);
}
pub(crate) fn wake_one(&mut self, waker: Waker) {
self.wake.push(waker);
}
pub(crate) fn forget_one(&mut self, waker: Waker) {
self.forget.push(waker);
}
pub(crate) fn drop_buf(&mut self, buf: PoolBuf) {
match self.drop_one {
None => self.drop_one = Some(buf),
Some(_) => self.drop_more.push(buf),
}
}
pub(crate) fn run(&mut self) {
drop(self.drop_one.take());
if !self.drop_more.is_empty() {
self.drop_more.clear();
}
if !self.forget.is_empty() {
self.forget = Wakers::default();
}
self.wake.wake_all();
}
}
impl Drop for After {
fn drop(&mut self) {
self.run();
}
}
#[derive(Default)]
pub(crate) struct Wakers {
first: Option<Waker>,
rest: Vec<Waker>,
}
impl Wakers {
pub(crate) fn register(&mut self, waker: &Waker) {
let known = self
.first
.iter()
.chain(self.rest.iter())
.any(|w| w.will_wake(waker));
if known {
return;
}
self.push(waker.clone());
}
fn push(&mut self, waker: Waker) {
if self.first.is_none() {
self.first = Some(waker);
} else {
self.rest.push(waker);
}
}
pub(crate) fn is_empty(&self) -> bool {
self.first.is_none()
}
#[cfg(any(readiness, test))]
pub(crate) fn len(&self) -> usize {
usize::from(self.first.is_some()) + self.rest.len()
}
fn append(&mut self, other: &mut Wakers) {
if let Some(w) = other.first.take() {
self.push(w);
}
self.rest.append(&mut other.rest);
}
fn wake_all(&mut self) {
if let Some(w) = self.first.take() {
w.wake();
}
if !self.rest.is_empty() {
for w in self.rest.drain(..) {
w.wake();
}
}
}
}
#[cfg(all(test, not(loom)))]
mod tests;
#[cfg(all(test, loom))]
mod loom_tests;