use std::{
cell::{Cell, RefCell},
collections::HashMap,
future::Future,
io,
mem::{self, ManuallyDrop},
pin::pin,
sync::{
Arc, Mutex, MutexGuard, Weak,
atomic::{AtomicBool, Ordering},
},
task::{Context, Poll, Wake, Waker},
thread::{self, Thread, ThreadId},
};
use crate::{
Shared,
mode::sealed::Sealed as _,
runtime::{Core, drives, drives_a_runtime, lock, set_driving},
};
pub(crate) fn current() -> io::Result<Arc<Core<Shared>>> {
if let Some(core) = driven() {
return Ok(core);
}
if !in_block_on() {
return shared_in(&SHARED);
}
if let Target::Core(core) = innermost() {
return Ok(core);
}
match OWN.try_with(shared_in) {
Ok(core) => core,
Err(_) => Core::with_seat(),
}
}
pub(crate) fn current_to_spawn_on() -> io::Result<Arc<Core<Shared>>> {
let core = current()?;
if in_block_on() && is_own(&core) {
HELD.with(|held| {
let mut held = held.borrow_mut();
if held.is_none() {
**held = Some(core.clone());
}
});
}
Ok(core)
}
pub(crate) fn own() -> Option<Arc<Core<Shared>>> {
OWN.try_with(|own| lock(own).upgrade()).ok().flatten()
}
pub(crate) fn block_on_seated<F>(core: &Arc<Core<Shared>>, future: F) -> F::Output
where
F: Future,
{
let target = Target::Core(core.clone());
let core = core.clone();
block_on(target, &move || Some(core.clone()), future)
}
pub(crate) enum Target {
Own,
Core(Arc<Core<Shared>>),
}
fn driven() -> Option<Arc<Core<Shared>>> {
Shared::driven().filter(|core| core.seat.is_some())
}
pub(crate) fn in_block_on() -> bool {
IN_BLOCK_ON
.try_with(Cell::get)
.is_ok_and(|inside| inside > 0)
}
fn innermost() -> Target {
let top = TARGETS.try_with(|targets| match targets.borrow().last() {
Some(Target::Core(core)) => Some(core.clone()),
Some(Target::Own) | None => None,
});
match top {
Ok(Some(core)) => Target::Core(core),
Ok(None) | Err(_) => Target::Own,
}
}
fn drives_next(inner: &Arc<Core<Shared>>) -> bool {
match innermost() {
Target::Own => is_own(inner),
Target::Core(core) => Arc::ptr_eq(&core, inner),
}
}
pub(crate) type Resolve<'a> = &'a dyn Fn() -> Option<Arc<Core<Shared>>>;
pub(crate) fn block_on<F>(target: Target, resolve: Resolve<'_>, future: F) -> F::Output
where
F: Future,
{
let _inside = InBlockOn::enter(target);
let mut leaving = Leaving {
resolve,
driving: None,
};
drive(resolve, &mut leaving.driving, future)
}
struct Leaving<'a> {
resolve: Resolve<'a>,
driving: Option<Driving>,
}
impl Drop for Leaving<'_> {
fn drop(&mut self) {
if self.driving.take().is_some() {
return;
}
let Some(inner) = (self.resolve)() else {
return;
};
stop_waiting_for(&inner);
}
}
fn stop_waiting_for(inner: &Arc<Core<Shared>>) {
let mut seat = seat(inner);
seat.stop_waiting();
if matches!(seat.holder, Holder::Nobody { .. }) {
seat.rouse_helper();
}
hand_over(inner, &mut seat);
}
pub(crate) fn ensure_helper(inner: &Arc<Core<Shared>>) {
let Some(seat) = &inner.seat else {
return;
};
if drives(&inner.remote) {
return;
}
if in_block_on() && drives_next(inner) {
return;
}
let mut seat = lock(seat);
if !matches!(
seat.holder,
Holder::Nobody {
parked_helper: None
}
) {
return;
}
spawn_helper(inner, &mut seat);
}
pub struct Seat {
holder: Holder,
waiting: HashMap<ThreadId, Thread>,
}
impl Seat {
pub(crate) fn new() -> Self {
Self {
holder: Holder::Nobody {
parked_helper: None,
},
waiting: HashMap::new(),
}
}
#[cfg(test)]
pub(crate) fn helper_running(&self) -> bool {
matches!(self.holder, Holder::Helper) || self.parked_helper().is_some()
}
#[cfg(all(test, feature = "event"))]
pub(crate) fn helper_parked(&self) -> bool {
self.parked_helper().is_some()
}
#[cfg(all(test, feature = "event"))]
pub(crate) fn waiting(&self) -> usize {
self.waiting.len()
}
fn stop_waiting(&mut self) {
self.waiting.remove(&thread::current().id());
}
fn free(&mut self) {
let parked_helper = match &mut self.holder {
Holder::Nobody { parked_helper } | Holder::BlockOn { parked_helper } => {
parked_helper.take()
}
Holder::Helper => None,
};
self.holder = Holder::Nobody { parked_helper };
set_driving::<Shared>(None);
for (_, thread) in self.waiting.drain() {
thread.unpark();
}
self.rouse_helper();
}
fn rouse_helper(&self) {
if let Some(thread) = self.parked_helper() {
thread.unpark();
}
}
fn parked_helper(&self) -> Option<&Thread> {
match &self.holder {
Holder::Nobody { parked_helper } | Holder::BlockOn { parked_helper } => {
parked_helper.as_ref()
}
Holder::Helper => None,
}
}
}
enum Holder {
Nobody { parked_helper: Option<Thread> },
BlockOn { parked_helper: Option<Thread> },
Helper,
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum Driver {
BlockOn,
Helper,
}
fn drive<F>(resolve: Resolve<'_>, driving: &mut Option<Driving>, future: F) -> F::Output
where
F: Future,
{
let mut future = pin!(future);
let signal = Arc::new(Signal {
thread: thread::current(),
woken: AtomicBool::new(false),
runtime: Mutex::new(Weak::new()),
});
let waker = Waker::from(signal.clone());
let mut cx = Context::from_waker(&waker);
let take_seat = |driving: &mut Option<Driving>| {
*driving = resolve().and_then(|inner| Driving::take(inner, Driver::BlockOn));
if let Some(driving) = driving {
*lock(&signal.runtime) = Arc::downgrade(&driving.inner);
}
};
let mut failed_waits = 0u32;
loop {
if driving.is_none() {
take_seat(driving);
}
signal.woken.swap(false, Ordering::AcqRel);
if let Poll::Ready(output) = future.as_mut().poll(&mut cx) {
return output;
}
if driving.is_none() {
take_seat(driving);
}
match &*driving {
Some(driving) => driving.round(&signal.woken, &mut failed_waits),
None => {
if !signal.woken.load(Ordering::Acquire) {
thread::park();
}
}
}
}
}
fn hand_over(inner: &Arc<Core<Shared>>, seat: &mut Seat) {
if !matches!(
seat.holder,
Holder::Nobody {
parked_helper: None
}
) || !inner.is_busy()
{
return;
}
spawn_helper(inner, seat);
}
fn spawn_helper(inner: &Arc<Core<Shared>>, seat: &mut Seat) {
let inner = inner.clone();
thread::Builder::new()
.name(THREAD_NAME.into())
.spawn(move || helper(inner))
.expect("the thread a runtime's helper runs on");
seat.holder = Holder::Helper;
}
const THREAD_NAME: &str = "zruntime";
fn helper(inner: Arc<Core<Shared>>) {
let mut failed_waits = 0u32;
let mut driving = Some(Driving::enter(inner.clone(), Driver::Helper));
loop {
if let Some(driving) = driving {
loop {
driving.run_batch();
if driving.leave_if_idle() {
return;
}
if driving.yield_if_wanted() {
break;
}
driving.wait(false, &mut failed_waits);
}
}
thread::park();
driving = Driving::take(inner.clone(), Driver::Helper);
}
}
struct Driving {
inner: Arc<Core<Shared>>,
who: Driver,
left: Cell<bool>,
}
impl Driving {
fn take(inner: Arc<Core<Shared>>, who: Driver) -> Option<Self> {
let mut seat = seat(&inner);
match (&mut seat.holder, who) {
(Holder::Nobody { parked_helper }, Driver::BlockOn) => {
let parked_helper = parked_helper.take();
seat.holder = Holder::BlockOn { parked_helper };
seat.stop_waiting();
}
(Holder::Nobody { .. }, Driver::Helper) => seat.holder = Holder::Helper,
(Holder::BlockOn { parked_helper }, Driver::Helper) => {
*parked_helper = Some(thread::current());
return None;
}
(Holder::Helper, Driver::Helper) => {
unreachable!("a second helper for the same runtime")
}
(holder, Driver::BlockOn) => {
let helper_in_seat = matches!(holder, Holder::Helper);
let thread = thread::current();
seat.waiting.insert(thread.id(), thread);
if helper_in_seat {
drop(seat);
inner.remote.notify();
}
return None;
}
}
drop(seat);
Some(Self::enter(inner, who))
}
fn enter(inner: Arc<Core<Shared>>, who: Driver) -> Self {
set_driving::<Shared>(Some(&inner));
Self {
inner,
who,
left: Cell::new(false),
}
}
fn run_batch(&self) {
self.inner.run_batch();
}
fn round(&self, woken: &AtomicBool, failed_waits: &mut u32) {
self.inner.round(woken, failed_waits);
}
fn wait(&self, at_once: bool, failed_waits: &mut u32) {
self.inner.wait(at_once, failed_waits);
}
fn leave_if_idle(&self) -> bool {
let mut seat = seat(&self.inner);
if self.inner.is_busy() {
return false;
}
seat.free();
self.left.set(true);
true
}
fn yield_if_wanted(&self) -> bool {
let mut seat = seat(&self.inner);
if seat.waiting.is_empty() {
return false;
}
seat.free();
seat.holder = Holder::Nobody {
parked_helper: Some(thread::current()),
};
self.left.set(true);
true
}
}
impl Drop for Driving {
fn drop(&mut self) {
if self.left.get() {
return;
}
let mut seat = seat(&self.inner);
seat.free();
if self.who == Driver::BlockOn {
hand_over(&self.inner, &mut seat);
}
}
}
struct Signal {
thread: Thread,
woken: AtomicBool,
runtime: Mutex<Weak<Core<Shared>>>,
}
impl Wake for Signal {
fn wake(self: Arc<Self>) {
self.wake_by_ref();
}
fn wake_by_ref(self: &Arc<Self>) {
self.woken.swap(true, Ordering::AcqRel);
self.thread.unpark();
let runtime = lock(&self.runtime).upgrade();
if let Some(inner) = runtime {
inner.remote.notify();
}
}
}
struct InBlockOn {
recorded: bool,
outer_held: Option<Arc<Core<Shared>>>,
}
impl InBlockOn {
fn enter(target: Target) -> Self {
assert!(
!drives_a_runtime(),
"block_on called from a task this runtime is running: the call would wait for the \
very thread it is on"
);
if in_block_on() {
leave_the_outer_call(&target);
}
IN_BLOCK_ON.with(|inside| inside.set(inside.get() + 1));
let recorded = TARGETS
.try_with(|targets| targets.borrow_mut().push(target))
.is_ok();
let outer_held = HELD.with(|held| held.borrow_mut().take());
Self {
recorded,
outer_held,
}
}
}
fn leave_the_outer_call(target: &Target) {
let outer = match innermost() {
Target::Own => own(),
Target::Core(core) => Some(core),
};
let Some(outer) = outer else {
return;
};
let nested = match target {
Target::Own => own(),
Target::Core(core) => Some(core.clone()),
};
if nested.is_some_and(|nested| Arc::ptr_eq(&nested, &outer)) {
return;
}
stop_waiting_for(&outer);
}
impl Drop for InBlockOn {
fn drop(&mut self) {
if self.recorded {
let popped = TARGETS.try_with(|targets| targets.borrow_mut().pop());
drop(popped);
}
let outer_held = self.outer_held.take();
let held = HELD.with(|held| mem::replace(&mut **held.borrow_mut(), outer_held));
drop(held);
IN_BLOCK_ON.with(|inside| inside.set(inside.get() - 1));
}
}
thread_local! {
static IN_BLOCK_ON: Cell<u32> = const { Cell::new(0) };
static TARGETS: RefCell<Vec<Target>> = const { RefCell::new(Vec::new()) };
static HELD: RefCell<ManuallyDrop<Option<Arc<Core<Shared>>>>> =
const { RefCell::new(ManuallyDrop::new(None)) };
}
pub(crate) fn seat(core: &Core<Shared>) -> MutexGuard<'_, Seat> {
let Some(seat) = &core.seat else {
unreachable!("a runtime the seat machinery runs has a seat");
};
lock(seat)
}
impl Core<Shared> {
pub(crate) fn is_busy(&self) -> bool {
self.remote.has_ready() || self.scheduler.live_tasks() > 0 || !self.reactor.is_idle()
}
}
fn is_own(inner: &Arc<Core<Shared>>) -> bool {
OWN.try_with(|own| std::ptr::eq(lock(own).as_ptr(), Arc::as_ptr(inner)))
.unwrap_or(false)
}
pub(crate) fn shared_in(registry: &Mutex<Weak<Core<Shared>>>) -> io::Result<Arc<Core<Shared>>> {
let mut shared = lock(registry);
if let Some(core) = shared.upgrade() {
return Ok(core);
}
let core = Core::with_seat()?;
*shared = Arc::downgrade(&core);
Ok(core)
}
#[cfg(test)]
pub(crate) fn own_for_the_call(core: &Arc<Core<Shared>>) -> impl Drop {
struct Restore(Weak<Core<Shared>>);
impl Drop for Restore {
fn drop(&mut self) {
OWN.with(|own| *lock(own) = std::mem::take(&mut self.0));
}
}
OWN.with(|own| Restore(std::mem::replace(&mut *lock(own), Arc::downgrade(core))))
}
thread_local! {
static OWN: Mutex<Weak<Core<Shared>>> = const { Mutex::new(Weak::new()) };
}
static SHARED: Mutex<Weak<Core<Shared>>> = Mutex::new(Weak::new());