forked from ROMEO/nexosim
Change scheduler interface and add external inputs example.
Relevant for issue #13.
This commit is contained in:
@ -119,7 +119,7 @@
|
||||
//! }
|
||||
//! impl Delay {
|
||||
//! pub fn input(&mut self, value: f64, context: &Context<Self>) {
|
||||
//! context.schedule_event(Duration::from_secs(1), Self::send, value).unwrap();
|
||||
//! context.scheduler.schedule_event(Duration::from_secs(1), Self::send, value).unwrap();
|
||||
//! }
|
||||
//!
|
||||
//! async fn send(&mut self, value: f64) {
|
||||
@ -190,7 +190,7 @@
|
||||
//! # }
|
||||
//! # impl Delay {
|
||||
//! # pub fn input(&mut self, value: f64, context: &Context<Self>) {
|
||||
//! # context.schedule_event(Duration::from_secs(1), Self::send, value).unwrap();
|
||||
//! # context.scheduler.schedule_event(Duration::from_secs(1), Self::send, value).unwrap();
|
||||
//! # }
|
||||
//! # async fn send(&mut self, value: f64) { // this method can be private
|
||||
//! # self.output.send(value).await;
|
||||
@ -250,7 +250,7 @@
|
||||
//! [`Simulation::process_event()`](simulation::Simulation::process_event) or
|
||||
//! [`Simulation::send_query()`](simulation::Simulation::process_query),
|
||||
//! 3. by scheduling events, using for instance
|
||||
//! [`Simulation::schedule_event()`](simulation::Simulation::schedule_event).
|
||||
//! [`Scheduler::schedule_event()`](simulation::Scheduler::schedule_event).
|
||||
//!
|
||||
//! When initialized with the default clock, the simulation will run as fast as
|
||||
//! possible, without regard for the actual wall clock time. Alternatively, the
|
||||
@ -289,7 +289,7 @@
|
||||
//! # }
|
||||
//! # impl Delay {
|
||||
//! # pub fn input(&mut self, value: f64, context: &Context<Self>) {
|
||||
//! # context.schedule_event(Duration::from_secs(1), Self::send, value).unwrap();
|
||||
//! # context.scheduler.schedule_event(Duration::from_secs(1), Self::send, value).unwrap();
|
||||
//! # }
|
||||
//! # async fn send(&mut self, value: f64) { // this method can be private
|
||||
//! # self.output.send(value).await;
|
||||
@ -370,7 +370,7 @@
|
||||
//!
|
||||
//! The first guarantee (and only the first) also extends to events scheduled
|
||||
//! from a simulation with a
|
||||
//! [`Simulation::schedule_*()`](simulation::Simulation::schedule_event) method:
|
||||
//! [`Scheduler::schedule_*()`](simulation::Scheduler::schedule_event) method:
|
||||
//! if the scheduler contains several events to be delivered at the same time to
|
||||
//! the same model, these events will always be processed in the order in which
|
||||
//! they were scheduled.
|
||||
|
@ -1,17 +1,7 @@
|
||||
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 crate::simulation::{self, LocalScheduler, Mailbox};
|
||||
|
||||
use super::Model;
|
||||
|
||||
@ -60,13 +50,13 @@ use super::Model;
|
||||
/// 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<Self>) {
|
||||
/// let time = context.time();
|
||||
/// let time = context.scheduler.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();
|
||||
/// context.scheduler.schedule_event(delay, Self::send_msg, greeting).unwrap();
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
@ -82,320 +72,21 @@ use super::Model;
|
||||
// https://github.com/rust-lang/rust/issues/78649
|
||||
pub struct Context<M: Model> {
|
||||
name: String,
|
||||
sender: Sender<M>,
|
||||
scheduler_queue: Arc<Mutex<SchedulerQueue>>,
|
||||
time: SyncCellReader<TearableAtomicTime>,
|
||||
|
||||
/// Local scheduler.
|
||||
pub scheduler: LocalScheduler<M>,
|
||||
}
|
||||
|
||||
impl<M: Model> Context<M> {
|
||||
/// Creates a new local context.
|
||||
pub(crate) fn new(
|
||||
name: String,
|
||||
sender: Sender<M>,
|
||||
scheduler_queue: Arc<Mutex<SchedulerQueue>>,
|
||||
time: SyncCellReader<TearableAtomicTime>,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
sender,
|
||||
scheduler_queue,
|
||||
time,
|
||||
}
|
||||
pub(crate) fn new(name: String, scheduler: LocalScheduler<M>) -> Self {
|
||||
Self { name, scheduler }
|
||||
}
|
||||
|
||||
/// 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<M: Model>(context: &Context<M>) -> 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<Self>) {
|
||||
/// 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<F, T, S>(
|
||||
&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<ActionKey>,
|
||||
/// }
|
||||
///
|
||||
/// impl CancellableAlarmClock {
|
||||
/// // Sets an alarm [input port].
|
||||
/// pub fn set(&mut self, setting: MonotonicTime, context: &Context<Self>) {
|
||||
/// 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<F, T, S>(
|
||||
&self,
|
||||
deadline: impl Deadline,
|
||||
func: F,
|
||||
arg: T,
|
||||
) -> Result<ActionKey, 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();
|
||||
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<Self>) {
|
||||
/// 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<F, T, S>(
|
||||
&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<ActionKey>,
|
||||
/// }
|
||||
///
|
||||
/// impl CancellableBeepingAlarmClock {
|
||||
/// // Sets an alarm [input port].
|
||||
/// pub fn set(&mut self, setting: MonotonicTime, context: &Context<Self>) {
|
||||
/// 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<F, T, S>(
|
||||
&self,
|
||||
deadline: impl Deadline,
|
||||
period: Duration,
|
||||
func: F,
|
||||
arg: T,
|
||||
) -> Result<ActionKey, 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();
|
||||
let event_key = schedule_periodic_keyed_event_at_unchecked(
|
||||
time,
|
||||
period,
|
||||
func,
|
||||
arg,
|
||||
sender,
|
||||
&self.scheduler_queue,
|
||||
);
|
||||
|
||||
Ok(event_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: Model> fmt::Debug for Context<M> {
|
||||
@ -501,8 +192,7 @@ impl<'a, M: Model> SetupContext<'a, M> {
|
||||
model,
|
||||
mailbox,
|
||||
submodel_name,
|
||||
self.context.scheduler_queue.clone(),
|
||||
self.context.time.clone(),
|
||||
self.context.scheduler.scheduler.clone(),
|
||||
self.executor,
|
||||
);
|
||||
}
|
||||
|
@ -557,6 +557,7 @@ mod tests {
|
||||
|
||||
use crate::channel::Receiver;
|
||||
use crate::model::Context;
|
||||
use crate::simulation::{Address, LocalScheduler, Scheduler};
|
||||
use crate::time::{MonotonicTime, TearableAtomicTime};
|
||||
use crate::util::priority_queue::PriorityQueue;
|
||||
use crate::util::sync_cell::SyncCell;
|
||||
@ -616,9 +617,10 @@ mod tests {
|
||||
SyncCell::new(TearableAtomicTime::new(MonotonicTime::EPOCH)).reader();
|
||||
let dummy_context = Context::new(
|
||||
String::new(),
|
||||
dummy_address,
|
||||
dummy_priority_queue,
|
||||
dummy_time,
|
||||
LocalScheduler::new(
|
||||
Scheduler::new(dummy_priority_queue, dummy_time),
|
||||
Address(dummy_address),
|
||||
),
|
||||
);
|
||||
block_on(mailbox.recv(&mut counter, &dummy_context)).unwrap();
|
||||
}
|
||||
@ -671,9 +673,10 @@ mod tests {
|
||||
SyncCell::new(TearableAtomicTime::new(MonotonicTime::EPOCH)).reader();
|
||||
let dummy_context = Context::new(
|
||||
String::new(),
|
||||
dummy_address,
|
||||
dummy_priority_queue,
|
||||
dummy_time,
|
||||
LocalScheduler::new(
|
||||
Scheduler::new(dummy_priority_queue, dummy_time),
|
||||
Address(dummy_address),
|
||||
),
|
||||
);
|
||||
block_on(mailbox.recv(&mut counter, &dummy_context)).unwrap();
|
||||
thread::sleep(std::time::Duration::from_millis(100));
|
||||
|
@ -440,6 +440,7 @@ mod tests {
|
||||
|
||||
use crate::channel::Receiver;
|
||||
use crate::model::Context;
|
||||
use crate::simulation::{Address, LocalScheduler, Scheduler};
|
||||
use crate::time::{MonotonicTime, TearableAtomicTime};
|
||||
use crate::util::priority_queue::PriorityQueue;
|
||||
use crate::util::sync_cell::SyncCell;
|
||||
@ -499,9 +500,10 @@ mod tests {
|
||||
SyncCell::new(TearableAtomicTime::new(MonotonicTime::EPOCH)).reader();
|
||||
let dummy_context = Context::new(
|
||||
String::new(),
|
||||
dummy_address,
|
||||
dummy_priority_queue,
|
||||
dummy_time,
|
||||
LocalScheduler::new(
|
||||
Scheduler::new(dummy_priority_queue, dummy_time),
|
||||
Address(dummy_address),
|
||||
),
|
||||
);
|
||||
block_on(mailbox.recv(&mut counter, &dummy_context)).unwrap();
|
||||
}
|
||||
@ -554,9 +556,10 @@ mod tests {
|
||||
SyncCell::new(TearableAtomicTime::new(MonotonicTime::EPOCH)).reader();
|
||||
let dummy_context = Context::new(
|
||||
String::new(),
|
||||
dummy_address,
|
||||
dummy_priority_queue,
|
||||
dummy_time,
|
||||
LocalScheduler::new(
|
||||
Scheduler::new(dummy_priority_queue, dummy_time),
|
||||
Address(dummy_address),
|
||||
),
|
||||
);
|
||||
block_on(mailbox.recv(&mut counter, &dummy_context)).unwrap();
|
||||
thread::sleep(std::time::Duration::from_millis(100));
|
||||
|
@ -127,12 +127,12 @@ mod scheduler;
|
||||
mod sim_init;
|
||||
|
||||
pub use mailbox::{Address, Mailbox};
|
||||
pub use scheduler::{
|
||||
Action, ActionKey, AutoActionKey, Deadline, LocalScheduler, Scheduler, SchedulingError,
|
||||
};
|
||||
pub(crate) use scheduler::{
|
||||
schedule_event_at_unchecked, schedule_keyed_event_at_unchecked,
|
||||
schedule_periodic_event_at_unchecked, schedule_periodic_keyed_event_at_unchecked,
|
||||
KeyedOnceAction, KeyedPeriodicAction, OnceAction, PeriodicAction, SchedulerQueue,
|
||||
};
|
||||
pub use scheduler::{Action, ActionKey, AutoActionKey, Deadline, SchedulingError};
|
||||
pub use sim_init::SimInit;
|
||||
|
||||
use std::error::Error;
|
||||
@ -149,7 +149,7 @@ use crate::ports::{InputFn, ReplierFn};
|
||||
use crate::time::{Clock, MonotonicTime, TearableAtomicTime};
|
||||
use crate::util::seq_futures::SeqFuture;
|
||||
use crate::util::slot;
|
||||
use crate::util::sync_cell::{SyncCell, SyncCellReader};
|
||||
use crate::util::sync_cell::SyncCell;
|
||||
|
||||
/// Simulation environment.
|
||||
///
|
||||
@ -161,10 +161,10 @@ use crate::util::sync_cell::{SyncCell, SyncCellReader};
|
||||
/// A [`Simulation`] object also manages an event scheduling queue and
|
||||
/// simulation time. The scheduling queue can be accessed from the simulation
|
||||
/// itself, but also from models via the optional
|
||||
/// [`&Context`](crate::model::Context) argument of input and replier port methods.
|
||||
/// Likewise, simulation time can be accessed with the [`Simulation::time()`]
|
||||
/// method, or from models with the [`Context::time()`](crate::model::Context::time)
|
||||
/// method.
|
||||
/// [`&Context`](crate::model::Context) argument of input and replier port
|
||||
/// methods. Likewise, simulation time can be accessed with the
|
||||
/// [`Simulation::time()`] method, or from models with the
|
||||
/// [`LocalScheduler::time()`](crate::simulation::LocalScheduler::time) method.
|
||||
///
|
||||
/// Events and queries can be scheduled immediately, *i.e.* for the current
|
||||
/// simulation time, using [`process_event()`](Simulation::process_event) and
|
||||
@ -173,7 +173,7 @@ use crate::util::sync_cell::{SyncCell, SyncCellReader};
|
||||
/// completed. In the case of queries, the response is returned.
|
||||
///
|
||||
/// Events can also be scheduled at a future simulation time using one of the
|
||||
/// [`schedule_*()`](Simulation::schedule_event) method. These methods queue an
|
||||
/// [`schedule_*()`](Scheduler::schedule_event) method. These methods queue an
|
||||
/// event without blocking.
|
||||
///
|
||||
/// Finally, the [`Simulation`] instance manages simulation time. A call to
|
||||
@ -257,192 +257,9 @@ impl Simulation {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Schedules an action at a future time.
|
||||
///
|
||||
/// An error is returned if the specified time is not in the future of the
|
||||
/// current simulation time.
|
||||
///
|
||||
/// If multiple actions send events at the same simulation time to the same
|
||||
/// model, these events are guaranteed to be processed according to the
|
||||
/// scheduling order of the actions.
|
||||
pub fn schedule(
|
||||
&mut self,
|
||||
deadline: impl Deadline,
|
||||
action: Action,
|
||||
) -> Result<(), SchedulingError> {
|
||||
let now = self.time();
|
||||
let time = deadline.into_time(now);
|
||||
if now >= time {
|
||||
return Err(SchedulingError::InvalidScheduledTime);
|
||||
}
|
||||
|
||||
let mut scheduler_queue = self.scheduler_queue.lock().unwrap();
|
||||
|
||||
// The channel ID is set to the same value for all actions. This
|
||||
// ensures that the relative scheduling order of all source events is
|
||||
// preserved, which is important if some of them target the same models.
|
||||
// The value 0 was chosen as it prevents collisions with channel IDs as
|
||||
// the latter are always non-zero.
|
||||
scheduler_queue.insert((time, 0), action);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Schedules an event at a future time.
|
||||
///
|
||||
/// An error is returned if the specified time is not in the future of the
|
||||
/// current simulation time.
|
||||
///
|
||||
/// Events scheduled for the same time and targeting the same model are
|
||||
/// guaranteed to be processed according to the scheduling order.
|
||||
///
|
||||
/// See also: [`Context::schedule_event`](crate::model::Context::schedule_event).
|
||||
pub fn schedule_event<M, F, T, S>(
|
||||
&mut self,
|
||||
deadline: impl Deadline,
|
||||
func: F,
|
||||
arg: T,
|
||||
address: impl Into<Address<M>>,
|
||||
) -> Result<(), SchedulingError>
|
||||
where
|
||||
M: Model,
|
||||
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);
|
||||
}
|
||||
schedule_event_at_unchecked(time, func, arg, address.into().0, &self.scheduler_queue);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Schedules a cancellable event at a future time and returns an event key.
|
||||
///
|
||||
/// An error is returned if the specified time is not in the future of the
|
||||
/// current simulation time.
|
||||
///
|
||||
/// Events scheduled for the same time and targeting the same model are
|
||||
/// guaranteed to be processed according to the scheduling order.
|
||||
///
|
||||
/// See also: [`Context::schedule_keyed_event`](crate::model::Context::schedule_keyed_event).
|
||||
pub fn schedule_keyed_event<M, F, T, S>(
|
||||
&mut self,
|
||||
deadline: impl Deadline,
|
||||
func: F,
|
||||
arg: T,
|
||||
address: impl Into<Address<M>>,
|
||||
) -> Result<ActionKey, SchedulingError>
|
||||
where
|
||||
M: Model,
|
||||
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 event_key = schedule_keyed_event_at_unchecked(
|
||||
time,
|
||||
func,
|
||||
arg,
|
||||
address.into().0,
|
||||
&self.scheduler_queue,
|
||||
);
|
||||
|
||||
Ok(event_key)
|
||||
}
|
||||
|
||||
/// Schedules a periodically recurring event at a future time.
|
||||
///
|
||||
/// An error is returned if the specified time is not in the future of the
|
||||
/// current simulation time or if the specified period is null.
|
||||
///
|
||||
/// Events scheduled for the same time and targeting the same model are
|
||||
/// guaranteed to be processed according to the scheduling order.
|
||||
///
|
||||
/// See also: [`Context::schedule_periodic_event`](crate::model::Context::schedule_periodic_event).
|
||||
pub fn schedule_periodic_event<M, F, T, S>(
|
||||
&mut self,
|
||||
deadline: impl Deadline,
|
||||
period: Duration,
|
||||
func: F,
|
||||
arg: T,
|
||||
address: impl Into<Address<M>>,
|
||||
) -> Result<(), SchedulingError>
|
||||
where
|
||||
M: Model,
|
||||
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);
|
||||
}
|
||||
schedule_periodic_event_at_unchecked(
|
||||
time,
|
||||
period,
|
||||
func,
|
||||
arg,
|
||||
address.into().0,
|
||||
&self.scheduler_queue,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Schedules a cancellable, periodically recurring event at a future time
|
||||
/// and returns an event key.
|
||||
///
|
||||
/// An error is returned if the specified time is not in the future of the
|
||||
/// current simulation time or if the specified period is null.
|
||||
///
|
||||
/// Events scheduled for the same time and targeting the same model are
|
||||
/// guaranteed to be processed according to the scheduling order.
|
||||
///
|
||||
/// See also: [`Context::schedule_keyed_periodic_event`](crate::model::Context::schedule_keyed_periodic_event).
|
||||
pub fn schedule_keyed_periodic_event<M, F, T, S>(
|
||||
&mut self,
|
||||
deadline: impl Deadline,
|
||||
period: Duration,
|
||||
func: F,
|
||||
arg: T,
|
||||
address: impl Into<Address<M>>,
|
||||
) -> Result<ActionKey, SchedulingError>
|
||||
where
|
||||
M: Model,
|
||||
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 event_key = schedule_periodic_keyed_event_at_unchecked(
|
||||
time,
|
||||
period,
|
||||
func,
|
||||
arg,
|
||||
address.into().0,
|
||||
&self.scheduler_queue,
|
||||
);
|
||||
|
||||
Ok(event_key)
|
||||
/// Returns scheduler.
|
||||
pub fn scheduler(&self) -> Scheduler {
|
||||
Scheduler::new(self.scheduler_queue.clone(), self.time.reader())
|
||||
}
|
||||
|
||||
/// Processes an action immediately, blocking until completion.
|
||||
@ -630,6 +447,7 @@ impl Simulation {
|
||||
None => {
|
||||
// Update the simulation time.
|
||||
self.time.write(target_time);
|
||||
self.clock.synchronize(target_time);
|
||||
return;
|
||||
}
|
||||
// The target time was not reached yet.
|
||||
@ -667,13 +485,10 @@ pub(crate) fn add_model<M: Model>(
|
||||
mut model: M,
|
||||
mailbox: Mailbox<M>,
|
||||
name: String,
|
||||
scheduler_queue: Arc<Mutex<SchedulerQueue>>,
|
||||
time: SyncCellReader<TearableAtomicTime>,
|
||||
scheduler: Scheduler,
|
||||
executor: &Executor,
|
||||
) {
|
||||
let sender = mailbox.0.sender();
|
||||
|
||||
let context = Context::new(name, sender, scheduler_queue, time);
|
||||
let context = Context::new(name, LocalScheduler::new(scheduler, mailbox.address()));
|
||||
let setup_context = SetupContext::new(&mailbox, &context, executor);
|
||||
|
||||
model.setup(&setup_context);
|
||||
|
@ -17,8 +17,532 @@ use crate::channel::Sender;
|
||||
use crate::executor::Executor;
|
||||
use crate::model::Model;
|
||||
use crate::ports::InputFn;
|
||||
use crate::time::MonotonicTime;
|
||||
use crate::simulation::Address;
|
||||
use crate::time::{MonotonicTime, TearableAtomicTime};
|
||||
use crate::util::priority_queue::PriorityQueue;
|
||||
use crate::util::sync_cell::SyncCellReader;
|
||||
|
||||
/// Scheduler.
|
||||
#[derive(Clone)]
|
||||
pub struct Scheduler {
|
||||
scheduler_queue: Arc<Mutex<SchedulerQueue>>,
|
||||
time: SyncCellReader<TearableAtomicTime>,
|
||||
}
|
||||
|
||||
impl Scheduler {
|
||||
pub(crate) fn new(
|
||||
scheduler_queue: Arc<Mutex<SchedulerQueue>>,
|
||||
time: SyncCellReader<TearableAtomicTime>,
|
||||
) -> Self {
|
||||
Self {
|
||||
scheduler_queue,
|
||||
time,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current simulation time.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use asynchronix::simulation::Scheduler;
|
||||
/// use asynchronix::time::MonotonicTime;
|
||||
///
|
||||
/// fn is_third_millenium(scheduler: &Scheduler) -> bool {
|
||||
/// let time = scheduler.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 action at a future time.
|
||||
///
|
||||
/// An error is returned if the specified time is not in the future of the
|
||||
/// current simulation time.
|
||||
///
|
||||
/// If multiple actions send events at the same simulation time to the same
|
||||
/// model, these events are guaranteed to be processed according to the
|
||||
/// scheduling order of the actions.
|
||||
pub fn schedule(&self, deadline: impl Deadline, action: Action) -> Result<(), SchedulingError> {
|
||||
let mut scheduler_queue = self.scheduler_queue.lock().unwrap();
|
||||
|
||||
let now = self.time();
|
||||
let time = deadline.into_time(now);
|
||||
if now >= time {
|
||||
return Err(SchedulingError::InvalidScheduledTime);
|
||||
}
|
||||
|
||||
// The channel ID is set to the same value for all actions. This
|
||||
// ensures that the relative scheduling order of all source events is
|
||||
// preserved, which is important if some of them target the same models.
|
||||
// The value 0 was chosen as it prevents collisions with channel IDs as
|
||||
// the latter are always non-zero.
|
||||
scheduler_queue.insert((time, 0), action);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Schedules an event at a future time.
|
||||
///
|
||||
/// An error is returned if the specified time is not in the future of the
|
||||
/// current simulation time.
|
||||
///
|
||||
/// Events scheduled for the same time and targeting the same model are
|
||||
/// guaranteed to be processed according to the scheduling order.
|
||||
///
|
||||
/// See also: [`LocalScheduler::schedule_event`](LocalScheduler::schedule_event).
|
||||
pub fn schedule_event<M, F, T, S>(
|
||||
&self,
|
||||
deadline: impl Deadline,
|
||||
func: F,
|
||||
arg: T,
|
||||
address: impl Into<Address<M>>,
|
||||
) -> Result<(), SchedulingError>
|
||||
where
|
||||
M: Model,
|
||||
F: for<'a> InputFn<'a, M, T, S>,
|
||||
T: Send + Clone + 'static,
|
||||
S: Send + 'static,
|
||||
{
|
||||
let mut scheduler_queue = self.scheduler_queue.lock().unwrap();
|
||||
let now = self.time();
|
||||
let time = deadline.into_time(now);
|
||||
if now >= time {
|
||||
return Err(SchedulingError::InvalidScheduledTime);
|
||||
}
|
||||
let sender = address.into().0;
|
||||
let channel_id = sender.channel_id();
|
||||
let action = Action::new(OnceAction::new(process_event(func, arg, sender)));
|
||||
|
||||
scheduler_queue.insert((time, channel_id), action);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Schedules a cancellable event at a future time and returns an event key.
|
||||
///
|
||||
/// An error is returned if the specified time is not in the future of the
|
||||
/// current simulation time.
|
||||
///
|
||||
/// Events scheduled for the same time and targeting the same model are
|
||||
/// guaranteed to be processed according to the scheduling order.
|
||||
///
|
||||
/// See also: [`LocalScheduler::schedule_keyed_event`](LocalScheduler::schedule_keyed_event).
|
||||
pub fn schedule_keyed_event<M, F, T, S>(
|
||||
&self,
|
||||
deadline: impl Deadline,
|
||||
func: F,
|
||||
arg: T,
|
||||
address: impl Into<Address<M>>,
|
||||
) -> Result<ActionKey, SchedulingError>
|
||||
where
|
||||
M: Model,
|
||||
F: for<'a> InputFn<'a, M, T, S>,
|
||||
T: Send + Clone + 'static,
|
||||
S: Send + 'static,
|
||||
{
|
||||
let mut scheduler_queue = self.scheduler_queue.lock().unwrap();
|
||||
let now = self.time();
|
||||
let time = deadline.into_time(now);
|
||||
if now >= time {
|
||||
return Err(SchedulingError::InvalidScheduledTime);
|
||||
}
|
||||
let event_key = ActionKey::new();
|
||||
let sender = address.into().0;
|
||||
let channel_id = sender.channel_id();
|
||||
let action = Action::new(KeyedOnceAction::new(
|
||||
|ek| send_keyed_event(ek, func, arg, sender),
|
||||
event_key.clone(),
|
||||
));
|
||||
|
||||
scheduler_queue.insert((time, channel_id), action);
|
||||
|
||||
Ok(event_key)
|
||||
}
|
||||
|
||||
/// Schedules a periodically recurring event at a future time.
|
||||
///
|
||||
/// An error is returned if the specified time is not in the future of the
|
||||
/// current simulation time or if the specified period is null.
|
||||
///
|
||||
/// Events scheduled for the same time and targeting the same model are
|
||||
/// guaranteed to be processed according to the scheduling order.
|
||||
///
|
||||
/// See also: [`LocalScheduler::schedule_periodic_event`](LocalScheduler::schedule_periodic_event).
|
||||
pub fn schedule_periodic_event<M, F, T, S>(
|
||||
&self,
|
||||
deadline: impl Deadline,
|
||||
period: Duration,
|
||||
func: F,
|
||||
arg: T,
|
||||
address: impl Into<Address<M>>,
|
||||
) -> Result<(), SchedulingError>
|
||||
where
|
||||
M: Model,
|
||||
F: for<'a> InputFn<'a, M, T, S> + Clone,
|
||||
T: Send + Clone + 'static,
|
||||
S: Send + 'static,
|
||||
{
|
||||
let mut scheduler_queue = self.scheduler_queue.lock().unwrap();
|
||||
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 = address.into().0;
|
||||
let channel_id = sender.channel_id();
|
||||
|
||||
let action = Action::new(PeriodicAction::new(
|
||||
|| process_event(func, arg, sender),
|
||||
period,
|
||||
));
|
||||
|
||||
scheduler_queue.insert((time, channel_id), action);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Schedules a cancellable, periodically recurring event at a future time
|
||||
/// and returns an event key.
|
||||
///
|
||||
/// An error is returned if the specified time is not in the future of the
|
||||
/// current simulation time or if the specified period is null.
|
||||
///
|
||||
/// Events scheduled for the same time and targeting the same model are
|
||||
/// guaranteed to be processed according to the scheduling order.
|
||||
///
|
||||
/// See also: [`LocalScheduler::schedule_keyed_periodic_event`](LocalScheduler::schedule_keyed_periodic_event).
|
||||
pub fn schedule_keyed_periodic_event<M, F, T, S>(
|
||||
&self,
|
||||
deadline: impl Deadline,
|
||||
period: Duration,
|
||||
func: F,
|
||||
arg: T,
|
||||
address: impl Into<Address<M>>,
|
||||
) -> Result<ActionKey, SchedulingError>
|
||||
where
|
||||
M: Model,
|
||||
F: for<'a> InputFn<'a, M, T, S> + Clone,
|
||||
T: Send + Clone + 'static,
|
||||
S: Send + 'static,
|
||||
{
|
||||
let mut scheduler_queue = self.scheduler_queue.lock().unwrap();
|
||||
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 event_key = ActionKey::new();
|
||||
let sender = address.into().0;
|
||||
let channel_id = sender.channel_id();
|
||||
let action = Action::new(KeyedPeriodicAction::new(
|
||||
|ek| send_keyed_event(ek, func, arg, sender),
|
||||
period,
|
||||
event_key.clone(),
|
||||
));
|
||||
scheduler_queue.insert((time, channel_id), action);
|
||||
|
||||
Ok(event_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Scheduler {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Scheduler")
|
||||
.field("time", &self.time())
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
/// Local scheduler.
|
||||
pub struct LocalScheduler<M: Model> {
|
||||
pub(crate) scheduler: Scheduler,
|
||||
address: Address<M>,
|
||||
}
|
||||
|
||||
impl<M: Model> LocalScheduler<M> {
|
||||
pub(crate) fn new(scheduler: Scheduler, address: Address<M>) -> Self {
|
||||
Self { scheduler, address }
|
||||
}
|
||||
|
||||
/// Returns the current simulation time.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use asynchronix::model::Model;
|
||||
/// use asynchronix::simulation::LocalScheduler;
|
||||
/// use asynchronix::time::MonotonicTime;
|
||||
///
|
||||
/// fn is_third_millenium<M: Model>(scheduler: &LocalScheduler<M>) -> bool {
|
||||
/// let time = scheduler.time();
|
||||
/// time >= MonotonicTime::new(978307200, 0).unwrap()
|
||||
/// && time < MonotonicTime::new(32535216000, 0).unwrap()
|
||||
/// }
|
||||
/// ```
|
||||
pub fn time(&self) -> MonotonicTime {
|
||||
self.scheduler.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<Self>) {
|
||||
/// if context.scheduler.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<F, T, S>(
|
||||
&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,
|
||||
{
|
||||
self.scheduler
|
||||
.schedule_event(deadline, func, arg, &self.address)
|
||||
}
|
||||
|
||||
/// 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<ActionKey>,
|
||||
/// }
|
||||
///
|
||||
/// impl CancellableAlarmClock {
|
||||
/// // Sets an alarm [input port].
|
||||
/// pub fn set(&mut self, setting: MonotonicTime, context: &Context<Self>) {
|
||||
/// self.cancel();
|
||||
/// match context.scheduler.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<F, T, S>(
|
||||
&self,
|
||||
deadline: impl Deadline,
|
||||
func: F,
|
||||
arg: T,
|
||||
) -> Result<ActionKey, SchedulingError>
|
||||
where
|
||||
F: for<'a> InputFn<'a, M, T, S>,
|
||||
T: Send + Clone + 'static,
|
||||
S: Send + 'static,
|
||||
{
|
||||
let event_key = self
|
||||
.scheduler
|
||||
.schedule_keyed_event(deadline, func, arg, &self.address)?;
|
||||
|
||||
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<Self>) {
|
||||
/// if context.scheduler.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<F, T, S>(
|
||||
&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,
|
||||
{
|
||||
self.scheduler
|
||||
.schedule_periodic_event(deadline, period, func, arg, &self.address)
|
||||
}
|
||||
|
||||
/// 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<ActionKey>,
|
||||
/// }
|
||||
///
|
||||
/// impl CancellableBeepingAlarmClock {
|
||||
/// // Sets an alarm [input port].
|
||||
/// pub fn set(&mut self, setting: MonotonicTime, context: &Context<Self>) {
|
||||
/// self.cancel();
|
||||
/// match context.scheduler.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<F, T, S>(
|
||||
&self,
|
||||
deadline: impl Deadline,
|
||||
period: Duration,
|
||||
func: F,
|
||||
arg: T,
|
||||
) -> Result<ActionKey, SchedulingError>
|
||||
where
|
||||
F: for<'a> InputFn<'a, M, T, S> + Clone,
|
||||
T: Send + Clone + 'static,
|
||||
S: Send + 'static,
|
||||
{
|
||||
let event_key = self.scheduler.schedule_keyed_periodic_event(
|
||||
deadline,
|
||||
period,
|
||||
func,
|
||||
arg,
|
||||
&self.address,
|
||||
)?;
|
||||
|
||||
Ok(event_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: Model> Clone for LocalScheduler<M> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
scheduler: self.scheduler.clone(),
|
||||
address: self.address.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: Model> fmt::Debug for LocalScheduler<M> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("LocalScheduler")
|
||||
.field("time", &self.time())
|
||||
.field("address", &self.address)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
/// Shorthand for the scheduler queue type.
|
||||
|
||||
@ -214,120 +738,6 @@ pub(crate) trait ActionInner: Send + 'static {
|
||||
fn spawn_and_forget(self: Box<Self>, executor: &Executor);
|
||||
}
|
||||
|
||||
/// Schedules an event at a future time.
|
||||
///
|
||||
/// This function does not check whether the specified time lies in the future
|
||||
/// of the current simulation time.
|
||||
pub(crate) fn schedule_event_at_unchecked<M, F, T, S>(
|
||||
time: MonotonicTime,
|
||||
func: F,
|
||||
arg: T,
|
||||
sender: Sender<M>,
|
||||
scheduler_queue: &Mutex<SchedulerQueue>,
|
||||
) where
|
||||
M: Model,
|
||||
F: for<'a> InputFn<'a, M, T, S>,
|
||||
T: Send + Clone + 'static,
|
||||
S: Send + 'static,
|
||||
{
|
||||
let channel_id = sender.channel_id();
|
||||
|
||||
let action = Action::new(OnceAction::new(process_event(func, arg, sender)));
|
||||
|
||||
let mut scheduler_queue = scheduler_queue.lock().unwrap();
|
||||
scheduler_queue.insert((time, channel_id), action);
|
||||
}
|
||||
|
||||
/// Schedules an event at a future time, returning an action key.
|
||||
///
|
||||
/// This function does not check whether the specified time lies in the future
|
||||
/// of the current simulation time.
|
||||
pub(crate) fn schedule_keyed_event_at_unchecked<M, F, T, S>(
|
||||
time: MonotonicTime,
|
||||
func: F,
|
||||
arg: T,
|
||||
sender: Sender<M>,
|
||||
scheduler_queue: &Mutex<SchedulerQueue>,
|
||||
) -> ActionKey
|
||||
where
|
||||
M: Model,
|
||||
F: for<'a> InputFn<'a, M, T, S>,
|
||||
T: Send + Clone + 'static,
|
||||
S: Send + 'static,
|
||||
{
|
||||
let event_key = ActionKey::new();
|
||||
let channel_id = sender.channel_id();
|
||||
let action = Action::new(KeyedOnceAction::new(
|
||||
|ek| send_keyed_event(ek, func, arg, sender),
|
||||
event_key.clone(),
|
||||
));
|
||||
|
||||
let mut scheduler_queue = scheduler_queue.lock().unwrap();
|
||||
scheduler_queue.insert((time, channel_id), action);
|
||||
|
||||
event_key
|
||||
}
|
||||
|
||||
/// Schedules a periodic event at a future time.
|
||||
///
|
||||
/// This function does not check whether the specified time lies in the future
|
||||
/// of the current simulation time.
|
||||
pub(crate) fn schedule_periodic_event_at_unchecked<M, F, T, S>(
|
||||
time: MonotonicTime,
|
||||
period: Duration,
|
||||
func: F,
|
||||
arg: T,
|
||||
sender: Sender<M>,
|
||||
scheduler_queue: &Mutex<SchedulerQueue>,
|
||||
) where
|
||||
M: Model,
|
||||
F: for<'a> InputFn<'a, M, T, S> + Clone,
|
||||
T: Send + Clone + 'static,
|
||||
S: Send + 'static,
|
||||
{
|
||||
let channel_id = sender.channel_id();
|
||||
|
||||
let action = Action::new(PeriodicAction::new(
|
||||
|| process_event(func, arg, sender),
|
||||
period,
|
||||
));
|
||||
|
||||
let mut scheduler_queue = scheduler_queue.lock().unwrap();
|
||||
scheduler_queue.insert((time, channel_id), action);
|
||||
}
|
||||
|
||||
/// Schedules an event at a future time, returning an action key.
|
||||
///
|
||||
/// This function does not check whether the specified time lies in the future
|
||||
/// of the current simulation time.
|
||||
pub(crate) fn schedule_periodic_keyed_event_at_unchecked<M, F, T, S>(
|
||||
time: MonotonicTime,
|
||||
period: Duration,
|
||||
func: F,
|
||||
arg: T,
|
||||
sender: Sender<M>,
|
||||
scheduler_queue: &Mutex<SchedulerQueue>,
|
||||
) -> ActionKey
|
||||
where
|
||||
M: Model,
|
||||
F: for<'a> InputFn<'a, M, T, S> + Clone,
|
||||
T: Send + Clone + 'static,
|
||||
S: Send + 'static,
|
||||
{
|
||||
let event_key = ActionKey::new();
|
||||
let channel_id = sender.channel_id();
|
||||
let action = Action::new(KeyedPeriodicAction::new(
|
||||
|ek| send_keyed_event(ek, func, arg, sender),
|
||||
period,
|
||||
event_key.clone(),
|
||||
));
|
||||
|
||||
let mut scheduler_queue = scheduler_queue.lock().unwrap();
|
||||
scheduler_queue.insert((time, channel_id), action);
|
||||
|
||||
event_key
|
||||
}
|
||||
|
||||
pin_project! {
|
||||
/// An object that can be converted to a future performing a single
|
||||
/// non-cancellable action.
|
||||
|
@ -8,7 +8,7 @@ use crate::time::{MonotonicTime, TearableAtomicTime};
|
||||
use crate::util::priority_queue::PriorityQueue;
|
||||
use crate::util::sync_cell::SyncCell;
|
||||
|
||||
use super::{add_model, Mailbox, SchedulerQueue, Simulation};
|
||||
use super::{add_model, Mailbox, Scheduler, SchedulerQueue, Simulation};
|
||||
|
||||
/// Builder for a multi-threaded, discrete-event simulation.
|
||||
pub struct SimInit {
|
||||
@ -58,17 +58,8 @@ impl SimInit {
|
||||
mailbox: Mailbox<M>,
|
||||
name: impl Into<String>,
|
||||
) -> Self {
|
||||
let scheduler_queue = self.scheduler_queue.clone();
|
||||
let time = self.time.reader();
|
||||
|
||||
add_model(
|
||||
model,
|
||||
mailbox,
|
||||
name.into(),
|
||||
scheduler_queue,
|
||||
time,
|
||||
&self.executor,
|
||||
);
|
||||
let scheduler = Scheduler::new(self.scheduler_queue.clone(), self.time.reader());
|
||||
add_model(model, mailbox, name.into(), scheduler, &self.executor);
|
||||
|
||||
self
|
||||
}
|
||||
|
@ -31,7 +31,7 @@
|
||||
//!
|
||||
//! // Sets an alarm [input port].
|
||||
//! pub fn set(&mut self, setting: MonotonicTime, context: &Context<Self>) {
|
||||
//! if context.schedule_event(setting, Self::ring, ()).is_err() {
|
||||
//! if context.scheduler.schedule_event(setting, Self::ring, ()).is_err() {
|
||||
//! println!("The alarm clock can only be set for a future time");
|
||||
//! }
|
||||
//! }
|
||||
|
Reference in New Issue
Block a user