1
0
forked from ROMEO/nexosim

Initial (g)RPC implementation

This commit is contained in:
Serge Barral
2024-04-25 11:12:54 +02:00
parent c984202005
commit e84e802f09
55 changed files with 5814 additions and 1996 deletions

View File

@ -1,14 +1,16 @@
use std::time::{Duration, Instant, SystemTime};
use tai_time::MonotonicClock;
use crate::time::MonotonicTime;
/// A type that can be used to synchronize a simulation.
///
/// This trait abstract over the different types of clocks, such as
/// This trait abstracts over different types of clocks, such as
/// as-fast-as-possible and real-time clocks.
///
/// A clock can be associated to a simulation at initialization time by calling
/// [`SimInit::init_with_clock()`](crate::simulation::SimInit::init_with_clock).
/// A clock can be associated to a simulation prior to initialization by calling
/// [`SimInit::set_clock()`](crate::simulation::SimInit::set_clock).
pub trait Clock: Send {
/// Blocks until the deadline.
fn synchronize(&mut self, deadline: MonotonicTime) -> SyncStatus;
@ -49,10 +51,7 @@ impl Clock for NoClock {
/// This clock accepts an arbitrary reference time and remains synchronized with
/// the system's monotonic clock.
#[derive(Copy, Clone, Debug)]
pub struct SystemClock {
wall_clock_ref: Instant,
simulation_ref: MonotonicTime,
}
pub struct SystemClock(MonotonicClock);
impl SystemClock {
/// Constructs a `SystemClock` with an offset between simulation clock and
@ -69,7 +68,7 @@ impl SystemClock {
/// use asynchronix::simulation::SimInit;
/// use asynchronix::time::{MonotonicTime, SystemClock};
///
/// let t0 = MonotonicTime::new(1_234_567_890, 0);
/// let t0 = MonotonicTime::new(1_234_567_890, 0).unwrap();
///
/// // Make the simulation start in 1s.
/// let clock = SystemClock::from_instant(t0, Instant::now() + Duration::from_secs(1));
@ -77,13 +76,14 @@ impl SystemClock {
/// let simu = SimInit::new()
/// // .add_model(...)
/// // .add_model(...)
/// .init_with_clock(t0, clock);
/// .set_clock(clock)
/// .init(t0);
/// ```
pub fn from_instant(simulation_ref: MonotonicTime, wall_clock_ref: Instant) -> Self {
Self {
wall_clock_ref,
Self(MonotonicClock::init_from_instant(
simulation_ref,
}
wall_clock_ref,
))
}
/// Constructs a `SystemClock` with an offset between simulation clock and
@ -109,7 +109,7 @@ impl SystemClock {
/// use asynchronix::simulation::SimInit;
/// use asynchronix::time::{MonotonicTime, SystemClock};
///
/// let t0 = MonotonicTime::new(1_234_567_890, 0);
/// let t0 = MonotonicTime::new(1_234_567_890, 0).unwrap();
///
/// // Make the simulation start at the next full second boundary.
/// let now_secs = UNIX_EPOCH.elapsed().unwrap().as_secs();
@ -120,58 +120,14 @@ impl SystemClock {
/// let simu = SimInit::new()
/// // .add_model(...)
/// // .add_model(...)
/// .init_with_clock(t0, clock);
/// .set_clock(clock)
/// .init(t0);
/// ```
pub fn from_system_time(simulation_ref: MonotonicTime, wall_clock_ref: SystemTime) -> Self {
// Select the best-correlated `Instant`/`SystemTime` pair from several
// samples to improve robustness towards possible thread suspension
// between the calls to `SystemTime::now()` and `Instant::now()`.
const SAMPLES: usize = 3;
let mut last_instant = Instant::now();
let mut min_delta = Duration::MAX;
let mut ref_time = None;
// Select the best-correlated instant/date pair.
for _ in 0..SAMPLES {
// The inner loop is to work around monotonic clock platform bugs
// that may cause `checked_duration_since` to fail.
let (date, instant, delta) = loop {
let date = SystemTime::now();
let instant = Instant::now();
let delta = instant.checked_duration_since(last_instant);
last_instant = instant;
if let Some(delta) = delta {
break (date, instant, delta);
}
};
// Store the current instant/date if the time elapsed since the last
// measurement is shorter than the previous candidate.
if min_delta > delta {
min_delta = delta;
ref_time = Some((instant, date));
}
}
// Set the selected instant/date as the wall clock reference and adjust
// the simulation reference accordingly.
let (instant_ref, date_ref) = ref_time.unwrap();
let simulation_ref = if date_ref > wall_clock_ref {
let correction = date_ref.duration_since(wall_clock_ref).unwrap();
simulation_ref + correction
} else {
let correction = wall_clock_ref.duration_since(date_ref).unwrap();
simulation_ref - correction
};
Self {
wall_clock_ref: instant_ref,
Self(MonotonicClock::init_from_system_time(
simulation_ref,
}
wall_clock_ref,
))
}
}
@ -179,22 +135,14 @@ impl Clock for SystemClock {
/// Blocks until the system time corresponds to the specified simulation
/// time.
fn synchronize(&mut self, deadline: MonotonicTime) -> SyncStatus {
let target_time = if deadline >= self.simulation_ref {
self.wall_clock_ref + deadline.duration_since(self.simulation_ref)
} else {
self.wall_clock_ref - self.simulation_ref.duration_since(deadline)
};
let now = self.0.now();
if now <= deadline {
spin_sleep::sleep(deadline.duration_since(now));
let now = Instant::now();
match target_time.checked_duration_since(now) {
Some(sleep_duration) => {
spin_sleep::sleep(sleep_duration);
SyncStatus::Synchronized
}
None => SyncStatus::OutOfSync(now.duration_since(target_time)),
return SyncStatus::Synchronized;
}
SyncStatus::OutOfSync(now.duration_since(deadline))
}
}
@ -233,3 +181,29 @@ impl Clock for AutoSystemClock {
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn smoke_system_clock() {
let t0 = MonotonicTime::EPOCH;
const TOLERANCE: f64 = 0.0005; // [s]
let now = Instant::now();
let mut clock = SystemClock::from_instant(t0, now);
let t1 = t0 + Duration::from_millis(200);
clock.synchronize(t1);
let elapsed = now.elapsed().as_secs_f64();
let dt = t1.duration_since(t0).as_secs_f64();
assert!(
(dt - elapsed) <= TOLERANCE,
"Expected t = {:.6}s +/- {:.6}s, measured t = {:.6}s",
dt,
TOLERANCE,
elapsed,
);
}
}

View File

@ -1,483 +1,10 @@
//! Monotonic simulation time.
use std::error::Error;
use std::fmt;
use std::ops::{Add, AddAssign, Sub, SubAssign};
use std::sync::atomic::{AtomicI64, AtomicU32, Ordering};
use std::time::{Duration, SystemTime};
use super::MonotonicTime;
use crate::util::sync_cell::TearableAtomic;
const NANOS_PER_SEC: u32 = 1_000_000_000;
/// A nanosecond-precision monotonic clock timestamp.
///
/// A timestamp specifies a [TAI] point in time. It is represented as a 64-bit
/// signed number of seconds and a positive number of nanoseconds, counted with
/// reference to 1970-01-01 00:00:00 TAI. This timestamp format has a number of
/// desirable properties:
///
/// - it enables cheap inter-operation with the standard [`Duration`] type which
/// uses a very similar internal representation,
/// - it constitutes a strict 96-bit superset of 80-bit PTP IEEE-1588
/// timestamps, with the same epoch,
/// - if required, exact conversion to a Unix timestamp is trivial and only
/// requires subtracting from this timestamp the number of leap seconds
/// between TAI and UTC time (see also the
/// [`as_unix_secs()`](MonotonicTime::as_unix_secs) method).
///
/// Although no date-time conversion methods are provided, conversion from
/// timestamp to TAI date-time representations and back can be easily performed
/// using `NaiveDateTime` from the [chrono] crate or `OffsetDateTime` from the
/// [time] crate, treating the timestamp as a regular (UTC) Unix timestamp.
///
/// [TAI]: https://en.wikipedia.org/wiki/International_Atomic_Time
/// [chrono]: https://crates.io/crates/chrono
/// [time]: https://crates.io/crates/time
///
/// # Examples
///
/// ```
/// use std::time::Duration;
/// use asynchronix::time::MonotonicTime;
///
/// // Set the timestamp to 2009-02-13 23:31:30.987654321 TAI.
/// let mut timestamp = MonotonicTime::new(1_234_567_890, 987_654_321);
///
/// // Increment the timestamp by 123.456s.
/// timestamp += Duration::new(123, 456_000_000);
///
/// assert_eq!(timestamp, MonotonicTime::new(1_234_568_014, 443_654_321));
/// assert_eq!(timestamp.as_secs(), 1_234_568_014);
/// assert_eq!(timestamp.subsec_nanos(), 443_654_321);
/// ```
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MonotonicTime {
/// The number of whole seconds in the future (if positive) or in the past
/// (if negative) of 1970-01-01 00:00:00 TAI.
///
/// Note that the automatic derivation of `PartialOrd` relies on
/// lexicographical comparison so the `secs` field must appear before
/// `nanos` in declaration order to be given higher priority.
secs: i64,
/// The sub-second number of nanoseconds in the future of the point in time
/// defined by `secs`.
nanos: u32,
}
impl MonotonicTime {
/// The epoch used by `MonotonicTime`, equal to 1970-01-01 00:00:00 TAI.
///
/// This epoch coincides with the PTP epoch defined in the IEEE-1588
/// standard.
pub const EPOCH: Self = Self { secs: 0, nanos: 0 };
/// The minimum possible `MonotonicTime` timestamp.
pub const MIN: Self = Self {
secs: i64::MIN,
nanos: 0,
};
/// The maximum possible `MonotonicTime` timestamp.
pub const MAX: Self = Self {
secs: i64::MAX,
nanos: NANOS_PER_SEC - 1,
};
/// Creates a timestamp directly from timestamp parts.
///
/// The number of seconds is relative to the [`EPOCH`](MonotonicTime::EPOCH)
/// (1970-01-01 00:00:00 TAI). It is negative for dates in the past of the
/// epoch.
///
/// The number of nanoseconds is always positive and always points towards
/// the future.
///
/// # Panics
///
/// This constructor will panic if the number of nanoseconds is greater than
/// or equal to 1 second.
///
/// # Example
///
/// ```
/// use std::time::Duration;
/// use asynchronix::time::MonotonicTime;
///
/// // A timestamp set to 2009-02-13 23:31:30.987654321 TAI.
/// let timestamp = MonotonicTime::new(1_234_567_890, 987_654_321);
///
/// // A timestamp set 3.5s before the epoch.
/// let timestamp = MonotonicTime::new(-4, 500_000_000);
/// assert_eq!(timestamp, MonotonicTime::EPOCH - Duration::new(3, 500_000_000));
/// ```
pub const fn new(secs: i64, subsec_nanos: u32) -> Self {
assert!(
subsec_nanos < NANOS_PER_SEC,
"invalid number of nanoseconds"
);
Self {
secs,
nanos: subsec_nanos,
}
}
/// Creates a timestamp from the current system time.
///
/// The argument is the current difference between TAI and UTC time in
/// seconds (a.k.a. leap seconds). For reference, this offset has been +37s
/// since 2017-01-01, a value which is to remain valid until at least
/// 2024-06-29. See the [official IERS bulletin
/// C](https://datacenter.iers.org/data/latestVersion/bulletinC.txt) for
/// leap second announcements or the [IETF
/// table](https://www.ietf.org/timezones/data/leap-seconds.list) for
/// current and historical values.
///
/// # Errors
///
/// This method will return an error if the reported system time is in the
/// past of the Unix epoch or if the offset-adjusted timestamp is outside
/// the representable range.
///
/// # Examples
///
/// ```
/// use asynchronix::time::MonotonicTime;
///
/// // Compute the current TAI time assuming that the current difference
/// // between TAI and UTC time is 37s.
/// let timestamp = MonotonicTime::from_system(37).unwrap();
/// ```
pub fn from_system(leap_secs: i64) -> Result<Self, SystemTimeError> {
let utc_timestamp = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.map_err(|_| SystemTimeError::InvalidSystemTime)?;
Self::new(leap_secs, 0)
.checked_add(utc_timestamp)
.ok_or(SystemTimeError::OutOfRange)
}
/// Returns the number of whole seconds relative to
/// [`EPOCH`](MonotonicTime::EPOCH) (1970-01-01 00:00:00 TAI).
///
/// Consistently with the interpretation of seconds and nanoseconds in the
/// [`new()`](Self::new) constructor, seconds are always rounded towards
/// `-∞`.
///
/// # Examples
///
/// ```
/// use std::time::Duration;
/// use asynchronix::time::MonotonicTime;
///
/// let timestamp = MonotonicTime::new(1_234_567_890, 987_654_321);
/// assert_eq!(timestamp.as_secs(), 1_234_567_890);
///
/// let timestamp = MonotonicTime::EPOCH - Duration::new(3, 500_000_000);
/// assert_eq!(timestamp.as_secs(), -4);
/// ```
pub const fn as_secs(&self) -> i64 {
self.secs
}
/// Returns the number of seconds of the corresponding Unix time.
///
/// The argument is the difference between TAI and UTC time in seconds
/// (a.k.a. leap seconds) applicable at the date represented by the
/// timestamp. See the [official IERS bulletin
/// C](https://datacenter.iers.org/data/latestVersion/bulletinC.txt) for
/// leap second announcements or the [IETF
/// table](https://www.ietf.org/timezones/data/leap-seconds.list) for
/// current and historical values.
///
/// This method merely subtracts the offset from the value returned by
/// [`as_secs()`](Self::as_secs) and checks for potential overflow; its main
/// purpose is to prevent mistakes regarding the direction in which the
/// offset should be applied.
///
/// Note that the nanosecond part of a Unix timestamp can be simply
/// retrieved with [`subsec_nanos()`](Self::subsec_nanos) since UTC and TAI
/// differ by a whole number of seconds.
///
/// # Panics
///
/// This will panic if the offset-adjusted timestamp cannot be represented
/// as an `i64`.
///
/// # Examples
///
/// ```
/// use asynchronix::time::MonotonicTime;
///
/// // Set the date to 2000-01-01 00:00:00 TAI.
/// let timestamp = MonotonicTime::new(946_684_800, 0);
///
/// // Convert to a Unix timestamp, accounting for the +32s difference between
/// // TAI and UTC on 2000-01-01.
/// let unix_secs = timestamp.as_unix_secs(32);
/// ```
pub const fn as_unix_secs(&self, leap_secs: i64) -> i64 {
if let Some(secs) = self.secs.checked_sub(leap_secs) {
secs
} else {
panic!("timestamp outside representable range");
}
}
/// Returns the sub-second fractional part in nanoseconds.
///
/// Note that nanoseconds always point towards the future even if the date
/// is in the past of the [`EPOCH`](MonotonicTime::EPOCH).
///
/// # Examples
///
/// ```
/// use asynchronix::time::MonotonicTime;
///
/// let timestamp = MonotonicTime::new(1_234_567_890, 987_654_321);
/// assert_eq!(timestamp.subsec_nanos(), 987_654_321);
/// ```
pub const fn subsec_nanos(&self) -> u32 {
self.nanos
}
/// Adds a duration to a timestamp, checking for overflow.
///
/// Returns `None` if overflow occurred.
///
/// # Examples
///
/// ```
/// use std::time::Duration;
/// use asynchronix::time::MonotonicTime;
///
/// let timestamp = MonotonicTime::new(1_234_567_890, 987_654_321);
/// assert!(timestamp.checked_add(Duration::new(10, 123_456_789)).is_some());
/// assert!(timestamp.checked_add(Duration::MAX).is_none());
/// ```
pub const fn checked_add(self, rhs: Duration) -> Option<Self> {
// A durations in seconds greater than `i64::MAX` is actually fine as
// long as the number of seconds does not effectively overflow which is
// why the below does not use `checked_add`. So technically the below
// addition may wrap around on the negative side due to the
// unsigned-to-signed cast of the duration, but this does not
// necessarily indicate an actual overflow. Actual overflow can be ruled
// out by verifying that the new timestamp is in the future of the old
// timestamp.
let mut secs = self.secs.wrapping_add(rhs.as_secs() as i64);
// Check for overflow.
if secs < self.secs {
return None;
}
let mut nanos = self.nanos + rhs.subsec_nanos();
if nanos >= NANOS_PER_SEC {
secs = if let Some(s) = secs.checked_add(1) {
s
} else {
return None;
};
nanos -= NANOS_PER_SEC;
}
Some(Self { secs, nanos })
}
/// Subtracts a duration from a timestamp, checking for overflow.
///
/// Returns `None` if overflow occurred.
///
/// # Examples
///
/// ```
/// use std::time::Duration;
/// use asynchronix::time::MonotonicTime;
///
/// let timestamp = MonotonicTime::new(1_234_567_890, 987_654_321);
/// assert!(timestamp.checked_sub(Duration::new(10, 123_456_789)).is_some());
/// assert!(timestamp.checked_sub(Duration::MAX).is_none());
/// ```
pub const fn checked_sub(self, rhs: Duration) -> Option<Self> {
// A durations in seconds greater than `i64::MAX` is actually fine as
// long as the number of seconds does not effectively overflow, which is
// why the below does not use `checked_sub`. So technically the below
// subtraction may wrap around on the positive side due to the
// unsigned-to-signed cast of the duration, but this does not
// necessarily indicate an actual overflow. Actual overflow can be ruled
// out by verifying that the new timestamp is in the past of the old
// timestamp.
let mut secs = self.secs.wrapping_sub(rhs.as_secs() as i64);
// Check for overflow.
if secs > self.secs {
return None;
}
let nanos = if self.nanos < rhs.subsec_nanos() {
secs = if let Some(s) = secs.checked_sub(1) {
s
} else {
return None;
};
(self.nanos + NANOS_PER_SEC) - rhs.subsec_nanos()
} else {
self.nanos - rhs.subsec_nanos()
};
Some(Self { secs, nanos })
}
/// Subtracts a timestamp from another timestamp.
///
/// # Panics
///
/// Panics if the argument lies in the future of `self`.
///
/// # Examples
///
/// ```
/// use std::time::Duration;
/// use asynchronix::time::MonotonicTime;
///
/// let timestamp_earlier = MonotonicTime::new(1_234_567_879, 987_654_321);
/// let timestamp_later = MonotonicTime::new(1_234_567_900, 123_456_789);
/// assert_eq!(
/// timestamp_later.duration_since(timestamp_earlier),
/// Duration::new(20, 135_802_468)
/// );
/// ```
pub fn duration_since(self, earlier: Self) -> Duration {
self.checked_duration_since(earlier)
.expect("attempt to substract a timestamp from an earlier timestamp")
}
/// Computes the duration elapsed between a timestamp and an earlier
/// timestamp, checking that the timestamps are appropriately ordered.
///
/// Returns `None` if the argument lies in the future of `self`.
///
/// # Examples
///
/// ```
/// use std::time::Duration;
/// use asynchronix::time::MonotonicTime;
///
/// let timestamp_earlier = MonotonicTime::new(1_234_567_879, 987_654_321);
/// let timestamp_later = MonotonicTime::new(1_234_567_900, 123_456_789);
/// assert!(timestamp_later.checked_duration_since(timestamp_earlier).is_some());
/// assert!(timestamp_earlier.checked_duration_since(timestamp_later).is_none());
/// ```
pub const fn checked_duration_since(self, earlier: Self) -> Option<Duration> {
// If the subtraction of the nanosecond fractions would overflow, carry
// over one second to the nanoseconds.
let (secs, nanos) = if earlier.nanos > self.nanos {
if let Some(s) = self.secs.checked_sub(1) {
(s, self.nanos + NANOS_PER_SEC)
} else {
return None;
}
} else {
(self.secs, self.nanos)
};
// Make sure the computation of the duration will not overflow the
// seconds.
if secs < earlier.secs {
return None;
}
// This subtraction may wrap around if the difference between the two
// timestamps is more than `i64::MAX`, but even if it does the result
// will be correct once cast to an unsigned integer.
let delta_secs = secs.wrapping_sub(earlier.secs) as u64;
// The below subtraction is guaranteed to never overflow.
let delta_nanos = nanos - earlier.nanos;
Some(Duration::new(delta_secs, delta_nanos))
}
}
impl Add<Duration> for MonotonicTime {
type Output = Self;
/// Adds a duration to a timestamp.
///
/// # Panics
///
/// This function panics if the resulting timestamp cannot be
/// represented. See [`MonotonicTime::checked_add`] for a panic-free
/// version.
fn add(self, other: Duration) -> Self {
self.checked_add(other)
.expect("overflow when adding duration to timestamp")
}
}
impl Sub<Duration> for MonotonicTime {
type Output = Self;
/// Subtracts a duration from a timestamp.
///
/// # Panics
///
/// This function panics if the resulting timestamp cannot be
/// represented. See [`MonotonicTime::checked_sub`] for a panic-free
/// version.
fn sub(self, other: Duration) -> Self {
self.checked_sub(other)
.expect("overflow when subtracting duration from timestamp")
}
}
impl AddAssign<Duration> for MonotonicTime {
/// Increments the timestamp by a duration.
///
/// # Panics
///
/// This function panics if the resulting timestamp cannot be represented.
fn add_assign(&mut self, other: Duration) {
*self = *self + other;
}
}
impl SubAssign<Duration> for MonotonicTime {
/// Decrements the timestamp by a duration.
///
/// # Panics
///
/// This function panics if the resulting timestamp cannot be represented.
fn sub_assign(&mut self, other: Duration) {
*self = *self - other;
}
}
/// An error that may be returned when initializing a [`MonotonicTime`] from
/// system time.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SystemTimeError {
/// The system time is in the past of the Unix epoch.
InvalidSystemTime,
/// The system time cannot be represented as a `MonotonicTime`.
OutOfRange,
}
impl fmt::Display for SystemTimeError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidSystemTime => write!(fmt, "invalid system time"),
Self::OutOfRange => write!(fmt, "timestamp outside representable range"),
}
}
}
impl Error for SystemTimeError {}
/// A tearable atomic adapter over a `MonotonicTime`.
///
/// This makes it possible to store the simulation time in a `SyncCell`, an
@ -490,8 +17,8 @@ pub(crate) struct TearableAtomicTime {
impl TearableAtomicTime {
pub(crate) fn new(time: MonotonicTime) -> Self {
Self {
secs: AtomicI64::new(time.secs),
nanos: AtomicU32::new(time.nanos),
secs: AtomicI64::new(time.as_secs()),
nanos: AtomicU32::new(time.subsec_nanos()),
}
}
}
@ -502,170 +29,17 @@ impl TearableAtomic for TearableAtomicTime {
fn tearable_load(&self) -> MonotonicTime {
// Load each field separately. This can never create invalid values of a
// `MonotonicTime`, even if the load is torn.
MonotonicTime {
secs: self.secs.load(Ordering::Relaxed),
nanos: self.nanos.load(Ordering::Relaxed),
}
MonotonicTime::new(
self.secs.load(Ordering::Relaxed),
self.nanos.load(Ordering::Relaxed),
)
.unwrap()
}
fn tearable_store(&self, value: MonotonicTime) {
// Write each field separately. This can never create invalid values of
// a `MonotonicTime`, even if the store is torn.
self.secs.store(value.secs, Ordering::Relaxed);
self.nanos.store(value.nanos, Ordering::Relaxed);
}
}
#[cfg(all(test, not(asynchronix_loom)))]
mod tests {
use super::*;
#[test]
fn time_equality() {
let t0 = MonotonicTime::new(123, 123_456_789);
let t1 = MonotonicTime::new(123, 123_456_789);
let t2 = MonotonicTime::new(123, 123_456_790);
let t3 = MonotonicTime::new(124, 123_456_789);
assert_eq!(t0, t1);
assert_ne!(t0, t2);
assert_ne!(t0, t3);
}
#[test]
fn time_ordering() {
let t0 = MonotonicTime::new(0, 1);
let t1 = MonotonicTime::new(1, 0);
assert!(t1 > t0);
}
#[cfg(not(miri))]
#[test]
fn time_from_system_smoke() {
const START_OF_2022: i64 = 1640995200;
const START_OF_2050: i64 = 2524608000;
let now_secs = MonotonicTime::from_system(0).unwrap().as_secs();
assert!(now_secs > START_OF_2022);
assert!(now_secs < START_OF_2050);
}
#[test]
#[should_panic]
fn time_invalid() {
MonotonicTime::new(123, 1_000_000_000);
}
#[test]
fn time_duration_since_smoke() {
let t0 = MonotonicTime::new(100, 100_000_000);
let t1 = MonotonicTime::new(123, 223_456_789);
assert_eq!(
t1.checked_duration_since(t0),
Some(Duration::new(23, 123_456_789))
);
}
#[test]
fn time_duration_with_carry() {
let t0 = MonotonicTime::new(100, 200_000_000);
let t1 = MonotonicTime::new(101, 100_000_000);
assert_eq!(
t1.checked_duration_since(t0),
Some(Duration::new(0, 900_000_000))
);
}
#[test]
fn time_duration_since_extreme() {
const MIN_TIME: MonotonicTime = MonotonicTime::new(i64::MIN, 0);
const MAX_TIME: MonotonicTime = MonotonicTime::new(i64::MAX, NANOS_PER_SEC - 1);
assert_eq!(
MAX_TIME.checked_duration_since(MIN_TIME),
Some(Duration::new(u64::MAX, NANOS_PER_SEC - 1))
);
}
#[test]
fn time_duration_since_invalid() {
let t0 = MonotonicTime::new(100, 0);
let t1 = MonotonicTime::new(99, 0);
assert_eq!(t1.checked_duration_since(t0), None);
}
#[test]
fn time_add_duration_smoke() {
let t = MonotonicTime::new(-100, 100_000_000);
let dt = Duration::new(400, 300_000_000);
assert_eq!(t + dt, MonotonicTime::new(300, 400_000_000));
}
#[test]
fn time_add_duration_with_carry() {
let t = MonotonicTime::new(-100, 900_000_000);
let dt1 = Duration::new(400, 100_000_000);
let dt2 = Duration::new(400, 300_000_000);
assert_eq!(t + dt1, MonotonicTime::new(301, 0));
assert_eq!(t + dt2, MonotonicTime::new(301, 200_000_000));
}
#[test]
fn time_add_duration_extreme() {
let t = MonotonicTime::new(i64::MIN, 0);
let dt = Duration::new(u64::MAX, NANOS_PER_SEC - 1);
assert_eq!(t + dt, MonotonicTime::new(i64::MAX, NANOS_PER_SEC - 1));
}
#[test]
#[should_panic]
fn time_add_duration_overflow() {
let t = MonotonicTime::new(i64::MIN, 1);
let dt = Duration::new(u64::MAX, NANOS_PER_SEC - 1);
let _ = t + dt;
}
#[test]
fn time_sub_duration_smoke() {
let t = MonotonicTime::new(100, 500_000_000);
let dt = Duration::new(400, 300_000_000);
assert_eq!(t - dt, MonotonicTime::new(-300, 200_000_000));
}
#[test]
fn time_sub_duration_with_carry() {
let t = MonotonicTime::new(100, 100_000_000);
let dt1 = Duration::new(400, 100_000_000);
let dt2 = Duration::new(400, 300_000_000);
assert_eq!(t - dt1, MonotonicTime::new(-300, 0));
assert_eq!(t - dt2, MonotonicTime::new(-301, 800_000_000));
}
#[test]
fn time_sub_duration_extreme() {
let t = MonotonicTime::new(i64::MAX, NANOS_PER_SEC - 1);
let dt = Duration::new(u64::MAX, NANOS_PER_SEC - 1);
assert_eq!(t - dt, MonotonicTime::new(i64::MIN, 0));
}
#[test]
#[should_panic]
fn time_sub_duration_overflow() {
let t = MonotonicTime::new(i64::MAX, NANOS_PER_SEC - 2);
let dt = Duration::new(u64::MAX, NANOS_PER_SEC - 1);
let _ = t - dt;
self.secs.store(value.as_secs(), Ordering::Relaxed);
self.nanos.store(value.subsec_nanos(), Ordering::Relaxed);
}
}

View File

@ -1,27 +1,35 @@
//! Scheduling functions and types.
use std::error::Error;
use std::fmt;
use std::future::Future;
use std::marker::PhantomData;
use std::hash::{Hash, Hasher};
use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll};
use std::time::Duration;
use std::{fmt, ptr};
use pin_project_lite::pin_project;
use recycle_box::{coerce_box, RecycleBox};
use crate::channel::{ChannelId, Sender};
use crate::channel::Sender;
use crate::executor::Executor;
use crate::model::{InputFn, Model};
use crate::model::Model;
use crate::ports::InputFn;
use crate::time::{MonotonicTime, TearableAtomicTime};
use crate::util::priority_queue::PriorityQueue;
use crate::util::sync_cell::SyncCellReader;
/// Shorthand for the scheduler queue type.
pub(crate) type SchedulerQueue = PriorityQueue<(MonotonicTime, ChannelId), Box<dyn ScheduledEvent>>;
// Why use both time and channel ID as the key? The short answer is that this
// ensures that events targeting the same model are sent in the order they were
// scheduled. More precisely, this ensures that events targeting the same model
// are ordered contiguously in the priority queue, which in turns allows the
// event loop to easily aggregate such events into single futures and thus
// control their relative order of execution.
pub(crate) type SchedulerQueue = PriorityQueue<(MonotonicTime, usize), Action>;
/// Trait abstracting over time-absolute and time-relative deadlines.
///
@ -81,7 +89,9 @@ impl Deadline for MonotonicTime {
///
/// ```
/// use std::time::Duration;
/// use asynchronix::model::{Model, Output}; use asynchronix::time::Scheduler;
/// use asynchronix::model::Model;
/// use asynchronix::ports::Output;
/// use asynchronix::time::Scheduler;
///
/// #[derive(Default)]
/// pub struct DelayedGreeter {
@ -141,8 +151,8 @@ impl<M: Model> Scheduler<M> {
///
/// fn is_third_millenium<M: Model>(scheduler: &Scheduler<M>) -> bool {
/// let time = scheduler.time();
///
/// time >= MonotonicTime::new(978307200, 0) && time < MonotonicTime::new(32535216000, 0)
/// time >= MonotonicTime::new(978307200, 0).unwrap()
/// && time < MonotonicTime::new(32535216000, 0).unwrap()
/// }
/// ```
pub fn time(&self) -> MonotonicTime {
@ -203,7 +213,8 @@ impl<M: Model> Scheduler<M> {
Ok(())
}
/// Schedules a cancellable event at a future time and returns an event key.
/// 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.
@ -212,12 +223,12 @@ impl<M: Model> Scheduler<M> {
///
/// ```
/// use asynchronix::model::Model;
/// use asynchronix::time::{EventKey, MonotonicTime, Scheduler};
/// use asynchronix::time::{ActionKey, MonotonicTime, Scheduler};
///
/// // An alarm clock that can be cancelled.
/// #[derive(Default)]
/// pub struct CancellableAlarmClock {
/// event_key: Option<EventKey>,
/// event_key: Option<ActionKey>,
/// }
///
/// impl CancellableAlarmClock {
@ -248,7 +259,7 @@ impl<M: Model> Scheduler<M> {
deadline: impl Deadline,
func: F,
arg: T,
) -> Result<EventKey, SchedulingError>
) -> Result<ActionKey, SchedulingError>
where
F: for<'a> InputFn<'a, M, T, S>,
T: Send + Clone + 'static,
@ -337,7 +348,7 @@ impl<M: Model> Scheduler<M> {
}
/// Schedules a cancellable, periodically recurring event at a future time
/// and returns an event key.
/// 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.
@ -348,13 +359,13 @@ impl<M: Model> Scheduler<M> {
/// use std::time::Duration;
///
/// use asynchronix::model::Model;
/// use asynchronix::time::{EventKey, MonotonicTime, Scheduler};
/// 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<EventKey>,
/// event_key: Option<ActionKey>,
/// }
///
/// impl CancellableBeepingAlarmClock {
@ -391,7 +402,7 @@ impl<M: Model> Scheduler<M> {
period: Duration,
func: F,
arg: T,
) -> Result<EventKey, SchedulingError>
) -> Result<ActionKey, SchedulingError>
where
F: for<'a> InputFn<'a, M, T, S> + Clone,
T: Send + Clone + 'static,
@ -425,34 +436,55 @@ impl<M: Model> fmt::Debug for Scheduler<M> {
}
}
/// Handle to a scheduled event.
/// Handle to a scheduled action.
///
/// An `EventKey` can be used to cancel a future event.
/// An `ActionKey` can be used to cancel a scheduled action.
#[derive(Clone, Debug)]
#[must_use = "prefer unkeyed scheduling methods if the event is never cancelled"]
pub struct EventKey {
#[must_use = "prefer unkeyed scheduling methods if the action is never cancelled"]
pub struct ActionKey {
is_cancelled: Arc<AtomicBool>,
}
impl EventKey {
/// Creates a key for a pending event.
impl ActionKey {
/// Creates a key for a pending action.
pub(crate) fn new() -> Self {
Self {
is_cancelled: Arc::new(AtomicBool::new(false)),
}
}
/// Checks whether the event was cancelled.
/// Checks whether the action was cancelled.
pub(crate) fn is_cancelled(&self) -> bool {
self.is_cancelled.load(Ordering::Relaxed)
}
/// Cancels the associated event.
/// Cancels the associated action.
pub fn cancel(self) {
self.is_cancelled.store(true, Ordering::Relaxed);
}
}
impl PartialEq for ActionKey {
/// Implements equality by considering clones to be equivalent, rather than
/// keys with the same `is_cancelled` value.
fn eq(&self, other: &Self) -> bool {
ptr::addr_eq(&*self.is_cancelled, &*other.is_cancelled)
}
}
impl Eq for ActionKey {}
impl Hash for ActionKey {
/// Implements `Hash`` by considering clones to be equivalent, rather than
/// keys with the same `is_cancelled` value.
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
ptr::hash(&*self.is_cancelled, state)
}
}
/// Error returned when the scheduled time or the repetition period are invalid.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SchedulingError {
@ -477,9 +509,73 @@ impl fmt::Display for SchedulingError {
impl Error for SchedulingError {}
/// A possibly periodic, possibly cancellable action that can be scheduled or
/// processed immediately.
pub struct Action {
inner: Box<dyn ActionInner>,
}
impl Action {
/// Creates a new `Action` from an `ActionInner`.
pub(crate) fn new<S: ActionInner>(s: S) -> Self {
Self { inner: Box::new(s) }
}
/// Reports whether the action was cancelled.
pub(crate) fn is_cancelled(&self) -> bool {
self.inner.is_cancelled()
}
/// If this is a periodic action, returns a boxed clone of this action and
/// its repetition period; otherwise returns `None`.
pub(crate) fn next(&self) -> Option<(Action, Duration)> {
self.inner
.next()
.map(|(inner, period)| (Self { inner }, period))
}
/// Returns a boxed future that performs the action.
pub(crate) fn into_future(self) -> Pin<Box<dyn Future<Output = ()> + Send>> {
self.inner.into_future()
}
/// Spawns the future that performs the action onto the provided executor.
///
/// This method is typically more efficient that spawning the boxed future
/// from `into_future` since it can directly spawn the unboxed future.
pub(crate) fn spawn_and_forget(self, executor: &Executor) {
self.inner.spawn_and_forget(executor)
}
}
impl fmt::Debug for Action {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("SchedulableEvent").finish_non_exhaustive()
}
}
/// Trait abstracting over the inner type of an action.
pub(crate) trait ActionInner: Send + 'static {
/// Reports whether the action was cancelled.
fn is_cancelled(&self) -> bool;
/// If this is a periodic action, returns a boxed clone of this action and
/// its repetition period; otherwise returns `None`.
fn next(&self) -> Option<(Box<dyn ActionInner>, Duration)>;
/// Returns a boxed future that performs the action.
fn into_future(self: Box<Self>) -> Pin<Box<dyn Future<Output = ()> + Send>>;
/// Spawns the future that performs the action onto the provided executor.
///
/// This method is typically more efficient that spawning the boxed future
/// from `into_future` since it can directly spawn the unboxed future.
fn spawn_and_forget(self: Box<Self>, executor: &Executor);
}
/// Schedules an event at a future time.
///
/// This method does not check whether the specified time lies in the future
/// This function does not check whether the specified time lies in the future
/// of the current simulation time.
pub(crate) fn schedule_event_at_unchecked<M, F, T, S>(
time: MonotonicTime,
@ -495,15 +591,15 @@ pub(crate) fn schedule_event_at_unchecked<M, F, T, S>(
{
let channel_id = sender.channel_id();
let event_dispatcher = Box::new(new_event_dispatcher(func, arg, sender));
let action = Action::new(OnceAction::new(process_event(func, arg, sender)));
let mut scheduler_queue = scheduler_queue.lock().unwrap();
scheduler_queue.insert((time, channel_id), event_dispatcher);
scheduler_queue.insert((time, channel_id), action);
}
/// Schedules an event at a future time, returning an event key.
/// Schedules an event at a future time, returning an action key.
///
/// This method does not check whether the specified time lies in the future
/// This function does not check whether the specified time lies in the future
/// of the current simulation time.
pub(crate) fn schedule_keyed_event_at_unchecked<M, F, T, S>(
time: MonotonicTime,
@ -511,31 +607,29 @@ pub(crate) fn schedule_keyed_event_at_unchecked<M, F, T, S>(
arg: T,
sender: Sender<M>,
scheduler_queue: &Mutex<SchedulerQueue>,
) -> EventKey
) -> ActionKey
where
M: Model,
F: for<'a> InputFn<'a, M, T, S>,
T: Send + Clone + 'static,
S: Send + 'static,
{
let event_key = EventKey::new();
let event_key = ActionKey::new();
let channel_id = sender.channel_id();
let event_dispatcher = Box::new(KeyedEventDispatcher::new(
let action = Action::new(KeyedOnceAction::new(
|ek| send_keyed_event(ek, func, arg, sender),
event_key.clone(),
func,
arg,
sender,
));
let mut scheduler_queue = scheduler_queue.lock().unwrap();
scheduler_queue.insert((time, channel_id), event_dispatcher);
scheduler_queue.insert((time, channel_id), action);
event_key
}
/// Schedules a periodic event at a future time.
///
/// This method does not check whether the specified time lies in the future
/// This function does not check whether the specified time lies in the future
/// of the current simulation time.
pub(crate) fn schedule_periodic_event_at_unchecked<M, F, T, S>(
time: MonotonicTime,
@ -552,15 +646,18 @@ pub(crate) fn schedule_periodic_event_at_unchecked<M, F, T, S>(
{
let channel_id = sender.channel_id();
let event_dispatcher = Box::new(PeriodicEventDispatcher::new(func, arg, sender, period));
let action = Action::new(PeriodicAction::new(
|| process_event(func, arg, sender),
period,
));
let mut scheduler_queue = scheduler_queue.lock().unwrap();
scheduler_queue.insert((time, channel_id), event_dispatcher);
scheduler_queue.insert((time, channel_id), action);
}
/// Schedules an event at a future time, returning an event key.
/// Schedules an event at a future time, returning an action key.
///
/// This method does not check whether the specified time lies in the future
/// This function does not check whether the specified time lies in the future
/// of the current simulation time.
pub(crate) fn schedule_periodic_keyed_event_at_unchecked<M, F, T, S>(
time: MonotonicTime,
@ -569,84 +666,52 @@ pub(crate) fn schedule_periodic_keyed_event_at_unchecked<M, F, T, S>(
arg: T,
sender: Sender<M>,
scheduler_queue: &Mutex<SchedulerQueue>,
) -> EventKey
) -> ActionKey
where
M: Model,
F: for<'a> InputFn<'a, M, T, S> + Clone,
T: Send + Clone + 'static,
S: Send + 'static,
{
let event_key = EventKey::new();
let event_key = ActionKey::new();
let channel_id = sender.channel_id();
let event_dispatcher = Box::new(PeriodicKeyedEventDispatcher::new(
event_key.clone(),
func,
arg,
sender,
let action = Action::new(KeyedPeriodicAction::new(
|ek| send_keyed_event(ek, func, arg, sender),
period,
event_key.clone(),
));
let mut scheduler_queue = scheduler_queue.lock().unwrap();
scheduler_queue.insert((time, channel_id), event_dispatcher);
scheduler_queue.insert((time, channel_id), action);
event_key
}
/// Trait for objects that can be converted to a future dispatching a scheduled
/// event.
pub(crate) trait ScheduledEvent: Send {
/// Reports whether the associated event was cancelled.
fn is_cancelled(&self) -> bool;
/// Returns a boxed clone of this event and the repetition period if this is
/// a periodic even, otherwise returns `None`.
fn next(&self) -> Option<(Box<dyn ScheduledEvent>, Duration)>;
/// Returns a boxed future dispatching the associated event.
fn into_future(self: Box<Self>) -> Pin<Box<dyn Future<Output = ()> + Send>>;
/// Spawns the future that dispatches the associated event onto the provided
/// executor.
///
/// This method is typically more efficient that spawning the boxed future
/// from `into_future` since it can directly spawn the unboxed future.
fn spawn_and_forget(self: Box<Self>, executor: &Executor);
}
pin_project! {
/// Object that can be converted to a future dispatching a non-cancellable
/// event.
/// An object that can be converted to a future performing a single
/// non-cancellable action.
///
/// Note that this particular event dispatcher is in fact already a future:
/// since the future cannot be cancelled and the dispatcher does not need to
/// be cloned, there is no need to defer the construction of the future.
/// This makes `into_future` a trivial cast, which saves a boxing operation.
pub(crate) struct EventDispatcher<F> {
/// Note that this particular action is in fact already a future: since the
/// future cannot be cancelled and the action does not need to be cloned,
/// there is no need to defer the construction of the future. This makes
/// `into_future` a trivial cast, which saves a boxing operation.
pub(crate) struct OnceAction<F> {
#[pin]
fut: F,
}
}
/// Constructs a new `EventDispatcher`.
///
/// Due to some limitations of type inference or of my understanding of it, the
/// constructor for this event dispatchers is a freestanding function.
fn new_event_dispatcher<M, F, T, S>(
func: F,
arg: T,
sender: Sender<M>,
) -> EventDispatcher<impl Future<Output = ()>>
impl<F> OnceAction<F>
where
M: Model,
F: for<'a> InputFn<'a, M, T, S>,
T: Send + Clone + 'static,
F: Future<Output = ()> + Send + 'static,
{
let fut = dispatch_event(func, arg, sender);
EventDispatcher { fut }
/// Constructs a new `OnceAction`.
pub(crate) fn new(fut: F) -> Self {
OnceAction { fut }
}
}
impl<F> Future for EventDispatcher<F>
impl<F> Future for OnceAction<F>
where
F: Future,
{
@ -658,14 +723,14 @@ where
}
}
impl<F> ScheduledEvent for EventDispatcher<F>
impl<F> ActionInner for OnceAction<F>
where
F: Future<Output = ()> + Send + 'static,
{
fn is_cancelled(&self) -> bool {
false
}
fn next(&self) -> Option<(Box<dyn ScheduledEvent>, Duration)> {
fn next(&self) -> Option<(Box<dyn ActionInner>, Duration)> {
None
}
fn into_future(self: Box<Self>) -> Pin<Box<dyn Future<Output = ()> + Send>> {
@ -677,230 +742,155 @@ where
}
}
/// Object that can be converted to a future dispatching a non-cancellable periodic
/// event.
pub(crate) struct PeriodicEventDispatcher<M, F, T, S>
/// An object that can be converted to a future performing a non-cancellable,
/// periodic action.
pub(crate) struct PeriodicAction<G, F>
where
M: Model,
G: (FnOnce() -> F) + Clone + Send + 'static,
F: Future<Output = ()> + Send + 'static,
{
func: F,
arg: T,
sender: Sender<M>,
/// A clonable generator for the associated future.
gen: G,
/// The action repetition period.
period: Duration,
_input_kind: PhantomData<S>,
}
impl<M, F, T, S> PeriodicEventDispatcher<M, F, T, S>
impl<G, F> PeriodicAction<G, F>
where
M: Model,
F: for<'a> InputFn<'a, M, T, S>,
T: Send + Clone + 'static,
G: (FnOnce() -> F) + Clone + Send + 'static,
F: Future<Output = ()> + Send + 'static,
{
/// Constructs a new `PeriodicEventDispatcher`.
fn new(func: F, arg: T, sender: Sender<M>, period: Duration) -> Self {
Self {
func,
arg,
sender,
period,
_input_kind: PhantomData,
}
/// Constructs a new `PeriodicAction`.
pub(crate) fn new(gen: G, period: Duration) -> Self {
Self { gen, period }
}
}
impl<M, F, T, S> ScheduledEvent for PeriodicEventDispatcher<M, F, T, S>
impl<G, F> ActionInner for PeriodicAction<G, F>
where
M: Model,
F: for<'a> InputFn<'a, M, T, S> + Clone,
T: Send + Clone + 'static,
S: Send + 'static,
G: (FnOnce() -> F) + Clone + Send + 'static,
F: Future<Output = ()> + Send + 'static,
{
fn is_cancelled(&self) -> bool {
false
}
fn next(&self) -> Option<(Box<dyn ScheduledEvent>, Duration)> {
let event = Box::new(Self::new(
self.func.clone(),
self.arg.clone(),
self.sender.clone(),
self.period,
));
fn next(&self) -> Option<(Box<dyn ActionInner>, Duration)> {
let event = Box::new(Self::new(self.gen.clone(), self.period));
Some((event, self.period))
}
fn into_future(self: Box<Self>) -> Pin<Box<dyn Future<Output = ()> + Send>> {
let Self {
func, arg, sender, ..
} = *self;
Box::pin(dispatch_event(func, arg, sender))
Box::pin((self.gen)())
}
fn spawn_and_forget(self: Box<Self>, executor: &Executor) {
let Self {
func, arg, sender, ..
} = *self;
let fut = dispatch_event(func, arg, sender);
executor.spawn_and_forget(fut);
executor.spawn_and_forget((self.gen)());
}
}
/// Object that can be converted to a future dispatching a cancellable event.
pub(crate) struct KeyedEventDispatcher<M, F, T, S>
/// An object that can be converted to a future performing a single, cancellable
/// action.
pub(crate) struct KeyedOnceAction<G, F>
where
M: Model,
F: for<'a> InputFn<'a, M, T, S>,
T: Send + Clone + 'static,
G: (FnOnce(ActionKey) -> F) + Send + 'static,
F: Future<Output = ()> + Send + 'static,
{
event_key: EventKey,
func: F,
arg: T,
sender: Sender<M>,
_input_kind: PhantomData<S>,
/// A generator for the associated future.
gen: G,
/// The event cancellation key.
event_key: ActionKey,
}
impl<M, F, T, S> KeyedEventDispatcher<M, F, T, S>
impl<G, F> KeyedOnceAction<G, F>
where
M: Model,
F: for<'a> InputFn<'a, M, T, S>,
T: Send + Clone + 'static,
G: (FnOnce(ActionKey) -> F) + Send + 'static,
F: Future<Output = ()> + Send + 'static,
{
/// Constructs a new `KeyedEventDispatcher`.
fn new(event_key: EventKey, func: F, arg: T, sender: Sender<M>) -> Self {
Self {
event_key,
func,
arg,
sender,
_input_kind: PhantomData,
}
/// Constructs a new `KeyedOnceAction`.
pub(crate) fn new(gen: G, event_key: ActionKey) -> Self {
Self { gen, event_key }
}
}
impl<M, F, T, S> ScheduledEvent for KeyedEventDispatcher<M, F, T, S>
impl<G, F> ActionInner for KeyedOnceAction<G, F>
where
M: Model,
F: for<'a> InputFn<'a, M, T, S>,
T: Send + Clone + 'static,
S: Send + 'static,
G: (FnOnce(ActionKey) -> F) + Send + 'static,
F: Future<Output = ()> + Send + 'static,
{
fn is_cancelled(&self) -> bool {
self.event_key.is_cancelled()
}
fn next(&self) -> Option<(Box<dyn ScheduledEvent>, Duration)> {
fn next(&self) -> Option<(Box<dyn ActionInner>, Duration)> {
None
}
fn into_future(self: Box<Self>) -> Pin<Box<dyn Future<Output = ()> + Send>> {
let Self {
event_key,
func,
arg,
sender,
..
} = *self;
Box::pin(dispatch_keyed_event(event_key, func, arg, sender))
Box::pin((self.gen)(self.event_key))
}
fn spawn_and_forget(self: Box<Self>, executor: &Executor) {
let Self {
event_key,
func,
arg,
sender,
..
} = *self;
let fut = dispatch_keyed_event(event_key, func, arg, sender);
executor.spawn_and_forget(fut);
executor.spawn_and_forget((self.gen)(self.event_key));
}
}
/// Object that can be converted to a future dispatching a cancellable event.
pub(crate) struct PeriodicKeyedEventDispatcher<M, F, T, S>
/// An object that can be converted to a future performing a periodic,
/// cancellable action.
pub(crate) struct KeyedPeriodicAction<G, F>
where
M: Model,
F: for<'a> InputFn<'a, M, T, S>,
T: Send + Clone + 'static,
G: (FnOnce(ActionKey) -> F) + Clone + Send + 'static,
F: Future<Output = ()> + Send + 'static,
{
event_key: EventKey,
func: F,
arg: T,
sender: Sender<M>,
/// A clonable generator for associated future.
gen: G,
/// The repetition period.
period: Duration,
_input_kind: PhantomData<S>,
/// The event cancellation key.
event_key: ActionKey,
}
impl<M, F, T, S> PeriodicKeyedEventDispatcher<M, F, T, S>
impl<G, F> KeyedPeriodicAction<G, F>
where
M: Model,
F: for<'a> InputFn<'a, M, T, S>,
T: Send + Clone + 'static,
G: (FnOnce(ActionKey) -> F) + Clone + Send + 'static,
F: Future<Output = ()> + Send + 'static,
{
/// Constructs a new `KeyedEventDispatcher`.
fn new(event_key: EventKey, func: F, arg: T, sender: Sender<M>, period: Duration) -> Self {
/// Constructs a new `KeyedPeriodicAction`.
pub(crate) fn new(gen: G, period: Duration, event_key: ActionKey) -> Self {
Self {
event_key,
func,
arg,
sender,
gen,
period,
_input_kind: PhantomData,
event_key,
}
}
}
impl<M, F, T, S> ScheduledEvent for PeriodicKeyedEventDispatcher<M, F, T, S>
impl<G, F> ActionInner for KeyedPeriodicAction<G, F>
where
M: Model,
F: for<'a> InputFn<'a, M, T, S> + Clone,
T: Send + Clone + 'static,
S: Send + 'static,
G: (FnOnce(ActionKey) -> F) + Clone + Send + 'static,
F: Future<Output = ()> + Send + 'static,
{
fn is_cancelled(&self) -> bool {
self.event_key.is_cancelled()
}
fn next(&self) -> Option<(Box<dyn ScheduledEvent>, Duration)> {
fn next(&self) -> Option<(Box<dyn ActionInner>, Duration)> {
let event = Box::new(Self::new(
self.event_key.clone(),
self.func.clone(),
self.arg.clone(),
self.sender.clone(),
self.gen.clone(),
self.period,
self.event_key.clone(),
));
Some((event, self.period))
}
fn into_future(self: Box<Self>) -> Pin<Box<dyn Future<Output = ()> + Send>> {
let Self {
event_key,
func,
arg,
sender,
..
} = *self;
Box::pin(dispatch_keyed_event(event_key, func, arg, sender))
Box::pin((self.gen)(self.event_key))
}
fn spawn_and_forget(self: Box<Self>, executor: &Executor) {
let Self {
event_key,
func,
arg,
sender,
..
} = *self;
let fut = dispatch_keyed_event(event_key, func, arg, sender);
executor.spawn_and_forget(fut);
executor.spawn_and_forget((self.gen)(self.event_key));
}
}
/// Asynchronously dispatch a regular, non-cancellable event.
async fn dispatch_event<M, F, T, S>(func: F, arg: T, sender: Sender<M>)
/// Asynchronously sends a non-cancellable event to a model input.
pub(crate) async fn process_event<M, F, T, S>(func: F, arg: T, sender: Sender<M>)
where
M: Model,
F: for<'a> InputFn<'a, M, T, S>,
T: Send + Clone + 'static,
T: Send + 'static,
{
let _ = sender
.send(
@ -916,9 +906,13 @@ where
.await;
}
/// Asynchronously dispatch a cancellable event.
async fn dispatch_keyed_event<M, F, T, S>(event_key: EventKey, func: F, arg: T, sender: Sender<M>)
where
/// Asynchronously sends a cancellable event to a model input.
pub(crate) async fn send_keyed_event<M, F, T, S>(
event_key: ActionKey,
func: F,
arg: T,
sender: Sender<M>,
) where
M: Model,
F: for<'a> InputFn<'a, M, T, S>,
T: Send + Clone + 'static,