use std::fmt; use std::sync::{Arc, Mutex}; use std::time::Duration; use crate::channel::Sender; use crate::executor::Executor; use crate::ports::InputFn; use crate::simulation::{ self, schedule_event_at_unchecked, schedule_keyed_event_at_unchecked, schedule_periodic_event_at_unchecked, schedule_periodic_keyed_event_at_unchecked, ActionKey, Deadline, Mailbox, SchedulerQueue, SchedulingError, }; use crate::time::{MonotonicTime, TearableAtomicTime}; use crate::util::sync_cell::SyncCellReader; use super::Model; /// A local context for models. /// /// A `Context` is a handle to the global context associated to a model /// instance. It can be used by the model to retrieve the simulation time or /// schedule delayed actions on itself. /// /// ### Caveat: self-scheduling `async` methods /// /// Due to a current rustc issue, `async` methods that schedule themselves will /// not compile unless an explicit `Send` bound is added to the returned future. /// This can be done by replacing the `async` signature with a partially /// desugared signature such as: /// /// ```ignore /// fn self_scheduling_method<'a>( /// &'a mut self, /// arg: MyEventType, /// context: &'a Context /// ) -> impl Future + Send + 'a { /// async move { /// /* implementation */ /// } /// } /// ``` /// /// Self-scheduling methods which are not `async` are not affected by this /// issue. /// /// # Examples /// /// A model that sends a greeting after some delay. /// /// ``` /// use std::time::Duration; /// use asynchronix::model::{Context, Model}; /// use asynchronix::ports::Output; /// /// #[derive(Default)] /// pub struct DelayedGreeter { /// msg_out: Output, /// } /// /// impl DelayedGreeter { /// // Triggers a greeting on the output port after some delay [input port]. /// pub async fn greet_with_delay(&mut self, delay: Duration, context: &Context) { /// let time = context.time(); /// let greeting = format!("Hello, this message was scheduled at: {:?}.", time); /// /// if delay.is_zero() { /// self.msg_out.send(greeting).await; /// } else { /// context.schedule_event(delay, Self::send_msg, greeting).unwrap(); /// } /// } /// /// // Sends a message to the output [private input port]. /// async fn send_msg(&mut self, msg: String) { /// self.msg_out.send(msg).await; /// } /// } /// impl Model for DelayedGreeter {} /// ``` // The self-scheduling caveat seems related to this issue: // https://github.com/rust-lang/rust/issues/78649 pub struct Context { name: String, sender: Sender, scheduler_queue: Arc>, time: SyncCellReader, } impl Context { /// Creates a new local context. pub(crate) fn new( name: String, sender: Sender, scheduler_queue: Arc>, time: SyncCellReader, ) -> Self { Self { name, sender, scheduler_queue, time, } } /// Returns the model instance name. pub fn name(&self) -> &str { &self.name } /// Returns the current simulation time. /// /// # Examples /// /// ``` /// use asynchronix::model::{Context, Model}; /// use asynchronix::time::MonotonicTime; /// /// fn is_third_millenium(context: &Context) -> bool { /// let time = context.time(); /// time >= MonotonicTime::new(978307200, 0).unwrap() /// && time < MonotonicTime::new(32535216000, 0).unwrap() /// } /// ``` pub fn time(&self) -> MonotonicTime { self.time.try_read().expect("internal simulation error: could not perform a synchronized read of the simulation time") } /// Schedules an event at a future time. /// /// An error is returned if the specified deadline is not in the future of /// the current simulation time. /// /// # Examples /// /// ``` /// use std::time::Duration; /// /// use asynchronix::model::{Context, Model}; /// /// // A timer. /// pub struct Timer {} /// /// impl Timer { /// // Sets an alarm [input port]. /// pub fn set(&mut self, setting: Duration, context: &Context) { /// if context.schedule_event(setting, Self::ring, ()).is_err() { /// println!("The alarm clock can only be set for a future time"); /// } /// } /// /// // Rings [private input port]. /// fn ring(&mut self) { /// println!("Brringggg"); /// } /// } /// /// impl Model for Timer {} /// ``` pub fn schedule_event( &self, deadline: impl Deadline, func: F, arg: T, ) -> Result<(), SchedulingError> where F: for<'a> InputFn<'a, M, T, S>, T: Send + Clone + 'static, S: Send + 'static, { let now = self.time(); let time = deadline.into_time(now); if now >= time { return Err(SchedulingError::InvalidScheduledTime); } let sender = self.sender.clone(); schedule_event_at_unchecked(time, func, arg, sender, &self.scheduler_queue); Ok(()) } /// Schedules a cancellable event at a future time and returns an action /// key. /// /// An error is returned if the specified deadline is not in the future of /// the current simulation time. /// /// # Examples /// /// ``` /// use asynchronix::model::{Context, Model}; /// use asynchronix::simulation::ActionKey; /// use asynchronix::time::MonotonicTime; /// /// // An alarm clock that can be cancelled. /// #[derive(Default)] /// pub struct CancellableAlarmClock { /// event_key: Option, /// } /// /// impl CancellableAlarmClock { /// // Sets an alarm [input port]. /// pub fn set(&mut self, setting: MonotonicTime, context: &Context) { /// self.cancel(); /// match context.schedule_keyed_event(setting, Self::ring, ()) { /// Ok(event_key) => self.event_key = Some(event_key), /// Err(_) => println!("The alarm clock can only be set for a future time"), /// }; /// } /// /// // Cancels the current alarm, if any [input port]. /// pub fn cancel(&mut self) { /// self.event_key.take().map(|k| k.cancel()); /// } /// /// // Rings the alarm [private input port]. /// fn ring(&mut self) { /// println!("Brringggg!"); /// } /// } /// /// impl Model for CancellableAlarmClock {} /// ``` pub fn schedule_keyed_event( &self, deadline: impl Deadline, func: F, arg: T, ) -> Result where F: for<'a> InputFn<'a, M, T, S>, T: Send + Clone + 'static, S: Send + 'static, { let now = self.time(); let time = deadline.into_time(now); if now >= time { return Err(SchedulingError::InvalidScheduledTime); } let sender = self.sender.clone(); let event_key = schedule_keyed_event_at_unchecked(time, func, arg, sender, &self.scheduler_queue); Ok(event_key) } /// Schedules a periodically recurring event at a future time. /// /// An error is returned if the specified deadline is not in the future of /// the current simulation time or if the specified period is null. /// /// # Examples /// /// ``` /// use std::time::Duration; /// /// use asynchronix::model::{Context, Model}; /// use asynchronix::time::MonotonicTime; /// /// // An alarm clock beeping at 1Hz. /// pub struct BeepingAlarmClock {} /// /// impl BeepingAlarmClock { /// // Sets an alarm [input port]. /// pub fn set(&mut self, setting: MonotonicTime, context: &Context) { /// if context.schedule_periodic_event( /// setting, /// Duration::from_secs(1), // 1Hz = 1/1s /// Self::beep, /// () /// ).is_err() { /// println!("The alarm clock can only be set for a future time"); /// } /// } /// /// // Emits a single beep [private input port]. /// fn beep(&mut self) { /// println!("Beep!"); /// } /// } /// /// impl Model for BeepingAlarmClock {} /// ``` pub fn schedule_periodic_event( &self, deadline: impl Deadline, period: Duration, func: F, arg: T, ) -> Result<(), SchedulingError> where F: for<'a> InputFn<'a, M, T, S> + Clone, T: Send + Clone + 'static, S: Send + 'static, { let now = self.time(); let time = deadline.into_time(now); if now >= time { return Err(SchedulingError::InvalidScheduledTime); } if period.is_zero() { return Err(SchedulingError::NullRepetitionPeriod); } let sender = self.sender.clone(); schedule_periodic_event_at_unchecked( time, period, func, arg, sender, &self.scheduler_queue, ); Ok(()) } /// Schedules a cancellable, periodically recurring event at a future time /// and returns an action key. /// /// An error is returned if the specified deadline is not in the future of /// the current simulation time or if the specified period is null. /// /// # Examples /// /// ``` /// use std::time::Duration; /// /// use asynchronix::model::{Context, Model}; /// use asynchronix::simulation::ActionKey; /// use asynchronix::time::MonotonicTime; /// /// // An alarm clock beeping at 1Hz that can be cancelled before it sets off, or /// // stopped after it sets off. /// #[derive(Default)] /// pub struct CancellableBeepingAlarmClock { /// event_key: Option, /// } /// /// impl CancellableBeepingAlarmClock { /// // Sets an alarm [input port]. /// pub fn set(&mut self, setting: MonotonicTime, context: &Context) { /// self.cancel(); /// match context.schedule_keyed_periodic_event( /// setting, /// Duration::from_secs(1), // 1Hz = 1/1s /// Self::beep, /// () /// ) { /// Ok(event_key) => self.event_key = Some(event_key), /// Err(_) => println!("The alarm clock can only be set for a future time"), /// }; /// } /// /// // Cancels or stops the alarm [input port]. /// pub fn cancel(&mut self) { /// self.event_key.take().map(|k| k.cancel()); /// } /// /// // Emits a single beep [private input port]. /// fn beep(&mut self) { /// println!("Beep!"); /// } /// } /// /// impl Model for CancellableBeepingAlarmClock {} /// ``` pub fn schedule_keyed_periodic_event( &self, deadline: impl Deadline, period: Duration, func: F, arg: T, ) -> Result where F: for<'a> InputFn<'a, M, T, S> + Clone, T: Send + Clone + 'static, S: Send + 'static, { let now = self.time(); let time = deadline.into_time(now); if now >= time { return Err(SchedulingError::InvalidScheduledTime); } if period.is_zero() { return Err(SchedulingError::NullRepetitionPeriod); } let sender = self.sender.clone(); let event_key = schedule_periodic_keyed_event_at_unchecked( time, period, func, arg, sender, &self.scheduler_queue, ); Ok(event_key) } } impl fmt::Debug for Context { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Context").finish_non_exhaustive() } } /// A setup context for models. /// /// A `SetupContext` can be used by models during the setup stage to /// create submodels and add them to the simulation bench. /// /// # Examples /// /// A model that contains two connected submodels. /// /// ``` /// use std::time::Duration; /// use asynchronix::model::{Model, SetupContext}; /// use asynchronix::ports::Output; /// use asynchronix::simulation::Mailbox; /// /// #[derive(Default)] /// pub struct SubmodelA { /// out: Output, /// } /// /// impl Model for SubmodelA {} /// /// #[derive(Default)] /// pub struct SubmodelB {} /// /// impl SubmodelB { /// pub async fn input(&mut self, value: u32) { /// println!("Received {}", value); /// } /// } /// /// impl Model for SubmodelB {} /// /// #[derive(Default)] /// pub struct Parent {} /// /// impl Model for Parent { /// fn setup( /// &mut self, /// setup_context: &SetupContext) { /// let mut a = SubmodelA::default(); /// let b = SubmodelB::default(); /// let a_mbox = Mailbox::new(); /// let b_mbox = Mailbox::new(); /// let a_name = setup_context.name().to_string() + "::a"; /// let b_name = setup_context.name().to_string() + "::b"; /// /// a.out.connect(SubmodelB::input, &b_mbox); /// /// setup_context.add_model(a, a_mbox, a_name); /// setup_context.add_model(b, b_mbox, b_name); /// } /// } /// /// ``` #[derive(Debug)] pub struct SetupContext<'a, M: Model> { /// Mailbox of the model. pub mailbox: &'a Mailbox, context: &'a Context, executor: &'a Executor, } impl<'a, M: Model> SetupContext<'a, M> { /// Creates a new local context. pub(crate) fn new( mailbox: &'a Mailbox, context: &'a Context, executor: &'a Executor, ) -> Self { Self { mailbox, context, executor, } } /// Returns the model instance name. pub fn name(&self) -> &str { &self.context.name } /// Adds a new model and its mailbox to the simulation bench. /// /// The `name` argument needs not be unique (it can be an empty string) and /// is used for convenience for model instance identification (e.g. for /// logging purposes). pub fn add_model(&self, model: N, mailbox: Mailbox, name: impl Into) { let mut submodel_name = name.into(); if !self.context.name().is_empty() && !submodel_name.is_empty() { submodel_name = self.context.name().to_string() + "." + &submodel_name; } simulation::add_model( model, mailbox, submodel_name, self.context.scheduler_queue.clone(), self.context.time.clone(), self.executor, ); } }