forked from ROMEO/nexosim
Merge step_by and step_until into a unique method
Now that `step_by` returns an error anyway (it was unfaillible before), there is no more incentive to keep it as a separate method. The `step_until` method now accepts an `impl Deadline`, which covers both cases (`Duration` and `MonotonicTime`).
This commit is contained in:
parent
95aac7721c
commit
b5aea810ae
@ -230,7 +230,7 @@ fn main() -> Result<(), SimulationError> {
|
||||
);
|
||||
|
||||
// All data processed.
|
||||
simu.step_by(Duration::from_millis(101))?;
|
||||
simu.step_until(Duration::from_millis(101))?;
|
||||
expect(
|
||||
&mut mode,
|
||||
Some(ModeId::Idle),
|
||||
@ -266,7 +266,7 @@ fn main() -> Result<(), SimulationError> {
|
||||
);
|
||||
|
||||
// Wait for short processing to finish, check results.
|
||||
simu.step_by(Duration::from_millis(11))?;
|
||||
simu.step_until(Duration::from_millis(11))?;
|
||||
expect(
|
||||
&mut mode,
|
||||
Some(ModeId::Idle),
|
||||
@ -279,7 +279,7 @@ fn main() -> Result<(), SimulationError> {
|
||||
|
||||
// Wait long enough, no state change as the long processing has been
|
||||
// cancelled.
|
||||
simu.step_by(Duration::from_millis(100))?;
|
||||
simu.step_until(Duration::from_millis(100))?;
|
||||
assert_eq!(mode.next(), None);
|
||||
assert_eq!(value.next(), None);
|
||||
assert_eq!(hk.next(), None);
|
||||
|
@ -175,7 +175,7 @@ fn main() -> Result<(), SimulationError> {
|
||||
|
||||
// Advance simulation time by 0.9s, which with a 10Hz PPS should correspond to
|
||||
// 9 position increments.
|
||||
simu.step_by(Duration::new(0, 900_000_000))?;
|
||||
simu.step_until(Duration::new(0, 900_000_000))?;
|
||||
t += Duration::new(0, 900_000_000);
|
||||
assert_eq!(simu.time(), t);
|
||||
for _ in 0..9 {
|
||||
|
@ -236,7 +236,7 @@ fn main() -> Result<(), SimulationError> {
|
||||
});
|
||||
|
||||
// Advance simulation, external messages will be collected.
|
||||
simu.step_by(Duration::from_secs(2))?;
|
||||
simu.step_until(Duration::from_secs(2))?;
|
||||
|
||||
// Shut down the server.
|
||||
let socket = sender_handle.join().unwrap();
|
||||
|
@ -251,7 +251,7 @@ fn main() -> Result<(), asynchronix::simulation::SimulationError> {
|
||||
|
||||
// Advance simulation time by 0.9s, which with a 10Hz PPS should correspond to
|
||||
// 9 position increments.
|
||||
simu.step_by(Duration::new(0, 900_000_000))?;
|
||||
simu.step_until(Duration::new(0, 900_000_000))?;
|
||||
t += Duration::new(0, 900_000_000);
|
||||
assert_eq!(simu.time(), t);
|
||||
for _ in 0..9 {
|
||||
@ -290,7 +290,7 @@ fn main() -> Result<(), asynchronix::simulation::SimulationError> {
|
||||
|
||||
// Advance simulation time by 0.7s, which with a 10Hz PPS should correspond to
|
||||
// 7 position increments.
|
||||
simu.step_by(Duration::new(0, 700_000_000))?;
|
||||
simu.step_until(Duration::new(0, 700_000_000))?;
|
||||
t += Duration::new(0, 700_000_000);
|
||||
assert_eq!(simu.time(), t);
|
||||
for _ in 0..7 {
|
||||
@ -310,7 +310,7 @@ fn main() -> Result<(), asynchronix::simulation::SimulationError> {
|
||||
|
||||
// Advance simulation time by 1.9s, which with a -10Hz PPS should correspond
|
||||
// to 19 position decrements.
|
||||
simu.step_by(Duration::new(1, 900_000_000))?;
|
||||
simu.step_until(Duration::new(1, 900_000_000))?;
|
||||
t += Duration::new(1, 900_000_000);
|
||||
assert_eq!(simu.time(), t);
|
||||
pos = (pos + Motor::STEPS_PER_REV - 19) % Motor::STEPS_PER_REV;
|
||||
|
@ -118,7 +118,9 @@ impl ControllerService {
|
||||
"the specified deadline lies in the past",
|
||||
))?;
|
||||
|
||||
simulation.step_by(duration).map_err(map_execution_error)?;
|
||||
simulation
|
||||
.step_until(duration)
|
||||
.map_err(map_execution_error)?;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -246,8 +246,8 @@
|
||||
//!
|
||||
//! 1. by advancing time, either until the next scheduled event with
|
||||
//! [`Simulation::step()`](simulation::Simulation::step), or until a specific
|
||||
//! deadline using for instance
|
||||
//! [`Simulation::step_by()`](simulation::Simulation::step_by).
|
||||
//! deadline with
|
||||
//! [`Simulation::step_until()`](simulation::Simulation::step_until).
|
||||
//! 2. by sending events or queries without advancing simulation time, using
|
||||
//! [`Simulation::process_event()`](simulation::Simulation::process_event) or
|
||||
//! [`Simulation::send_query()`](simulation::Simulation::process_query),
|
||||
|
@ -122,9 +122,7 @@ mod scheduler;
|
||||
mod sim_init;
|
||||
|
||||
pub use mailbox::{Address, Mailbox};
|
||||
pub use scheduler::{
|
||||
Action, ActionKey, AutoActionKey, Deadline, LocalScheduler, Scheduler, SchedulingError,
|
||||
};
|
||||
pub use scheduler::{Action, ActionKey, AutoActionKey, LocalScheduler, Scheduler, SchedulingError};
|
||||
pub(crate) use scheduler::{
|
||||
KeyedOnceAction, KeyedPeriodicAction, OnceAction, PeriodicAction, SchedulerQueue,
|
||||
};
|
||||
@ -148,7 +146,7 @@ use crate::channel::ChannelObserver;
|
||||
use crate::executor::{Executor, ExecutorError, Signal};
|
||||
use crate::model::{BuildContext, Context, Model, ProtoModel};
|
||||
use crate::ports::{InputFn, ReplierFn};
|
||||
use crate::time::{AtomicTime, Clock, MonotonicTime, SyncStatus};
|
||||
use crate::time::{AtomicTime, Clock, Deadline, MonotonicTime, SyncStatus};
|
||||
use crate::util::seq_futures::SeqFuture;
|
||||
use crate::util::slot;
|
||||
|
||||
@ -189,9 +187,8 @@ thread_local! { pub(crate) static CURRENT_MODEL_ID: Cell<ModelId> = const { Cell
|
||||
/// desired wall clock time, and finally
|
||||
/// 3. run all computations scheduled for the new simulation time.
|
||||
///
|
||||
/// The [`step_by()`](Simulation::step_by) and
|
||||
/// [`step_until()`](Simulation::step_until) methods operate similarly but
|
||||
/// iterate until the target simulation time has been reached.
|
||||
/// The [`step_until()`](Simulation::step_until) method operates similarly but
|
||||
/// iterates until the target simulation time has been reached.
|
||||
pub struct Simulation {
|
||||
executor: Executor,
|
||||
scheduler_queue: Arc<Mutex<SchedulerQueue>>,
|
||||
@ -260,19 +257,6 @@ impl Simulation {
|
||||
self.step_to_next_bounded(MonotonicTime::MAX).map(|_| ())
|
||||
}
|
||||
|
||||
/// Iteratively advances the simulation time by the specified duration, as
|
||||
/// if by calling [`Simulation::step()`] repeatedly.
|
||||
///
|
||||
/// This method blocks until all events scheduled up to the specified target
|
||||
/// time have completed. The simulation time upon completion is equal to the
|
||||
/// initial simulation time incremented by the specified duration, whether
|
||||
/// or not an event was scheduled for that time.
|
||||
pub fn step_by(&mut self, duration: Duration) -> Result<(), ExecutionError> {
|
||||
let target_time = self.time.read() + duration;
|
||||
|
||||
self.step_until_unchecked(target_time)
|
||||
}
|
||||
|
||||
/// Iteratively advances the simulation time until the specified deadline,
|
||||
/// as if by calling [`Simulation::step()`] repeatedly.
|
||||
///
|
||||
@ -280,8 +264,10 @@ impl Simulation {
|
||||
/// time have completed. The simulation time upon completion is equal to the
|
||||
/// specified target time, whether or not an event was scheduled for that
|
||||
/// time.
|
||||
pub fn step_until(&mut self, target_time: MonotonicTime) -> Result<(), ExecutionError> {
|
||||
if self.time.read() >= target_time {
|
||||
pub fn step_until(&mut self, deadline: impl Deadline) -> Result<(), ExecutionError> {
|
||||
let now = self.time.read();
|
||||
let target_time = deadline.into_time(now);
|
||||
if target_time < now {
|
||||
return Err(ExecutionError::InvalidDeadline(target_time));
|
||||
}
|
||||
self.step_until_unchecked(target_time)
|
||||
|
@ -18,7 +18,7 @@ use crate::executor::Executor;
|
||||
use crate::model::Model;
|
||||
use crate::ports::InputFn;
|
||||
use crate::simulation::Address;
|
||||
use crate::time::{AtomicTimeReader, MonotonicTime};
|
||||
use crate::time::{AtomicTimeReader, Deadline, MonotonicTime};
|
||||
use crate::util::priority_queue::PriorityQueue;
|
||||
|
||||
/// Scheduler.
|
||||
@ -557,30 +557,6 @@ impl<M: Model> fmt::Debug for LocalScheduler<M> {
|
||||
// control their relative order of execution.
|
||||
pub(crate) type SchedulerQueue = PriorityQueue<(MonotonicTime, usize), Action>;
|
||||
|
||||
/// Trait abstracting over time-absolute and time-relative deadlines.
|
||||
///
|
||||
/// This trait is implemented by [`std::time::Duration`] and
|
||||
/// [`MonotonicTime`].
|
||||
pub trait Deadline {
|
||||
/// Make this deadline into an absolute timestamp, using the provided
|
||||
/// current time as a reference.
|
||||
fn into_time(self, now: MonotonicTime) -> MonotonicTime;
|
||||
}
|
||||
|
||||
impl Deadline for Duration {
|
||||
#[inline(always)]
|
||||
fn into_time(self, now: MonotonicTime) -> MonotonicTime {
|
||||
now + self
|
||||
}
|
||||
}
|
||||
|
||||
impl Deadline for MonotonicTime {
|
||||
#[inline(always)]
|
||||
fn into_time(self, _: MonotonicTime) -> MonotonicTime {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Managed handle to a scheduled action.
|
||||
///
|
||||
/// An `AutoActionKey` is a managed handle to a scheduled action that cancels
|
||||
|
@ -55,3 +55,27 @@ pub(crate) use monotonic_time::TearableAtomicTime;
|
||||
|
||||
pub(crate) type AtomicTime = crate::util::sync_cell::SyncCell<TearableAtomicTime>;
|
||||
pub(crate) type AtomicTimeReader = crate::util::sync_cell::SyncCellReader<TearableAtomicTime>;
|
||||
|
||||
/// Trait abstracting over time-absolute and time-relative deadlines.
|
||||
///
|
||||
/// This trait is implemented by [`std::time::Duration`] and
|
||||
/// [`MonotonicTime`].
|
||||
pub trait Deadline {
|
||||
/// Make this deadline into an absolute timestamp, using the provided
|
||||
/// current time as a reference.
|
||||
fn into_time(self, now: MonotonicTime) -> MonotonicTime;
|
||||
}
|
||||
|
||||
impl Deadline for std::time::Duration {
|
||||
#[inline(always)]
|
||||
fn into_time(self, now: MonotonicTime) -> MonotonicTime {
|
||||
now + self
|
||||
}
|
||||
}
|
||||
|
||||
impl Deadline for MonotonicTime {
|
||||
#[inline(always)]
|
||||
fn into_time(self, _: MonotonicTime) -> MonotonicTime {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ impl Model for TestModel {}
|
||||
// synchronization tolerance.
|
||||
//
|
||||
// Returns the last simulation tick at completion or when the error occurred, and
|
||||
// the result of `Simulation::step_by`.
|
||||
// the result of `Simulation::step_until`.
|
||||
fn clock_sync(
|
||||
num_threads: usize,
|
||||
block_time_ms: u64,
|
||||
@ -58,7 +58,7 @@ fn clock_sync(
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let res = simu.step_by(delta);
|
||||
let res = simu.step_until(delta);
|
||||
let last_tick = simu.time().duration_since(t0);
|
||||
|
||||
(last_tick, res)
|
||||
|
Loading…
x
Reference in New Issue
Block a user