1
0
forked from ROMEO/nexosim

Merge pull request #1 from asynchronics/feature-submodels

Add setup step
This commit is contained in:
Jauhien Piatlicki 2024-04-26 11:55:03 +02:00 committed by GitHub
commit 97b173a081
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 836 additions and 798 deletions

View File

@ -17,7 +17,7 @@ jobs:
matrix: matrix:
rust: rust:
- stable - stable
- 1.64.0 - 1.75.0
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v3 uses: actions/checkout@v3

View File

@ -9,7 +9,7 @@ name = "asynchronix"
authors = ["Serge Barral <serge.barral@asynchronics.com>"] authors = ["Serge Barral <serge.barral@asynchronics.com>"]
version = "0.2.2" version = "0.2.2"
edition = "2021" edition = "2021"
rust-version = "1.64" rust-version = "1.75"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
repository = "https://github.com/asynchronics/asynchronix" repository = "https://github.com/asynchronics/asynchronix"
readme = "../README.md" readme = "../README.md"

View File

@ -31,14 +31,12 @@
//! (-) //! (-)
//! ``` //! ```
use std::future::Future;
use std::pin::Pin;
use std::time::Duration; use std::time::Duration;
use asynchronix::model::{InitializedModel, Model}; use asynchronix::model::{Context, InitializedModel, Model};
use asynchronix::ports::{EventSlot, Output}; use asynchronix::ports::{EventSlot, Output};
use asynchronix::simulation::{Mailbox, SimInit}; use asynchronix::simulation::{ActionKey, Mailbox, SimInit};
use asynchronix::time::{ActionKey, MonotonicTime, Scheduler}; use asynchronix::time::MonotonicTime;
/// Water pump. /// Water pump.
pub struct Pump { pub struct Pump {
@ -122,7 +120,7 @@ impl Controller {
} }
/// Starts brewing or cancels the current brew -- input port. /// Starts brewing or cancels the current brew -- input port.
pub async fn brew_cmd(&mut self, _: (), scheduler: &Scheduler<Self>) { pub async fn brew_cmd(&mut self, _: (), context: &Context<Self>) {
// If a brew was ongoing, sending the brew command is interpreted as a // If a brew was ongoing, sending the brew command is interpreted as a
// request to cancel it. // request to cancel it.
if let Some(key) = self.stop_brew_key.take() { if let Some(key) = self.stop_brew_key.take() {
@ -141,7 +139,7 @@ impl Controller {
// Schedule the `stop_brew()` method and turn on the pump. // Schedule the `stop_brew()` method and turn on the pump.
self.stop_brew_key = Some( self.stop_brew_key = Some(
scheduler context
.schedule_keyed_event(self.brew_time, Self::stop_brew, ()) .schedule_keyed_event(self.brew_time, Self::stop_brew, ())
.unwrap(), .unwrap(),
); );
@ -190,7 +188,7 @@ impl Tank {
} }
/// Water volume added [m³] -- input port. /// Water volume added [m³] -- input port.
pub async fn fill(&mut self, added_volume: f64, scheduler: &Scheduler<Self>) { pub async fn fill(&mut self, added_volume: f64, context: &Context<Self>) {
// Ignore zero and negative values. We could also impose a maximum based // Ignore zero and negative values. We could also impose a maximum based
// on tank capacity. // on tank capacity.
if added_volume <= 0.0 { if added_volume <= 0.0 {
@ -208,11 +206,11 @@ impl Tank {
state.set_empty_key.cancel(); state.set_empty_key.cancel();
// Update the volume, saturating at 0 in case of rounding errors. // Update the volume, saturating at 0 in case of rounding errors.
let time = scheduler.time(); let time = context.time();
let elapsed_time = time.duration_since(state.last_volume_update).as_secs_f64(); let elapsed_time = time.duration_since(state.last_volume_update).as_secs_f64();
self.volume = (self.volume - state.flow_rate * elapsed_time).max(0.0); self.volume = (self.volume - state.flow_rate * elapsed_time).max(0.0);
self.schedule_empty(state.flow_rate, time, scheduler).await; self.schedule_empty(state.flow_rate, time, context).await;
// There is no need to broadcast the state of the water sense since // There is no need to broadcast the state of the water sense since
// it could not be previously `Empty` (otherwise the dynamic state // it could not be previously `Empty` (otherwise the dynamic state
@ -230,10 +228,10 @@ impl Tank {
/// # Panics /// # Panics
/// ///
/// This method will panic if the flow rate is negative. /// This method will panic if the flow rate is negative.
pub async fn set_flow_rate(&mut self, flow_rate: f64, scheduler: &Scheduler<Self>) { pub async fn set_flow_rate(&mut self, flow_rate: f64, context: &Context<Self>) {
assert!(flow_rate >= 0.0); assert!(flow_rate >= 0.0);
let time = scheduler.time(); let time = context.time();
// If the flow rate was non-zero up to now, update the volume. // If the flow rate was non-zero up to now, update the volume.
if let Some(state) = self.dynamic_state.take() { if let Some(state) = self.dynamic_state.take() {
@ -245,7 +243,7 @@ impl Tank {
self.volume = (self.volume - state.flow_rate * elapsed_time).max(0.0); self.volume = (self.volume - state.flow_rate * elapsed_time).max(0.0);
} }
self.schedule_empty(flow_rate, time, scheduler).await; self.schedule_empty(flow_rate, time, context).await;
} }
/// Schedules a callback for when the tank becomes empty. /// Schedules a callback for when the tank becomes empty.
@ -258,7 +256,7 @@ impl Tank {
&mut self, &mut self,
flow_rate: f64, flow_rate: f64,
time: MonotonicTime, time: MonotonicTime,
scheduler: &Scheduler<Self>, context: &Context<Self>,
) { ) {
// Determine when the tank will be empty at the current flow rate. // Determine when the tank will be empty at the current flow rate.
let duration_until_empty = if self.volume == 0.0 { let duration_until_empty = if self.volume == 0.0 {
@ -275,7 +273,7 @@ impl Tank {
let duration_until_empty = Duration::from_secs_f64(duration_until_empty); let duration_until_empty = Duration::from_secs_f64(duration_until_empty);
// Schedule the next update. // Schedule the next update.
match scheduler.schedule_keyed_event(duration_until_empty, Self::set_empty, ()) { match context.schedule_keyed_event(duration_until_empty, Self::set_empty, ()) {
Ok(set_empty_key) => { Ok(set_empty_key) => {
let state = TankDynamicState { let state = TankDynamicState {
last_volume_update: time, last_volume_update: time,
@ -302,21 +300,16 @@ impl Tank {
impl Model for Tank { impl Model for Tank {
/// Broadcasts the initial state of the water sense. /// Broadcasts the initial state of the water sense.
fn init( async fn init(mut self, _: &Context<Self>) -> InitializedModel<Self> {
mut self, self.water_sense
_scheduler: &Scheduler<Self>, .send(if self.volume == 0.0 {
) -> Pin<Box<dyn Future<Output = InitializedModel<Self>> + Send + '_>> { WaterSenseState::Empty
Box::pin(async move { } else {
self.water_sense WaterSenseState::NotEmpty
.send(if self.volume == 0.0 { })
WaterSenseState::Empty .await;
} else {
WaterSenseState::NotEmpty
})
.await;
self.into() self.into()
})
} }
} }

View File

@ -15,13 +15,12 @@
//! ``` //! ```
use std::future::Future; use std::future::Future;
use std::pin::Pin;
use std::time::Duration; use std::time::Duration;
use asynchronix::model::{InitializedModel, Model}; use asynchronix::model::{Context, InitializedModel, Model};
use asynchronix::ports::{EventBuffer, Output}; use asynchronix::ports::{EventBuffer, Output};
use asynchronix::simulation::{Mailbox, SimInit}; use asynchronix::simulation::{Mailbox, SimInit};
use asynchronix::time::{MonotonicTime, Scheduler}; use asynchronix::time::MonotonicTime;
/// Stepper motor. /// Stepper motor.
pub struct Motor { pub struct Motor {
@ -88,15 +87,9 @@ impl Motor {
impl Model for Motor { impl Model for Motor {
/// Broadcasts the initial position of the motor. /// Broadcasts the initial position of the motor.
fn init( async fn init(mut self, _: &Context<Self>) -> InitializedModel<Self> {
mut self, self.position.send(self.pos).await;
_scheduler: &Scheduler<Self>, self.into()
) -> Pin<Box<dyn Future<Output = InitializedModel<Self>> + Send + '_>> {
Box::pin(async move {
self.position.send(self.pos).await;
self.into()
})
} }
} }
@ -130,7 +123,7 @@ impl Driver {
} }
/// Sets the pulse rate (sign = direction) [Hz] -- input port. /// Sets the pulse rate (sign = direction) [Hz] -- input port.
pub async fn pulse_rate(&mut self, pps: f64, scheduler: &Scheduler<Self>) { pub async fn pulse_rate(&mut self, pps: f64, context: &Context<Self>) {
let pps = pps.signum() * pps.abs().clamp(Self::MIN_PPS, Self::MAX_PPS); let pps = pps.signum() * pps.abs().clamp(Self::MIN_PPS, Self::MAX_PPS);
if pps == self.pps { if pps == self.pps {
return; return;
@ -142,7 +135,7 @@ impl Driver {
// Trigger the rotation if the motor is currently idle. Otherwise the // Trigger the rotation if the motor is currently idle. Otherwise the
// new value will be accounted for at the next pulse. // new value will be accounted for at the next pulse.
if is_idle { if is_idle {
self.send_pulse((), scheduler).await; self.send_pulse((), context).await;
} }
} }
@ -153,7 +146,7 @@ impl Driver {
fn send_pulse<'a>( fn send_pulse<'a>(
&'a mut self, &'a mut self,
_: (), _: (),
scheduler: &'a Scheduler<Self>, context: &'a Context<Self>,
) -> impl Future<Output = ()> + Send + 'a { ) -> impl Future<Output = ()> + Send + 'a {
async move { async move {
let current_out = match self.next_phase { let current_out = match self.next_phase {
@ -174,7 +167,7 @@ impl Driver {
let pulse_duration = Duration::from_secs_f64(1.0 / self.pps.abs()); let pulse_duration = Duration::from_secs_f64(1.0 / self.pps.abs());
// Schedule the next pulse. // Schedule the next pulse.
scheduler context
.schedule_event(pulse_duration, Self::send_pulse, ()) .schedule_event(pulse_duration, Self::send_pulse, ())
.unwrap(); .unwrap();
} }

View File

@ -18,8 +18,7 @@ use recycle_box::RecycleBox;
use queue::{PopError, PushError, Queue}; use queue::{PopError, PushError, Queue};
use recycle_box::coerce_box; use recycle_box::coerce_box;
use crate::model::Model; use crate::model::{Context, Model};
use crate::time::Scheduler;
/// Data shared between the receiver and the senders. /// Data shared between the receiver and the senders.
struct Inner<M> { struct Inner<M> {
@ -45,7 +44,7 @@ impl<M: 'static> Inner<M> {
} }
/// A receiver which can asynchronously execute `async` message that take an /// A receiver which can asynchronously execute `async` message that take an
/// argument of type `&mut M` and an optional `&Scheduler<M>` argument. /// argument of type `&mut M` and an optional `&Context<M>` argument.
pub(crate) struct Receiver<M> { pub(crate) struct Receiver<M> {
/// Shared data. /// Shared data.
inner: Arc<Inner<M>>, inner: Arc<Inner<M>>,
@ -90,7 +89,7 @@ impl<M: Model> Receiver<M> {
pub(crate) async fn recv( pub(crate) async fn recv(
&mut self, &mut self,
model: &mut M, model: &mut M,
scheduler: &Scheduler<M>, context: &Context<M>,
) -> Result<(), RecvError> { ) -> Result<(), RecvError> {
let msg = unsafe { let msg = unsafe {
self.inner self.inner
@ -106,7 +105,7 @@ impl<M: Model> Receiver<M> {
match msg { match msg {
Some(mut msg) => { Some(mut msg) => {
// Consume the message to obtain a boxed future. // Consume the message to obtain a boxed future.
let fut = msg.call_once(model, scheduler, self.future_box.take().unwrap()); let fut = msg.call_once(model, context, self.future_box.take().unwrap());
// Now that `msg` was consumed and its slot in the queue was // Now that `msg` was consumed and its slot in the queue was
// freed, signal to one awaiting sender that one slot is // freed, signal to one awaiting sender that one slot is
@ -188,7 +187,7 @@ impl<M: Model> Sender<M> {
where where
F: for<'a> FnOnce( F: for<'a> FnOnce(
&'a mut M, &'a mut M,
&'a Scheduler<M>, &'a Context<M>,
RecycleBox<()>, RecycleBox<()>,
) -> RecycleBox<dyn Future<Output = ()> + Send + 'a> ) -> RecycleBox<dyn Future<Output = ()> + Send + 'a>
+ Send + Send
@ -311,7 +310,7 @@ impl<M> fmt::Debug for Sender<M> {
} }
/// A closure that can be called once to create a future boxed in a `RecycleBox` /// A closure that can be called once to create a future boxed in a `RecycleBox`
/// from an `&mut M`, a `&Scheduler<M>` and an empty `RecycleBox`. /// from an `&mut M`, a `&Context<M>` and an empty `RecycleBox`.
/// ///
/// This is basically a workaround to emulate an `FnOnce` with the equivalent of /// This is basically a workaround to emulate an `FnOnce` with the equivalent of
/// an `FnMut` so that it is possible to call it as a `dyn` trait stored in a /// an `FnMut` so that it is possible to call it as a `dyn` trait stored in a
@ -327,7 +326,7 @@ trait MessageFn<M: Model>: Send {
fn call_once<'a>( fn call_once<'a>(
&mut self, &mut self,
model: &'a mut M, model: &'a mut M,
scheduler: &'a Scheduler<M>, context: &'a Context<M>,
recycle_box: RecycleBox<()>, recycle_box: RecycleBox<()>,
) -> RecycleBox<dyn Future<Output = ()> + Send + 'a>; ) -> RecycleBox<dyn Future<Output = ()> + Send + 'a>;
} }
@ -349,7 +348,7 @@ impl<F, M: Model> MessageFn<M> for MessageFnOnce<F, M>
where where
F: for<'a> FnOnce( F: for<'a> FnOnce(
&'a mut M, &'a mut M,
&'a Scheduler<M>, &'a Context<M>,
RecycleBox<()>, RecycleBox<()>,
) -> RecycleBox<dyn Future<Output = ()> + Send + 'a> ) -> RecycleBox<dyn Future<Output = ()> + Send + 'a>
+ Send, + Send,
@ -357,12 +356,12 @@ where
fn call_once<'a>( fn call_once<'a>(
&mut self, &mut self,
model: &'a mut M, model: &'a mut M,
scheduler: &'a Scheduler<M>, context: &'a Context<M>,
recycle_box: RecycleBox<()>, recycle_box: RecycleBox<()>,
) -> RecycleBox<dyn Future<Output = ()> + Send + 'a> { ) -> RecycleBox<dyn Future<Output = ()> + Send + 'a> {
let closure = self.msg_fn.take().unwrap(); let closure = self.msg_fn.take().unwrap();
(closure)(model, scheduler, recycle_box) (closure)(model, context, recycle_box)
} }
} }

View File

@ -45,7 +45,7 @@
//! * _input ports_, which are synchronous or asynchronous methods that //! * _input ports_, which are synchronous or asynchronous methods that
//! implement the [`InputFn`](ports::InputFn) trait and take an `&mut self` //! implement the [`InputFn`](ports::InputFn) trait and take an `&mut self`
//! argument, a message argument, and an optional //! argument, a message argument, and an optional
//! [`&Scheduler`](time::Scheduler) argument, //! [`&Context`](model::Context) argument,
//! * _replier ports_, which are similar to input ports but implement the //! * _replier ports_, which are similar to input ports but implement the
//! [`ReplierFn`](ports::ReplierFn) trait and return a reply. //! [`ReplierFn`](ports::ReplierFn) trait and return a reply.
//! //!
@ -54,12 +54,17 @@
//! are referred to as *requests* and *replies*. //! are referred to as *requests* and *replies*.
//! //!
//! Models must implement the [`Model`](model::Model) trait. The main purpose of //! Models must implement the [`Model`](model::Model) trait. The main purpose of
//! this trait is to allow models to specify an `init()` method that is //! this trait is to allow models to specify
//! guaranteed to run once and only once when the simulation is initialized, //! * a `setup()` method that is called once during model addtion to simulation,
//! _i.e._ after all models have been connected but before the simulation //! this method allows e.g. creation and interconnection of submodels inside
//! starts. The `init()` method has a default implementation, so models that do //! the model,
//! not require initialization can simply implement the trait with a one-liner //! * an `init()` method that is guaranteed to run once and only once when the
//! such as `impl Model for MyModel {}`. //! simulation is initialized, _i.e._ after all models have been connected but
//! before the simulation starts.
//!
//! The `setup()` and `init()` methods have default implementations, so models
//! that do not require setup and initialization can simply implement the trait
//! with a one-liner such as `impl Model for MyModel {}`.
//! //!
//! #### A simple model //! #### A simple model
//! //!
@ -93,29 +98,28 @@
//! impl Model for Multiplier {} //! impl Model for Multiplier {}
//! ``` //! ```
//! //!
//! #### A model using the local scheduler //! #### A model using the local context
//! //!
//! Models frequently need to schedule actions at a future time or simply get //! Models frequently need to schedule actions at a future time or simply get
//! access to the current simulation time. To do so, input and replier methods //! access to the current simulation time. To do so, input and replier methods
//! can take an optional argument that gives them access to a local scheduler. //! can take an optional argument that gives them access to a local context.
//! //!
//! To show how the local scheduler can be used in practice, let us implement //! To show how the local context can be used in practice, let us implement
//! `Delay`, a model which simply forwards its input unmodified after a 1s //! `Delay`, a model which simply forwards its input unmodified after a 1s
//! delay: //! delay:
//! //!
//! ``` //! ```
//! use std::time::Duration; //! use std::time::Duration;
//! use asynchronix::model::Model; //! use asynchronix::model::{Context, Model};
//! use asynchronix::ports::Output; //! use asynchronix::ports::Output;
//! use asynchronix::time::Scheduler;
//! //!
//! #[derive(Default)] //! #[derive(Default)]
//! pub struct Delay { //! pub struct Delay {
//! pub output: Output<f64>, //! pub output: Output<f64>,
//! } //! }
//! impl Delay { //! impl Delay {
//! pub fn input(&mut self, value: f64, scheduler: &Scheduler<Self>) { //! pub fn input(&mut self, value: f64, context: &Context<Self>) {
//! scheduler.schedule_event(Duration::from_secs(1), Self::send, value).unwrap(); //! context.schedule_event(Duration::from_secs(1), Self::send, value).unwrap();
//! } //! }
//! //!
//! async fn send(&mut self, value: f64) { //! async fn send(&mut self, value: f64) {
@ -137,7 +141,7 @@
//! [`Address`](simulation::Mailbox)es pointing to that mailbox. //! [`Address`](simulation::Mailbox)es pointing to that mailbox.
//! //!
//! Addresses are used among others to connect models: each output or requestor //! Addresses are used among others to connect models: each output or requestor
//! ports has a `connect()` method that takes as argument a function pointer to //! port has a `connect()` method that takes as argument a function pointer to
//! the corresponding input or replier port method and the address of the //! the corresponding input or replier port method and the address of the
//! targeted model. //! targeted model.
//! //!
@ -168,9 +172,8 @@
//! ``` //! ```
//! # mod models { //! # mod models {
//! # use std::time::Duration; //! # use std::time::Duration;
//! # use asynchronix::model::Model; //! # use asynchronix::model::{Context, Model};
//! # use asynchronix::ports::Output; //! # use asynchronix::ports::Output;
//! # use asynchronix::time::Scheduler;
//! # #[derive(Default)] //! # #[derive(Default)]
//! # pub struct Multiplier { //! # pub struct Multiplier {
//! # pub output: Output<f64>, //! # pub output: Output<f64>,
@ -186,8 +189,8 @@
//! # pub output: Output<f64>, //! # pub output: Output<f64>,
//! # } //! # }
//! # impl Delay { //! # impl Delay {
//! # pub fn input(&mut self, value: f64, scheduler: &Scheduler<Self>) { //! # pub fn input(&mut self, value: f64, context: &Context<Self>) {
//! # scheduler.schedule_event(Duration::from_secs(1), Self::send, value).unwrap(); //! # context.schedule_event(Duration::from_secs(1), Self::send, value).unwrap();
//! # } //! # }
//! # async fn send(&mut self, value: f64) { // this method can be private //! # async fn send(&mut self, value: f64) { // this method can be private
//! # self.output.send(value).await; //! # self.output.send(value).await;
@ -268,9 +271,8 @@
//! ``` //! ```
//! # mod models { //! # mod models {
//! # use std::time::Duration; //! # use std::time::Duration;
//! # use asynchronix::model::Model; //! # use asynchronix::model::{Context, Model};
//! # use asynchronix::ports::Output; //! # use asynchronix::ports::Output;
//! # use asynchronix::time::Scheduler;
//! # #[derive(Default)] //! # #[derive(Default)]
//! # pub struct Multiplier { //! # pub struct Multiplier {
//! # pub output: Output<f64>, //! # pub output: Output<f64>,
@ -286,8 +288,8 @@
//! # pub output: Output<f64>, //! # pub output: Output<f64>,
//! # } //! # }
//! # impl Delay { //! # impl Delay {
//! # pub fn input(&mut self, value: f64, scheduler: &Scheduler<Self>) { //! # pub fn input(&mut self, value: f64, context: &Context<Self>) {
//! # scheduler.schedule_event(Duration::from_secs(1), Self::send, value).unwrap(); //! # context.schedule_event(Duration::from_secs(1), Self::send, value).unwrap();
//! # } //! # }
//! # async fn send(&mut self, value: f64) { // this method can be private //! # async fn send(&mut self, value: f64) { // this method can be private
//! # self.output.send(value).await; //! # self.output.send(value).await;
@ -395,15 +397,14 @@
//! //!
//! * the [`model`] module provides more details about the signatures of input //! * the [`model`] module provides more details about the signatures of input
//! and replier port methods and discusses model initialization in the //! and replier port methods and discusses model initialization in the
//! documentation of [`model::Model`], //! documentation of [`model::Model`] and self-scheduling methods as well as
//! scheduling cancellation in the documentation of [`model::Context`],
//! * the [`simulation`] module discusses how the capacity of mailboxes may //! * the [`simulation`] module discusses how the capacity of mailboxes may
//! affect the simulation, how connections can be modified after the //! affect the simulation, how connections can be modified after the
//! simulation was instantiated, and which pathological situations can lead to //! simulation was instantiated, and which pathological situations can lead to
//! a deadlock, //! a deadlock,
//! * the [`time`] module discusses in particular self-scheduling methods and //! * the [`time`] module discusses in particular the monotonic timestamp format
//! scheduling cancellation in the documentation of [`time::Scheduler`] while //! used for simulations ([`time::MonotonicTime`]).
//! the monotonic timestamp format used for simulations is documented in
//! [`time::MonotonicTime`].
#![warn(missing_docs, missing_debug_implementations, unreachable_pub)] #![warn(missing_docs, missing_debug_implementations, unreachable_pub)]
pub(crate) mod channel; pub(crate) mod channel;

View File

@ -2,16 +2,19 @@
//! //!
//! # Model trait //! # Model trait
//! //!
//! Every model must implement the [`Model`] trait. This trait defines an //! Every model must implement the [`Model`] trait. This trait defines
//! asynchronous initialization method, [`Model::init()`], which main purpose is //! * a setup method, [`Model::setup()`], which main purpose is to create,
//! to enable models to perform specific actions only once all models have been //! connect and add to the simulation bench submodels and perform other setup
//! connected and migrated to the simulation, but before the simulation actually //! steps,
//! starts. //! * an asynchronous initialization method, [`Model::init()`], which main
//! purpose is to enable models to perform specific actions only once all
//! models have been connected and migrated to the simulation, but before the
//! simulation actually starts.
//! //!
//! #### Examples //! #### Examples
//! //!
//! A model that does not require initialization can simply use the default //! A model that does not require setup and initialization can simply use the
//! implementation of the `Model` trait: //! default implementation of the `Model` trait:
//! //!
//! ``` //! ```
//! use asynchronix::model::Model; //! use asynchronix::model::Model;
@ -22,28 +25,31 @@
//! impl Model for MyModel {} //! impl Model for MyModel {}
//! ``` //! ```
//! //!
//! Otherwise, a custom `init()` method can be implemented: //! Otherwise, custom `setup()` or `init()` methods can be implemented:
//! //!
//! ``` //! ```
//! use std::future::Future; //! use std::future::Future;
//! use std::pin::Pin; //! use std::pin::Pin;
//! //!
//! use asynchronix::model::{InitializedModel, Model}; //! use asynchronix::model::{Context, InitializedModel, Model, SetupContext};
//! use asynchronix::time::Scheduler;
//! //!
//! pub struct MyModel { //! pub struct MyModel {
//! // ... //! // ...
//! } //! }
//! impl Model for MyModel { //! impl Model for MyModel {
//! fn init( //! fn setup(
//! mut self, //! &mut self,
//! scheduler: &Scheduler<Self> //! setup_context: &SetupContext<Self>) {
//! ) -> Pin<Box<dyn Future<Output = InitializedModel<Self>> + Send + '_>>{ //! println!("...setup...");
//! Box::pin(async move { //! }
//! println!("...initialization...");
//! //!
//! self.into() //! async fn init(
//! }) //! mut self,
//! context: &Context<Self>
//! ) -> InitializedModel<Self> {
//! println!("...initialization...");
//!
//! self.into()
//! } //! }
//! } //! }
//! ``` //! ```
@ -103,17 +109,17 @@
//! ```ignore //! ```ignore
//! fn(&mut self) // argument elided, implies `T=()` //! fn(&mut self) // argument elided, implies `T=()`
//! fn(&mut self, T) //! fn(&mut self, T)
//! fn(&mut self, T, &Scheduler<Self>) //! fn(&mut self, T, &Context<Self>)
//! async fn(&mut self) // argument elided, implies `T=()` //! async fn(&mut self) // argument elided, implies `T=()`
//! async fn(&mut self, T) //! async fn(&mut self, T)
//! async fn(&mut self, T, &Scheduler<Self>) //! async fn(&mut self, T, &Context<Self>)
//! where //! where
//! Self: Model, //! Self: Model,
//! T: Clone + Send + 'static, //! T: Clone + Send + 'static,
//! R: Send + 'static, //! R: Send + 'static,
//! ``` //! ```
//! //!
//! The scheduler argument is useful for methods that need access to the //! The context argument is useful for methods that need access to the
//! simulation time or that need to schedule an action at a future date. //! simulation time or that need to schedule an action at a future date.
//! //!
//! A replier port for a request of type `T` with a reply of type `R` may in //! A replier port for a request of type `T` with a reply of type `R` may in
@ -123,7 +129,7 @@
//! ```ignore //! ```ignore
//! async fn(&mut self) -> R // argument elided, implies `T=()` //! async fn(&mut self) -> R // argument elided, implies `T=()`
//! async fn(&mut self, T) -> R //! async fn(&mut self, T) -> R
//! async fn(&mut self, T, &Scheduler<Self>) -> R //! async fn(&mut self, T, &Context<Self>) -> R
//! where //! where
//! Self: Model, //! Self: Model,
//! T: Clone + Send + 'static, //! T: Clone + Send + 'static,
@ -134,7 +140,7 @@
//! can be connected to input and requestor ports when assembling the simulation //! can be connected to input and requestor ports when assembling the simulation
//! bench. However, input ports may instead be defined as private methods if //! bench. However, input ports may instead be defined as private methods if
//! they are only used by the model itself to schedule future actions (see the //! they are only used by the model itself to schedule future actions (see the
//! [`Scheduler`] examples). //! [`Context`] examples).
//! //!
//! Changing the signature of an input or replier port is not considered to //! Changing the signature of an input or replier port is not considered to
//! alter the public interface of a model provided that the event, request and //! alter the public interface of a model provided that the event, request and
@ -143,17 +149,16 @@
//! #### Example //! #### Example
//! //!
//! ``` //! ```
//! use asynchronix::model::Model; //! use asynchronix::model::{Context, Model};
//! use asynchronix::time::Scheduler;
//! //!
//! pub struct MyModel { //! pub struct MyModel {
//! // ... //! // ...
//! } //! }
//! impl MyModel { //! impl MyModel {
//! pub fn my_input(&mut self, input: String, scheduler: &Scheduler<Self>) { //! pub fn my_input(&mut self, input: String, context: &Context<Self>) {
//! // ... //! // ...
//! } //! }
//! pub async fn my_replier(&mut self, request: u32) -> bool { // scheduler argument elided //! pub async fn my_replier(&mut self, request: u32) -> bool { // context argument elided
//! // ... //! // ...
//! # unimplemented!() //! # unimplemented!()
//! } //! }
@ -163,14 +168,19 @@
//! //!
use std::future::Future; use std::future::Future;
use std::pin::Pin;
use crate::time::Scheduler; pub use context::{Context, SetupContext};
mod context;
/// Trait to be implemented by all models. /// Trait to be implemented by all models.
/// ///
/// This trait enables models to perform specific actions in the /// This trait enables models to perform specific actions during setup and
/// [`Model::init()`] method only once all models have been connected and /// initialization. The [`Model::setup()`] method is run only once when models
/// are being added to the simulation bench. This method allows in particular
/// sub-models to be created, connected and added to the simulation.
///
/// The [`Model::init()`] method is run only once all models have been connected and
/// migrated to the simulation bench, but before the simulation actually starts. /// migrated to the simulation bench, but before the simulation actually starts.
/// A common use for `init` is to send messages to connected models at the /// A common use for `init` is to send messages to connected models at the
/// beginning of the simulation. /// beginning of the simulation.
@ -179,6 +189,37 @@ use crate::time::Scheduler;
/// to prevent an already initialized model from being added to the simulation /// to prevent an already initialized model from being added to the simulation
/// bench. /// bench.
pub trait Model: Sized + Send + 'static { pub trait Model: Sized + Send + 'static {
/// Performs model setup.
///
/// This method is executed exactly once for all models of the simulation
/// when the [`SimInit::add_model()`](crate::simulation::SimInit::add_model)
/// method is called.
///
/// The default implementation does nothing.
///
/// # Examples
///
/// ```
/// use std::future::Future;
/// use std::pin::Pin;
///
/// use asynchronix::model::{InitializedModel, Model, SetupContext};
///
/// pub struct MyModel {
/// // ...
/// }
///
/// impl Model for MyModel {
/// fn setup(
/// &mut self,
/// setup_context: &SetupContext<Self>
/// ) {
/// println!("...setup...");
/// }
/// }
/// ```
fn setup(&mut self, _: &SetupContext<Self>) {}
/// Performs asynchronous model initialization. /// Performs asynchronous model initialization.
/// ///
/// This asynchronous method is executed exactly once for all models of the /// This asynchronous method is executed exactly once for all models of the
@ -188,47 +229,31 @@ pub trait Model: Sized + Send + 'static {
/// The default implementation simply converts the model to an /// The default implementation simply converts the model to an
/// `InitializedModel` without any side effect. /// `InitializedModel` without any side effect.
/// ///
/// *Note*: it is currently necessary to box the returned future; this
/// limitation will be lifted once Rust supports `async` methods in traits.
///
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use std::future::Future; /// use std::future::Future;
/// use std::pin::Pin; /// use std::pin::Pin;
/// ///
/// use asynchronix::model::{InitializedModel, Model}; /// use asynchronix::model::{Context, InitializedModel, Model};
/// use asynchronix::time::Scheduler;
/// ///
/// pub struct MyModel { /// pub struct MyModel {
/// // ... /// // ...
/// } /// }
/// ///
/// impl Model for MyModel { /// impl Model for MyModel {
/// fn init( /// async fn init(
/// self, /// self,
/// scheduler: &Scheduler<Self> /// context: &Context<Self>
/// ) -> Pin<Box<dyn Future<Output = InitializedModel<Self>> + Send + '_>>{ /// ) -> InitializedModel<Self> {
/// Box::pin(async move { /// println!("...initialization...");
/// println!("...initialization...");
/// ///
/// self.into() /// self.into()
/// })
/// } /// }
/// } /// }
/// ``` /// ```
fn init(self, _: &Context<Self>) -> impl Future<Output = InitializedModel<Self>> + Send {
// Removing the boxing constraint requires the async { self.into() }
// `return_position_impl_trait_in_trait` and `async_fn_in_trait` features.
// Tracking issue: <https://github.com/rust-lang/rust/issues/91611>.
fn init(
self,
scheduler: &Scheduler<Self>,
) -> Pin<Box<dyn Future<Output = InitializedModel<Self>> + Send + '_>> {
Box::pin(async move {
let _ = scheduler; // suppress the unused argument warning
self.into()
})
} }
} }

View File

@ -0,0 +1,485 @@
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<Self>
/// ) -> impl Future<Output=()> + 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<String>,
/// }
///
/// 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 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<M: Model> {
sender: Sender<M>,
scheduler_queue: Arc<Mutex<SchedulerQueue>>,
time: SyncCellReader<TearableAtomicTime>,
}
impl<M: Model> Context<M> {
/// Creates a new local context.
pub(crate) fn new(
sender: Sender<M>,
scheduler_queue: Arc<Mutex<SchedulerQueue>>,
time: SyncCellReader<TearableAtomicTime>,
) -> Self {
Self {
sender,
scheduler_queue,
time,
}
}
/// 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> {
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<u32>,
/// }
///
/// 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<Self>) {
/// let mut a = SubmodelA::default();
/// let b = SubmodelB::default();
/// let a_mbox = Mailbox::new();
/// let b_mbox = Mailbox::new();
///
/// a.out.connect(SubmodelB::input, &b_mbox);
///
/// setup_context.add_model(a, a_mbox);
/// setup_context.add_model(b, b_mbox);
/// }
/// }
///
/// ```
#[derive(Debug)]
pub struct SetupContext<'a, M: Model> {
/// Mailbox of the model.
pub mailbox: &'a Mailbox<M>,
context: &'a Context<M>,
executor: &'a Executor,
}
impl<'a, M: Model> SetupContext<'a, M> {
/// Creates a new local context.
pub(crate) fn new(
mailbox: &'a Mailbox<M>,
context: &'a Context<M>,
executor: &'a Executor,
) -> Self {
Self {
mailbox,
context,
executor,
}
}
/// Adds a new model and its mailbox to the simulation bench.
pub fn add_model<N: Model>(&self, model: N, mailbox: Mailbox<N>) {
simulation::add_model(
model,
mailbox,
self.context.scheduler_queue.clone(),
self.context.time.clone(),
self.executor,
);
}
}

View File

@ -6,14 +6,14 @@
pub struct WithoutArguments {} pub struct WithoutArguments {}
/// Marker type for regular simulation model methods that take a mutable /// Marker type for regular simulation model methods that take a mutable
/// reference to the model and a message, without scheduler argument. /// reference to the model and a message, without context argument.
#[derive(Debug)] #[derive(Debug)]
pub struct WithoutScheduler {} pub struct WithoutContext {}
/// Marker type for regular simulation model methods that take a mutable /// Marker type for regular simulation model methods that take a mutable
/// reference to the model, a message and an explicit scheduler argument. /// reference to the model, a message and an explicit context argument.
#[derive(Debug)] #[derive(Debug)]
pub struct WithScheduler {} pub struct WithContext {}
/// Marker type for asynchronous simulation model methods that take a mutable /// Marker type for asynchronous simulation model methods that take a mutable
/// reference to the model, without any other argument. /// reference to the model, without any other argument.
@ -21,11 +21,11 @@ pub struct WithScheduler {}
pub struct AsyncWithoutArguments {} pub struct AsyncWithoutArguments {}
/// Marker type for asynchronous simulation model methods that take a mutable /// Marker type for asynchronous simulation model methods that take a mutable
/// reference to the model and a message, without scheduler argument. /// reference to the model and a message, without context argument.
#[derive(Debug)] #[derive(Debug)]
pub struct AsyncWithoutScheduler {} pub struct AsyncWithoutContext {}
/// Marker type for asynchronous simulation model methods that take a mutable /// Marker type for asynchronous simulation model methods that take a mutable
/// reference to the model, a message and an explicit scheduler argument. /// reference to the model, a message and an explicit context argument.
#[derive(Debug)] #[derive(Debug)]
pub struct AsyncWithScheduler {} pub struct AsyncWithContext {}

View File

@ -2,8 +2,7 @@
use std::future::{ready, Future, Ready}; use std::future::{ready, Future, Ready};
use crate::model::Model; use crate::model::{Context, Model};
use crate::time::Scheduler;
use super::markers; use super::markers;
@ -15,9 +14,9 @@ use super::markers;
/// ///
/// ```ignore /// ```ignore
/// FnOnce(&mut M, T) /// FnOnce(&mut M, T)
/// FnOnce(&mut M, T, &Scheduler<M>) /// FnOnce(&mut M, T, &Context<M>)
/// async fn(&mut M, T) /// async fn(&mut M, T)
/// async fn(&mut M, T, &Scheduler<M>) /// async fn(&mut M, T, &Context<M>)
/// where /// where
/// M: Model /// M: Model
/// ``` /// ```
@ -35,7 +34,7 @@ pub trait InputFn<'a, M: Model, T, S>: Send + 'static {
type Future: Future<Output = ()> + Send + 'a; type Future: Future<Output = ()> + Send + 'a;
/// Calls the method. /// Calls the method.
fn call(self, model: &'a mut M, arg: T, scheduler: &'a Scheduler<M>) -> Self::Future; fn call(self, model: &'a mut M, arg: T, context: &'a Context<M>) -> Self::Future;
} }
impl<'a, M, F> InputFn<'a, M, (), markers::WithoutArguments> for F impl<'a, M, F> InputFn<'a, M, (), markers::WithoutArguments> for F
@ -45,36 +44,36 @@ where
{ {
type Future = Ready<()>; type Future = Ready<()>;
fn call(self, model: &'a mut M, _arg: (), _scheduler: &'a Scheduler<M>) -> Self::Future { fn call(self, model: &'a mut M, _arg: (), _context: &'a Context<M>) -> Self::Future {
self(model); self(model);
ready(()) ready(())
} }
} }
impl<'a, M, T, F> InputFn<'a, M, T, markers::WithoutScheduler> for F impl<'a, M, T, F> InputFn<'a, M, T, markers::WithoutContext> for F
where where
M: Model, M: Model,
F: FnOnce(&'a mut M, T) + Send + 'static, F: FnOnce(&'a mut M, T) + Send + 'static,
{ {
type Future = Ready<()>; type Future = Ready<()>;
fn call(self, model: &'a mut M, arg: T, _scheduler: &'a Scheduler<M>) -> Self::Future { fn call(self, model: &'a mut M, arg: T, _context: &'a Context<M>) -> Self::Future {
self(model, arg); self(model, arg);
ready(()) ready(())
} }
} }
impl<'a, M, T, F> InputFn<'a, M, T, markers::WithScheduler> for F impl<'a, M, T, F> InputFn<'a, M, T, markers::WithContext> for F
where where
M: Model, M: Model,
F: FnOnce(&'a mut M, T, &'a Scheduler<M>) + Send + 'static, F: FnOnce(&'a mut M, T, &'a Context<M>) + Send + 'static,
{ {
type Future = Ready<()>; type Future = Ready<()>;
fn call(self, model: &'a mut M, arg: T, scheduler: &'a Scheduler<M>) -> Self::Future { fn call(self, model: &'a mut M, arg: T, context: &'a Context<M>) -> Self::Future {
self(model, arg, scheduler); self(model, arg, context);
ready(()) ready(())
} }
@ -88,12 +87,12 @@ where
{ {
type Future = Fut; type Future = Fut;
fn call(self, model: &'a mut M, _arg: (), _scheduler: &'a Scheduler<M>) -> Self::Future { fn call(self, model: &'a mut M, _arg: (), _context: &'a Context<M>) -> Self::Future {
self(model) self(model)
} }
} }
impl<'a, M, T, Fut, F> InputFn<'a, M, T, markers::AsyncWithoutScheduler> for F impl<'a, M, T, Fut, F> InputFn<'a, M, T, markers::AsyncWithoutContext> for F
where where
M: Model, M: Model,
Fut: Future<Output = ()> + Send + 'a, Fut: Future<Output = ()> + Send + 'a,
@ -101,21 +100,21 @@ where
{ {
type Future = Fut; type Future = Fut;
fn call(self, model: &'a mut M, arg: T, _scheduler: &'a Scheduler<M>) -> Self::Future { fn call(self, model: &'a mut M, arg: T, _context: &'a Context<M>) -> Self::Future {
self(model, arg) self(model, arg)
} }
} }
impl<'a, M, T, Fut, F> InputFn<'a, M, T, markers::AsyncWithScheduler> for F impl<'a, M, T, Fut, F> InputFn<'a, M, T, markers::AsyncWithContext> for F
where where
M: Model, M: Model,
Fut: Future<Output = ()> + Send + 'a, Fut: Future<Output = ()> + Send + 'a,
F: FnOnce(&'a mut M, T, &'a Scheduler<M>) -> Fut + Send + 'static, F: FnOnce(&'a mut M, T, &'a Context<M>) -> Fut + Send + 'static,
{ {
type Future = Fut; type Future = Fut;
fn call(self, model: &'a mut M, arg: T, scheduler: &'a Scheduler<M>) -> Self::Future { fn call(self, model: &'a mut M, arg: T, context: &'a Context<M>) -> Self::Future {
self(model, arg, scheduler) self(model, arg, context)
} }
} }
@ -127,7 +126,7 @@ where
/// ///
/// ```ignore /// ```ignore
/// async fn(&mut M, T) -> R /// async fn(&mut M, T) -> R
/// async fn(&mut M, T, &Scheduler<M>) -> R /// async fn(&mut M, T, &Context<M>) -> R
/// where /// where
/// M: Model /// M: Model
/// ``` /// ```
@ -144,7 +143,7 @@ pub trait ReplierFn<'a, M: Model, T, R, S>: Send + 'static {
type Future: Future<Output = R> + Send + 'a; type Future: Future<Output = R> + Send + 'a;
/// Calls the method. /// Calls the method.
fn call(self, model: &'a mut M, arg: T, scheduler: &'a Scheduler<M>) -> Self::Future; fn call(self, model: &'a mut M, arg: T, context: &'a Context<M>) -> Self::Future;
} }
impl<'a, M, R, Fut, F> ReplierFn<'a, M, (), R, markers::AsyncWithoutArguments> for F impl<'a, M, R, Fut, F> ReplierFn<'a, M, (), R, markers::AsyncWithoutArguments> for F
@ -155,12 +154,12 @@ where
{ {
type Future = Fut; type Future = Fut;
fn call(self, model: &'a mut M, _arg: (), _scheduler: &'a Scheduler<M>) -> Self::Future { fn call(self, model: &'a mut M, _arg: (), _context: &'a Context<M>) -> Self::Future {
self(model) self(model)
} }
} }
impl<'a, M, T, R, Fut, F> ReplierFn<'a, M, T, R, markers::AsyncWithoutScheduler> for F impl<'a, M, T, R, Fut, F> ReplierFn<'a, M, T, R, markers::AsyncWithoutContext> for F
where where
M: Model, M: Model,
Fut: Future<Output = R> + Send + 'a, Fut: Future<Output = R> + Send + 'a,
@ -168,20 +167,20 @@ where
{ {
type Future = Fut; type Future = Fut;
fn call(self, model: &'a mut M, arg: T, _scheduler: &'a Scheduler<M>) -> Self::Future { fn call(self, model: &'a mut M, arg: T, _context: &'a Context<M>) -> Self::Future {
self(model, arg) self(model, arg)
} }
} }
impl<'a, M, T, R, Fut, F> ReplierFn<'a, M, T, R, markers::AsyncWithScheduler> for F impl<'a, M, T, R, Fut, F> ReplierFn<'a, M, T, R, markers::AsyncWithContext> for F
where where
M: Model, M: Model,
Fut: Future<Output = R> + Send + 'a, Fut: Future<Output = R> + Send + 'a,
F: FnOnce(&'a mut M, T, &'a Scheduler<M>) -> Fut + Send + 'static, F: FnOnce(&'a mut M, T, &'a Context<M>) -> Fut + Send + 'static,
{ {
type Future = Fut; type Future = Fut;
fn call(self, model: &'a mut M, arg: T, scheduler: &'a Scheduler<M>) -> Self::Future { fn call(self, model: &'a mut M, arg: T, context: &'a Context<M>) -> Self::Future {
self(model, arg, scheduler) self(model, arg, context)
} }
} }

View File

@ -505,7 +505,7 @@ mod tests {
use futures_executor::block_on; use futures_executor::block_on;
use crate::channel::Receiver; use crate::channel::Receiver;
use crate::time::Scheduler; use crate::model::Context;
use crate::time::{MonotonicTime, TearableAtomicTime}; use crate::time::{MonotonicTime, TearableAtomicTime};
use crate::util::priority_queue::PriorityQueue; use crate::util::priority_queue::PriorityQueue;
use crate::util::sync_cell::SyncCell; use crate::util::sync_cell::SyncCell;
@ -563,9 +563,9 @@ mod tests {
let dummy_priority_queue = Arc::new(Mutex::new(PriorityQueue::new())); let dummy_priority_queue = Arc::new(Mutex::new(PriorityQueue::new()));
let dummy_time = let dummy_time =
SyncCell::new(TearableAtomicTime::new(MonotonicTime::EPOCH)).reader(); SyncCell::new(TearableAtomicTime::new(MonotonicTime::EPOCH)).reader();
let dummy_scheduler = let dummy_context =
Scheduler::new(dummy_address, dummy_priority_queue, dummy_time); Context::new(dummy_address, dummy_priority_queue, dummy_time);
block_on(mailbox.recv(&mut counter, &dummy_scheduler)).unwrap(); block_on(mailbox.recv(&mut counter, &dummy_context)).unwrap();
} }
}) })
}) })
@ -614,9 +614,9 @@ mod tests {
let dummy_priority_queue = Arc::new(Mutex::new(PriorityQueue::new())); let dummy_priority_queue = Arc::new(Mutex::new(PriorityQueue::new()));
let dummy_time = let dummy_time =
SyncCell::new(TearableAtomicTime::new(MonotonicTime::EPOCH)).reader(); SyncCell::new(TearableAtomicTime::new(MonotonicTime::EPOCH)).reader();
let dummy_scheduler = let dummy_context =
Scheduler::new(dummy_address, dummy_priority_queue, dummy_time); Context::new(dummy_address, dummy_priority_queue, dummy_time);
block_on(mailbox.recv(&mut counter, &dummy_scheduler)).unwrap(); block_on(mailbox.recv(&mut counter, &dummy_context)).unwrap();
thread::sleep(std::time::Duration::from_millis(100)); thread::sleep(std::time::Duration::from_millis(100));
} }
}) })

View File

@ -8,9 +8,8 @@ use std::time::Duration;
use crate::model::Model; use crate::model::Model;
use crate::ports::InputFn; use crate::ports::InputFn;
use crate::ports::{LineError, LineId}; use crate::ports::{LineError, LineId};
use crate::simulation::Address; use crate::simulation::{
use crate::time::{ Action, ActionKey, Address, KeyedOnceAction, KeyedPeriodicAction, OnceAction, PeriodicAction,
Action, ActionKey, KeyedOnceAction, KeyedPeriodicAction, OnceAction, PeriodicAction,
}; };
use crate::util::slot; use crate::util::slot;

View File

@ -430,7 +430,7 @@ mod tests {
use futures_executor::block_on; use futures_executor::block_on;
use crate::channel::Receiver; use crate::channel::Receiver;
use crate::time::Scheduler; use crate::model::Context;
use crate::time::{MonotonicTime, TearableAtomicTime}; use crate::time::{MonotonicTime, TearableAtomicTime};
use crate::util::priority_queue::PriorityQueue; use crate::util::priority_queue::PriorityQueue;
use crate::util::sync_cell::SyncCell; use crate::util::sync_cell::SyncCell;
@ -488,9 +488,9 @@ mod tests {
let dummy_priority_queue = Arc::new(Mutex::new(PriorityQueue::new())); let dummy_priority_queue = Arc::new(Mutex::new(PriorityQueue::new()));
let dummy_time = let dummy_time =
SyncCell::new(TearableAtomicTime::new(MonotonicTime::EPOCH)).reader(); SyncCell::new(TearableAtomicTime::new(MonotonicTime::EPOCH)).reader();
let dummy_scheduler = let dummy_context =
Scheduler::new(dummy_address, dummy_priority_queue, dummy_time); Context::new(dummy_address, dummy_priority_queue, dummy_time);
block_on(mailbox.recv(&mut counter, &dummy_scheduler)).unwrap(); block_on(mailbox.recv(&mut counter, &dummy_context)).unwrap();
} }
}) })
}) })
@ -539,9 +539,9 @@ mod tests {
let dummy_priority_queue = Arc::new(Mutex::new(PriorityQueue::new())); let dummy_priority_queue = Arc::new(Mutex::new(PriorityQueue::new()));
let dummy_time = let dummy_time =
SyncCell::new(TearableAtomicTime::new(MonotonicTime::EPOCH)).reader(); SyncCell::new(TearableAtomicTime::new(MonotonicTime::EPOCH)).reader();
let dummy_scheduler = let dummy_context =
Scheduler::new(dummy_address, dummy_priority_queue, dummy_time); Context::new(dummy_address, dummy_priority_queue, dummy_time);
block_on(mailbox.recv(&mut counter, &dummy_scheduler)).unwrap(); block_on(mailbox.recv(&mut counter, &dummy_context)).unwrap();
thread::sleep(std::time::Duration::from_millis(100)); thread::sleep(std::time::Duration::from_millis(100));
} }
}) })

View File

@ -11,7 +11,10 @@ pub struct ServerError {
#[derive(Clone, PartialEq, ::prost::Message)] #[derive(Clone, PartialEq, ::prost::Message)]
pub struct AnyRequest { pub struct AnyRequest {
/// Expects exactly 1 variant. /// Expects exactly 1 variant.
#[prost(oneof = "any_request::Request", tags = "1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11")] #[prost(
oneof = "any_request::Request",
tags = "1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11"
)]
pub request: ::core::option::Option<any_request::Request>, pub request: ::core::option::Option<any_request::Request>,
} }
/// Nested message and enum types in `AnyRequest`. /// Nested message and enum types in `AnyRequest`.
@ -48,7 +51,10 @@ pub mod any_request {
#[derive(Clone, PartialEq, ::prost::Message)] #[derive(Clone, PartialEq, ::prost::Message)]
pub struct AnyReply { pub struct AnyReply {
/// Contains exactly 1 variant. /// Contains exactly 1 variant.
#[prost(oneof = "any_reply::Reply", tags = "1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 100")] #[prost(
oneof = "any_reply::Reply",
tags = "1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 100"
)]
pub reply: ::core::option::Option<any_reply::Reply>, pub reply: ::core::option::Option<any_reply::Reply>,
} }
/// Nested message and enum types in `AnyReply`. /// Nested message and enum types in `AnyReply`.

View File

@ -411,31 +411,19 @@ pub mod simulation_server {
async fn schedule_event( async fn schedule_event(
&self, &self,
request: tonic::Request<super::ScheduleEventRequest>, request: tonic::Request<super::ScheduleEventRequest>,
) -> std::result::Result< ) -> std::result::Result<tonic::Response<super::ScheduleEventReply>, tonic::Status>;
tonic::Response<super::ScheduleEventReply>,
tonic::Status,
>;
async fn cancel_event( async fn cancel_event(
&self, &self,
request: tonic::Request<super::CancelEventRequest>, request: tonic::Request<super::CancelEventRequest>,
) -> std::result::Result< ) -> std::result::Result<tonic::Response<super::CancelEventReply>, tonic::Status>;
tonic::Response<super::CancelEventReply>,
tonic::Status,
>;
async fn process_event( async fn process_event(
&self, &self,
request: tonic::Request<super::ProcessEventRequest>, request: tonic::Request<super::ProcessEventRequest>,
) -> std::result::Result< ) -> std::result::Result<tonic::Response<super::ProcessEventReply>, tonic::Status>;
tonic::Response<super::ProcessEventReply>,
tonic::Status,
>;
async fn process_query( async fn process_query(
&self, &self,
request: tonic::Request<super::ProcessQueryRequest>, request: tonic::Request<super::ProcessQueryRequest>,
) -> std::result::Result< ) -> std::result::Result<tonic::Response<super::ProcessQueryReply>, tonic::Status>;
tonic::Response<super::ProcessQueryReply>,
tonic::Status,
>;
async fn read_events( async fn read_events(
&self, &self,
request: tonic::Request<super::ReadEventsRequest>, request: tonic::Request<super::ReadEventsRequest>,
@ -472,10 +460,7 @@ pub mod simulation_server {
max_encoding_message_size: None, max_encoding_message_size: None,
} }
} }
pub fn with_interceptor<F>( pub fn with_interceptor<F>(inner: T, interceptor: F) -> InterceptedService<Self, F>
inner: T,
interceptor: F,
) -> InterceptedService<Self, F>
where where
F: tonic::service::Interceptor, F: tonic::service::Interceptor,
{ {
@ -531,21 +516,15 @@ pub mod simulation_server {
"/simulation.Simulation/Init" => { "/simulation.Simulation/Init" => {
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
struct InitSvc<T: Simulation>(pub Arc<T>); struct InitSvc<T: Simulation>(pub Arc<T>);
impl<T: Simulation> tonic::server::UnaryService<super::InitRequest> impl<T: Simulation> tonic::server::UnaryService<super::InitRequest> for InitSvc<T> {
for InitSvc<T> {
type Response = super::InitReply; type Response = super::InitReply;
type Future = BoxFuture< type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
tonic::Response<Self::Response>,
tonic::Status,
>;
fn call( fn call(
&mut self, &mut self,
request: tonic::Request<super::InitRequest>, request: tonic::Request<super::InitRequest>,
) -> Self::Future { ) -> Self::Future {
let inner = Arc::clone(&self.0); let inner = Arc::clone(&self.0);
let fut = async move { let fut = async move { <T as Simulation>::init(&inner, request).await };
<T as Simulation>::init(&inner, request).await
};
Box::pin(fut) Box::pin(fut)
} }
} }
@ -575,21 +554,15 @@ pub mod simulation_server {
"/simulation.Simulation/Time" => { "/simulation.Simulation/Time" => {
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
struct TimeSvc<T: Simulation>(pub Arc<T>); struct TimeSvc<T: Simulation>(pub Arc<T>);
impl<T: Simulation> tonic::server::UnaryService<super::TimeRequest> impl<T: Simulation> tonic::server::UnaryService<super::TimeRequest> for TimeSvc<T> {
for TimeSvc<T> {
type Response = super::TimeReply; type Response = super::TimeReply;
type Future = BoxFuture< type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
tonic::Response<Self::Response>,
tonic::Status,
>;
fn call( fn call(
&mut self, &mut self,
request: tonic::Request<super::TimeRequest>, request: tonic::Request<super::TimeRequest>,
) -> Self::Future { ) -> Self::Future {
let inner = Arc::clone(&self.0); let inner = Arc::clone(&self.0);
let fut = async move { let fut = async move { <T as Simulation>::time(&inner, request).await };
<T as Simulation>::time(&inner, request).await
};
Box::pin(fut) Box::pin(fut)
} }
} }
@ -619,21 +592,15 @@ pub mod simulation_server {
"/simulation.Simulation/Step" => { "/simulation.Simulation/Step" => {
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
struct StepSvc<T: Simulation>(pub Arc<T>); struct StepSvc<T: Simulation>(pub Arc<T>);
impl<T: Simulation> tonic::server::UnaryService<super::StepRequest> impl<T: Simulation> tonic::server::UnaryService<super::StepRequest> for StepSvc<T> {
for StepSvc<T> {
type Response = super::StepReply; type Response = super::StepReply;
type Future = BoxFuture< type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
tonic::Response<Self::Response>,
tonic::Status,
>;
fn call( fn call(
&mut self, &mut self,
request: tonic::Request<super::StepRequest>, request: tonic::Request<super::StepRequest>,
) -> Self::Future { ) -> Self::Future {
let inner = Arc::clone(&self.0); let inner = Arc::clone(&self.0);
let fut = async move { let fut = async move { <T as Simulation>::step(&inner, request).await };
<T as Simulation>::step(&inner, request).await
};
Box::pin(fut) Box::pin(fut)
} }
} }
@ -663,23 +630,16 @@ pub mod simulation_server {
"/simulation.Simulation/StepUntil" => { "/simulation.Simulation/StepUntil" => {
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
struct StepUntilSvc<T: Simulation>(pub Arc<T>); struct StepUntilSvc<T: Simulation>(pub Arc<T>);
impl< impl<T: Simulation> tonic::server::UnaryService<super::StepUntilRequest> for StepUntilSvc<T> {
T: Simulation,
> tonic::server::UnaryService<super::StepUntilRequest>
for StepUntilSvc<T> {
type Response = super::StepUntilReply; type Response = super::StepUntilReply;
type Future = BoxFuture< type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
tonic::Response<Self::Response>,
tonic::Status,
>;
fn call( fn call(
&mut self, &mut self,
request: tonic::Request<super::StepUntilRequest>, request: tonic::Request<super::StepUntilRequest>,
) -> Self::Future { ) -> Self::Future {
let inner = Arc::clone(&self.0); let inner = Arc::clone(&self.0);
let fut = async move { let fut =
<T as Simulation>::step_until(&inner, request).await async move { <T as Simulation>::step_until(&inner, request).await };
};
Box::pin(fut) Box::pin(fut)
} }
} }
@ -709,15 +669,11 @@ pub mod simulation_server {
"/simulation.Simulation/ScheduleEvent" => { "/simulation.Simulation/ScheduleEvent" => {
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
struct ScheduleEventSvc<T: Simulation>(pub Arc<T>); struct ScheduleEventSvc<T: Simulation>(pub Arc<T>);
impl< impl<T: Simulation> tonic::server::UnaryService<super::ScheduleEventRequest>
T: Simulation, for ScheduleEventSvc<T>
> tonic::server::UnaryService<super::ScheduleEventRequest> {
for ScheduleEventSvc<T> {
type Response = super::ScheduleEventReply; type Response = super::ScheduleEventReply;
type Future = BoxFuture< type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
tonic::Response<Self::Response>,
tonic::Status,
>;
fn call( fn call(
&mut self, &mut self,
request: tonic::Request<super::ScheduleEventRequest>, request: tonic::Request<super::ScheduleEventRequest>,
@ -755,15 +711,9 @@ pub mod simulation_server {
"/simulation.Simulation/CancelEvent" => { "/simulation.Simulation/CancelEvent" => {
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
struct CancelEventSvc<T: Simulation>(pub Arc<T>); struct CancelEventSvc<T: Simulation>(pub Arc<T>);
impl< impl<T: Simulation> tonic::server::UnaryService<super::CancelEventRequest> for CancelEventSvc<T> {
T: Simulation,
> tonic::server::UnaryService<super::CancelEventRequest>
for CancelEventSvc<T> {
type Response = super::CancelEventReply; type Response = super::CancelEventReply;
type Future = BoxFuture< type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
tonic::Response<Self::Response>,
tonic::Status,
>;
fn call( fn call(
&mut self, &mut self,
request: tonic::Request<super::CancelEventRequest>, request: tonic::Request<super::CancelEventRequest>,
@ -801,15 +751,9 @@ pub mod simulation_server {
"/simulation.Simulation/ProcessEvent" => { "/simulation.Simulation/ProcessEvent" => {
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
struct ProcessEventSvc<T: Simulation>(pub Arc<T>); struct ProcessEventSvc<T: Simulation>(pub Arc<T>);
impl< impl<T: Simulation> tonic::server::UnaryService<super::ProcessEventRequest> for ProcessEventSvc<T> {
T: Simulation,
> tonic::server::UnaryService<super::ProcessEventRequest>
for ProcessEventSvc<T> {
type Response = super::ProcessEventReply; type Response = super::ProcessEventReply;
type Future = BoxFuture< type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
tonic::Response<Self::Response>,
tonic::Status,
>;
fn call( fn call(
&mut self, &mut self,
request: tonic::Request<super::ProcessEventRequest>, request: tonic::Request<super::ProcessEventRequest>,
@ -847,15 +791,9 @@ pub mod simulation_server {
"/simulation.Simulation/ProcessQuery" => { "/simulation.Simulation/ProcessQuery" => {
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
struct ProcessQuerySvc<T: Simulation>(pub Arc<T>); struct ProcessQuerySvc<T: Simulation>(pub Arc<T>);
impl< impl<T: Simulation> tonic::server::UnaryService<super::ProcessQueryRequest> for ProcessQuerySvc<T> {
T: Simulation,
> tonic::server::UnaryService<super::ProcessQueryRequest>
for ProcessQuerySvc<T> {
type Response = super::ProcessQueryReply; type Response = super::ProcessQueryReply;
type Future = BoxFuture< type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
tonic::Response<Self::Response>,
tonic::Status,
>;
fn call( fn call(
&mut self, &mut self,
request: tonic::Request<super::ProcessQueryRequest>, request: tonic::Request<super::ProcessQueryRequest>,
@ -893,15 +831,9 @@ pub mod simulation_server {
"/simulation.Simulation/ReadEvents" => { "/simulation.Simulation/ReadEvents" => {
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
struct ReadEventsSvc<T: Simulation>(pub Arc<T>); struct ReadEventsSvc<T: Simulation>(pub Arc<T>);
impl< impl<T: Simulation> tonic::server::UnaryService<super::ReadEventsRequest> for ReadEventsSvc<T> {
T: Simulation,
> tonic::server::UnaryService<super::ReadEventsRequest>
for ReadEventsSvc<T> {
type Response = super::ReadEventsReply; type Response = super::ReadEventsReply;
type Future = BoxFuture< type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
tonic::Response<Self::Response>,
tonic::Status,
>;
fn call( fn call(
&mut self, &mut self,
request: tonic::Request<super::ReadEventsRequest>, request: tonic::Request<super::ReadEventsRequest>,
@ -939,23 +871,16 @@ pub mod simulation_server {
"/simulation.Simulation/OpenSink" => { "/simulation.Simulation/OpenSink" => {
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
struct OpenSinkSvc<T: Simulation>(pub Arc<T>); struct OpenSinkSvc<T: Simulation>(pub Arc<T>);
impl< impl<T: Simulation> tonic::server::UnaryService<super::OpenSinkRequest> for OpenSinkSvc<T> {
T: Simulation,
> tonic::server::UnaryService<super::OpenSinkRequest>
for OpenSinkSvc<T> {
type Response = super::OpenSinkReply; type Response = super::OpenSinkReply;
type Future = BoxFuture< type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
tonic::Response<Self::Response>,
tonic::Status,
>;
fn call( fn call(
&mut self, &mut self,
request: tonic::Request<super::OpenSinkRequest>, request: tonic::Request<super::OpenSinkRequest>,
) -> Self::Future { ) -> Self::Future {
let inner = Arc::clone(&self.0); let inner = Arc::clone(&self.0);
let fut = async move { let fut =
<T as Simulation>::open_sink(&inner, request).await async move { <T as Simulation>::open_sink(&inner, request).await };
};
Box::pin(fut) Box::pin(fut)
} }
} }
@ -985,23 +910,16 @@ pub mod simulation_server {
"/simulation.Simulation/CloseSink" => { "/simulation.Simulation/CloseSink" => {
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
struct CloseSinkSvc<T: Simulation>(pub Arc<T>); struct CloseSinkSvc<T: Simulation>(pub Arc<T>);
impl< impl<T: Simulation> tonic::server::UnaryService<super::CloseSinkRequest> for CloseSinkSvc<T> {
T: Simulation,
> tonic::server::UnaryService<super::CloseSinkRequest>
for CloseSinkSvc<T> {
type Response = super::CloseSinkReply; type Response = super::CloseSinkReply;
type Future = BoxFuture< type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
tonic::Response<Self::Response>,
tonic::Status,
>;
fn call( fn call(
&mut self, &mut self,
request: tonic::Request<super::CloseSinkRequest>, request: tonic::Request<super::CloseSinkRequest>,
) -> Self::Future { ) -> Self::Future {
let inner = Arc::clone(&self.0); let inner = Arc::clone(&self.0);
let fut = async move { let fut =
<T as Simulation>::close_sink(&inner, request).await async move { <T as Simulation>::close_sink(&inner, request).await };
};
Box::pin(fut) Box::pin(fut)
} }
} }
@ -1028,18 +946,14 @@ pub mod simulation_server {
}; };
Box::pin(fut) Box::pin(fut)
} }
_ => { _ => Box::pin(async move {
Box::pin(async move { Ok(http::Response::builder()
Ok( .status(200)
http::Response::builder() .header("grpc-status", "12")
.status(200) .header("content-type", "application/grpc")
.header("grpc-status", "12") .body(empty_body())
.header("content-type", "application/grpc") .unwrap())
.body(empty_body()) }),
.unwrap(),
)
})
}
} }
} }
} }

View File

@ -98,9 +98,9 @@
//! connects or disconnects a port, such as: //! connects or disconnects a port, such as:
//! //!
//! ``` //! ```
//! # use asynchronix::model::Model; //! # use asynchronix::model::{Context, Model};
//! # use asynchronix::ports::Output; //! # use asynchronix::ports::Output;
//! # use asynchronix::time::{MonotonicTime, Scheduler}; //! # use asynchronix::time::MonotonicTime;
//! # use asynchronix::simulation::{Mailbox, SimInit}; //! # use asynchronix::simulation::{Mailbox, SimInit};
//! # pub struct ModelA { //! # pub struct ModelA {
//! # pub output: Output<i32>, //! # pub output: Output<i32>,
@ -123,9 +123,16 @@
//! ); //! );
//! ``` //! ```
mod mailbox; mod mailbox;
mod scheduler;
mod sim_init; mod sim_init;
pub use mailbox::{Address, Mailbox}; pub use mailbox::{Address, Mailbox};
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, Deadline, SchedulingError};
pub use sim_init::SimInit; pub use sim_init::SimInit;
use std::error::Error; use std::error::Error;
@ -137,15 +144,12 @@ use std::time::Duration;
use recycle_box::{coerce_box, RecycleBox}; use recycle_box::{coerce_box, RecycleBox};
use crate::executor::Executor; use crate::executor::Executor;
use crate::model::Model; use crate::model::{Context, Model, SetupContext};
use crate::ports::{InputFn, ReplierFn}; use crate::ports::{InputFn, ReplierFn};
use crate::time::{ use crate::time::{Clock, MonotonicTime, TearableAtomicTime};
self, Action, ActionKey, Clock, Deadline, MonotonicTime, SchedulerQueue, SchedulingError,
TearableAtomicTime,
};
use crate::util::seq_futures::SeqFuture; use crate::util::seq_futures::SeqFuture;
use crate::util::slot; use crate::util::slot;
use crate::util::sync_cell::SyncCell; use crate::util::sync_cell::{SyncCell, SyncCellReader};
/// Simulation environment. /// Simulation environment.
/// ///
@ -157,9 +161,9 @@ use crate::util::sync_cell::SyncCell;
/// A [`Simulation`] object also manages an event scheduling queue and /// A [`Simulation`] object also manages an event scheduling queue and
/// simulation time. The scheduling queue can be accessed from the simulation /// simulation time. The scheduling queue can be accessed from the simulation
/// itself, but also from models via the optional /// itself, but also from models via the optional
/// [`&Scheduler`](time::Scheduler) argument of input and replier port methods. /// [`&Context`](crate::model::Context) argument of input and replier port methods.
/// Likewise, simulation time can be accessed with the [`Simulation::time()`] /// Likewise, simulation time can be accessed with the [`Simulation::time()`]
/// method, or from models with the [`Scheduler::time()`](time::Scheduler::time) /// method, or from models with the [`Context::time()`](crate::model::Context::time)
/// method. /// method.
/// ///
/// Events and queries can be scheduled immediately, *i.e.* for the current /// Events and queries can be scheduled immediately, *i.e.* for the current
@ -177,7 +181,7 @@ use crate::util::sync_cell::SyncCell;
/// ///
/// 1. increment simulation time until that of the next scheduled event in /// 1. increment simulation time until that of the next scheduled event in
/// chronological order, then /// chronological order, then
/// 2. call [`Clock::synchronize()`](time::Clock::synchronize) which, unless the /// 2. call [`Clock::synchronize()`](crate::time::Clock::synchronize) which, unless the
/// simulation is configured to run as fast as possible, blocks until the /// simulation is configured to run as fast as possible, blocks until the
/// desired wall clock time, and finally /// desired wall clock time, and finally
/// 3. run all computations scheduled for the new simulation time. /// 3. run all computations scheduled for the new simulation time.
@ -217,7 +221,7 @@ impl Simulation {
/// that event as well as all other event scheduled for the same time. /// that event as well as all other event scheduled for the same time.
/// ///
/// Processing is gated by a (possibly blocking) call to /// Processing is gated by a (possibly blocking) call to
/// [`Clock::synchronize()`](time::Clock::synchronize) on the configured /// [`Clock::synchronize()`](crate::time::Clock::synchronize) on the configured
/// simulation clock. This method blocks until all newly processed events /// simulation clock. This method blocks until all newly processed events
/// have completed. /// have completed.
pub fn step(&mut self) { pub fn step(&mut self) {
@ -292,7 +296,7 @@ impl Simulation {
/// Events scheduled for the same time and targeting the same model are /// Events scheduled for the same time and targeting the same model are
/// guaranteed to be processed according to the scheduling order. /// guaranteed to be processed according to the scheduling order.
/// ///
/// See also: [`time::Scheduler::schedule_event`]. /// See also: [`Context::schedule_event`](crate::model::Context::schedule_event).
pub fn schedule_event<M, F, T, S>( pub fn schedule_event<M, F, T, S>(
&mut self, &mut self,
deadline: impl Deadline, deadline: impl Deadline,
@ -311,8 +315,7 @@ impl Simulation {
if now >= time { if now >= time {
return Err(SchedulingError::InvalidScheduledTime); return Err(SchedulingError::InvalidScheduledTime);
} }
schedule_event_at_unchecked(time, func, arg, address.into().0, &self.scheduler_queue);
time::schedule_event_at_unchecked(time, func, arg, address.into().0, &self.scheduler_queue);
Ok(()) Ok(())
} }
@ -325,7 +328,7 @@ impl Simulation {
/// Events scheduled for the same time and targeting the same model are /// Events scheduled for the same time and targeting the same model are
/// guaranteed to be processed according to the scheduling order. /// guaranteed to be processed according to the scheduling order.
/// ///
/// See also: [`time::Scheduler::schedule_keyed_event`]. /// See also: [`Context::schedule_keyed_event`](crate::model::Context::schedule_keyed_event).
pub fn schedule_keyed_event<M, F, T, S>( pub fn schedule_keyed_event<M, F, T, S>(
&mut self, &mut self,
deadline: impl Deadline, deadline: impl Deadline,
@ -344,7 +347,7 @@ impl Simulation {
if now >= time { if now >= time {
return Err(SchedulingError::InvalidScheduledTime); return Err(SchedulingError::InvalidScheduledTime);
} }
let event_key = time::schedule_keyed_event_at_unchecked( let event_key = schedule_keyed_event_at_unchecked(
time, time,
func, func,
arg, arg,
@ -363,7 +366,7 @@ impl Simulation {
/// Events scheduled for the same time and targeting the same model are /// Events scheduled for the same time and targeting the same model are
/// guaranteed to be processed according to the scheduling order. /// guaranteed to be processed according to the scheduling order.
/// ///
/// See also: [`time::Scheduler::schedule_periodic_event`]. /// See also: [`Context::schedule_periodic_event`](crate::model::Context::schedule_periodic_event).
pub fn schedule_periodic_event<M, F, T, S>( pub fn schedule_periodic_event<M, F, T, S>(
&mut self, &mut self,
deadline: impl Deadline, deadline: impl Deadline,
@ -386,7 +389,7 @@ impl Simulation {
if period.is_zero() { if period.is_zero() {
return Err(SchedulingError::NullRepetitionPeriod); return Err(SchedulingError::NullRepetitionPeriod);
} }
time::schedule_periodic_event_at_unchecked( schedule_periodic_event_at_unchecked(
time, time,
period, period,
func, func,
@ -407,7 +410,7 @@ impl Simulation {
/// Events scheduled for the same time and targeting the same model are /// Events scheduled for the same time and targeting the same model are
/// guaranteed to be processed according to the scheduling order. /// guaranteed to be processed according to the scheduling order.
/// ///
/// See also: [`time::Scheduler::schedule_keyed_periodic_event`]. /// See also: [`Context::schedule_keyed_periodic_event`](crate::model::Context::schedule_keyed_periodic_event).
pub fn schedule_keyed_periodic_event<M, F, T, S>( pub fn schedule_keyed_periodic_event<M, F, T, S>(
&mut self, &mut self,
deadline: impl Deadline, deadline: impl Deadline,
@ -430,7 +433,7 @@ impl Simulation {
if period.is_zero() { if period.is_zero() {
return Err(SchedulingError::NullRepetitionPeriod); return Err(SchedulingError::NullRepetitionPeriod);
} }
let event_key = time::schedule_periodic_keyed_event_at_unchecked( let event_key = schedule_periodic_keyed_event_at_unchecked(
time, time,
period, period,
func, func,
@ -658,3 +661,25 @@ impl fmt::Display for QueryError {
} }
impl Error for QueryError {} impl Error for QueryError {}
/// Adds a model and its mailbox to the simulation bench.
pub(crate) fn add_model<M: Model>(
mut model: M,
mailbox: Mailbox<M>,
scheduler_queue: Arc<Mutex<SchedulerQueue>>,
time: SyncCellReader<TearableAtomicTime>,
executor: &Executor,
) {
let sender = mailbox.0.sender();
let context = Context::new(sender, scheduler_queue, time);
let setup_context = SetupContext::new(&mailbox, &context, executor);
model.setup(&setup_context);
let mut receiver = mailbox.0;
executor.spawn_and_forget(async move {
let mut model = model.init(&context).await.0;
while receiver.recv(&mut model, &context).await.is_ok() {}
});
}

View File

@ -17,9 +17,8 @@ use crate::channel::Sender;
use crate::executor::Executor; use crate::executor::Executor;
use crate::model::Model; use crate::model::Model;
use crate::ports::InputFn; use crate::ports::InputFn;
use crate::time::{MonotonicTime, TearableAtomicTime}; use crate::time::MonotonicTime;
use crate::util::priority_queue::PriorityQueue; use crate::util::priority_queue::PriorityQueue;
use crate::util::sync_cell::SyncCellReader;
/// Shorthand for the scheduler queue type. /// Shorthand for the scheduler queue type.
@ -55,387 +54,6 @@ impl Deadline for MonotonicTime {
} }
} }
/// A local scheduler for models.
///
/// A `Scheduler` is a handle to the global scheduler 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,
/// scheduler: &'a Scheduler<Self>
/// ) -> impl Future<Output=()> + 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::Model;
/// use asynchronix::ports::Output;
/// use asynchronix::time::Scheduler;
///
/// #[derive(Default)]
/// pub struct DelayedGreeter {
/// msg_out: Output<String>,
/// }
///
/// impl DelayedGreeter {
/// // Triggers a greeting on the output port after some delay [input port].
/// pub async fn greet_with_delay(&mut self, delay: Duration, scheduler: &Scheduler<Self>) {
/// let time = scheduler.time();
/// let greeting = format!("Hello, this message was scheduled at: {:?}.", time);
///
/// if delay.is_zero() {
/// self.msg_out.send(greeting).await;
/// } else {
/// scheduler.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 Scheduler<M: Model> {
sender: Sender<M>,
scheduler_queue: Arc<Mutex<SchedulerQueue>>,
time: SyncCellReader<TearableAtomicTime>,
}
impl<M: Model> Scheduler<M> {
/// Creates a new local scheduler.
pub(crate) fn new(
sender: Sender<M>,
scheduler_queue: Arc<Mutex<SchedulerQueue>>,
time: SyncCellReader<TearableAtomicTime>,
) -> Self {
Self {
sender,
scheduler_queue,
time,
}
}
/// Returns the current simulation time.
///
/// # Examples
///
/// ```
/// use asynchronix::model::Model;
/// use asynchronix::time::{MonotonicTime, Scheduler};
///
/// fn is_third_millenium<M: Model>(scheduler: &Scheduler<M>) -> 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 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::Model;
/// use asynchronix::time::Scheduler;
///
/// // A timer.
/// pub struct Timer {}
///
/// impl Timer {
/// // Sets an alarm [input port].
/// pub fn set(&mut self, setting: Duration, scheduler: &Scheduler<Self>) {
/// if 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,
{
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::Model;
/// use asynchronix::time::{ActionKey, MonotonicTime, Scheduler};
///
/// // 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, scheduler: &Scheduler<Self>) {
/// self.cancel();
/// match 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 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::Model;
/// use asynchronix::time::{MonotonicTime, Scheduler};
///
/// // An alarm clock beeping at 1Hz.
/// pub struct BeepingAlarmClock {}
///
/// impl BeepingAlarmClock {
/// // Sets an alarm [input port].
/// pub fn set(&mut self, setting: MonotonicTime, scheduler: &Scheduler<Self>) {
/// if 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,
{
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::Model;
/// use asynchronix::time::{ActionKey, MonotonicTime, Scheduler};
///
/// // 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, scheduler: &Scheduler<Self>) {
/// self.cancel();
/// match 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 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 Scheduler<M> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Scheduler").finish_non_exhaustive()
}
}
/// Handle to a scheduled action. /// Handle to a scheduled action.
/// ///
/// An `ActionKey` can be used to cancel a scheduled action. /// An `ActionKey` can be used to cancel a scheduled action.
@ -468,7 +86,7 @@ impl PartialEq for ActionKey {
/// Implements equality by considering clones to be equivalent, rather than /// Implements equality by considering clones to be equivalent, rather than
/// keys with the same `is_cancelled` value. /// keys with the same `is_cancelled` value.
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
ptr::addr_eq(&*self.is_cancelled, &*other.is_cancelled) ptr::eq(&*self.is_cancelled, &*other.is_cancelled)
} }
} }

View File

@ -3,12 +3,12 @@ use std::sync::{Arc, Mutex};
use crate::executor::Executor; use crate::executor::Executor;
use crate::model::Model; use crate::model::Model;
use crate::time::{Clock, NoClock, Scheduler}; use crate::time::{Clock, NoClock};
use crate::time::{MonotonicTime, SchedulerQueue, TearableAtomicTime}; use crate::time::{MonotonicTime, TearableAtomicTime};
use crate::util::priority_queue::PriorityQueue; use crate::util::priority_queue::PriorityQueue;
use crate::util::sync_cell::SyncCell; use crate::util::sync_cell::SyncCell;
use super::{Mailbox, Simulation}; use super::{add_model, Mailbox, SchedulerQueue, Simulation};
/// Builder for a multi-threaded, discrete-event simulation. /// Builder for a multi-threaded, discrete-event simulation.
pub struct SimInit { pub struct SimInit {
@ -44,15 +44,8 @@ impl SimInit {
pub fn add_model<M: Model>(self, model: M, mailbox: Mailbox<M>) -> Self { pub fn add_model<M: Model>(self, model: M, mailbox: Mailbox<M>) -> Self {
let scheduler_queue = self.scheduler_queue.clone(); let scheduler_queue = self.scheduler_queue.clone();
let time = self.time.reader(); let time = self.time.reader();
let mut receiver = mailbox.0;
self.executor.spawn_and_forget(async move { add_model(model, mailbox, scheduler_queue, time, &self.executor);
let sender = receiver.sender();
let scheduler = Scheduler::new(sender, scheduler_queue, time);
let mut model = model.init(&scheduler).await.0;
while receiver.recv(&mut model, &scheduler).await.is_ok() {}
});
self self
} }

View File

@ -4,9 +4,7 @@
//! //!
//! * [`MonotonicTime`]: a monotonic timestamp based on the [TAI] time standard, //! * [`MonotonicTime`]: a monotonic timestamp based on the [TAI] time standard,
//! * [`Clock`]: a trait for types that can synchronize a simulation, //! * [`Clock`]: a trait for types that can synchronize a simulation,
//! implemented for instance by [`SystemClock`] and [`AutoSystemClock`], //! implemented for instance by [`SystemClock`] and [`AutoSystemClock`].
//! * [`Scheduler`]: a model-local handle to the global scheduler that can be
//! used by models to schedule future actions onto themselves.
//! //!
//! [TAI]: https://en.wikipedia.org/wiki/International_Atomic_Time //! [TAI]: https://en.wikipedia.org/wiki/International_Atomic_Time
//! //!
@ -17,8 +15,8 @@
//! the specified timestamp. //! the specified timestamp.
//! //!
//! ``` //! ```
//! use asynchronix::model::Model; //! use asynchronix::model::{Context, Model};
//! use asynchronix::time::{MonotonicTime, Scheduler}; //! use asynchronix::time::MonotonicTime;
//! //!
//! // An alarm clock model. //! // An alarm clock model.
//! pub struct AlarmClock { //! pub struct AlarmClock {
@ -32,8 +30,8 @@
//! } //! }
//! //!
//! // Sets an alarm [input port]. //! // Sets an alarm [input port].
//! pub fn set(&mut self, setting: MonotonicTime, scheduler: &Scheduler<Self>) { //! pub fn set(&mut self, setting: MonotonicTime, context: &Context<Self>) {
//! if scheduler.schedule_event(setting, Self::ring, ()).is_err() { //! if context.schedule_event(setting, Self::ring, ()).is_err() {
//! println!("The alarm clock can only be set for a future time"); //! println!("The alarm clock can only be set for a future time");
//! } //! }
//! } //! }
@ -49,15 +47,8 @@
mod clock; mod clock;
mod monotonic_time; mod monotonic_time;
mod scheduler;
pub use tai_time::MonotonicTime; pub use tai_time::MonotonicTime;
pub use clock::{AutoSystemClock, Clock, NoClock, SyncStatus, SystemClock}; pub use clock::{AutoSystemClock, Clock, NoClock, SyncStatus, SystemClock};
pub(crate) use monotonic_time::TearableAtomicTime; pub(crate) use monotonic_time::TearableAtomicTime;
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, Deadline, Scheduler, SchedulingError};

View File

@ -143,7 +143,6 @@ impl<T: TearableAtomic> SyncCell<T> {
/// A handle to a `SyncCell` that enables synchronized reads from multiple /// A handle to a `SyncCell` that enables synchronized reads from multiple
/// threads. /// threads.
#[derive(Clone)]
pub(crate) struct SyncCellReader<T: TearableAtomic> { pub(crate) struct SyncCellReader<T: TearableAtomic> {
inner: Arc<Inner<T>>, inner: Arc<Inner<T>>,
} }
@ -186,6 +185,14 @@ impl<T: TearableAtomic> SyncCellReader<T> {
} }
} }
impl<T: TearableAtomic> Clone for SyncCellReader<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
/// An error returned when attempting to perform a read operation concurrently /// An error returned when attempting to perform a read operation concurrently
/// with a write operation. /// with a write operation.
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]

View File

@ -2,10 +2,10 @@
use std::time::Duration; use std::time::Duration;
use asynchronix::model::Model; use asynchronix::model::{Context, Model};
use asynchronix::ports::{EventBuffer, Output}; use asynchronix::ports::{EventBuffer, Output};
use asynchronix::simulation::{Mailbox, SimInit}; use asynchronix::simulation::{ActionKey, Mailbox, SimInit};
use asynchronix::time::{ActionKey, MonotonicTime, Scheduler}; use asynchronix::time::MonotonicTime;
#[test] #[test]
fn model_schedule_event() { fn model_schedule_event() {
@ -14,9 +14,9 @@ fn model_schedule_event() {
output: Output<()>, output: Output<()>,
} }
impl TestModel { impl TestModel {
fn trigger(&mut self, _: (), scheduler: &Scheduler<Self>) { fn trigger(&mut self, _: (), context: &Context<Self>) {
scheduler context
.schedule_event(scheduler.time() + Duration::from_secs(2), Self::action, ()) .schedule_event(context.time() + Duration::from_secs(2), Self::action, ())
.unwrap(); .unwrap();
} }
async fn action(&mut self) { async fn action(&mut self) {
@ -51,12 +51,12 @@ fn model_cancel_future_keyed_event() {
key: Option<ActionKey>, key: Option<ActionKey>,
} }
impl TestModel { impl TestModel {
fn trigger(&mut self, _: (), scheduler: &Scheduler<Self>) { fn trigger(&mut self, _: (), context: &Context<Self>) {
scheduler context
.schedule_event(scheduler.time() + Duration::from_secs(1), Self::action1, ()) .schedule_event(context.time() + Duration::from_secs(1), Self::action1, ())
.unwrap(); .unwrap();
self.key = scheduler self.key = context
.schedule_keyed_event(scheduler.time() + Duration::from_secs(2), Self::action2, ()) .schedule_keyed_event(context.time() + Duration::from_secs(2), Self::action2, ())
.ok(); .ok();
} }
async fn action1(&mut self) { async fn action1(&mut self) {
@ -97,12 +97,12 @@ fn model_cancel_same_time_keyed_event() {
key: Option<ActionKey>, key: Option<ActionKey>,
} }
impl TestModel { impl TestModel {
fn trigger(&mut self, _: (), scheduler: &Scheduler<Self>) { fn trigger(&mut self, _: (), context: &Context<Self>) {
scheduler context
.schedule_event(scheduler.time() + Duration::from_secs(2), Self::action1, ()) .schedule_event(context.time() + Duration::from_secs(2), Self::action1, ())
.unwrap(); .unwrap();
self.key = scheduler self.key = context
.schedule_keyed_event(scheduler.time() + Duration::from_secs(2), Self::action2, ()) .schedule_keyed_event(context.time() + Duration::from_secs(2), Self::action2, ())
.ok(); .ok();
} }
async fn action1(&mut self) { async fn action1(&mut self) {
@ -142,10 +142,10 @@ fn model_schedule_periodic_event() {
output: Output<i32>, output: Output<i32>,
} }
impl TestModel { impl TestModel {
fn trigger(&mut self, _: (), scheduler: &Scheduler<Self>) { fn trigger(&mut self, _: (), context: &Context<Self>) {
scheduler context
.schedule_periodic_event( .schedule_periodic_event(
scheduler.time() + Duration::from_secs(2), context.time() + Duration::from_secs(2),
Duration::from_secs(3), Duration::from_secs(3),
Self::action, Self::action,
42, 42,
@ -190,10 +190,10 @@ fn model_cancel_periodic_event() {
key: Option<ActionKey>, key: Option<ActionKey>,
} }
impl TestModel { impl TestModel {
fn trigger(&mut self, _: (), scheduler: &Scheduler<Self>) { fn trigger(&mut self, _: (), context: &Context<Self>) {
self.key = scheduler self.key = context
.schedule_keyed_periodic_event( .schedule_keyed_periodic_event(
scheduler.time() + Duration::from_secs(2), context.time() + Duration::from_secs(2),
Duration::from_secs(3), Duration::from_secs(3),
Self::action, Self::action,
(), (),

View File

@ -2,6 +2,8 @@
use std::time::Duration; use std::time::Duration;
#[cfg(not(miri))]
use asynchronix::model::Context;
use asynchronix::model::Model; use asynchronix::model::Model;
use asynchronix::ports::{EventBuffer, Output}; use asynchronix::ports::{EventBuffer, Output};
use asynchronix::simulation::{Address, Mailbox, SimInit, Simulation}; use asynchronix::simulation::{Address, Mailbox, SimInit, Simulation};
@ -219,21 +221,9 @@ impl TimestampModel {
} }
#[cfg(not(miri))] #[cfg(not(miri))]
impl Model for TimestampModel { impl Model for TimestampModel {
fn init( async fn init(mut self, _: &Context<Self>) -> asynchronix::model::InitializedModel<Self> {
mut self, self.stamp.send((Instant::now(), SystemTime::now())).await;
_scheduler: &asynchronix::time::Scheduler<Self>, self.into()
) -> std::pin::Pin<
Box<
dyn futures_util::Future<Output = asynchronix::model::InitializedModel<Self>>
+ Send
+ '_,
>,
> {
Box::pin(async {
self.stamp.send((Instant::now(), SystemTime::now())).await;
self.into()
})
} }
} }
@ -267,7 +257,7 @@ fn timestamp_bench(
#[test] #[test]
fn simulation_system_clock_from_instant() { fn simulation_system_clock_from_instant() {
let t0 = MonotonicTime::EPOCH; let t0 = MonotonicTime::EPOCH;
const TOLERANCE: f64 = 0.0005; // [s] const TOLERANCE: f64 = 0.005; // [s]
// The reference simulation time is set in the past of t0 so that the // The reference simulation time is set in the past of t0 so that the
// simulation starts in the future when the reference wall clock time is // simulation starts in the future when the reference wall clock time is