use std::sync::Arc;
use super::runtime::{self, Inner};
use super::shard::Shared;
pub(crate) fn pause() {
let inner = runtime::current();
inner.clock.pause();
Guest::join_current(&inner);
wake_all(&inner);
}
pub(crate) fn resume() {
let inner = runtime::current();
inner.clock.resume();
wake_all(&inner);
}
pub(crate) fn advance(by: core::time::Duration) {
let inner = runtime::current();
inner.clock.advance(by);
super::context::current(|current| {
if let Some(shard) = current {
shard.fire_timers_now();
}
});
wake_all(&inner);
}
fn wake_all(inner: &Inner) {
for shard in inner.workers.shards() {
shard.unpark();
}
for shard in inner.guests_made().into_iter().flat_map(|g| g.values()) {
shard.unpark();
}
}
pub(crate) fn try_advance(inner: &Inner, me: Option<&Shared>) -> bool {
let clock = &inner.clock;
if !clock.is_paused() {
return false;
}
while !clock.claim_advance() {
core::hint::spin_loop();
}
let due = attempt(inner, me);
clock.release_advance();
due
}
fn attempt(inner: &Inner, me: Option<&Shared>) -> bool {
let clock = &inner.clock;
let members = clock.members();
if inner.pool.jobs() != 0 {
return false;
}
let mut seen: Vec<(Arc<Shared>, u64)> = Vec::new();
let mut earliest = u64::MAX;
let guests = inner.guests_made().into_iter().flat_map(|g| g.values());
for shard in inner.workers.shards().cloned().chain(guests) {
if !shard.idle() {
return false;
}
earliest = earliest.min(shard.timer_at());
let parks = shard.parks();
seen.push((shard, parks));
}
if earliest == u64::MAX {
return false;
}
let unchanged = clock.members() == members
&& inner.pool.jobs() == 0
&& seen
.iter()
.all(|(shard, parks)| shard.idle() && shard.parks() == *parks);
if !unchanged {
return false;
}
clock.advance_to_nanos(earliest);
let mut due = false;
for (shard, _) in &seen {
if shard.timer_at() <= earliest {
if me.is_some_and(|me| core::ptr::eq(me, &**shard)) {
due = true;
} else {
shard.unpark();
}
}
}
due
}
pub(crate) struct Guest<'a> {
inner: &'a Arc<Inner>,
key: usize,
}
impl<'a> Guest<'a> {
#[inline]
pub(crate) fn enter(inner: &'a Arc<Inner>, shard: &Arc<Shared>) -> Guest<'a> {
let key = Arc::as_ptr(shard) as usize;
if inner.clock.is_paused() {
Self::join(inner, key, shard);
}
Guest { inner, key }
}
#[inline(never)]
fn join(inner: &Inner, key: usize, shard: &Arc<Shared>) {
inner.guests().insert(key, Arc::clone(shard));
inner.clock.member_changed();
}
fn join_current(inner: &Arc<Inner>) {
let Some(own) = super::context::own_shard_if_made() else {
return;
};
let on_own = super::context::current(|current| {
current.is_some_and(|shard| core::ptr::eq(shard, &*own))
});
if on_own {
let key = Arc::as_ptr(&own.shared) as usize;
if inner
.guests()
.insert(key, Arc::clone(&own.shared))
.is_none()
{
inner.clock.member_changed();
}
}
}
}
impl Drop for Guest<'_> {
#[inline]
fn drop(&mut self) {
if self.inner.clock.is_altered() {
self.leave();
}
}
}
impl Guest<'_> {
#[inline(never)]
fn leave(&self) {
if self
.inner
.guests_made()
.is_some_and(|guests| guests.remove(&self.key).is_some())
{
self.inner.clock.member_changed();
try_advance(self.inner, None);
}
}
}