1
0
forked from ROMEO/nexosim

Merge pull request #61 from asynchronics/feature/unified_step_until

Merge step_by and step_until into a unique method
This commit is contained in:
Serge Barral 2024-11-14 17:19:13 +01:00 committed by GitHub
commit 1cefe4b2f6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 48 additions and 60 deletions

View File

@ -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);

View File

@ -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 {

View File

@ -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();

View File

@ -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;

View File

@ -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)?;
}
};

View File

@ -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),

View File

@ -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)

View File

@ -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

View File

@ -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
}
}

View File

@ -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)