use core::future::Future;
use core::pin::{Pin, pin};
use core::task::{Context, Poll, Waker};
use std::sync::Arc;
use std::time::{Duration, Instant};
use super::Shard;
use super::signal::Signal;
use crate::rt::context::{self, Polled};
use crate::rt::task::Runnable;
use crate::sync::internal::Ordering;
pub(crate) struct Detached;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Worked {
Stopped,
Detached,
}
impl Shard {
pub(super) fn run(&self, runnable: Runnable) -> Polled {
context::poll(&self.shared, || {
runnable.run();
})
.1
}
pub(crate) fn block_on<F: Future>(&self, future: F) -> F::Output {
let mut future = pin!(future);
context::deeper(self.limits.max_nesting, || {
let signal = Arc::new(Signal::new(&self.shared));
match self.run_loop(future.as_mut(), &signal) {
Ok(output) => output,
Err(Detached) => finish_on_own(future, &signal),
}
})
}
pub(crate) fn run_worker(&self) -> Worked {
let stopped = pin!(self.shared.stopped());
let signal = Arc::new(Signal::new(&self.shared));
match self.run_loop(stopped, &signal) {
Ok(()) => Worked::Stopped,
Err(Detached) => Worked::Detached,
}
}
pub(crate) fn run_then_leave<F: Future>(
&self,
mut future: Pin<&mut F>,
signal: &Arc<Signal>,
) -> F::Output {
struct Leaving<'a>(&'a Shard);
impl Drop for Leaving<'_> {
fn drop(&mut self) {
self.0.leave();
}
}
let _leaving = Leaving(self);
loop {
if let Ok(output) = self.run_loop(future.as_mut(), signal) {
return output;
}
}
}
fn run_loop<F: Future>(
&self,
mut future: Pin<&mut F>,
signal: &Arc<Signal>,
) -> Result<F::Output, Detached> {
let waker = Waker::from(Arc::clone(signal));
let mut cx = Context::from_waker(&waker);
let mut self_woken = 0u32;
let mut owed = 0usize;
loop {
if owed == 0
&& signal.woken.load(Ordering::Relaxed)
&& signal.woken.swap(false, Ordering::SeqCst)
{
let (poll, polled) = context::poll(&self.shared, || {
let budget = crate::coop::enter();
let poll = future.as_mut().poll(&mut cx);
crate::coop::leave(budget);
poll
});
if let Poll::Ready(output) = poll {
return Ok(output);
}
if polled == Polled::Detached {
return Err(Detached);
}
if signal.woken.load(Ordering::Relaxed) {
owed = self.queued();
}
}
if let Some(runnable) = self.next() {
owed = owed.saturating_sub(1);
if self.run(runnable) == Polled::Detached {
return Err(Detached);
}
let since = self.since_drive.get() + 1;
if since >= self.tuning.driver_interval {
self.count_again(since);
self.drive_or_report(Some(Duration::ZERO));
} else {
self.since_drive.set(since);
}
continue;
}
owed = 0;
if !self.shared.io_ops.is_empty() {
self.apply_io_ops();
}
self.fire_timers();
if self.has_local_work() || signal.woken.load(Ordering::SeqCst) {
self_woken += 1;
if self_woken >= self.tuning.driver_interval {
self_woken = 0;
self.count_again(self.since_drive.get());
self.drive_or_report(Some(Duration::ZERO));
}
continue;
}
self.count_again(self.since_drive.get());
let shared = &self.shared;
if self.spin.run(|| {
self.has_remote_work()
|| signal.woken.load(Ordering::Relaxed)
|| self.io_ready_now()
}) {
continue;
}
self.before_park();
if shared
.park
.begin_park(|| self.has_remote_work() || signal.woken.load(Ordering::SeqCst))
{
let slept = Instant::now();
if let Some(park) = &self.tuning.hooks.park {
park();
}
self.drive_or_report(None);
shared.park.end_park();
self.parked_ended();
if let Some(unpark) = &self.tuning.hooks.unpark {
unpark();
}
self.slept_for(slept.elapsed());
}
self.after_park();
}
}
pub(crate) fn turn(&self, max: usize, wait: Option<Duration>) -> std::io::Result<usize> {
if self.run_ready() == Polled::Detached {
return Ok(0);
}
if !self.shared.io_ops.is_empty() {
self.apply_io_ops();
}
self.fire_timers();
let shared = &self.shared;
let quiet = wait != Some(Duration::ZERO) && !self.has_local_work();
if quiet {
self.before_park();
}
let sleeps = quiet && shared.park.begin_park(|| self.has_remote_work());
let wait = if sleeps || wait == Some(Duration::ZERO) {
wait
} else {
Some(Duration::ZERO)
};
let timeout = match (wait, self.timer_wait(sleeps)) {
(Some(w), Some(t)) => Some(w.min(t)),
(w, t) => w.or(t),
};
self.publish();
let result = self.poll_io(max, timeout);
if sleeps {
shared.park.end_park();
self.parked_ended();
}
if quiet {
self.after_park();
}
let events = result?;
self.fire_timers();
if self.run_ready() == Polled::Detached {
return Ok(0);
}
Ok(events)
}
pub(super) fn run_ready(&self) -> Polled {
while let Some(runnable) = self.next() {
let polled = self.run(runnable);
if polled == Polled::Detached {
return polled;
}
self.polls.set(self.polls.get() + 1);
}
Polled::In
}
pub(super) fn count_again(&self, runs: u32) {
self.polls.set(self.polls.get() + u64::from(runs));
self.since_drive.set(0);
}
#[expect(
clippy::panic,
reason = "a failure of the poller itself cannot be handed to any task: it ends the shard loudly"
)]
fn drive_or_report(&self, wait: Option<Duration>) {
if let Err(err) = self.drive(wait) {
panic!("the shard's poller failed: {err}");
}
}
pub(crate) fn hand_over_queues(&self) {
let lifo = self.lifo.take();
let queued: Vec<Runnable> = self.queue.borrow_mut().drain(..).collect();
self.lifo_streak.set(0);
for runnable in lifo.into_iter().chain(queued) {
runnable.schedule();
}
}
}
#[expect(
clippy::panic,
reason = "documented under `# Panics` of `block_on`: the thread's shard cannot start"
)]
fn finish_on_own<F: Future>(future: Pin<&mut F>, signal: &Arc<Signal>) -> F::Output {
let own = match context::own_shard() {
Ok(own) => own,
Err(err) => panic!("this thread's shard could not start: {err}"),
};
signal.target.move_to(&own.shared);
let _current = context::enter(&own);
let inner = context::runtime();
let _guest = inner
.as_ref()
.map(|inner| crate::rt::paused::Guest::enter(inner, &own.shared));
#[cfg(port)]
own.take_port_back();
own.run_then_leave(future, signal)
}