#[cfg(unix)]
mod direct;
mod hooks;
#[cfg(completion)]
pub(crate) mod ops;
mod owner;
#[cfg(port)]
pub(crate) mod port;
#[cfg(unix)]
mod quick;
mod read;
pub(crate) mod requests;
#[cfg(ring)]
pub(crate) mod ring;
pub(crate) mod write;
use self::hooks::SourceHooks;
use core::cell::UnsafeCell;
#[cfg(completion)]
use core::sync::atomic::AtomicBool;
use core::sync::atomic::{AtomicU8, AtomicU32, AtomicU64, Ordering};
use std::io;
use std::net::SocketAddr;
#[cfg(unix)]
use std::os::unix::net as unix;
use std::sync::Arc;
use std::task::{Poll, Wake};
use self::read::ReadState;
use self::requests::Ask;
use self::write::WriteState;
use super::raw::{AtomicRaw, Raw};
use super::side::{After, Side};
use crate::rt::context;
#[cfg(readiness)]
use crate::rt::park::Readiness;
use crate::rt::shard::{IoOp, Shared};
#[derive(Clone, Copy)]
pub(crate) struct Io<'a> {
pub(crate) via: Via<'a>,
pub(crate) shard: Option<&'a crate::rt::shard::Shard>,
}
impl Io<'_> {
pub(crate) fn pool(&self) -> Option<&crate::buf::Pool> {
self.shard.map(crate::rt::shard::Shard::pool)
}
}
#[derive(Clone, Copy)]
pub(crate) enum Via<'a> {
#[cfg(readiness)]
Fd(Raw),
#[cfg(completion)]
Ops(route::Route<'a>),
#[cfg(not(completion))]
#[expect(dead_code, reason = "uninhabited: it only carries the lifetime")]
None(core::convert::Infallible, core::marker::PhantomData<&'a ()>),
}
#[cfg(completion)]
pub(crate) mod route {
#[cfg(port)]
pub(crate) use super::port::PortIo as Route;
#[cfg(ring)]
pub(crate) use super::ring::RingIo as Route;
}
const UNSENT: u8 = 1;
#[cfg(readiness)]
const WRITE_IDLE: u8 = 2;
const NO_FD: Raw = super::raw::NONE;
const NO_TOKEN: u64 = u64::MAX;
const UNREGISTERED: u8 = 0;
const REGISTERING: u8 = 1;
const REGISTERED: u8 = 2;
mod read_work {
pub(super) const UNSURE: u8 = 1;
pub(super) const UNSTARVE: u8 = 2;
pub(super) const ABANDON: u8 = 4;
#[cfg(completion)]
pub(super) const DONE: u8 = 8;
#[cfg(completion)]
pub(super) const SUBMIT: u8 = 16;
#[cfg(ring)]
pub(super) const ARRIVALS: u8 = 32;
#[cfg(port)]
pub(super) const LOOK: u8 = 64;
}
mod write_work {
pub(super) const UNSTARVE: u8 = 1;
pub(super) const ABANDON: u8 = 2;
#[cfg(completion)]
pub(super) const DONE: u8 = 4;
#[cfg(completion)]
pub(super) const SUBMIT: u8 = 8;
#[cfg(port)]
pub(super) const LOOK: u8 = 16;
}
pub(crate) const OWNED_DATAGRAM_MIN: usize = 12 * 1024;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Kind {
#[cfg(unix)]
Stream,
Tcp,
Bytes,
File,
TcpListener,
#[cfg(unix)]
UnixListener,
Udp,
#[cfg(unix)]
UnixDatagram,
#[cfg(unix)]
Fd,
}
impl Kind {
#[cfg_attr(
not(completion),
expect(dead_code, reason = "the completion drivers' only")
)]
pub(crate) fn listens(self) -> bool {
match self {
Kind::TcpListener => true,
#[cfg(unix)]
Kind::UnixListener => true,
_ => false,
}
}
pub(crate) fn streams(self) -> bool {
match self {
Kind::Tcp => true,
#[cfg(unix)]
Kind::Stream => true,
_ => false,
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
#[inline(always)]
pub(crate) fn counts_left(self) -> bool {
if crate::io::sys::UNIX_INQ {
self.streams()
} else {
self == Kind::Tcp
}
}
pub(crate) fn datagrams(self) -> bool {
match self {
Kind::Udp => true,
#[cfg(unix)]
Kind::UnixDatagram => true,
_ => false,
}
}
}
#[cfg(unix)]
crate::buf::spare::reused!(unix::SocketAddr, crate::io::sys::UnixAddr);
#[derive(Clone, Debug)]
pub(crate) enum Addr {
Inet(SocketAddr),
#[cfg(unix)]
Unix(crate::buf::spare::Spare<unix::SocketAddr>),
}
pub(crate) trait Sender: Sized {
#[cfg(unix)]
fn receive(fd: Raw, kind: Kind, out: &mut [u8], peek: bool) -> io::Result<(usize, Self)>;
fn from_addr(addr: Addr) -> io::Result<Self>;
}
impl Sender for Addr {
#[cfg(unix)]
#[inline(always)]
fn receive(fd: Raw, kind: Kind, out: &mut [u8], peek: bool) -> io::Result<(usize, Self)> {
match kind {
Kind::UnixDatagram => crate::io::sys::recv_from_unix(fd, out, peek)
.map(|(n, a)| (n, Addr::Unix(crate::buf::spare::Spare::new(a)))),
_ => crate::io::sys::recv_from_udp(fd, out, peek).map(|(n, a)| (n, Addr::Inet(a))),
}
}
fn from_addr(addr: Addr) -> io::Result<Self> {
Ok(addr)
}
}
impl Sender for SocketAddr {
#[cfg(unix)]
#[inline(always)]
fn receive(fd: Raw, _kind: Kind, out: &mut [u8], peek: bool) -> io::Result<(usize, Self)> {
crate::io::sys::recv_from_udp(fd, out, peek)
}
fn from_addr(addr: Addr) -> io::Result<Self> {
match addr {
Addr::Inet(addr) => Ok(addr),
#[cfg(unix)]
Addr::Unix(_) => Err(io::Error::new(
io::ErrorKind::InvalidData,
"a UDP datagram came with no internet address",
)),
}
}
}
#[cfg(unix)]
impl Sender for unix::SocketAddr {
#[inline(always)]
fn receive(fd: Raw, _kind: Kind, out: &mut [u8], peek: bool) -> io::Result<(usize, Self)> {
crate::io::sys::recv_from_unix(fd, out, peek)
}
fn from_addr(addr: Addr) -> io::Result<Self> {
match addr {
Addr::Unix(addr) => Ok(addr.into_inner()),
Addr::Inet(_) => Err(io::Error::new(
io::ErrorKind::InvalidData,
"a Unix socket's peer has an internet address",
)),
}
}
}
#[derive(Clone, Debug)]
pub(crate) enum Target {
Connected,
Inet(SocketAddr),
#[cfg(unix)]
Unix(crate::buf::spare::Spare<crate::io::sys::UnixAddr>),
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct Failure {
code: Option<i32>,
kind: io::ErrorKind,
}
impl Failure {
pub(crate) fn of(err: &io::Error) -> Self {
Self {
code: err.raw_os_error(),
kind: err.kind(),
}
}
pub(crate) fn error(self) -> io::Error {
match self.code {
Some(code) => io::Error::from_raw_os_error(code),
None => self.kind.into(),
}
}
}
struct Registration {
state: AtomicU8,
token: AtomicU64,
shard: UnsafeCell<Option<Arc<Shared>>>,
#[cfg(completion)]
ops: AtomicBool,
#[cfg(ring)]
slot: AtomicU32,
#[cfg(ring)]
datagrams_ahead: AtomicBool,
home: core::sync::atomic::AtomicUsize,
#[cfg(ring)]
here: core::sync::atomic::AtomicUsize,
#[cfg(port)]
awaits_idle: AtomicBool,
}
pub(crate) struct Source {
fd: AtomicRaw,
retired_fd: AtomicRaw,
epoch: AtomicU32,
retired_epoch: AtomicU32,
kind: Kind,
read: Side<ReadState>,
write: Side<WriteState>,
read_owner: owner::Owner,
write_owner: owner::Owner,
reg: Registration,
requests: AtomicU8,
ask: Ask,
unsent: AtomicU8,
#[cfg(unix)]
quick: quick::Quick,
#[cfg(completion)]
read_done: ops::Mailbox,
#[cfg(completion)]
write_done: ops::Mailbox,
#[cfg(ring)]
read_arrivals: crate::rt::shard::counted::LazyCounted<(ring::Arrival, u32)>,
#[cfg(completion)]
ops_out: core::sync::atomic::AtomicU8,
#[cfg(port)]
port: port::PortState,
}
unsafe impl Send for Source {}
unsafe impl Sync for Source {}
impl Source {
#[cfg(ring)]
fn on_ring(&self) -> bool {
self.reg.ops.load(Ordering::Acquire)
}
#[cfg(not(ring))]
fn on_ring(&self) -> bool {
false
}
#[cfg(ring)]
fn datagrams_ahead(&self) -> bool {
self.reg.datagrams_ahead.load(Ordering::Acquire)
}
#[cfg(ring)]
pub(crate) fn registers_with(&self, ahead: bool) {
self.reg.datagrams_ahead.store(ahead, Ordering::Release);
}
#[cfg(not(ring))]
fn datagrams_ahead(&self) -> bool {
false
}
pub(crate) fn new(fd: Raw, kind: Kind) -> Arc<Self> {
let source = Arc::<Self>::new_uninit();
let slot = Arc::as_ptr(&source).cast_mut();
unsafe {
slot.write(core::mem::MaybeUninit::new(Self {
fd: AtomicRaw::new(fd),
retired_fd: AtomicRaw::new(NO_FD),
epoch: AtomicU32::new(0),
retired_epoch: AtomicU32::new(0),
kind,
read: Side::biased(ReadState::default()),
write: Side::biased(WriteState::default()),
read_owner: owner::Owner::new(),
write_owner: owner::Owner::new(),
reg: Registration {
state: AtomicU8::new(UNREGISTERED),
token: AtomicU64::new(NO_TOKEN),
shard: UnsafeCell::new(None),
#[cfg(completion)]
ops: AtomicBool::new(false),
#[cfg(ring)]
slot: AtomicU32::new(u32::MAX),
#[cfg(ring)]
datagrams_ahead: AtomicBool::new(false),
home: core::sync::atomic::AtomicUsize::new(0),
#[cfg(ring)]
here: core::sync::atomic::AtomicUsize::new(0),
#[cfg(port)]
awaits_idle: AtomicBool::new(false),
},
requests: AtomicU8::new(0),
ask: Ask::default(),
unsent: AtomicU8::new(0),
#[cfg(unix)]
quick: quick::Quick::new(kind),
#[cfg(completion)]
read_done: ops::Mailbox::default(),
#[cfg(completion)]
write_done: ops::Mailbox::default(),
#[cfg(ring)]
read_arrivals: crate::rt::shard::counted::LazyCounted::new(),
#[cfg(completion)]
ops_out: core::sync::atomic::AtomicU8::new(0),
#[cfg(port)]
port: Default::default(),
}))
};
unsafe { source.assume_init() }
}
pub(crate) fn kind(&self) -> Kind {
self.kind
}
fn hooks(self: &Arc<Self>) -> SourceHooks<'_> {
self.hooks_in(None)
}
fn hooks_in<'a>(
self: &'a Arc<Self>,
shard: Option<&'a crate::rt::shard::Shard>,
) -> SourceHooks<'a> {
SourceHooks {
src: self,
shard,
claim: None,
#[cfg(readiness)]
token: None,
}
}
pub(crate) fn raw_fd(&self) -> io::Result<Raw> {
self.fd()
}
#[inline(always)]
fn fd(&self) -> io::Result<Raw> {
match self.fd.load(Ordering::Acquire) {
NO_FD => Err(io::Error::new(
io::ErrorKind::NotConnected,
"the handle's IO object is not in place",
)),
fd => Ok(fd),
}
}
#[inline(always)]
fn with_io<R>(self: &Arc<Self>, f: impl FnOnce(Io<'_>) -> R) -> io::Result<R> {
let fd = self.fd()?;
#[cfg(completion)]
let held = (self.kind != Kind::File).then(context::current_held);
#[cfg(completion)]
let io = match &held {
Some(held) => Io {
via: self.via(fd, held.shard())?,
shard: held.shard(),
},
None => Self::file_io(fd)?,
};
#[cfg(not(completion))]
let io = Self::file_io(fd)?;
#[cfg(completion)]
let not_yet = matches!(io.via, Via::Ops(route::Route::NotYet));
let out = f(io);
#[cfg(completion)]
if not_yet {
self.submit_if_registered(io.shard);
}
Ok(out)
}
#[inline(always)]
fn file_io(fd: Raw) -> io::Result<Io<'static>> {
#[cfg(readiness)]
return Ok(Io {
via: Via::Fd(fd),
shard: None,
});
#[cfg(not(readiness))]
{
let _ = fd;
Err(io::Error::new(
io::ErrorKind::Unsupported,
"a regular file is read and written through its handle",
))
}
}
#[cfg(completion)]
#[inline(always)]
fn via<'a>(
self: &'a Arc<Self>,
fd: Raw,
current: Option<&'a crate::rt::shard::Shard>,
) -> io::Result<Via<'a>> {
#[cfg(ring)]
if let Some(shard) = current
&& let Some(ring) = shard.ring()
&& self.reg.here.load(Ordering::Acquire) == shard.shared_addr()
{
return Ok(Via::Ops(route::Route::Here {
ring,
source: self,
file: self.reg.slot.load(Ordering::Acquire),
}));
}
if !self.reg.ops.load(Ordering::Acquire) {
#[cfg(readiness)]
if self.reg.state.load(Ordering::Acquire) == REGISTERED
|| !current.map_or_else(crate::rt::runtime::service_rings, |s| s.ring().is_some())
{
return Ok(Via::Fd(fd));
}
#[cfg(not(readiness))]
let _ = fd;
self.ensure_registered()?;
}
Ok(Via::Ops(
self.route(current).unwrap_or(route::Route::NotYet),
))
}
#[cfg(completion)]
#[cold]
#[inline(never)]
fn submit_if_registered(self: &Arc<Self>, current: Option<&crate::rt::shard::Shard>) {
if self.reg.state.load(Ordering::Acquire) == REGISTERED {
let mut after = After::default();
let hooks = self.hooks_in(current);
self.read.defer(read_work::SUBMIT, &mut after, &hooks);
self.write.defer(write_work::SUBMIT, &mut after, &hooks);
self.settle(after);
}
}
#[cfg(not(completion))]
fn ops_idle(&self) -> bool {
true
}
fn serving_fd(&self, token: Option<u64>) -> Option<Raw> {
if token.is_some_and(|t| self.reg.token.load(Ordering::Acquire) != t) {
return None;
}
match self.fd.load(Ordering::Acquire) {
NO_FD => None,
fd => Some(fd),
}
}
fn catch_up(&self, seen: &mut u32) -> bool {
let now = self.epoch.load(Ordering::Acquire);
if *seen == now {
return false;
}
*seen = now;
true
}
#[inline(always)]
fn settle(self: &Arc<Self>, after: After) {
if let Err(after) = after.forget_if_nothing() {
self.settle_rest(after);
}
}
#[inline(never)]
fn settle_rest(self: &Arc<Self>, mut after: After) {
self.kick(&mut after);
if after.starved() {
Arc::clone(self).wait_for_buffer();
}
after.run();
}
#[cfg_attr(
not(ring),
expect(unused_variables, reason = "only a ring source kicks")
)]
fn kick(self: &Arc<Self>, after: &mut After) {
#[cfg(ring)]
if let Some(home) = after.take_kick() {
home.post_io(crate::rt::shard::IoOp::RingKick(Arc::clone(self)));
}
}
pub(crate) fn wait_for_buffer(self: Arc<Self>) {
crate::buf::with_pool(|pool| {
if let Some(pool) = pool {
pool.wait_for_return(self.into());
}
});
}
#[inline(always)]
fn finish_poll<R>(
self: &Arc<Self>,
after: After,
poll: Poll<io::Result<R>>,
) -> Poll<io::Result<R>> {
let watch = after.must_register() || poll.is_pending();
self.settle(after);
if watch && let Err(err) = self.ensure_registered() {
return Poll::Ready(Err(err));
}
poll
}
#[inline(always)]
fn ensure_registered(self: &Arc<Self>) -> io::Result<()> {
if self.reg.state.load(Ordering::Acquire) != UNREGISTERED {
return Ok(());
}
self.register()
}
#[inline(never)]
fn register(self: &Arc<Self>) -> io::Result<()> {
if self
.reg
.state
.compare_exchange(
UNREGISTERED,
REGISTERING,
Ordering::AcqRel,
Ordering::Acquire,
)
.is_err()
{
return Ok(());
}
let registered = context::current(|current| {
current.map(|shard| {
if shard.shared.is_stopped() {
self.registration_abandoned();
Err(requests::stopped_shard())
} else {
shard.register_source(self)
}
})
});
match registered {
Some(Err(err)) if requests::is_stopped_shard(&err) => {
self.look_again();
Ok(())
}
Some(result) => result,
None => {
crate::rt::runtime::service_shard().post_io(IoOp::Register(Arc::clone(self)));
Ok(())
}
}
}
pub(crate) fn registration_abandoned(&self) {
self.reg.state.store(UNREGISTERED, Ordering::Release);
}
fn detach(self: &Arc<Self>, read: &ReadState, write: &WriteState) {
#[cfg(ring)]
if self.ring_attach_under_way() {
self.abandon_ring_attach();
return;
}
if self.reg.state.load(Ordering::Acquire) != REGISTERED {
return;
}
let shard = unsafe { (*self.reg.shard.get()).take() };
self.clear_here();
self.reg.state.store(UNREGISTERED, Ordering::Release);
if let Some(shard) = shard {
self.undo_with(&shard, read, write);
shard.release_source(self.reg.token.load(Ordering::Acquire));
}
self.registration_gone();
}
#[cfg_attr(not(ring), expect(unused_variables))]
fn undo_with(self: &Arc<Self>, shard: &Arc<Shared>, read: &ReadState, write: &WriteState) {
#[cfg(ring)]
if self.reg.ops.swap(false, Ordering::AcqRel) {
let slot = self.reg.slot.swap(u32::MAX, Ordering::AcqRel);
self.detach_ring(shard, slot, [read.out(), write.out()]);
return;
}
#[cfg(port)]
if self.reg.ops.swap(false, Ordering::AcqRel) {
self.detach_port();
}
#[cfg(readiness)]
{
let fd = self.fd.load(Ordering::Acquire);
if fd != NO_FD
&& let Some(registrar) = shard.registrar()
{
let _ = registrar.delete(fd, self.kind == Kind::Tcp);
}
}
}
fn registration_gone(&self) {
self.clear_here();
self.reg.token.store(NO_TOKEN, Ordering::Release);
self.reg.home.store(0, Ordering::Release);
}
pub(crate) fn revive(&self, fd: Raw) {
if self.fd.load(Ordering::Acquire) != NO_FD {
return;
}
if self.retired_fd.load(Ordering::Acquire) != fd {
let retired = self.retired_epoch.load(Ordering::Acquire);
let _ = self.epoch.compare_exchange(
retired,
retired.wrapping_add(1),
Ordering::AcqRel,
Ordering::Acquire,
);
}
let _ = self
.fd
.compare_exchange(NO_FD, fd, Ordering::AcqRel, Ordering::Acquire);
}
#[inline]
pub(crate) fn is_retired(&self) -> bool {
self.fd.load(Ordering::Acquire) == NO_FD
}
pub(crate) fn registered_with(&self, home: usize, token: u64) -> bool {
self.reg.token.load(Ordering::Acquire) == token
&& self.reg.home.load(Ordering::Acquire) == home
}
#[cfg(ring)]
fn publish_here(&self, home: usize) {
self.reg.here.store(home, Ordering::Release);
}
fn clear_here(&self) {
#[cfg(ring)]
self.reg.here.store(0, Ordering::Release);
}
pub(crate) fn has_unsent(&self) -> bool {
self.unsent.load(Ordering::Acquire) & UNSENT != 0
}
#[cfg(readiness)]
#[inline(always)]
fn write_wants_no_edge(&self, shard: &crate::rt::shard::Shard) -> bool {
self.write.biased_to(shard.shared_addr())
&& self.unsent.load(Ordering::Relaxed) & WRITE_IDLE != 0
}
#[cfg(readiness)]
pub(crate) fn on_event(
self: &Arc<Self>,
token: u64,
readiness: Readiness,
shard: &crate::rt::shard::Shard,
) {
let writable = readiness.writable && !self.quick.write_clear();
let mut after = After::default();
let hooks = SourceHooks {
src: self,
shard: Some(shard),
claim: None,
token: Some(token),
};
if readiness.readable {
let unsure = if readiness.urgent || readiness.hangup {
read_work::UNSURE
} else {
0
};
self.read.drive(&mut after, &hooks, unsure);
}
if writable && !self.write_wants_no_edge(shard) {
self.write.drive(&mut after, &hooks, 0);
}
self.settle(after);
}
}
#[cfg(test)]
impl Source {
pub(crate) fn shared(&self) -> (bool, bool) {
(self.read.is_shared(), self.write.is_shared())
}
pub(crate) fn registration(&self) -> (usize, u64) {
(
self.reg.home.load(Ordering::SeqCst),
self.reg.token.load(Ordering::SeqCst),
)
}
pub(crate) fn read_report(self: &Arc<Self>) -> String {
let (word, deferred) = self.read.words();
let held = match self.read.try_hold() {
Some(mut held) => {
let line = held.get().report();
let mut after = After::default();
held.release(&mut after, &self.hooks());
after.run();
line
}
None => "held".into(),
};
format!("guard {word:#x}, deferred {deferred:#x}: {held}")
}
pub(crate) fn write_report(self: &Arc<Self>) -> String {
let (word, deferred) = self.write.words();
let held = match self.write.try_hold() {
Some(mut held) => {
let line = held.get().report();
let mut after = After::default();
held.release(&mut after, &self.hooks());
after.run();
line
}
None => "held".into(),
};
#[cfg(completion)]
let ops = format!(
", ops registered {}, ops out {}",
self.reg.ops.load(Ordering::SeqCst),
self.ops_out.load(Ordering::SeqCst)
);
#[cfg(not(completion))]
let ops = String::new();
format!(
"guard {word:#x}, deferred {deferred:#x}: {held}; registration {}, retired {}{ops}",
self.reg.state.load(Ordering::SeqCst),
self.is_retired(),
)
}
pub(crate) fn has_read_ahead(self: &Arc<Self>) -> bool {
let mut after = After::default();
let hooks = self.hooks();
self.read.try_hold().is_some_and(|mut held| {
let found = held.get().has_result();
held.release(&mut after, &hooks);
found
})
}
fn has_write_behind(self: &Arc<Self>) -> bool {
let mut after = After::default();
let hooks = self.hooks();
self.write.try_hold().is_some_and(|mut held| {
let found = held.get().has_behind();
held.release(&mut after, &hooks);
found
})
}
}
impl Source {
#[cfg(not(ring))]
pub(crate) fn touch(self: &Arc<Self>, shard: &crate::rt::shard::Shard) {
let mut after = After::default();
let hooks = self.hooks_in(Some(shard));
self.read.defer(0, &mut after, &hooks);
self.write.defer(0, &mut after, &hooks);
self.settle(after);
}
}
impl Drop for Source {
fn drop(&mut self) {
let mut after = After::default();
self.read.get_mut().discard(&mut after);
self.write.get_mut().discard(&mut after);
after.run();
}
}
impl Wake for Source {
fn wake(self: Arc<Self>) {
self.wake_by_ref();
}
fn wake_by_ref(self: &Arc<Self>) {
let mut after = After::default();
let hooks = self.hooks();
self.read.defer(read_work::UNSTARVE, &mut after, &hooks);
self.write.defer(write_work::UNSTARVE, &mut after, &hooks);
self.settle(after);
}
}
#[cfg(test)]
mod tests;