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. // All data processed.
simu.step_by(Duration::from_millis(101))?; simu.step_until(Duration::from_millis(101))?;
expect( expect(
&mut mode, &mut mode,
Some(ModeId::Idle), Some(ModeId::Idle),
@ -266,7 +266,7 @@ fn main() -> Result<(), SimulationError> {
); );
// Wait for short processing to finish, check results. // Wait for short processing to finish, check results.
simu.step_by(Duration::from_millis(11))?; simu.step_until(Duration::from_millis(11))?;
expect( expect(
&mut mode, &mut mode,
Some(ModeId::Idle), Some(ModeId::Idle),
@ -279,7 +279,7 @@ fn main() -> Result<(), SimulationError> {
// Wait long enough, no state change as the long processing has been // Wait long enough, no state change as the long processing has been
// cancelled. // cancelled.
simu.step_by(Duration::from_millis(100))?; simu.step_until(Duration::from_millis(100))?;
assert_eq!(mode.next(), None); assert_eq!(mode.next(), None);
assert_eq!(value.next(), None); assert_eq!(value.next(), None);
assert_eq!(hk.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 // Advance simulation time by 0.9s, which with a 10Hz PPS should correspond to
// 9 position increments. // 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); t += Duration::new(0, 900_000_000);
assert_eq!(simu.time(), t); assert_eq!(simu.time(), t);
for _ in 0..9 { for _ in 0..9 {

View File

@ -236,7 +236,7 @@ fn main() -> Result<(), SimulationError> {
}); });
// Advance simulation, external messages will be collected. // 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. // Shut down the server.
let socket = sender_handle.join().unwrap(); 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 // Advance simulation time by 0.9s, which with a 10Hz PPS should correspond to
// 9 position increments. // 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); t += Duration::new(0, 900_000_000);
assert_eq!(simu.time(), t); assert_eq!(simu.time(), t);
for _ in 0..9 { 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 // Advance simulation time by 0.7s, which with a 10Hz PPS should correspond to
// 7 position increments. // 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); t += Duration::new(0, 700_000_000);
assert_eq!(simu.time(), t); assert_eq!(simu.time(), t);
for _ in 0..7 { 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 // Advance simulation time by 1.9s, which with a -10Hz PPS should correspond
// to 19 position decrements. // 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); t += Duration::new(1, 900_000_000);
assert_eq!(simu.time(), t); assert_eq!(simu.time(), t);
pos = (pos + Motor::STEPS_PER_REV - 19) % Motor::STEPS_PER_REV; 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", "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 //! 1. by advancing time, either until the next scheduled event with
//! [`Simulation::step()`](simulation::Simulation::step), or until a specific //! [`Simulation::step()`](simulation::Simulation::step), or until a specific
//! deadline using for instance //! deadline with
//! [`Simulation::step_by()`](simulation::Simulation::step_by). //! [`Simulation::step_until()`](simulation::Simulation::step_until).
//! 2. by sending events or queries without advancing simulation time, using //! 2. by sending events or queries without advancing simulation time, using
//! [`Simulation::process_event()`](simulation::Simulation::process_event) or //! [`Simulation::process_event()`](simulation::Simulation::process_event) or
//! [`Simulation::send_query()`](simulation::Simulation::process_query), //! [`Simulation::send_query()`](simulation::Simulation::process_query),

View File

@ -122,9 +122,7 @@ mod scheduler;
mod sim_init; mod sim_init;
pub use mailbox::{Address, Mailbox}; pub use mailbox::{Address, Mailbox};
pub use scheduler::{ pub use scheduler::{Action, ActionKey, AutoActionKey, LocalScheduler, Scheduler, SchedulingError};
Action, ActionKey, AutoActionKey, Deadline, LocalScheduler, Scheduler, SchedulingError,
};
pub(crate) use scheduler::{ pub(crate) use scheduler::{
KeyedOnceAction, KeyedPeriodicAction, OnceAction, PeriodicAction, SchedulerQueue, KeyedOnceAction, KeyedPeriodicAction, OnceAction, PeriodicAction, SchedulerQueue,
}; };
@ -148,7 +146,7 @@ use crate::channel::ChannelObserver;
use crate::executor::{Executor, ExecutorError, Signal}; use crate::executor::{Executor, ExecutorError, Signal};
use crate::model::{BuildContext, Context, Model, ProtoModel}; use crate::model::{BuildContext, Context, Model, ProtoModel};
use crate::ports::{InputFn, ReplierFn}; 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::seq_futures::SeqFuture;
use crate::util::slot; 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 /// 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.
/// ///
/// The [`step_by()`](Simulation::step_by) and /// The [`step_until()`](Simulation::step_until) method operates similarly but
/// [`step_until()`](Simulation::step_until) methods operate similarly but /// iterates until the target simulation time has been reached.
/// iterate until the target simulation time has been reached.
pub struct Simulation { pub struct Simulation {
executor: Executor, executor: Executor,
scheduler_queue: Arc<Mutex<SchedulerQueue>>, scheduler_queue: Arc<Mutex<SchedulerQueue>>,
@ -260,19 +257,6 @@ impl Simulation {
self.step_to_next_bounded(MonotonicTime::MAX).map(|_| ()) 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, /// Iteratively advances the simulation time until the specified deadline,
/// as if by calling [`Simulation::step()`] repeatedly. /// 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 /// time have completed. The simulation time upon completion is equal to the
/// specified target time, whether or not an event was scheduled for that /// specified target time, whether or not an event was scheduled for that
/// time. /// time.
pub fn step_until(&mut self, target_time: MonotonicTime) -> Result<(), ExecutionError> { pub fn step_until(&mut self, deadline: impl Deadline) -> Result<(), ExecutionError> {
if self.time.read() >= target_time { let now = self.time.read();
let target_time = deadline.into_time(now);
if target_time < now {
return Err(ExecutionError::InvalidDeadline(target_time)); return Err(ExecutionError::InvalidDeadline(target_time));
} }
self.step_until_unchecked(target_time) self.step_until_unchecked(target_time)

View File

@ -18,7 +18,7 @@ use crate::executor::Executor;
use crate::model::Model; use crate::model::Model;
use crate::ports::InputFn; use crate::ports::InputFn;
use crate::simulation::Address; use crate::simulation::Address;
use crate::time::{AtomicTimeReader, MonotonicTime}; use crate::time::{AtomicTimeReader, Deadline, MonotonicTime};
use crate::util::priority_queue::PriorityQueue; use crate::util::priority_queue::PriorityQueue;
/// Scheduler. /// Scheduler.
@ -557,30 +557,6 @@ impl<M: Model> fmt::Debug for LocalScheduler<M> {
// control their relative order of execution. // control their relative order of execution.
pub(crate) type SchedulerQueue = PriorityQueue<(MonotonicTime, usize), Action>; 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. /// Managed handle to a scheduled action.
/// ///
/// An `AutoActionKey` is a managed handle to a scheduled action that cancels /// 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 AtomicTime = crate::util::sync_cell::SyncCell<TearableAtomicTime>;
pub(crate) type AtomicTimeReader = crate::util::sync_cell::SyncCellReader<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. // synchronization tolerance.
// //
// Returns the last simulation tick at completion or when the error occurred, and // 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( fn clock_sync(
num_threads: usize, num_threads: usize,
block_time_ms: u64, block_time_ms: u64,
@ -58,7 +58,7 @@ fn clock_sync(
.unwrap(); .unwrap();
} }
let res = simu.step_by(delta); let res = simu.step_until(delta);
let last_tick = simu.time().duration_since(t0); let last_tick = simu.time().duration_since(t0);
(last_tick, res) (last_tick, res)