use std::{marker::PhantomData, mem, panic::Location, sync::atomic::Ordering};
use super::{Core, FLASH, FlashInner, SyncHolder};
use crate::{
common::thread_id::ACTIVE_NAMED_THREADS,
flash::{
ctx::{self, Credit},
ids::ThreadKey,
},
native::thread::current,
};
fn current_thread_key() -> ThreadKey {
ThreadKey::of(current().id())
}
fn current_thread_name() -> Option<String> {
std::thread::current().name().map(str::to_owned)
}
fn in_async_poll() -> bool {
ctx::poll_depth() > 0
}
pub(crate) fn mark_dedicated() {
ctx::set_dedicated(true);
ctx::set_credit(Credit::Running);
FLASH.sync_holder_running();
}
fn is_dedicated() -> bool {
ctx::dedicated()
}
#[must_use]
pub(crate) struct DedicatedSlot {
named: bool,
}
impl DedicatedSlot {
pub(crate) fn claim_dedicated(self) -> Participant {
debug_assert!(self.named, "claim_dedicated on a spawn_blocking slot");
let named = self.named;
mem::forget(self);
mark_dedicated();
Participant {
named,
_not_send: PhantomData,
}
}
pub(crate) fn claim_pooled(self) -> PoolParticipant {
debug_assert!(!self.named, "claim_pooled on a spawn_named slot");
mem::forget(self);
let prev = ctx::dedicated();
mark_dedicated();
PoolParticipant {
prev_dedicated: prev,
_not_send: PhantomData,
}
}
pub(crate) fn reserve() -> Self {
FLASH.pre_count_dedicated();
Self { named: false }
}
pub(crate) fn reserve_named() -> Self {
ACTIVE_NAMED_THREADS.fetch_add(1, Ordering::Release);
FLASH.pre_count_dedicated();
Self { named: true }
}
}
impl Drop for DedicatedSlot {
fn drop(&mut self) {
FLASH.release_unclaimed_slot();
if self.named {
ACTIVE_NAMED_THREADS.fetch_sub(1, Ordering::Release);
}
}
}
#[must_use]
pub(crate) struct Participant {
_not_send: PhantomData<*mut ()>,
named: bool,
}
impl Participant {
pub(crate) fn unreserved() -> Self {
Self {
named: false,
_not_send: PhantomData,
}
}
}
impl Drop for Participant {
fn drop(&mut self) {
FLASH.on_participant_exit();
if self.named {
ACTIVE_NAMED_THREADS.fetch_sub(1, Ordering::Release);
}
}
}
#[must_use]
pub(crate) struct PoolParticipant {
_not_send: PhantomData<*mut ()>,
prev_dedicated: bool,
}
impl Drop for PoolParticipant {
fn drop(&mut self) {
FLASH.on_participant_exit();
ctx::set_dedicated(self.prev_dedicated);
}
}
pub(in crate::flash) struct AsyncPollGuard {
prev_cur: Option<(u64, &'static Location<'static>)>,
_not_send: PhantomData<*mut ()>,
}
impl AsyncPollGuard {
pub(in crate::flash) fn enter(id: u64, loc: &'static Location<'static>) -> Self {
ctx::set_poll_depth(ctx::poll_depth().saturating_add(1));
let prev_cur = ctx::swap_cur_async(Some((id, loc)));
Self {
prev_cur,
_not_send: PhantomData,
}
}
}
impl Drop for AsyncPollGuard {
fn drop(&mut self) {
ctx::swap_cur_async(self.prev_cur);
ctx::set_poll_depth(ctx::poll_depth().saturating_sub(1));
}
}
#[must_use]
pub(crate) struct WaitGuard<'a> {
flash: &'a FlashInner,
_not_send: PhantomData<*mut ()>,
}
impl WaitGuard<'_> {
#[cfg(test)]
pub(super) fn mark_running(self) {
mem::forget(self);
mark_running();
}
pub(crate) fn resume(self) {
let flash = self.flash;
mem::forget(self);
flash.resume_after_wait();
}
}
impl Drop for WaitGuard<'_> {
fn drop(&mut self) {
debug_assert!(
std::thread::panicking(),
"WaitGuard dropped without resume()/mark_running() — wrapped wait left unsettled"
);
}
}
fn mark_running() {
ctx::set_credit(Credit::Running);
}
pub(crate) fn reset_credit() {
ctx::set_credit(Credit::None);
}
impl FlashInner {
pub(super) fn enter_wait_locked(&self, s: &mut Core) -> WaitGuard<'_> {
if in_async_poll() {
debug_assert!(
s.registry.active_async > 0,
"bridged wait must be inside a counted async poll"
);
s.registry.active_async -= 1;
if let Some((id, _)) = ctx::cur_async() {
s.registry.active_async_holders.remove(&id);
}
} else if !is_dedicated() {
} else {
match ctx::credit() {
Credit::None => ctx::set_credit(Credit::Parked),
Credit::Running => {
debug_assert!(
s.registry.active > 0,
"running participant must be counted in active"
);
s.registry.active -= 1;
ctx::set_credit(Credit::Parked);
s.registry.active_sync_holders.remove(¤t_thread_key());
}
Credit::Parked => {
debug_assert!(false, "a thread cannot enter two wrapped waits at once");
}
}
}
WaitGuard {
flash: self,
_not_send: PhantomData,
}
}
pub(in crate::flash) fn on_participant_exit(&self) {
let was = ctx::credit();
ctx::set_credit(Credit::None);
if was != Credit::Running {
return;
}
let mut s = self.core.lock();
debug_assert!(
s.registry.active > 0,
"exiting running participant must be counted"
);
s.registry.active -= 1;
s.registry.active_sync_holders.remove(¤t_thread_key());
let adv = s.try_advance(&self.clock);
drop(s);
adv.fire();
}
pub(in crate::flash) fn pre_count_dedicated(&self) {
self.core.lock().registry.active += 1;
}
fn release_unclaimed_slot(&self) {
let mut s = self.core.lock();
debug_assert!(
s.registry.active > 0,
"unclaimed slot release without a matching reserve"
);
s.registry.active -= 1;
let adv = s.try_advance(&self.clock);
drop(s);
adv.fire();
}
pub(in crate::flash) fn resume_after_wait(&self) {
if in_async_poll() {
let mut s = self.core.lock();
debug_assert!(
s.registry.active > 0,
"bridged resume without a firer active bump"
);
s.registry.active -= 1;
s.registry.active_async += 1;
if let Some((id, loc)) = ctx::cur_async() {
s.registry.active_async_holders.insert(id, loc);
}
drop(s);
return;
}
if !is_dedicated() {
let mut s = self.core.lock();
debug_assert!(
s.registry.active > 0,
"non-pacer resume without a firer active bump"
);
s.registry.active -= 1;
let adv = s.try_advance(&self.clock);
drop(s);
adv.fire();
return;
}
mark_running();
self.sync_holder_running();
}
pub(in crate::flash) fn sync_holder_running(&self) {
let key = current_thread_key();
let name = current_thread_name();
self.core
.lock()
.registry
.active_sync_holders
.insert(key, SyncHolder { name });
}
}