pub use crate::continuation::erased::State;
use core::{any::Any, fmt, ptr::NonNull, task::RawWaker};
use crate::{
config::Config,
continuation,
ptr::{IntrusivelyCounted, Irc},
simulator::Prec,
};
pub trait Sim: Any + IntrusivelyCounted {
fn active(&self) -> Option<ContPuck>;
fn now(&self) -> &dyn fmt::Display;
fn global(&self) -> &dyn Any;
fn waker(&self) -> RawWaker;
fn defer(&self);
}
pub trait Continuation: Any + IntrusivelyCounted + fmt::Debug {
fn subject(&self) -> &dyn Any;
fn label(&self) -> continuation::Label;
fn prec(&self) -> Prec;
fn state(&self) -> State;
}
#[derive(Clone)]
pub struct ContPuck(Irc<dyn Continuation>);
impl ContPuck {
pub fn downcast<C: Config>(self) -> Result<continuation::Puck<C>, Self> {
match self.0.downcast::<continuation::Continuation<'static, C>>() {
Ok(task) => Ok(continuation::Puck::checked(task).unwrap()),
Err(old) => Err(ContPuck(old)),
}
}
pub fn shared(&self) -> &dyn Any {
self.0.subject()
}
pub fn label(&self) -> continuation::Label {
self.0.label()
}
pub fn prec(&self) -> Prec {
self.0.prec()
}
pub fn state(&self) -> State {
self.0.state()
}
}
impl<C: Config> From<continuation::Puck<C>> for ContPuck {
fn from(task: continuation::Puck<C>) -> Self {
let coerced = Irc::into_raw(task.into_inner()) as NonNull<dyn Continuation>;
ContPuck(unsafe { Irc::from_raw(coerced) })
}
}
impl fmt::Debug for ContPuck {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
use shared::SIMULATOR;
#[must_use = "dropping this guard restores the previous thread-local simulator"]
pub(crate) fn hook(sim: Irc<impl Sim>) -> impl Drop {
scopeguard::guard(
SIMULATOR.replace(Some(Irc::map(sim, |inner| -> &dyn Sim { inner }))),
|sim| {
SIMULATOR.replace(sim);
},
)
}
pub fn with<R>(f: impl FnOnce(&dyn Sim) -> R) -> Option<R> {
SIMULATOR.with(|inner| {
let sim = inner.take()?;
let res = f(&*sim);
inner.set(Some(sim));
Some(res)
})
}
pub fn active() -> Option<ContPuck> {
SIMULATOR.with(|inner| {
let sim = inner.take()?;
let res = sim.active();
inner.set(Some(sim));
res
})
}
pub struct ModelTime<W>(pub W)
where
W: Fn(&mut dyn fmt::Write, &dyn Sim) -> fmt::Result;
impl<W> fmt::Display for ModelTime<W>
where
W: Fn(&mut dyn fmt::Write, &dyn Sim) -> fmt::Result,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
with(move |sim| self.0(f, sim)).unwrap_or(Ok(()))
}
}
#[macro_export]
macro_rules! model_time {
() => { $crate::model_time!("[{time}]") };
($($arg:tt)*) => {
$crate::erased::ModelTime(
move |w,s| ::core::fmt::write(w, ::core::format_args!($($arg)*, time = s.now()))
)
};
}
#[cfg(feature = "tracing")]
#[cfg_attr(docsrs, doc(cfg(feature = "tracing")))]
impl<W> tracing_subscriber::fmt::time::FormatTime for ModelTime<W>
where
W: Fn(&mut dyn fmt::Write, &dyn Sim) -> fmt::Result,
{
fn format_time(&self, w: &mut tracing_subscriber::fmt::format::Writer<'_>) -> fmt::Result {
with(move |sim| self.0(w, sim)).unwrap_or(Ok(()))
}
}
#[cfg(feature = "std")]
mod shared {
use super::{Irc, Sim};
use std::{cell::Cell, thread_local};
thread_local! {
pub static SIMULATOR: Cell<Option<Irc<dyn Sim>>> = const { Cell::new(None) };
}
}
#[cfg(not(feature = "std"))]
mod shared {
use super::{Irc, Sim};
use core::{
cell::Cell,
sync::atomic::{AtomicBool, Ordering},
};
pub static SIMULATOR: LocalKey = LocalKey::new();
pub struct LocalKey {
sim: Cell<Option<Irc<dyn Sim>>>,
lock: AtomicBool,
}
impl LocalKey {
const fn new() -> Self {
Self {
sim: Cell::new(None),
lock: AtomicBool::new(true),
}
}
pub fn with<R>(&self, f: impl FnOnce(&Cell<Option<Irc<dyn Sim>>>) -> R) -> R {
assert!(
self.lock.swap(false, Ordering::SeqCst),
"detected multiple threads running simulators"
);
let res = f(&self.sim);
self.lock.store(true, Ordering::SeqCst);
res
}
pub fn replace(&self, sim: Option<Irc<dyn Sim>>) -> Option<Irc<dyn Sim>> {
assert!(
self.lock.swap(false, Ordering::SeqCst),
"detected multiple threads running simulators"
);
let res = self.sim.replace(sim);
self.lock.store(true, Ordering::SeqCst);
res
}
}
unsafe impl Sync for LocalKey {}
}