use core::cell::{Cell, OnceCell, RefCell};
use core::ptr::{addr_of, addr_of_mut};
use std::collections::VecDeque;
use std::io;
use std::rc::Rc;
use std::sync::Arc;
use super::counted::Counted;
use super::{
Counters, Driver, Inbox, Limits, ParkState, Poller, Shard, Shared, Sources, Spin, Wheel,
children, counters, locals, reserve_group,
};
use crate::buf::Pool;
use crate::rt::handoff::Monitor;
use crate::rt::quartz::Quartz;
use crate::rt::tuning::Tuning;
use crate::sync::atomic_waker::AtomicWaker;
use crate::sync::internal::{AtomicBool, AtomicU64, AtomicUsize};
macro_rules! write_fields {
($ty:ident at $slot:ident, [$($written:ident),*], {
$($(#[$attr:meta])* $field:ident: $value:expr,)*
}) => {
$(
$(#[$attr])*
{
let value = $value;
unsafe { addr_of_mut!((*$slot).$field).write(value) };
}
)*
let _every_field = |whole: &$ty| {
let $ty { $($written: _,)* $($(#[$attr])* $field: _,)* } = whole;
};
};
}
impl Shard {
pub(crate) fn new_rc(limits: Limits) -> io::Result<Rc<Self>> {
let mut cell = Rc::<Self>::new_uninit();
let Some(slot) = Rc::get_mut(&mut cell) else {
return Err(unshared());
};
unsafe { Self::write(slot.as_mut_ptr(), limits, None, Tuning::default(), None)? };
Ok(unsafe { cell.assume_init() })
}
#[cfg(test)]
pub(crate) fn new(limits: Limits) -> io::Result<Self> {
let mut shard = core::mem::MaybeUninit::<Self>::uninit();
unsafe { Self::write(shard.as_mut_ptr(), limits, None, Tuning::default(), None)? };
Ok(unsafe { shard.assume_init() })
}
pub(crate) fn worker(
limits: Limits,
tuning: Tuning,
runtime: u64,
index: usize,
monitor: Option<Arc<Monitor>>,
) -> io::Result<Arc<Self>> {
let mut cell = Arc::<Self>::new_uninit();
let Some(slot) = Arc::get_mut(&mut cell) else {
return Err(unshared());
};
unsafe {
Self::write(
slot.as_mut_ptr(),
limits,
Some((runtime, index)),
tuning,
monitor,
)?;
};
let shard = unsafe { cell.assume_init() };
#[cfg(feature = "io-stats")]
if shard.tuning.count_io {
shard.shared.counters.count_io();
}
if shard.tuning.record_latencies {
shard.shared.counters.record_latencies();
#[cfg(all(completion, feature = "io-stats"))]
shard.time_io();
}
shard.count_drive_from(0, shard.tuning.driver_interval);
#[cfg(ring)]
if let Some(ring) = shard.ring() {
ring.set_zero_copy_min(shard.tuning.zero_copy_min);
}
Ok(shard)
}
unsafe fn write(
slot: *mut Self,
limits: Limits,
owner: Option<(u64, usize)>,
tuning: Tuning,
monitor: Option<Arc<Monitor>>,
) -> io::Result<()> {
let mut shared = Arc::<Shared>::new_uninit();
let Some(shared_slot) = Arc::get_mut(&mut shared).map(|cell| cell.as_mut_ptr()) else {
return Err(unshared());
};
let pool = Pool::new(limits.buffer_cap);
let reserved = reserve_group(&pool, limits.backend)?;
let (notifier, kernel) = unsafe { driver_at(addr_of_mut!((*slot).driver), limits)? };
let driver = unsafe { &*addr_of!((*slot).driver) };
let made = match driver {
#[cfg(ring)]
Driver::Ring(ring) => ring.recv_reservation(),
#[cfg(ring)]
Driver::Gone => 0,
Driver::Poll(_) => 0,
};
pool.unreserve(reserved.saturating_sub(made));
write_fields!(Shared at shared_slot, [], {
inbox: Inbox::new(),
park: ParkState::new(),
notifier: notifier,
load: AtomicUsize::new(0),
tasks: crate::rt::task::list::TaskList::new(),
detached: AtomicUsize::new(0),
stopped: AtomicBool::new(false),
draining: AtomicBool::new(false),
stop_waker: AtomicWaker::new(),
timer_ops: Counted::new(),
#[cfg(readiness)]
registrar: kernel,
#[cfg(port)]
port: kernel,
io_ops: Counted::new(),
io_closed: AtomicBool::new(false),
counters: Counters::new(driver.backend(), pool.cap(), owner),
runtime: std::sync::OnceLock::new(),
runtime_addr: AtomicUsize::new(0),
writes: AtomicUsize::new(0),
released: AtomicBool::new(false),
timer_at: AtomicU64::new(u64::MAX),
parks: AtomicU64::new(0),
#[cfg(unix)]
signal_pump: std::sync::atomic::AtomicBool::new(false),
});
let shared = unsafe { shared.assume_init() };
counters::register(&shared);
write_fields!(Shard at slot, [driver], {
shared: shared,
queue: RefCell::new(VecDeque::new()),
lifo: Cell::new(None),
lifo_streak: Cell::new(0),
until_inbox: Cell::new(tuning.inbox_interval),
homes_first: Cell::new(false),
drive_left: Cell::new(1),
drive_at: Cell::new(1),
last_drive: Cell::new(Quartz::now()),
polls: Cell::new(0),
sleeps: Cell::new(0),
slept: Cell::new(core::time::Duration::ZERO),
remote_wakes: Cell::new(0),
looks: Cell::new(0),
spin: Spin::new(),
owner: Cell::new(owner),
monitor: monitor,
home: OnceCell::new(),
limits: limits,
tuning: tuning,
wheel: const { RefCell::new(Wheel::new()) },
precise: const { RefCell::new(Wheel::new()) },
epoch: crate::time::clock::origin(),
fired: RefCell::new(Vec::new()),
side: RefCell::new(Vec::new()),
foreign_poll: Cell::new(false),
side_seq: Cell::new(0),
serving: Cell::new(core::ptr::null()),
signal: Cell::new(None),
sources: RefCell::new(Sources::default()),
children: RefCell::new(children::Children::default()),
pool: pool,
locals: locals::Locals::default(),
#[cfg(port)]
keeper: RefCell::new(None),
});
Ok(())
}
}
#[cfg(readiness)]
unsafe fn driver_at(
dst: *mut Driver,
limits: Limits,
) -> io::Result<(super::Notifier, Option<super::Registrar>)> {
let poller = || -> io::Result<_> {
let poller = Poller::new()?;
let kernel = (poller.notifier(), Some(poller.registrar()));
unsafe { dst.write(Driver::Poll(RefCell::new(poller))) };
Ok(kernel)
};
match limits.backend {
crate::rt::backend::Backend::Poll => poller(),
#[cfg(ring)]
crate::rt::backend::Backend::Ring(cfg) | crate::rt::backend::Backend::RingOrPoll(cfg) => {
let made = super::ring::ShardRing::for_shard(cfg);
match made {
Ok(ref ring) => {
let notifier = ring.notifier();
unsafe { dst.write(Driver::Ring(made?)) };
Ok((notifier, None))
}
Err(err)
if matches!(limits.backend, crate::rt::backend::Backend::RingOrPoll(_))
&& (err.raw_os_error() == Some(libc::ENOMEM)
|| err.kind() == io::ErrorKind::Unsupported) =>
{
poller()
}
Err(err) => Err(err),
}
}
}
}
#[cfg(port)]
unsafe fn driver_at(
dst: *mut Driver,
_limits: Limits,
) -> io::Result<(super::Notifier, Arc<crate::rt::park::iocp::Port>)> {
let poller = Poller::new()?;
let kernel = (poller.notifier(), Arc::clone(poller.port()));
unsafe { dst.write(Driver::Poll(RefCell::new(poller))) };
Ok(kernel)
}
fn unshared() -> io::Error {
io::Error::other("a new shard's memory was shared before it was made")
}