forked from ROMEO/nexosim
Merge pull request #1 from asynchronics/feature-submodels
Add setup step
This commit is contained in:
commit
97b173a081
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -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
|
||||||
|
@ -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"
|
||||||
|
@ -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,11 +300,7 @@ 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,
|
|
||||||
_scheduler: &Scheduler<Self>,
|
|
||||||
) -> Pin<Box<dyn Future<Output = InitializedModel<Self>> + Send + '_>> {
|
|
||||||
Box::pin(async move {
|
|
||||||
self.water_sense
|
self.water_sense
|
||||||
.send(if self.volume == 0.0 {
|
.send(if self.volume == 0.0 {
|
||||||
WaterSenseState::Empty
|
WaterSenseState::Empty
|
||||||
@ -316,7 +310,6 @@ impl Model for Tank {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
self.into()
|
self.into()
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
|
||||||
_scheduler: &Scheduler<Self>,
|
|
||||||
) -> Pin<Box<dyn Future<Output = InitializedModel<Self>> + Send + '_>> {
|
|
||||||
Box::pin(async move {
|
|
||||||
self.position.send(self.pos).await;
|
self.position.send(self.pos).await;
|
||||||
|
|
||||||
self.into()
|
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();
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
//! setup_context: &SetupContext<Self>) {
|
||||||
|
//! println!("...setup...");
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! async fn init(
|
||||||
//! mut self,
|
//! mut 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()
|
||||||
//! })
|
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
@ -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()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
485
asynchronix/src/model/context.rs
Normal file
485
asynchronix/src/model/context.rs
Normal 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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 {}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -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`.
|
||||||
|
@ -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(
|
|
||||||
http::Response::builder()
|
|
||||||
.status(200)
|
.status(200)
|
||||||
.header("grpc-status", "12")
|
.header("grpc-status", "12")
|
||||||
.header("content-type", "application/grpc")
|
.header("content-type", "application/grpc")
|
||||||
.body(empty_body())
|
.body(empty_body())
|
||||||
.unwrap(),
|
.unwrap())
|
||||||
)
|
}),
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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() {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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};
|
|
||||||
|
@ -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)]
|
||||||
|
@ -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,
|
||||||
(),
|
(),
|
||||||
|
@ -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,
|
|
||||||
_scheduler: &asynchronix::time::Scheduler<Self>,
|
|
||||||
) -> 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.stamp.send((Instant::now(), SystemTime::now())).await;
|
||||||
|
|
||||||
self.into()
|
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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user