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, ScheduleInfo};
mod lent;
pub(crate) use lent::{ForgetLost, Unlent, lent_back};
#[cfg(not(target_arch = "wasm32"))]
pub(crate) use lent::{lends_a_value, without_shard};
pub(crate) const DEFAULT_MAX_NESTING: usize = 256;
pub(crate) struct Here {
current: Cell<*const Shard>,
in_task: Cell<bool>,
out: Cell<*const Slot>,
depth: Cell<usize>,
slot: Cell<*const Slot>,
epochs: Cell<u64>,
saved: 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>,
letting_go: Cell<*const Arc<Shared>>,
lost: Cell<*const Shard>,
lent: Cell<*const Shard>,
}
impl Here {
const fn new() -> Self {
Self {
current: Cell::new(core::ptr::null()),
in_task: Cell::new(false),
out: Cell::new(core::ptr::null()),
depth: Cell::new(0),
slot: Cell::new(core::ptr::null()),
epochs: Cell::new(0),
saved: 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()),
letting_go: Cell::new(core::ptr::null()),
lost: Cell::new(core::ptr::null()),
lent: Cell::new(core::ptr::null()),
}
}
#[cold]
fn detach(&self) {
if let Some(shard) = unsafe { self.current.get().as_ref() } {
self.detached.set(shard.shared_addr());
self.lost.set(shard);
shard.shared.detached_began();
aim_home(self);
}
self.current.set(core::ptr::null());
self.slot.set(core::ptr::null());
self.out.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 {
slot: &'static Slot,
word: u64,
}
impl Bracket {
#[inline(always)]
fn take(here: &'static Here, slot: *const Slot) -> Option<Self> {
let slot = unsafe { &*slot };
match slot.mark() {
(word, Back::In) => {
here.out.set(core::ptr::null());
Some(Self { slot, word })
}
(_, Back::Taken) => {
detach_here();
None
}
}
}
}
impl Drop for Bracket {
#[inline(always)]
fn drop(&mut self) {
local().out.set(self.slot);
self.slot.unmark(self.word);
}
}
#[inline(always)]
pub(crate) fn current<R>(f: impl FnOnce(Option<&Shard>) -> R) -> R {
let held = current_held();
f(held.shard())
}
#[inline(always)]
pub(crate) fn current_held() -> Current {
let here = local();
let out = here.out.get();
if out.is_null() {
return Current {
shard: here.current.get(),
_bracket: None,
};
}
bracketed()
}
#[inline(always)]
pub(crate) fn bracketed() -> Current {
let here = local();
let out = here.out.get();
debug_assert!(!out.is_null(), "a bracket where none is needed");
let bracket = Bracket::take(here, out);
Current {
shard: if bracket.is_some() {
here.current.get()
} else {
core::ptr::null()
},
_bracket: bracket,
}
}
#[inline(always)]
pub(crate) fn unbracketed(here: &Here) -> Result<Option<&Shard>, Bracketed> {
if here.out.get().is_null() {
Ok(unsafe { here.current.get().as_ref() })
} else {
Err(Bracketed)
}
}
#[inline(always)]
pub(crate) fn running_inside(here: &Here) -> Option<&Shard> {
unbracketed(here).ok().flatten()
}
pub(crate) struct Bracketed;
#[inline(always)]
pub(crate) fn running(here: &Here) -> Option<&Shard> {
unsafe { here.current.get().as_ref() }
}
pub(crate) enum Marked {
Not,
In,
Taken,
}
#[inline(always)]
pub(crate) fn mark_call() -> Marked {
let here = local();
if here.out.get().is_null() {
return Marked::Not;
}
debug_assert!(core::ptr::eq(here.out.get(), here.slot.get()));
let slot = unsafe { &*here.slot.get() };
match slot.mark() {
(word, Back::In) => {
here.saved.set(word);
here.out.set(core::ptr::null());
Marked::In
}
(_, Back::Taken) => {
detach_here();
Marked::Taken
}
}
}
#[inline(always)]
pub(crate) fn push_lifo_marked<O>(
runnable: Runnable,
info: ScheduleInfo,
ours: O,
) -> Result<(), Runnable>
where
O: FnOnce(&Shard) -> bool,
{
let here = local();
let out = here.out.get();
debug_assert!(core::ptr::eq(out, here.slot.get()) || here.slot.get().is_null());
let slot = unsafe { &*out };
let Some(word) = slot.mark_unless_claimed() else {
return Err(runnable);
};
debug_assert!(
core::ptr::eq(out, here.slot.get()),
"the leaf marked a taken slot"
);
let pushed = match running(here) {
Some(current) if ours(current) => {
current.push_lifo_alone(runnable, info, here.in_task.get())
}
_ => Err(runnable),
};
slot.unmark(word);
pushed
}
#[cold]
#[inline(never)]
fn detach_here() {
local().detach();
}
#[inline(always)]
pub(crate) fn unmark_call() {
let here = local();
let slot = here.slot.get();
debug_assert!(here.out.get().is_null(), "a call ended that no mark began");
debug_assert!(
!slot.is_null(),
"a marked call polled: its thread lost the shard in it"
);
here.out.set(slot);
unsafe { &*slot }.unmark(here.saved.get());
}
#[cold]
#[inline(never)]
pub(crate) fn unmark_unwound() {
let here = local();
if here.out.get().is_null() && !here.slot.get().is_null() {
unmark_call();
}
}
pub(crate) struct Current {
shard: *const Shard,
_bracket: Option<Bracket>,
}
impl Current {
#[inline(always)]
pub(crate) fn shard(&self) -> Option<&Shard> {
unsafe { self.shard.as_ref() }
}
}
pub(crate) fn letting_go<R>(shared: &Arc<Shared>, f: impl FnOnce() -> R) -> R {
struct Restore(*const Arc<Shared>);
impl Drop for Restore {
fn drop(&mut self) {
local().letting_go.set(self.0);
}
}
let _restore = Restore(local().letting_go.replace(shared));
f()
}
pub(crate) fn lost_shared<R>(f: impl FnOnce(Option<&Arc<Shared>>) -> R) -> R {
f(unsafe { local().lost.get().as_ref() }.map(|shard| &shard.shared))
}
pub(crate) fn lost_a_shard() -> bool {
!local().lost.get().is_null()
}
#[cold]
#[inline(never)]
fn aim_home(here: &Here) {
if here.lost.get().is_null() {
return;
}
let (home, own) = unsafe { (here.home.get().as_ref(), here.own.get().as_ref()) };
if let (Some(home), Some(own)) = (home, own) {
home.target().move_to(&own.shared);
}
}
pub(crate) fn letting_go_of<R>(f: impl FnOnce(Option<&Arc<Shared>>) -> R) -> R {
f(unsafe { local().letting_go.get().as_ref() })
}
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))
}
}
#[inline]
pub(crate) fn watched() -> bool {
!local().slot.get().is_null()
}
pub(crate) fn detached_from_addr() -> usize {
local().detached.get()
}
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)),
out: here.out.replace(core::ptr::null()),
_shard: core::marker::PhantomData,
}
}
pub(crate) struct Entered<'a> {
previous: *const Shard,
slot: *const Slot,
out: *const Slot,
_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.out.set(self.out);
}
}
#[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, poll);
}
poll_watched(here, shared, poll)
}
#[inline(always)]
fn poll_unwatched<R>(here: &Here, poll: impl FnOnce() -> R) -> (R, Polled) {
struct Back<'a>(&'a Here, bool);
impl Drop for Back<'_> {
#[inline(always)]
fn drop(&mut self) {
self.0.in_task.set(self.1);
}
}
let back = Back(here, here.in_task.replace(true));
let result = poll();
drop(back);
(result, Polled::In)
}
#[inline(never)]
fn poll_watched<R>(here: &'static Here, shared: &Shared, poll: impl FnOnce() -> R) -> (R, Polled) {
struct Back {
here: &'static Here,
slot: &'static Slot,
epoch: u64,
in_task: bool,
}
impl Drop for Back {
#[inline(always)]
fn drop(&mut self) {
let here = self.here;
here.out.set(core::ptr::null());
if self.slot.come_back(self.epoch) == super::handoff::slot::Back::Taken {
here.detach();
}
here.in_task.set(self.in_task);
}
}
let slot = unsafe { &*here.slot.get() };
let epoch = here.epochs.get() + 1;
here.epochs.set(epoch);
let back = Back {
here,
slot,
epoch,
in_task: here.in_task.replace(true),
};
here.out.set(slot);
slot.go_out(epoch);
let result = poll();
drop(back);
(result, polled(here, shared))
}
#[inline(always)]
pub(crate) fn run_task(shared: &Shared, run: impl FnOnce()) -> Polled {
let here = local();
if here.slot.get().is_null() {
return poll_unwatched(here, run).1;
}
run_task_watched(here, run);
polled(here, shared)
}
#[inline(never)]
fn run_task_watched(here: &'static Here, run: impl FnOnce()) {
let slot = unsafe { &*here.slot.get() };
let epoch = here.epochs.get() + 1;
here.epochs.set(epoch);
let in_task = here.in_task.replace(true);
here.out.set(slot);
slot.go_out(epoch);
super::task::abort_on_panic(run);
here.out.set(core::ptr::null());
if slot.come_back(epoch) == Back::Taken {
here.detach();
}
here.in_task.set(in_task);
}
#[inline]
pub(crate) fn detached_from(shared: &Shared) -> bool {
polled(local(), shared) == Polled::Detached
}
#[inline(always)]
fn polled(here: &Here, shared: &Shared) -> Polled {
if here.detached.get() == core::ptr::from_ref(shared) as usize {
Polled::Detached
} else {
Polled::In
}
}
#[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 = Shard::new_rc(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));
aim_home(local());
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) fn swap_runtime(inner: &Arc<Inner>) -> Option<Option<Arc<Inner>>> {
RUNTIME
.try_with(|r| r.replace(Some(Arc::clone(inner))))
.ok()
}
pub(crate) fn put_runtime(previous: Option<Arc<Inner>>) {
drop(RuntimeSet { previous });
}
pub(crate) fn mark_own(served: &Arc<Inner>) -> bool {
let own = local().own.get();
current(|current| {
current.is_some_and(|shard| core::ptr::eq(shard, own) && shard.mark_served(served))
})
}
pub(crate) fn unmark_own() {
if let Some(own) = unsafe { local().own.get().as_ref() } {
own.unmark();
}
}
pub(crate) struct ServedMark(bool);
impl ServedMark {
pub(crate) fn new(served: Option<&Arc<Inner>>) -> Self {
Self(served.is_some_and(mark_own))
}
}
impl Drop for ServedMark {
fn drop(&mut self) {
if self.0 {
unmark_own();
}
}
}
pub(crate) struct RuntimeSet {
previous: Option<Arc<Inner>>,
}
impl RuntimeSet {
pub(crate) fn previous(&self) -> Option<&Arc<Inner>> {
self.previous.as_ref()
}
}
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())
}
}