use core::cell::{Cell, RefCell};
use std::io;
use std::rc::Rc;
use std::sync::Arc;
use super::handoff::home::Home;
use super::handoff::slot::{Back, Slot};
use super::runtime::Inner;
use super::shard::{Limits, Shard, Shared};
use super::task::Runnable;
pub(crate) const DEFAULT_MAX_NESTING: usize = 256;
pub(crate) struct Here {
current: Cell<*const Shard>,
in_task: Cell<bool>,
inside: Cell<bool>,
depth: Cell<usize>,
slot: Cell<*const Slot>,
epoch: Cell<u64>,
epochs: Cell<u64>,
detached: Cell<usize>,
home: Cell<*const Home>,
pub(crate) task: Cell<Option<super::task::Id>>,
pub(crate) budget: Cell<u64>,
runtime: Cell<*const Inner>,
own: Cell<*const Shard>,
}
impl Here {
const fn new() -> Self {
Self {
current: Cell::new(core::ptr::null()),
in_task: Cell::new(false),
inside: Cell::new(false),
depth: Cell::new(0),
slot: Cell::new(core::ptr::null()),
epoch: Cell::new(0),
epochs: Cell::new(0),
detached: Cell::new(0),
home: Cell::new(core::ptr::null()),
task: Cell::new(None),
budget: Cell::new(crate::coop::UNBUDGETED),
runtime: Cell::new(core::ptr::null()),
own: Cell::new(core::ptr::null()),
}
}
#[inline]
fn come_back(&self, epoch: u64) -> bool {
match unsafe { self.slot.get().as_ref() } {
Some(slot) if slot.come_back(epoch) == Back::Taken => {
self.detach();
false
}
_ => true,
}
}
#[cold]
fn detach(&self) {
if let Some(shard) = unsafe { self.current.get().as_ref() } {
self.detached.set(shard.shared_addr());
}
self.current.set(core::ptr::null());
self.slot.set(core::ptr::null());
}
}
thread_local! {
static HERE: Here = const { Here::new() };
static OWN: OwnCell = const { OwnCell(RefCell::new(None)) };
static RUNTIME: RuntimeCell = const { RuntimeCell(Cell::new(None)) };
static HOME: HomeCell = const { HomeCell(RefCell::new(None)) };
}
struct RuntimeCell(Cell<Option<Arc<Inner>>>);
impl RuntimeCell {
fn replace(&self, runtime: Option<Arc<Inner>>) -> Option<Arc<Inner>> {
local()
.runtime
.set(runtime.as_ref().map_or(core::ptr::null(), Arc::as_ptr));
self.0.replace(runtime)
}
}
impl Drop for RuntimeCell {
fn drop(&mut self) {
local().runtime.set(core::ptr::null());
}
}
struct OwnCell(RefCell<Option<Rc<Shard>>>);
impl Drop for OwnCell {
fn drop(&mut self) {
local().own.set(core::ptr::null());
}
}
struct HomeCell(RefCell<Option<Arc<Home>>>);
impl Drop for HomeCell {
fn drop(&mut self) {
local().home.set(core::ptr::null());
}
}
#[inline(always)]
pub(crate) fn local() -> &'static Here {
HERE.with(|here| unsafe { &*core::ptr::from_ref(here) })
}
struct Bracket<'a>(&'a Here);
impl Drop for Bracket<'_> {
fn drop(&mut self) {
let here = self.0;
here.inside.set(false);
if let Some(slot) = unsafe { here.slot.get().as_ref() } {
slot.go_out(here.epoch.get());
}
}
}
#[inline(always)]
pub(crate) fn current<R>(f: impl FnOnce(Option<&Shard>) -> R) -> R {
let here = local();
let shard = here.current.get();
if shard.is_null() {
return f(None);
}
let _bracket = match bracket(here) {
Some(bracket) => bracket,
None => return f(None),
};
f(unsafe { shard.as_ref() })
}
#[inline(always)]
fn bracket(here: &'static Here) -> Option<Option<Bracket<'static>>> {
if here.inside.get() || here.slot.get().is_null() {
Some(None)
} else if here.come_back(here.epoch.get()) {
here.inside.set(true);
Some(Some(Bracket(here)))
} else {
None
}
}
#[inline(always)]
pub(crate) fn current_held() -> Current {
let here = local();
let shard = here.current.get();
if shard.is_null() {
return Current {
shard,
_bracket: None,
};
}
match bracket(here) {
Some(bracket) => Current {
shard,
_bracket: bracket,
},
None => Current {
shard: core::ptr::null(),
_bracket: None,
},
}
}
pub(crate) struct Current {
shard: *const Shard,
_bracket: Option<Bracket<'static>>,
}
impl Current {
#[inline(always)]
pub(crate) fn shard(&self) -> Option<&Shard> {
unsafe { self.shard.as_ref() }
}
}
#[inline(always)]
pub(crate) fn running_inside(here: &Here) -> Option<&Shard> {
if here.inside.get() | here.slot.get().is_null() {
unsafe { here.current.get().as_ref() }
} else {
None
}
}
impl Here {
#[inline(always)]
pub(crate) fn in_task(&self) -> bool {
self.in_task.get()
}
}
pub(crate) fn current_pinned<R>(f: impl FnOnce(Option<&Shard>) -> R) -> R {
struct Pinned<'a>(&'a Slot);
impl Drop for Pinned<'_> {
fn drop(&mut self) {
self.0.unpin();
}
}
current(|shard| {
let Some(shard) = shard else {
return f(None);
};
let slot = local().slot.get();
let _pinned = unsafe { slot.as_ref() }.map(|slot| {
slot.pin();
Pinned(slot)
});
f(Some(shard))
})
}
pub(crate) fn with_slot<R>(f: impl FnOnce(Option<&Slot>) -> R) -> R {
f(unsafe { local().slot.get().as_ref() })
}
#[cfg(ring)]
pub(crate) fn here<R>(f: impl FnOnce(Option<&Shard>) -> R) -> R {
current(|shard| match shard {
Some(shard) => f(Some(shard)),
None => {
let own = own_shard_if_made();
f(own.as_deref())
}
})
}
#[inline]
pub(crate) fn own_shard_if_made() -> Option<Rc<Shard>> {
let own = local().own.get();
if own.is_null() {
return None;
}
unsafe {
Rc::increment_strong_count(own);
Some(Rc::from_raw(own))
}
}
pub(crate) fn enter(shard: &Shard) -> Entered<'_> {
enter_watched(shard, None)
}
pub(crate) fn enter_watched<'a>(shard: &'a Shard, slot: Option<&'a Slot>) -> Entered<'a> {
let here = local();
if slot.is_some() {
here.detached.set(0);
}
enter_with(here, shard, slot)
}
fn enter_with<'a>(here: &Here, shard: &'a Shard, slot: Option<&'a Slot>) -> Entered<'a> {
Entered {
previous: here.current.replace(shard),
slot: here
.slot
.replace(slot.map_or(core::ptr::null(), core::ptr::from_ref)),
inside: here.inside.replace(true),
_shard: core::marker::PhantomData,
}
}
pub(crate) struct Entered<'a> {
previous: *const Shard,
slot: *const Slot,
inside: bool,
_shard: core::marker::PhantomData<&'a Shard>,
}
impl Drop for Entered<'_> {
fn drop(&mut self) {
let here = local();
here.current.set(self.previous);
here.slot.set(self.slot);
here.inside.set(self.inside);
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Polled {
In,
Detached,
}
#[inline]
pub(crate) fn poll<R>(shared: &Shared, poll: impl FnOnce() -> R) -> (R, Polled) {
let here = local();
if here.slot.get().is_null() {
return poll_unwatched(here, shared, poll);
}
poll_watched(here, shared, poll)
}
#[inline(always)]
fn poll_unwatched<R>(here: &Here, shared: &Shared, poll: impl FnOnce() -> R) -> (R, Polled) {
struct Back<'a>(&'a Here, bool);
impl Drop for Back<'_> {
fn drop(&mut self) {
self.0.in_task.set(self.1);
self.0.inside.set(true);
}
}
let back = Back(here, here.in_task.replace(true));
here.inside.set(false);
let result = poll();
drop(back);
let polled = if here.detached.get() == core::ptr::from_ref(shared) as usize {
Polled::Detached
} else {
Polled::In
};
(result, polled)
}
#[inline(never)]
fn poll_watched<R>(here: &'static Here, shared: &Shared, poll: impl FnOnce() -> R) -> (R, Polled) {
struct Out<'a> {
here: &'a Here,
shared: usize,
in_task: bool,
epoch: u64,
mine: u64,
done: bool,
}
impl Out<'_> {
fn back(&mut self) -> Polled {
self.done = true;
let here = self.here;
here.come_back(self.mine);
here.in_task.set(self.in_task);
here.inside.set(true);
here.epoch.set(self.epoch);
if here.detached.get() == self.shared {
Polled::Detached
} else {
Polled::In
}
}
}
impl Drop for Out<'_> {
fn drop(&mut self) {
if !self.done {
self.back();
}
}
}
let mine = here.epochs.get() + 1;
here.epochs.set(mine);
let mut out = Out {
here,
shared: core::ptr::from_ref(shared) as usize,
in_task: here.in_task.replace(true),
epoch: here.epoch.replace(mine),
mine,
done: false,
};
here.inside.set(false);
if let Some(slot) = unsafe { here.slot.get().as_ref() } {
slot.go_out(mine);
}
let result = poll();
(result, out.back())
}
#[inline]
pub(crate) fn in_task() -> bool {
local().in_task.get()
}
pub(crate) fn with_in_task<R>(in_task: bool, f: impl FnOnce() -> R) -> R {
struct Restore<'a>(&'a Here, bool);
impl Drop for Restore<'_> {
fn drop(&mut self) {
self.0.in_task.set(self.1);
}
}
let here = local();
let _restore = Restore(here, here.in_task.replace(in_task));
f()
}
#[expect(
clippy::panic,
reason = "documented under `# Panics` of `block_on` (`Builder::max_nesting`)"
)]
pub(crate) fn deeper<R>(max: usize, f: impl FnOnce() -> R) -> R {
struct Up<'a>(&'a Here);
impl Drop for Up<'_> {
fn drop(&mut self) {
self.0.depth.set(self.0.depth.get() - 1);
}
}
let here = local();
let depth = here.depth.get();
if depth >= max {
panic!(
"block_on is nested {max} deep on one thread, the most allowed: each level runs \
on the thread's stack (raise the limit with `Builder::max_nesting`)"
);
}
here.depth.set(depth + 1);
let _up = Up(here);
f()
}
#[inline]
pub(crate) fn own_shard() -> io::Result<Rc<Shard>> {
match own_shard_if_made() {
Some(own) => Ok(own),
None => make_own_shard(),
}
}
#[inline(never)]
fn make_own_shard() -> io::Result<Rc<Shard>> {
OWN.with(|own| {
let own = &own.0;
if let Some(shard) = own.borrow().as_ref() {
return Ok(Rc::clone(shard));
}
let shard = Rc::new(Shard::new(Limits {
max_nesting: DEFAULT_MAX_NESTING,
buffer_cap: crate::buf::DEFAULT_CAP,
backend: super::backend::chosen()?,
})?);
*own.borrow_mut() = Some(Rc::clone(&shard));
local().own.set(Rc::as_ptr(&shard));
Ok(shard)
})
}
#[inline]
pub(crate) fn runtime() -> Option<Arc<Inner>> {
let inner = local().runtime.get();
if inner.is_null() {
return None;
}
unsafe {
Arc::increment_strong_count(inner);
Some(Arc::from_raw(inner))
}
}
#[inline]
pub(crate) fn with_runtime<R>(f: impl FnOnce(Option<&Inner>) -> R) -> R {
f(unsafe { local().runtime.get().as_ref() })
}
#[inline]
pub(crate) fn runtime_is(inner: *const Inner) -> bool {
core::ptr::eq(local().runtime.get(), inner)
}
pub(crate) fn set_runtime(inner: &Arc<Inner>) -> RuntimeSet {
let previous = RUNTIME.with(|r| r.replace(Some(Arc::clone(inner))));
RuntimeSet { previous }
}
pub(crate) fn try_set_runtime(inner: &Arc<Inner>) -> Option<RuntimeSet> {
let previous = RUNTIME
.try_with(|r| r.replace(Some(Arc::clone(inner))))
.ok()?;
Some(RuntimeSet { previous })
}
pub(crate) struct RuntimeSet {
previous: Option<Arc<Inner>>,
}
impl Drop for RuntimeSet {
fn drop(&mut self) {
let previous = self.previous.take();
let _ = RUNTIME.try_with(|r| drop(r.replace(previous)));
}
}
#[inline]
pub(crate) fn home_for(shard: &Shard) -> Arc<Home> {
let home = local().home.get();
if home.is_null() {
return shard.own_home();
}
unsafe {
Arc::increment_strong_count(home);
Arc::from_raw(home)
}
}
pub(crate) fn set_home(home: Arc<Home>) {
set_home_ptr(&home);
HOME.with(|slot| *slot.0.borrow_mut() = Some(home));
}
fn set_home_ptr(home: &Arc<Home>) {
local().home.set(Arc::as_ptr(home));
}
#[inline]
pub(crate) fn is_home(home: &Home) -> bool {
core::ptr::eq(local().home.get(), home)
}
pub(crate) fn pop_home() -> Option<Runnable> {
unsafe { local().home.get().as_ref().and_then(|home| home.pop()) }
}
pub(crate) fn home_is_drained() -> bool {
unsafe {
local()
.home
.get()
.as_ref()
.is_none_or(|home| home.is_drained())
}
}