use std::{
panic::Location,
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
task::Waker,
};
use super::{
Clock, Core, CvDesc, CvId, FLASH, FlashInner, Registry, WaiterId,
credit::WaitGuard,
gate::{AtomicTaskState, ParkOutcome, TaskState, WakeOutcome},
wake::{Token, Wake},
};
use crate::flash::{ctx, diag, ids::ThreadKey};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) enum WaitKind {
Timed,
Thread(ThreadKey),
Condvar(CvId),
}
pub(super) struct Entry {
pub(super) kind: WaitKind,
pub(super) wake: Wake,
}
impl Registry {
fn account_woken(&mut self, woken: &[Wake]) {
self.active += woken.iter().filter(|w| !w.is_task()).count();
for w in woken {
w.mark_granted_under_lock();
}
}
fn fresh_cv(&mut self) -> CvId {
let id = self.next_cv;
self.next_cv += 1;
CvId(id)
}
fn fresh_id(&mut self) -> WaiterId {
let id = self.next_id;
self.next_id += 1;
WaiterId(id)
}
}
#[must_use]
pub(crate) struct WakeBatch(Vec<Wake>);
impl WakeBatch {
pub(crate) fn fire(self) {
for w in self.0 {
w.fire();
}
}
}
impl Core {
fn release_async(&mut self, clock: &Clock) -> WakeBatch {
debug_assert!(
self.registry.active_async > 0,
"async release without a matching acquire"
);
self.registry.active_async -= 1;
self.try_advance(clock)
}
pub(super) fn try_advance(&mut self, clock: &Clock) -> WakeBatch {
if self.registry.active != 0 || self.registry.active_async != 0 {
return WakeBatch(Vec::new());
}
if !self.sched.yielders.is_empty()
&& self
.sched
.timed
.values()
.all(|e| matches!(e.kind, WaitKind::Thread(_)))
{
let woken: Vec<Wake> = std::mem::take(&mut self.sched.yielders)
.into_values()
.collect();
self.registry.account_woken(&woken);
return WakeBatch(woken);
}
let Some((&(min, _), _)) = self.sched.timed.iter().next() else {
return WakeBatch(Vec::new());
};
if self.sched.real_io != 0
&& let Some((anchor_real, anchor_virtual)) = self.sched.pace_anchor
{
let elapsed = u64::try_from(anchor_real.elapsed().as_nanos()).unwrap_or(u64::MAX);
if min > anchor_virtual.saturating_add(elapsed) {
return WakeBatch(Vec::new());
}
}
debug_assert!(
min >= clock.now_nanos(),
"virtual clock must not move backward"
);
clock.store(min);
#[cfg(test)]
self.sched.advance_log.push(min);
let mut woken = Vec::new();
while let Some((&(d, _), _)) = self.sched.timed.iter().next() {
if d != min {
break;
}
if let Some((_, entry)) = self.sched.timed.pop_first() {
woken.push(entry.wake);
}
}
for (_, wake) in std::mem::take(&mut self.sched.yielders) {
woken.push(wake);
}
self.registry.account_woken(&woken);
WakeBatch(woken)
}
}
impl FlashInner {
pub(in crate::flash) fn next_condvar_id(&self) -> CvId {
self.core.lock().registry.fresh_cv()
}
pub(in crate::flash) fn describe_cvid(
&self,
cvid: CvId,
kind: diag::PrimKind,
loc: &'static Location<'static>,
) {
if !diag::trace_enabled() {
return;
}
let created_on = std::thread::current().name().map(str::to_owned);
self.core.lock().registry.cv_desc.insert(
cvid.0,
CvDesc {
kind,
created_at: loc,
created_on,
},
);
}
#[cfg(test)]
pub(in crate::flash) fn park_for(&self, d: crate::flash::Duration) {
let delta = crate::flash::duration_to_nanos(d);
let token = Token::new();
let mut s = self.core.lock();
let deadline = self.clock.now_nanos().saturating_add(delta);
let id = s.registry.fresh_id();
s.sched.timed.insert(
(deadline, id),
Entry {
wake: Wake::Sync(Arc::clone(&token)),
kind: WaitKind::Timed,
},
);
let wait = self.enter_wait_locked(&mut s);
let adv = s.try_advance(&self.clock);
drop(s);
adv.fire();
token.wait();
wait.mark_running();
}
pub(in crate::flash) fn park_timed_unparkable(
&self,
d: crate::flash::Duration,
thread_id: ThreadKey,
) {
let delta = crate::flash::duration_to_nanos(d);
let token = Token::new();
let mut s = self.core.lock();
if s.sched.unpark_pending.remove(&thread_id) {
return;
}
let deadline = self.clock.now_nanos().saturating_add(delta);
let id = s.registry.fresh_id();
s.sched.timed.insert(
(deadline, id),
Entry {
wake: Wake::Sync(Arc::clone(&token)),
kind: WaitKind::Thread(thread_id),
},
);
let wait = self.enter_wait_locked(&mut s);
let adv = s.try_advance(&self.clock);
drop(s);
adv.fire();
token.wait();
wait.resume();
}
pub(in crate::flash) fn sleep_timed(&self, d: crate::flash::Duration) {
let delta = crate::flash::duration_to_nanos(d);
let token = Token::new();
let mut s = self.core.lock();
let deadline = self.clock.now_nanos().saturating_add(delta);
let id = s.registry.fresh_id();
s.sched.timed.insert(
(deadline, id),
Entry {
wake: Wake::Sync(Arc::clone(&token)),
kind: WaitKind::Timed,
},
);
let wait = self.enter_wait_locked(&mut s);
let adv = s.try_advance(&self.clock);
drop(s);
adv.fire();
token.wait();
wait.resume();
}
pub(in crate::flash) fn unpark(&self, thread_id: ThreadKey) {
let mut s = self.core.lock();
let key = s
.sched
.timed
.iter()
.find(|(_, e)| e.kind == WaitKind::Thread(thread_id))
.map(|(&k, _)| k);
if let Some(key) = key
&& let Some(entry) = s.sched.timed.remove(&key)
{
s.registry.account_woken(std::slice::from_ref(&entry.wake));
drop(s);
entry.wake.fire();
return;
}
s.sched.unpark_pending.insert(thread_id);
}
pub(in crate::flash) fn yield_until_advance(&self) {
let token = Token::new();
let mut s = self.core.lock();
if s.sched.timed.is_empty() {
drop(s);
std::thread::yield_now();
return;
}
let id = s.registry.fresh_id();
s.sched.yielders.insert(id, Wake::Sync(Arc::clone(&token)));
let wait = self.enter_wait_locked(&mut s);
let adv = s.try_advance(&self.clock);
drop(s);
adv.fire();
token.wait();
wait.resume();
}
}
impl FlashInner {
pub(in crate::flash) fn cancel_yield(&self, id: WaiterId) {
self.core.lock().sched.yielders.remove(&id);
}
pub(in crate::flash) fn register_condvar_timed(
&self,
deadline_nanos: u64,
cvid: CvId,
) -> (Arc<Token>, WakeBatch, WaitGuard<'_>) {
let token = Token::new();
let mut s = self.core.lock();
let deadline_nanos = deadline_nanos.max(self.clock.now_nanos());
let id = s.registry.fresh_id();
s.sched.timed.insert(
(deadline_nanos, id),
Entry {
wake: Wake::Sync(Arc::clone(&token)),
kind: WaitKind::Condvar(cvid),
},
);
let wait = self.enter_wait_locked(&mut s);
let adv = s.try_advance(&self.clock);
drop(s);
(token, adv, wait)
}
pub(in crate::flash) fn register_condvar_untimed(
&self,
cvid: CvId,
) -> (Arc<Token>, WakeBatch, WaitGuard<'_>) {
let token = Token::new();
let mut s = self.core.lock();
let id = s.registry.fresh_id();
s.sched.indef.insert(
id,
Entry {
wake: Wake::Sync(Arc::clone(&token)),
kind: WaitKind::Condvar(cvid),
},
);
let wait = self.enter_wait_locked(&mut s);
let adv = s.try_advance(&self.clock);
drop(s);
(token, adv, wait)
}
pub(in crate::flash) fn register_yield_async(
&self,
waker: Waker,
) -> (WaiterId, Arc<AtomicBool>, WakeBatch) {
let granted = Arc::new(AtomicBool::new(false));
let mut s = self.core.lock();
let id = s.registry.fresh_id();
s.sched.yielders.insert(
id,
Wake::Task {
waker,
granted: Arc::clone(&granted),
task: ctx::cur_async(),
},
);
let adv = s.try_advance(&self.clock);
drop(s);
(id, granted, adv)
}
pub(in crate::flash) fn signal_condvar(&self, cvid: CvId, all: bool) {
let mut s = self.core.lock();
let timed_keys: Vec<(u64, WaiterId)> = s
.sched
.timed
.iter()
.filter(|(_, e)| e.kind == WaitKind::Condvar(cvid))
.map(|(&k, _)| k)
.collect();
let indef_keys: Vec<WaiterId> = s
.sched
.indef
.iter()
.filter(|(_, e)| e.kind == WaitKind::Condvar(cvid))
.map(|(&k, _)| k)
.collect();
let mut woken = Vec::new();
for key in timed_keys {
if !all && !woken.is_empty() {
break;
}
if let Some(entry) = s.sched.timed.remove(&key) {
woken.push(entry.wake);
}
}
for key in indef_keys {
if !all && !woken.is_empty() {
break;
}
if let Some(entry) = s.sched.indef.remove(&key) {
woken.push(entry.wake);
}
}
s.registry.account_woken(&woken);
drop(s);
for t in woken {
t.fire();
}
}
}
impl FlashInner {
pub(in crate::flash) fn register_notify_async(
&self,
cvid: CvId,
waker: Waker,
) -> (Option<AsyncHandle>, WakeBatch) {
let granted = Arc::new(AtomicBool::new(false));
let mut s = self.core.lock();
if s.sched.notify_permits.remove(&cvid) {
return (None, WakeBatch(Vec::new()));
}
let id = s.registry.fresh_id();
s.sched.indef.insert(
id,
Entry {
wake: Wake::Task {
waker,
granted: Arc::clone(&granted),
task: ctx::cur_async(),
},
kind: WaitKind::Condvar(cvid),
},
);
let adv = s.try_advance(&self.clock);
drop(s);
(
Some(AsyncHandle {
granted,
timed_key: None,
indef_key: Some(id),
}),
adv,
)
}
pub(in crate::flash) fn register_sleep_async(
&self,
delta_nanos: u64,
waker: Waker,
) -> (AsyncHandle, WakeBatch) {
let granted = Arc::new(AtomicBool::new(false));
let mut s = self.core.lock();
let deadline_nanos = self.clock.now_nanos().saturating_add(delta_nanos);
let id = s.registry.fresh_id();
let key = (deadline_nanos, id);
s.sched.timed.insert(
key,
Entry {
wake: Wake::Task {
waker,
granted: Arc::clone(&granted),
task: ctx::cur_async(),
},
kind: WaitKind::Timed,
},
);
let adv = s.try_advance(&self.clock);
drop(s);
(
AsyncHandle {
granted,
timed_key: Some(key),
indef_key: None,
},
adv,
)
}
}
impl FlashInner {
pub(in crate::flash) fn async_acquire(&self, loc: &'static Location<'static>) -> u64 {
let mut s = self.core.lock();
let id = s.registry.next_task_id;
s.registry.next_task_id += 1;
s.registry.active_async += 1;
s.registry.active_async_holders.insert(id, loc);
id
}
pub(super) fn gate_complete(&self, state: &AtomicTaskState, id: u64) {
let mut s = self.core.lock();
state.store(TaskState::Done);
s.registry.active_async_holders.remove(&id);
let adv = s.release_async(&self.clock);
drop(s);
adv.fire();
}
pub(super) fn gate_drop_release(&self, state: &AtomicTaskState, id: u64) {
let mut s = self.core.lock();
match state.swap(TaskState::Done) {
TaskState::Runnable | TaskState::Running | TaskState::RunningNotified => {
s.registry.active_async_holders.remove(&id);
let adv = s.release_async(&self.clock);
drop(s);
adv.fire();
}
TaskState::Parked | TaskState::Done => {}
}
}
pub(super) fn gate_park(&self, state: &AtomicTaskState, id: u64) -> ParkOutcome {
let mut s = self.core.lock();
if state.compare_exchange(TaskState::Running, TaskState::Parked) {
s.registry.active_async_holders.remove(&id);
let adv = s.release_async(&self.clock);
drop(s);
adv.fire();
ParkOutcome::Parked
} else {
state.store(TaskState::Runnable);
ParkOutcome::WokenMidPoll
}
}
pub(super) fn gate_wake_parked(
&self,
state: &AtomicTaskState,
id: u64,
loc: &'static Location<'static>,
) -> WakeOutcome {
let mut s = self.core.lock();
if state.compare_exchange(TaskState::Parked, TaskState::Runnable) {
s.registry.active_async += 1;
s.registry.active_async_holders.insert(id, loc);
WakeOutcome::Resumed
} else {
WakeOutcome::NotParked
}
}
}
impl FlashInner {
pub(in crate::flash) fn cancel_async_wait(&self, handle: &AsyncHandle) {
let mut s = self.core.lock();
match (handle.timed_key, handle.indef_key) {
(Some(key), _) => {
s.sched.timed.remove(&key);
}
(_, Some(id)) => {
s.sched.indef.remove(&id);
}
_ => {}
}
}
pub(in crate::flash) fn register_channel_async(
&self,
cvid: CvId,
waker: Waker,
) -> (AsyncHandle, WakeBatch) {
let granted = Arc::new(AtomicBool::new(false));
let mut s = self.core.lock();
let id = s.registry.fresh_id();
s.sched.indef.insert(
id,
Entry {
wake: Wake::Task {
waker,
granted: Arc::clone(&granted),
task: ctx::cur_async(),
},
kind: WaitKind::Condvar(cvid),
},
);
let adv = s.try_advance(&self.clock);
drop(s);
(
AsyncHandle {
granted,
timed_key: None,
indef_key: Some(id),
},
adv,
)
}
pub(in crate::flash) fn signal_channel(&self, cvid: CvId, all: bool) {
let mut s = self.core.lock();
let keys: Vec<WaiterId> = s
.sched
.indef
.iter()
.filter(|(_, e)| e.kind == WaitKind::Condvar(cvid))
.map(|(&k, _)| k)
.collect();
let mut woken = Vec::new();
for key in keys {
if !all && !woken.is_empty() {
break;
}
if let Some(entry) = s.sched.indef.remove(&key) {
woken.push(entry.wake);
}
}
if woken.is_empty() {
return;
}
s.registry.account_woken(&woken);
drop(s);
for w in woken {
w.fire();
}
}
pub(in crate::flash) fn signal_notify(&self, cvid: CvId) {
let mut s = self.core.lock();
let woken_key = s
.sched
.indef
.iter()
.find(|(_, e)| e.kind == WaitKind::Condvar(cvid))
.map(|(&k, _)| k);
let mut woken = Vec::new();
if let Some(key) = woken_key
&& let Some(entry) = s.sched.indef.remove(&key)
{
woken.push(entry.wake);
}
if woken.is_empty() {
s.sched.notify_permits.insert(cvid);
} else {
s.registry.account_woken(&woken);
}
drop(s);
for t in woken {
t.fire();
}
}
}
pub(crate) struct AsyncHandle {
granted: Arc<AtomicBool>,
indef_key: Option<WaiterId>,
timed_key: Option<(u64, WaiterId)>,
}
impl AsyncHandle {
pub(crate) fn granted(&self) -> bool {
self.granted.load(Ordering::Acquire)
}
}
pub(crate) fn next_condvar_id() -> CvId {
FLASH.next_condvar_id()
}
pub(crate) fn describe_cvid(cvid: CvId, kind: diag::PrimKind, loc: &'static Location<'static>) {
FLASH.describe_cvid(cvid, kind, loc);
}
#[cfg(test)]
pub(crate) fn park_for(d: crate::flash::Duration) {
FLASH.park_for(d);
}
pub(crate) fn park_timed_unparkable(d: crate::flash::Duration, thread_id: ThreadKey) {
FLASH.park_timed_unparkable(d, thread_id);
}
pub(crate) fn sleep_timed(d: crate::flash::Duration) {
FLASH.sleep_timed(d);
}
pub(crate) fn unpark(thread_id: ThreadKey) {
FLASH.unpark(thread_id);
}
pub(crate) fn yield_until_advance() {
FLASH.yield_until_advance();
}
pub(crate) fn register_yield_async(waker: Waker) -> (WaiterId, Arc<AtomicBool>, WakeBatch) {
FLASH.register_yield_async(waker)
}
pub(crate) fn cancel_yield(id: WaiterId) {
FLASH.cancel_yield(id);
}
pub(crate) fn register_condvar_timed(
deadline_nanos: u64,
cvid: CvId,
) -> (Arc<Token>, WakeBatch, WaitGuard<'static>) {
FLASH.register_condvar_timed(deadline_nanos, cvid)
}
pub(crate) fn register_condvar_untimed(cvid: CvId) -> (Arc<Token>, WakeBatch, WaitGuard<'static>) {
FLASH.register_condvar_untimed(cvid)
}
pub(crate) fn signal_condvar(cvid: CvId, all: bool) {
FLASH.signal_condvar(cvid, all);
}
pub(crate) fn register_sleep_async(delta_nanos: u64, waker: Waker) -> (AsyncHandle, WakeBatch) {
FLASH.register_sleep_async(delta_nanos, waker)
}
pub(crate) fn register_notify_async(cvid: CvId, waker: Waker) -> (Option<AsyncHandle>, WakeBatch) {
FLASH.register_notify_async(cvid, waker)
}
pub(crate) fn async_acquire(loc: &'static Location<'static>) -> u64 {
FLASH.async_acquire(loc)
}
pub(crate) fn cancel_async_wait(handle: &AsyncHandle) {
FLASH.cancel_async_wait(handle);
}
pub(crate) fn signal_notify(cvid: CvId) {
FLASH.signal_notify(cvid);
}
pub(crate) fn register_channel_async(cvid: CvId, waker: Waker) -> (AsyncHandle, WakeBatch) {
FLASH.register_channel_async(cvid, waker)
}
pub(crate) fn signal_channel(cvid: CvId, all: bool) {
FLASH.signal_channel(cvid, all);
}
pub(crate) fn dump() -> String {
FLASH.to_string()
}
#[cfg(test)]
#[must_use]
pub(crate) struct TestHold<'a> {
flash: &'a FlashInner,
}
#[cfg(test)]
impl FlashInner {
pub(in crate::flash) fn active_count(&self) -> usize {
self.core.lock().registry.active
}
pub(in crate::flash) fn advance_log(&self) -> Vec<u64> {
self.core.lock().sched.advance_log.clone()
}
pub(in crate::flash) fn async_active_count(&self) -> usize {
self.core.lock().registry.active_async
}
pub(in crate::flash) fn diag_yield_count(&self) -> usize {
self.core.lock().sched.yielders.len()
}
pub(in crate::flash) fn indef_count(&self) -> usize {
self.core.lock().sched.indef.len()
}
pub(in crate::flash) fn test_hold(&self) -> TestHold<'_> {
self.core.lock().registry.active += 1;
TestHold { flash: self }
}
pub(in crate::flash) fn timed_count(&self) -> usize {
self.core.lock().sched.timed.len()
}
}
#[cfg(test)]
impl Drop for TestHold<'_> {
fn drop(&mut self) {
let mut s = self.flash.core.lock();
debug_assert!(s.registry.active > 0, "TestHold drop without matching hold");
s.registry.active -= 1;
let adv = s.try_advance(&self.flash.clock);
drop(s);
adv.fire();
}
}
#[cfg(test)]
pub(crate) fn async_active_count() -> usize {
FLASH.async_active_count()
}
#[cfg(test)]
pub(crate) fn diag_yield_count() -> usize {
FLASH.diag_yield_count()
}