use core::sync::atomic::compiler_fence;
use core::task::{Poll, Waker};
use crate::rt::handoff::fence;
use crate::sync::atomic_waker::{AtomicWaker, Registered};
use crate::sync::internal::fence as full_fence;
use crate::sync::internal::{AtomicBool, AtomicPtr, AtomicU8, AtomicUsize, Ordering, UnsafeCell};
mod after;
#[cfg(any(ring, test))]
pub(crate) use after::Unwound;
#[cfg(test)]
use after::run_unwound;
pub(crate) use after::{After, Wakers};
const BUSY: u8 = 1;
const READY: u8 = 2;
const WAITING: u8 = 4;
const DEFER: u8 = 8;
const REQ: u8 = 16;
#[cfg(test)]
pub(crate) static WITNESS: [core::sync::atomic::AtomicUsize; 3] = [
core::sync::atomic::AtomicUsize::new(0),
core::sync::atomic::AtomicUsize::new(0),
core::sync::atomic::AtomicUsize::new(0),
];
#[cfg(test)]
fn witness(path: usize) {
WITNESS[path].fetch_add(1, core::sync::atomic::Ordering::Relaxed);
}
const UNFENCED: u8 = 2;
const SIDE_SLOTS: usize = 4;
pub(crate) struct Side<S> {
state: AtomicU8,
deferred: AtomicU8,
contended: AtomicPtr<WakerSet<SIDE_SLOTS>>,
own: AtomicU8,
intent: AtomicU8,
shared: AtomicBool,
owner: AtomicUsize,
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) {}
fn owner(&self) -> usize {
0
}
fn claimed(&self) {}
fn nudge(&self) {}
fn releases_inline(&self) -> bool {
false
}
}
pub(crate) struct Held<'a, S> {
side: &'a Side<S>,
bits: u32,
}
const EXPECT: u32 = 0xff;
const OWN_READY: u32 = 1 << 8;
const BIASED: u32 = 1 << 9;
const SAW_REQUESTS: u32 = 1 << 10;
const REST_SHIFT: u32 = 16;
#[cfg(test)]
struct Nobody;
#[cfg(test)]
impl<S> Hooks<S> for Nobody {
fn serve(&self, _: &mut S, _: &mut After) {}
}
enum Take<'a, S> {
Held(Held<'a, S>),
Busy,
Left,
Unserved,
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum Role {
Shared,
Owner,
Foreign,
}
impl Role {
fn through_word(self) -> Self {
match self {
Role::Owner => Role::Shared,
other => other,
}
}
}
impl<S> Side<S> {
pub(crate) fn new(inner: S) -> Self {
Self::with_mode(inner, true)
}
pub(crate) fn biased(inner: S) -> Self {
Self::with_mode(inner, !fence::available())
}
fn with_mode(inner: S, shared: bool) -> Self {
Self {
state: AtomicU8::new(0),
deferred: AtomicU8::new(0),
contended: AtomicPtr::new(core::ptr::null_mut()),
own: AtomicU8::new(0),
intent: AtomicU8::new(0),
shared: AtomicBool::new(shared),
owner: AtomicUsize::new(0),
inner: UnsafeCell::new(inner),
}
}
#[inline(always)]
fn role(&self, hooks: &impl Hooks<S>) -> Role {
if self.shared.load(Ordering::Acquire) {
return Role::Shared;
}
let who = hooks.owner();
let owns =
who != 0 && (self.owner.load(Ordering::Relaxed) == who || self.claim(who, hooks));
if owns && self.own.load(Ordering::Relaxed) == 0 {
Role::Owner
} else {
Role::Foreign
}
}
#[cold]
#[inline(never)]
fn claim(&self, who: usize, hooks: &impl Hooks<S>) -> bool {
let claimed = self.owner.load(Ordering::Relaxed) == 0
&& self
.owner
.compare_exchange(0, who, Ordering::AcqRel, Ordering::Relaxed)
.is_ok();
if claimed {
hooks.claimed();
}
claimed
}
#[inline(always)]
fn try_biased(&self) -> Option<Held<'_, S>> {
self.own.store(1, Ordering::Relaxed);
compiler_fence(Ordering::SeqCst);
if self.intent.load(Ordering::Relaxed) == 0 && !self.shared.load(Ordering::Relaxed) {
return Some(Held {
side: self,
bits: u32::from(BUSY) | BIASED,
});
}
self.biased_refused();
None
}
#[cold]
#[inline(never)]
fn biased_refused(&self) {
self.own.store(0, Ordering::Relaxed);
self.switch_if_unfenced();
#[cfg(test)]
witness(2);
}
fn take_word(&self, extra: u8) -> Take<'_, S> {
let prev = self.state.fetch_or(BUSY | extra, Ordering::AcqRel);
if prev & BUSY == 0 {
Take::Held(Held {
side: self,
bits: u32::from(BUSY | (extra & READY))
| if extra & READY != 0 { OWN_READY } else { 0 },
})
} else {
Take::Busy
}
}
fn take_foreign(&self, extra: u8, hooks: &impl Hooks<S>) -> Take<'_, S> {
self.intent.fetch_or(1, Ordering::SeqCst);
if !fence::heavy_retried() {
return self.unserved(extra, hooks);
}
if self.own.load(Ordering::Acquire) == 0 {
self.shared.store(true, Ordering::Relaxed);
return self.take_word(extra);
}
if extra != 0 {
let prev = self.state.fetch_or(extra, Ordering::AcqRel);
if prev & BUSY == 0 && self.own.load(Ordering::Acquire) == 0 {
self.shared.store(true, Ordering::Relaxed);
return self.take_word(extra);
}
}
#[cfg(test)]
witness(0);
Take::Left
}
fn unserved(&self, extra: u8, hooks: &impl Hooks<S>) -> Take<'_, S> {
full_fence(Ordering::SeqCst);
if self.owner.load(Ordering::SeqCst) == 0 {
self.shared.store(true, Ordering::Relaxed);
return self.take_word(extra);
}
self.intent.fetch_or(UNFENCED, Ordering::SeqCst);
if extra != 0 {
self.state.fetch_or(extra, Ordering::AcqRel);
hooks.nudge();
}
Take::Unserved
}
#[inline(always)]
fn take_as(&self, role: Role, extra: u8, hooks: &impl Hooks<S>) -> Take<'_, S> {
match role {
Role::Shared => self.take_word(extra),
Role::Owner => match self.try_biased() {
Some(held) => Take::Held(held),
None => self.take_word(extra),
},
Role::Foreign => self.take_foreign(extra, hooks),
}
}
#[inline]
fn take(&self, extra: u8, hooks: &impl Hooks<S>) -> Option<Held<'_, S>> {
match self.take_as(self.role(hooks), extra, hooks) {
Take::Held(held) => Some(held),
Take::Busy | Take::Left | Take::Unserved => None,
}
}
#[cfg(test)]
pub(crate) fn try_hold(&self) -> Option<Held<'_, S>> {
self.take(0, &Nobody)
}
#[cfg(completion)]
#[inline]
pub(crate) fn try_hold_for(&self, hooks: &impl Hooks<S>) -> Option<Held<'_, S>> {
self.take(0, hooks)
}
#[cfg(readiness)]
#[inline(always)]
pub(crate) fn biased_to(&self, who: usize) -> bool {
!self.shared.load(Ordering::Relaxed)
&& self.intent.load(Ordering::Relaxed) == 0
&& self.owner.load(Ordering::Relaxed) == who
}
#[cfg(test)]
pub(crate) fn is_shared(&self) -> bool {
self.shared.load(Ordering::SeqCst)
}
#[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, hooks: &impl Hooks<S>) -> Option<Held<'_, S>> {
self.take(REQ, hooks)
}
#[inline(always)]
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 mut held = match self.take_as(self.role(hooks), 0, hooks) {
Take::Held(held) => held,
missed => match self.poll_missed(missed, waker, hooks) {
Some(held) => held,
None => return Poll::Pending,
},
};
hooks.taken(held.get(), after);
let mut out = Poll::Pending;
loop {
if out.is_pending() {
out = step(held.get(), after);
}
match held.release_or_again(after, hooks) {
Ok(()) => return out,
Err(again) => held = again,
}
}
}
#[cold]
#[inline(never)]
fn poll_missed<'a>(
&'a self,
take: Take<'a, S>,
waker: &Waker,
hooks: &impl Hooks<S>,
) -> Option<Held<'a, S>> {
match take {
Take::Held(held) => Some(held),
Take::Busy => self.contend(waker, false),
Take::Left => self.contend(waker, true),
Take::Unserved => {
self.leave_waker(waker, hooks);
None
}
}
}
#[inline(always)]
fn contend(&self, waker: &Waker, left: bool) -> Option<Held<'_, S>> {
if !self.contended().register(waker) {
return None;
}
if self.state.fetch_or(WAITING, Ordering::AcqRel) & BUSY != 0 {
return None;
}
if left {
if self.own.load(Ordering::Acquire) != 0 {
return None;
}
self.shared.store(true, Ordering::Relaxed);
}
match self.take_word(0) {
Take::Held(held) => Some(held),
Take::Busy | Take::Left | Take::Unserved => None,
}
}
fn leave_waker(&self, waker: &Waker, hooks: &impl Hooks<S>) {
if self.contended().register(waker) {
self.state.fetch_or(WAITING, Ordering::AcqRel);
hooks.nudge();
}
}
#[cfg(readiness)]
#[inline]
pub(crate) fn drive(&self, after: &mut After, hooks: &impl Hooks<S>, defer: u8) {
let role = self.role(hooks);
let (mut held, bits) = match self.hold_biased(role) {
Some(held) => (held, defer),
None => {
if defer != 0 {
self.deferred.fetch_or(defer, Ordering::Release);
}
let extra = if defer != 0 { READY | DEFER } else { READY };
match self.take_as(role.through_word(), extra, hooks) {
Take::Held(held) => (held, 0),
Take::Busy | Take::Left | Take::Unserved => return,
}
}
};
hooks.taken(held.get(), after);
held.run_deferred(bits, 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>) {
let role = self.role(hooks);
let (mut held, direct) = match self.hold_biased(role) {
Some(held) => (held, bits),
None => {
self.deferred.fetch_or(bits, Ordering::Release);
match self.take_as(role.through_word(), DEFER, hooks) {
Take::Held(held) => (held, 0),
Take::Busy | Take::Left | Take::Unserved => return,
}
}
};
hooks.taken(held.get(), after);
held.run_deferred(direct, hooks, after);
held.release(after, hooks);
}
fn hold_biased(&self, role: Role) -> Option<Held<'_, S>> {
if role == Role::Owner {
self.try_biased()
} else {
None
}
}
pub(crate) fn get_mut(&mut self) -> &mut S {
self.inner.with_mut(|p| {
unsafe { &mut *p }
})
}
fn switch_if_unfenced(&self) {
if self.intent.load(Ordering::Relaxed) & UNFENCED != 0 {
self.shared.store(true, Ordering::Release);
}
}
#[inline(always)]
fn release_biased(&self) -> bool {
self.own.store(0, Ordering::Release);
compiler_fence(Ordering::SeqCst);
if self.intent.load(Ordering::Relaxed) == 0 {
return false;
}
self.release_biased_came()
}
#[cold]
#[inline(never)]
fn release_biased_came(&self) -> bool {
self.switch_if_unfenced();
let left = self.state.fetch_or(0, Ordering::SeqCst);
if left & (WAITING | READY | DEFER | REQ) == 0 {
return false;
}
let held = self.state.fetch_or(BUSY, Ordering::AcqRel) & BUSY == 0;
#[cfg(test)]
witness(1);
held
}
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, direct: u8, hooks: &impl Hooks<S>, after: &mut After) {
let mut bits = direct;
if self.side.deferred.load(Ordering::Relaxed) != 0 {
bits |= self.side.deferred.swap(0, Ordering::Acquire);
}
if bits != 0 {
hooks.deferred(self.get(), bits, after);
}
}
#[inline]
pub(crate) fn release(mut self, after: &mut After, hooks: &impl Hooks<S>) {
while let Err(again) = self.release_or_again(after, hooks) {
self = again;
}
}
#[inline(always)]
fn release_or_again(mut self, after: &mut After, hooks: &impl Hooks<S>) -> Result<(), Self> {
if self.biased() {
hooks.releasing(self.get());
if !self.side.release_biased() {
core::mem::forget(self);
hooks.released(false, after);
return Ok(());
}
self.bits = (self.bits & !(BIASED | EXPECT)) | u32::from(BUSY);
} else if hooks.releases_inline() && self.bits >> REST_SHIFT == 0 {
hooks.releasing(self.get());
if self
.side
.state
.compare_exchange(self.expect(), 0, Ordering::AcqRel, Ordering::Acquire)
.is_ok()
{
let saw_requests = self.bits & SAW_REQUESTS != 0;
core::mem::forget(self);
hooks.released(saw_requests, after);
return Ok(());
}
}
self.release_word(after, hooks)
}
#[inline(always)]
fn biased(&self) -> bool {
self.bits & BIASED != 0
}
#[inline(always)]
fn expect(&self) -> u8 {
(self.bits & EXPECT) as u8
}
#[inline(never)]
fn release_word(mut self, after: &mut After, hooks: &impl Hooks<S>) -> Result<(), Self> {
self.finish_flags(after, hooks);
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 self.bits & OWN_READY != 0 {
flags &= !READY;
}
self.bits = (self.bits & SAW_REQUESTS)
| u32::from(BUSY)
| (u32::from(flags & (WAITING | REQ)) << REST_SHIFT);
if flags & DEFER != 0 {
self.run_deferred(0, hooks, after);
}
if flags & READY != 0 {
hooks.serve(self.get(), after);
return Err(self);
}
self.finish_flags(after, hooks);
}
let saw_requests = self.bits & SAW_REQUESTS != 0;
core::mem::forget(self);
hooks.released(saw_requests, after);
Ok(())
}
fn finish_flags(&mut self, after: &mut After, hooks: &impl Hooks<S>) {
let flags = (self.bits >> REST_SHIFT) as u8;
self.bits &= !(0xff << REST_SHIFT);
if flags & WAITING != 0 {
self.side.wake_contended(after);
}
if flags & REQ != 0 {
self.bits |= SAW_REQUESTS;
hooks.requests(self.get(), after);
}
}
}
impl<S> Drop for Held<'_, S> {
fn drop(&mut self) {
if self.biased() {
self.side.own.store(0, Ordering::Relaxed);
compiler_fence(Ordering::SeqCst);
if self.side.intent.load(Ordering::Relaxed) != 0
&& self.side.state.fetch_or(0, Ordering::SeqCst) & WAITING != 0
{
let mut after = After::default();
self.side.wake_contended(&mut after);
}
return;
}
let flags = self.side.state.swap(0, Ordering::AcqRel);
if flags & WAITING != 0 {
let mut after = After::default();
self.side.wake_contended(&mut after);
}
}
}
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()),
}
}
#[inline]
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)
}
#[inline]
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(
known: Option<&crate::buf::Pool>,
len: usize,
) -> Option<std::io::Error> {
crate::buf::with_pool_or(known, |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.room()
),
)
})
})
}
#[cfg(all(test, not(loom)))]
mod tests;
#[cfg(all(test, not(loom)))]
mod biased_tests;
#[cfg(all(test, loom))]
mod loom_tests;