pub use crate::continuation::erased::State;
use core::{any::Any, fmt, marker::PhantomData, ptr::NonNull, task::RawWaker};
use crate::{
config::Config,
continuation,
ptr::{IntrusivelyCounted, Irc},
simulator::{Prec, SimBox},
};
pub trait Sim: Any + IntrusivelyCounted<Inner = SimBox> {
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<Inner = continuation::ContBox> + 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 {
Guard::new(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
})
}
struct Guard {
prev: Option<Irc<dyn Sim>>,
}
impl Guard {
fn new(sim: Irc<impl Sim>) -> Self {
Guard {
prev: SIMULATOR.replace(Some(Irc::map(sim, |inner| -> &dyn Sim { inner }))),
}
}
}
impl Drop for Guard {
fn drop(&mut self) {
SIMULATOR.replace(self.prev.take());
}
}
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")]
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(()))
}
}
#[derive(Copy, Clone)]
pub struct ClockTime<U> {
value: isize,
_unit: PhantomData<U>,
}
impl ClockTime<()> {
pub const fn seconds(value: isize) -> impl fmt::Display {
ClockTime::<Seconds> {
value,
_unit: PhantomData,
}
}
pub const fn minutes(value: isize) -> impl fmt::Display {
ClockTime::<Minutes> {
value,
_unit: PhantomData,
}
}
pub const fn hours(value: isize) -> impl fmt::Display {
ClockTime::<Hours> {
value,
_unit: PhantomData,
}
}
pub const fn days(value: isize) -> impl fmt::Display {
ClockTime::<Days> {
value,
_unit: PhantomData,
}
}
pub const fn years(value: isize) -> impl fmt::Display {
ClockTime::<Years> {
value,
_unit: PhantomData,
}
}
}
impl<U> ClockTime<U> {
fn write_sign(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.value < 0 {
write!(f, "-")
} else if f.sign_plus() {
write!(f, "+")
} else {
Ok(())
}
}
}
impl fmt::Display for ClockTime<Years> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.write_sign(f)?;
Years(self.value.unsigned_abs()).fmt(f)
}
}
impl fmt::Display for ClockTime<Days> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.write_sign(f)?;
Days(self.value.unsigned_abs()).fmt(f)
}
}
impl fmt::Display for ClockTime<Hours> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.write_sign(f)?;
Hours(self.value.unsigned_abs()).fmt(f)
}
}
impl fmt::Display for ClockTime<Minutes> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.write_sign(f)?;
Minutes(self.value.unsigned_abs()).fmt(f)
}
}
impl fmt::Display for ClockTime<Seconds> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.write_sign(f)?;
Seconds(self.value.unsigned_abs()).fmt(f)
}
}
struct Seconds(usize);
struct Minutes(usize);
struct Hours(usize);
struct Days(usize);
struct Years(usize);
impl fmt::Display for Years {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f).and_then(|_| f.write_str("a"))
}
}
impl fmt::Display for Days {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut quantity = self.0;
if quantity >= 365 || f.precision().map(|w| w > 4).unwrap_or(false) {
Years(quantity / 365).fmt(f)?;
quantity %= 365;
f.write_str(" ")?;
}
write!(f, "{quantity}d")
}
}
impl fmt::Display for Hours {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut quantity = self.0;
if quantity >= 24 || f.precision().map(|w| w > 3).unwrap_or(false) {
Days(quantity / 24).fmt(f)?;
quantity %= 24;
f.write_str(" ")?;
}
write!(f, "{quantity:02}h")
}
}
impl fmt::Display for Minutes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut quantity = self.0;
if quantity >= 60 || f.precision().map(|w| w > 2).unwrap_or(false) {
Hours(quantity / 60).fmt(f)?;
quantity %= 60;
f.write_str(" ")?;
}
write!(f, "{quantity:02}m")
}
}
impl fmt::Display for Seconds {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut quantity = self.0;
if quantity >= 60 || f.precision().map(|w| w > 1).unwrap_or(false) {
Minutes(quantity / 60).fmt(f)?;
quantity %= 60;
f.write_str(" ")?;
}
write!(f, "{quantity:02}s")
}
}
#[cfg(feature = "std")]
mod shared {
use super::{Irc, Sim};
use std::cell::Cell;
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 {}
}