blob: 93d23620e12b84c02af2441487a1973720c4846d [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Type-safe bindings for Zircon timer objects.
use crate::{
object_get_info_single, ok, sys, AsHandleRef, Handle, HandleBased, HandleRef, ObjectQuery,
Status, Topic,
};
use std::cmp::{Eq, Ord, PartialEq, PartialOrd};
use std::hash::Hash;
use std::{ops, time as stdtime};
use zerocopy::{FromBytes, Immutable, IntoBytes};
/// A timestamp from the monontonic clock. Does not advance while the system is suspended.
pub type MonotonicInstant = Instant<MonotonicTimeline, NsUnit>;
/// A timestamp from a user-defined clock with arbitrary behavior.
pub type SyntheticInstant = Instant<SyntheticTimeline, NsUnit>;
/// A timestamp from the boot clock. Advances while the system is suspended.
pub type BootInstant = Instant<BootTimeline>;
/// A timestamp from system ticks. Has an arbitrary unit that can be measured with
/// `Ticks::per_second()`.
pub type Ticks<T> = Instant<T, TicksUnit>;
/// A timestamp from system ticks on the monotonic timeline. Does not advance while the system is
/// suspended.
pub type MonotonicTicks = Instant<MonotonicTimeline, TicksUnit>;
/// A timestamp from system ticks on the boot timeline. Advances while the system is suspended.
pub type BootTicks = Instant<BootTimeline, TicksUnit>;
/// A duration on the monotonic timeline.
pub type MonotonicDuration = Duration<MonotonicTimeline>;
/// A duration on the boot timeline.
pub type BootDuration = Duration<BootTimeline>;
/// A duration from a user-defined clock with arbitrary behavior.
pub type SyntheticDuration = Duration<SyntheticTimeline, NsUnit>;
/// A duration between two system ticks monotonic timestamps.
pub type MonotonicDurationTicks = Duration<MonotonicTimeline, TicksUnit>;
/// A duration between two system ticks boot timestamps.
pub type BootDurationTicks = Duration<BootTimeline, TicksUnit>;
/// A timestamp from the kernel. Generic over both the timeline and the units it is measured in.
#[derive(
Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord, FromBytes, IntoBytes, Immutable,
)]
#[repr(transparent)]
pub struct Instant<T, U = NsUnit>(sys::zx_time_t, std::marker::PhantomData<(T, U)>);
impl<T, U> std::fmt::Debug for Instant<T, U> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// Avoid line noise from the marker type but do include the timeline in the output.
let timeline_name = std::any::type_name::<T>();
let short_timeline_name =
timeline_name.rsplit_once("::").map(|(_, n)| n).unwrap_or(timeline_name);
let units_name = std::any::type_name::<U>();
let short_units_name = units_name.rsplit_once("::").map(|(_, n)| n).unwrap_or(units_name);
f.debug_tuple(&format!("Instant<{short_timeline_name}, {short_units_name}>"))
.field(&self.0)
.finish()
}
}
impl MonotonicInstant {
/// Get the current monotonic time which does not advance during system suspend.
///
/// Wraps the
/// [zx_clock_get_monotonic](https://fuchsia.dev/fuchsia-src/reference/syscalls/clock_get_monotonic.md)
/// syscall.
pub fn get() -> Self {
unsafe { Self::from_nanos(sys::zx_clock_get_monotonic()) }
}
/// Compute a deadline for the time in the future that is the given `Duration` away.
///
/// Wraps the
/// [zx_deadline_after](https://fuchsia.dev/fuchsia-src/reference/syscalls/deadline_after.md)
/// syscall.
pub fn after(duration: MonotonicDuration) -> Self {
unsafe { Self::from_nanos(sys::zx_deadline_after(duration.0)) }
}
/// Sleep until the given time.
///
/// Wraps the
/// [zx_nanosleep](https://fuchsia.dev/fuchsia-src/reference/syscalls/nanosleep.md)
/// syscall.
pub fn sleep(self) {
unsafe {
sys::zx_nanosleep(self.0);
}
}
}
impl BootInstant {
/// Get the current boot time which advances during system suspend.
pub fn get() -> Self {
// SAFETY: FFI call that is always sound to call.
unsafe { Self::from_nanos(sys::zx_clock_get_boot()) }
}
/// Compute a deadline for the time in the future that is the given `Duration` away.
pub fn after(duration: BootDuration) -> Self {
Self::from_nanos(Self::get().into_nanos().saturating_add(duration.0))
}
}
impl<T: Timeline, U: TimeUnit> Instant<T, U> {
pub const ZERO: Instant<T, U> = Instant(0, std::marker::PhantomData);
}
impl<T: Timeline> Instant<T> {
pub const INFINITE: Instant<T, NsUnit> =
Instant(sys::ZX_TIME_INFINITE, std::marker::PhantomData);
pub const INFINITE_PAST: Instant<T, NsUnit> =
Instant(sys::ZX_TIME_INFINITE_PAST, std::marker::PhantomData);
/// Returns the number of nanoseconds since the epoch contained by this `Time`.
pub const fn into_nanos(self) -> i64 {
self.0
}
/// Return a strongly-typed `Time` from a raw number of nanoseconds.
pub const fn from_nanos(nanos: i64) -> Self {
Instant(nanos, std::marker::PhantomData)
}
}
impl MonotonicTicks {
/// Read the number of high-precision timer ticks on the monotonic timeline. These ticks may be
/// processor cycles, high speed timer, profiling timer, etc. They do not advance while the
/// system is suspended.
///
/// Wraps the
/// [zx_ticks_get](https://fuchsia.dev/fuchsia-src/reference/syscalls/ticks_get.md)
/// syscall.
pub fn get() -> Self {
// SAFETY: FFI call that is always sound to call.
Self(unsafe { sys::zx_ticks_get() }, std::marker::PhantomData)
}
}
impl BootTicks {
/// Read the number of high-precision timer ticks on the boot timeline. These ticks may be
/// processor cycles, high speed timer, profiling timer, etc. They advance while the
/// system is suspended.
pub fn get() -> Self {
// SAFETY: FFI call that is always sound to call.
Self(unsafe { sys::zx_ticks_get_boot() }, std::marker::PhantomData)
}
}
impl<T: Timeline> Ticks<T> {
/// Return the number of ticks contained by this `Ticks`.
pub const fn into_raw(self) -> i64 {
self.0
}
/// Return a strongly-typed `Ticks` from a raw number of system ticks.
pub const fn from_raw(raw: i64) -> Self {
Self(raw, std::marker::PhantomData)
}
/// Return the number of high-precision timer ticks in a second.
///
/// Wraps the
/// [zx_ticks_per_second](https://fuchsia.dev/fuchsia-src/reference/syscalls/ticks_per_second.md)
/// syscall.
pub fn per_second() -> i64 {
// SAFETY: FFI call that is always sound to call.
unsafe { sys::zx_ticks_per_second() }
}
}
impl<T: Timeline, U: TimeUnit> ops::Add<Duration<T, U>> for Instant<T, U> {
type Output = Instant<T, U>;
fn add(self, dur: Duration<T, U>) -> Self::Output {
Self(self.0.saturating_add(dur.0), std::marker::PhantomData)
}
}
impl<T: Timeline, U: TimeUnit> ops::Sub<Duration<T, U>> for Instant<T, U> {
type Output = Instant<T, U>;
fn sub(self, dur: Duration<T, U>) -> Self::Output {
Self(self.0.saturating_sub(dur.0), std::marker::PhantomData)
}
}
impl<T: Timeline, U: TimeUnit> ops::Sub<Instant<T, U>> for Instant<T, U> {
type Output = Duration<T, U>;
fn sub(self, rhs: Instant<T, U>) -> Self::Output {
Duration(self.0.saturating_sub(rhs.0), std::marker::PhantomData)
}
}
impl<T: Timeline, U: TimeUnit> ops::AddAssign<Duration<T, U>> for Instant<T, U> {
fn add_assign(&mut self, dur: Duration<T, U>) {
self.0 = self.0.saturating_add(dur.0);
}
}
impl<T: Timeline, U: TimeUnit> ops::SubAssign<Duration<T, U>> for Instant<T, U> {
fn sub_assign(&mut self, dur: Duration<T, U>) {
self.0 = self.0.saturating_sub(dur.0);
}
}
/// A marker trait for times to prevent accidental comparison between different timelines.
pub trait Timeline: Default + Copy + Clone + PartialEq + Eq {}
/// A marker type for the system's monotonic timeline which pauses during suspend.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct MonotonicTimeline;
impl Timeline for MonotonicTimeline {}
/// A marker type for the system's boot timeline which continues running during suspend.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct BootTimeline;
impl Timeline for BootTimeline {}
/// A marker type representing a synthetic timeline defined by a kernel clock object.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct SyntheticTimeline;
impl Timeline for SyntheticTimeline {}
/// A marker trait for times and durations to prevent accidental comparison between different units.
pub trait TimeUnit {}
/// A marker type representing nanoseconds.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct NsUnit;
impl TimeUnit for NsUnit {}
/// A marker type representing system ticks.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct TicksUnit;
impl TimeUnit for TicksUnit {}
#[derive(
Debug,
Default,
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
Hash,
FromBytes,
IntoBytes,
Immutable,
)]
#[repr(transparent)]
pub struct Duration<T, U = NsUnit>(sys::zx_duration_t, std::marker::PhantomData<(T, U)>);
impl<T: Timeline> From<stdtime::Duration> for Duration<T, NsUnit> {
fn from(dur: stdtime::Duration) -> Self {
Duration::from_seconds(dur.as_secs() as i64)
+ Duration::from_nanos(dur.subsec_nanos() as i64)
}
}
impl<T: Timeline, U: TimeUnit> ops::Add<Instant<T, U>> for Duration<T, U> {
type Output = Instant<T, U>;
fn add(self, time: Instant<T, U>) -> Self::Output {
Instant(self.0.saturating_add(time.0), std::marker::PhantomData)
}
}
impl<T: Timeline, U: TimeUnit> ops::Add for Duration<T, U> {
type Output = Duration<T, U>;
fn add(self, rhs: Duration<T, U>) -> Self::Output {
Self(self.0.saturating_add(rhs.0), std::marker::PhantomData)
}
}
impl<T: Timeline, U: TimeUnit> ops::Sub for Duration<T, U> {
type Output = Duration<T, U>;
fn sub(self, rhs: Duration<T, U>) -> Duration<T, U> {
Self(self.0.saturating_sub(rhs.0), std::marker::PhantomData)
}
}
impl<T: Timeline, U: TimeUnit> ops::AddAssign for Duration<T, U> {
fn add_assign(&mut self, rhs: Duration<T, U>) {
self.0 = self.0.saturating_add(rhs.0);
}
}
impl<T: Timeline, U: TimeUnit> ops::SubAssign for Duration<T, U> {
fn sub_assign(&mut self, rhs: Duration<T, U>) {
self.0 = self.0.saturating_sub(rhs.0);
}
}
impl<T: Timeline, S: Into<i64>, U: TimeUnit> ops::Mul<S> for Duration<T, U> {
type Output = Self;
fn mul(self, mul: S) -> Self {
Self(self.0.saturating_mul(mul.into()), std::marker::PhantomData)
}
}
impl<S: Into<i64>, T: Timeline, U: TimeUnit> ops::Div<S> for Duration<T, U> {
type Output = Self;
fn div(self, div: S) -> Self {
Self(self.0.saturating_div(div.into()), std::marker::PhantomData)
}
}
impl<T: Timeline, U: TimeUnit> ops::Neg for Duration<T, U> {
type Output = Self;
fn neg(self) -> Self::Output {
Self(self.0.saturating_neg(), std::marker::PhantomData)
}
}
impl<T: Timeline> Duration<T, NsUnit> {
pub const INFINITE: Duration<T> = Duration(sys::zx_duration_t::MAX, std::marker::PhantomData);
pub const INFINITE_PAST: Duration<T> =
Duration(sys::zx_duration_t::MIN, std::marker::PhantomData);
pub const ZERO: Duration<T> = Duration(0, std::marker::PhantomData);
/// Returns the number of nanoseconds contained by this `Duration`.
pub const fn into_nanos(self) -> i64 {
self.0
}
/// Returns the total number of whole microseconds contained by this `Duration`.
pub const fn into_micros(self) -> i64 {
self.0 / 1_000
}
/// Returns the total number of whole milliseconds contained by this `Duration`.
pub const fn into_millis(self) -> i64 {
self.into_micros() / 1_000
}
/// Returns the total number of whole seconds contained by this `Duration`.
pub const fn into_seconds(self) -> i64 {
self.into_millis() / 1_000
}
/// Returns the duration as a floating-point value in seconds.
pub fn into_seconds_f64(self) -> f64 {
self.into_nanos() as f64 / 1_000_000_000f64
}
/// Returns the total number of whole minutes contained by this `Duration`.
pub const fn into_minutes(self) -> i64 {
self.into_seconds() / 60
}
/// Returns the total number of whole hours contained by this `Duration`.
pub const fn into_hours(self) -> i64 {
self.into_minutes() / 60
}
pub const fn from_nanos(nanos: i64) -> Self {
Duration(nanos, std::marker::PhantomData)
}
pub const fn from_micros(micros: i64) -> Self {
Duration(micros.saturating_mul(1_000), std::marker::PhantomData)
}
pub const fn from_millis(millis: i64) -> Self {
Duration::from_micros(millis.saturating_mul(1_000))
}
pub const fn from_seconds(secs: i64) -> Self {
Duration::from_millis(secs.saturating_mul(1_000))
}
pub const fn from_minutes(min: i64) -> Self {
Duration::from_seconds(min.saturating_mul(60))
}
pub const fn from_hours(hours: i64) -> Self {
Duration::from_minutes(hours.saturating_mul(60))
}
}
impl<T: Timeline> Duration<T, TicksUnit> {
/// Return the raw number of ticks represented by this `Duration`.
pub const fn into_raw(self) -> i64 {
self.0
}
/// Return a typed wrapper around the provided number of ticks.
pub const fn from_raw(raw: i64) -> Self {
Self(raw, std::marker::PhantomData)
}
}
impl MonotonicDuration {
/// Sleep for the given amount of time.
pub fn sleep(self) {
MonotonicInstant::after(self).sleep()
}
}
/// An object representing a Zircon timer, such as the one returned by
/// [zx_timer_create](https://fuchsia.dev/fuchsia-src/reference/syscalls/timer_create.md).
///
/// As essentially a subtype of `Handle`, it can be freely interconverted.
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
// TODO(https://fxbug.dev/361661898) remove default type when FIDL understands mono vs. boot timers
pub struct Timer<T = MonotonicTimeline>(Handle, std::marker::PhantomData<T>);
#[repr(C)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, FromBytes, Immutable)]
pub struct TimerInfo<T: Timeline> {
pub options: u32,
// NB: Currently, this field is irrelevant, because the clock ID will always match the timeline
// of the Timer which is represented by this TimerInfo. For example, with a MonotonicTimer,
// clock_id is always 0 (ZX_CLOCK_MONOTONIC). If the timer syscalls eventually allow arbitrary
// clock IDs, we can make this field public.
clock_id: u32,
pub deadline: Instant<T>,
pub slack: Duration<T>,
}
pub type MonotonicTimerInfo = TimerInfo<MonotonicTimeline>;
pub type BootTimerInfo = TimerInfo<BootTimeline>;
pub type SyntheticTimerInfo = TimerInfo<SyntheticTimeline>;
static_assertions::assert_eq_size!(MonotonicTimerInfo, sys::zx_info_timer_t);
static_assertions::assert_eq_size!(BootTimerInfo, sys::zx_info_timer_t);
static_assertions::assert_eq_size!(SyntheticTimerInfo, sys::zx_info_timer_t);
impl<T: Timeline> Default for TimerInfo<T> {
fn default() -> Self {
Self::from_raw(sys::zx_info_timer_t::default())
}
}
impl<T: Timeline> TimerInfo<T> {
fn from_raw(info: sys::zx_info_timer_t) -> Self {
zerocopy::transmute!(info)
}
}
struct TimerInfoQuery;
unsafe impl ObjectQuery for TimerInfoQuery {
const TOPIC: Topic = Topic::TIMER;
type InfoTy = sys::zx_info_timer_t;
}
/// A timer that measures its deadlines against the monotonic clock.
pub type MonotonicTimer = Timer<MonotonicTimeline>;
/// A timer that measures its deadlines against the boot clock.
pub type BootTimer = Timer<BootTimeline>;
impl<T: Timeline> Timer<T> {
/// Wraps the
/// [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
/// syscall for the ZX_INFO_TIMER_T topic.
pub fn info(&self) -> Result<TimerInfo<T>, Status> {
Ok(TimerInfo::from_raw(object_get_info_single::<TimerInfoQuery>(self.as_handle_ref())?))
}
}
impl Timer<MonotonicTimeline> {
/// Create a timer, an object that can signal when a specified point on the monotonic clock has
/// been reached. Wraps the
/// [zx_timer_create](https://fuchsia.dev/fuchsia-src/reference/syscalls/timer_create.md)
/// syscall.
///
/// # Panics
///
/// If the kernel reports no memory available to create a timer or the process' job policy
/// denies timer creation.
pub fn create() -> Self {
let mut out = 0;
let opts = 0;
let status = unsafe { sys::zx_timer_create(opts, sys::ZX_CLOCK_MONOTONIC, &mut out) };
ok(status)
.expect("timer creation always succeeds except with OOM or when job policy denies it");
unsafe { Self::from(Handle::from_raw(out)) }
}
}
impl Timer<BootTimeline> {
/// Create a timer, an object that can signal when a specified point on the boot clock has been
/// reached. Wraps the
/// [zx_timer_create](https://fuchsia.dev/fuchsia-src/reference/syscalls/timer_create.md)
/// syscall.
///
/// If the timer elapses while the system is suspended it will not wake the system.
///
/// # Panics
///
/// If the kernel reports no memory available to create a timer or the process' job policy
/// denies timer creation.
pub fn create() -> Self {
let mut out = 0;
let opts = 0;
let status = unsafe { sys::zx_timer_create(opts, sys::ZX_CLOCK_BOOT, &mut out) };
ok(status)
.expect("timer creation always succeeds except with OOM or when job policy denies it");
unsafe { Self::from(Handle::from_raw(out)) }
}
}
impl<T: Timeline> Timer<T> {
/// Start a one-shot timer that will fire when `deadline` passes. Wraps the
/// [zx_timer_set](https://fuchsia.dev/fuchsia-src/reference/syscalls/timer_set.md)
/// syscall.
pub fn set(&self, deadline: Instant<T>, slack: Duration<T, NsUnit>) -> Result<(), Status> {
let status = unsafe {
sys::zx_timer_set(self.raw_handle(), deadline.into_nanos(), slack.into_nanos())
};
ok(status)
}
/// Cancels a pending timer that was started with set(). Wraps the
/// [zx_timer_cancel](https://fuchsia.dev/fuchsia-src/reference/syscalls/timer_cancel.md)
/// syscall.
pub fn cancel(&self) -> Result<(), Status> {
let status = unsafe { sys::zx_timer_cancel(self.raw_handle()) };
ok(status)
}
}
impl<T: Timeline> AsHandleRef for Timer<T> {
fn as_handle_ref(&self) -> HandleRef<'_> {
self.0.as_handle_ref()
}
}
impl<T: Timeline> From<Handle> for Timer<T> {
fn from(handle: Handle) -> Self {
Timer(handle, std::marker::PhantomData)
}
}
impl<T: Timeline> From<Timer<T>> for Handle {
fn from(x: Timer<T>) -> Handle {
x.0
}
}
impl<T: Timeline> HandleBased for Timer<T> {}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Signals, WaitResult};
#[test]
fn time_debug_repr_is_short() {
assert_eq!(
format!("{:?}", MonotonicInstant::from_nanos(0)),
"Instant<MonotonicTimeline, NsUnit>(0)"
);
assert_eq!(
format!("{:?}", SyntheticInstant::from_nanos(0)),
"Instant<SyntheticTimeline, NsUnit>(0)"
);
}
#[test]
fn monotonic_time_increases() {
let time1 = MonotonicInstant::get();
Duration::from_nanos(1_000).sleep();
let time2 = MonotonicInstant::get();
assert!(time2 > time1);
}
#[test]
fn ticks_increases() {
let ticks1 = MonotonicTicks::get();
Duration::from_nanos(1_000).sleep();
let ticks2 = MonotonicTicks::get();
assert!(ticks2 > ticks1);
}
#[test]
fn boot_time_increases() {
let time1 = BootInstant::get();
Duration::from_nanos(1_000).sleep();
let time2 = BootInstant::get();
assert!(time2 > time1);
}
#[test]
fn boot_ticks_increases() {
let ticks1 = BootTicks::get();
Duration::from_nanos(1_000).sleep();
let ticks2 = BootTicks::get();
assert!(ticks2 > ticks1);
}
#[test]
fn tick_length() {
let sleep_time = Duration::from_millis(1);
let ticks1 = MonotonicTicks::get();
sleep_time.sleep();
let ticks2 = MonotonicTicks::get();
// The number of ticks should have increased by at least 1 ms worth
let sleep_ticks = MonotonicDurationTicks::from_raw(
sleep_time.into_millis() * (MonotonicTicks::per_second() / 1000),
);
assert!(ticks2 >= (ticks1 + sleep_ticks));
}
#[test]
fn sleep() {
let sleep_ns = Duration::from_millis(1);
let time1 = MonotonicInstant::get();
sleep_ns.sleep();
let time2 = MonotonicInstant::get();
assert!(time2 > time1 + sleep_ns);
}
#[test]
fn from_std() {
let std_dur = stdtime::Duration::new(25, 25);
let dur = MonotonicDuration::from(std_dur);
let std_dur_nanos = (1_000_000_000 * std_dur.as_secs()) + std_dur.subsec_nanos() as u64;
assert_eq!(std_dur_nanos as i64, dur.into_nanos());
}
#[test]
fn i64_conversions() {
let nanos_in_one_hour = 3_600_000_000_000;
let dur_from_nanos = MonotonicDuration::from_nanos(nanos_in_one_hour);
let dur_from_hours = MonotonicDuration::from_hours(1);
assert_eq!(dur_from_nanos, dur_from_hours);
assert_eq!(dur_from_nanos.into_nanos(), dur_from_hours.into_nanos());
assert_eq!(dur_from_nanos.into_nanos(), nanos_in_one_hour);
assert_eq!(dur_from_nanos.into_hours(), 1);
}
#[test]
fn monotonic_timer_basic() {
let slack = Duration::from_millis(0);
let ten_ms = Duration::from_millis(10);
let five_secs = Duration::from_seconds(5);
// Create a timer
let timer = MonotonicTimer::create();
let info = timer.info().expect("info() failed");
assert_eq!(info.clock_id, sys::ZX_CLOCK_MONOTONIC);
assert_eq!(info.deadline, Instant::ZERO);
// NB: We don't check slack, because the kernel can change it based on its own policies.
// Should not signal yet.
assert_eq!(
timer.wait_handle(Signals::TIMER_SIGNALED, MonotonicInstant::after(ten_ms)),
WaitResult::TimedOut(Signals::empty()),
);
// Set it, and soon it should signal.
let instant = MonotonicInstant::after(five_secs);
assert_eq!(timer.set(instant, slack), Ok(()));
assert_eq!(
timer.wait_handle(Signals::TIMER_SIGNALED, Instant::INFINITE),
WaitResult::Ok(Signals::TIMER_SIGNALED)
);
// Once the timer has fired, its deadline is reset to zero.
assert_eq!(timer.info().expect("info() failed").deadline, Instant::ZERO);
// Cancel it, and it should stop signalling.
assert_eq!(timer.cancel(), Ok(()));
assert_eq!(
timer.wait_handle(Signals::TIMER_SIGNALED, MonotonicInstant::after(ten_ms)),
WaitResult::TimedOut(Signals::empty()),
);
assert_eq!(timer.info().expect("info() failed").deadline, Instant::ZERO);
assert_eq!(timer.set(Instant::INFINITE, slack), Ok(()));
assert_eq!(timer.info().expect("info() failed").deadline, Instant::INFINITE);
}
#[test]
fn boot_timer_basic() {
let slack = Duration::from_millis(0);
let ten_ms = Duration::from_millis(10);
let five_secs = Duration::from_seconds(5);
// Create a timer
let timer = BootTimer::create();
let info = timer.info().expect("info() failed");
assert_eq!(info.clock_id, sys::ZX_CLOCK_BOOT);
assert_eq!(info.deadline, Instant::ZERO);
// NB: We don't check slack, because the kernel can change it based on its own policies.
// Should not signal yet.
assert_eq!(
timer.wait_handle(Signals::TIMER_SIGNALED, MonotonicInstant::after(ten_ms)),
WaitResult::TimedOut(Signals::empty())
);
// Set it, and soon it should signal.
let instant = BootInstant::after(five_secs);
assert_eq!(timer.set(instant, slack), Ok(()));
assert_eq!(
timer.wait_handle(Signals::TIMER_SIGNALED, Instant::INFINITE),
WaitResult::Ok(Signals::TIMER_SIGNALED)
);
// Once the timer has fired, its deadline is reset to zero.
assert_eq!(timer.info().expect("info() failed").deadline, Instant::ZERO);
// Cancel it, and it should stop signalling.
assert_eq!(timer.cancel(), Ok(()));
assert_eq!(
timer.wait_handle(Signals::TIMER_SIGNALED, MonotonicInstant::after(ten_ms)),
WaitResult::TimedOut(Signals::empty())
);
assert_eq!(timer.info().expect("info() failed").deadline, Instant::ZERO);
assert_eq!(timer.set(Instant::INFINITE, slack), Ok(()));
assert_eq!(timer.info().expect("info() failed").deadline, Instant::INFINITE);
}
#[test]
fn time_minus_time() {
let lhs = MonotonicInstant::from_nanos(10);
let rhs = MonotonicInstant::from_nanos(30);
assert_eq!(lhs - rhs, Duration::from_nanos(-20));
}
#[test]
fn time_saturation() {
// Addition
assert_eq!(
MonotonicInstant::from_nanos(10) + Duration::from_nanos(30),
MonotonicInstant::from_nanos(40)
);
assert_eq!(
MonotonicInstant::from_nanos(10) + Duration::INFINITE,
MonotonicInstant::INFINITE
);
assert_eq!(
MonotonicInstant::from_nanos(-10) + Duration::INFINITE_PAST,
MonotonicInstant::INFINITE_PAST
);
// Subtraction
assert_eq!(
MonotonicInstant::from_nanos(10) - Duration::from_nanos(30),
MonotonicInstant::from_nanos(-20)
);
assert_eq!(
MonotonicInstant::from_nanos(-10) - Duration::INFINITE,
MonotonicInstant::INFINITE_PAST
);
assert_eq!(
MonotonicInstant::from_nanos(10) - Duration::INFINITE_PAST,
MonotonicInstant::INFINITE
);
// Assigning addition
{
let mut t = MonotonicInstant::from_nanos(10);
t += Duration::from_nanos(30);
assert_eq!(t, MonotonicInstant::from_nanos(40));
}
{
let mut t = MonotonicInstant::from_nanos(10);
t += Duration::INFINITE;
assert_eq!(t, MonotonicInstant::INFINITE);
}
{
let mut t = MonotonicInstant::from_nanos(-10);
t += Duration::INFINITE_PAST;
assert_eq!(t, MonotonicInstant::INFINITE_PAST);
}
// Assigning subtraction
{
let mut t = MonotonicInstant::from_nanos(10);
t -= Duration::from_nanos(30);
assert_eq!(t, MonotonicInstant::from_nanos(-20));
}
{
let mut t = MonotonicInstant::from_nanos(-10);
t -= Duration::INFINITE;
assert_eq!(t, MonotonicInstant::INFINITE_PAST);
}
{
let mut t = MonotonicInstant::from_nanos(10);
t -= Duration::INFINITE_PAST;
assert_eq!(t, MonotonicInstant::INFINITE);
}
}
#[test]
fn duration_saturation() {
// Addition
assert_eq!(
MonotonicDuration::from_nanos(10) + Duration::from_nanos(30),
Duration::from_nanos(40)
);
assert_eq!(MonotonicDuration::from_nanos(10) + Duration::INFINITE, Duration::INFINITE);
assert_eq!(
MonotonicDuration::from_nanos(-10) + Duration::INFINITE_PAST,
Duration::INFINITE_PAST
);
// Subtraction
assert_eq!(
MonotonicDuration::from_nanos(10) - Duration::from_nanos(30),
Duration::from_nanos(-20)
);
assert_eq!(
MonotonicDuration::from_nanos(-10) - Duration::INFINITE,
Duration::INFINITE_PAST
);
assert_eq!(MonotonicDuration::from_nanos(10) - Duration::INFINITE_PAST, Duration::INFINITE);
// Multiplication
assert_eq!(MonotonicDuration::from_nanos(10) * 3, Duration::from_nanos(30));
assert_eq!(MonotonicDuration::from_nanos(10) * i64::MAX, Duration::INFINITE);
assert_eq!(MonotonicDuration::from_nanos(10) * i64::MIN, Duration::INFINITE_PAST);
// Division
assert_eq!(MonotonicDuration::from_nanos(30) / 3, Duration::from_nanos(10));
assert_eq!(MonotonicDuration::INFINITE_PAST / -1, Duration::INFINITE);
// Negation
assert_eq!(-MonotonicDuration::from_nanos(30), Duration::from_nanos(-30));
assert_eq!(-MonotonicDuration::INFINITE_PAST, Duration::INFINITE);
// Assigning addition
{
let mut t = MonotonicDuration::from_nanos(10);
t += Duration::from_nanos(30);
assert_eq!(t, Duration::from_nanos(40));
}
{
let mut t = MonotonicDuration::from_nanos(10);
t += Duration::INFINITE;
assert_eq!(t, Duration::INFINITE);
}
{
let mut t = MonotonicDuration::from_nanos(-10);
t += Duration::INFINITE_PAST;
assert_eq!(t, Duration::INFINITE_PAST);
}
// Assigning subtraction
{
let mut t = MonotonicDuration::from_nanos(10);
t -= Duration::from_nanos(30);
assert_eq!(t, Duration::from_nanos(-20));
}
{
let mut t = MonotonicDuration::from_nanos(-10);
t -= Duration::INFINITE;
assert_eq!(t, Duration::INFINITE_PAST);
}
{
let mut t = MonotonicDuration::from_nanos(10);
t -= Duration::INFINITE_PAST;
assert_eq!(t, Duration::INFINITE);
}
}
#[test]
fn time_minus_time_saturates() {
assert_eq!(
MonotonicInstant::INFINITE - MonotonicInstant::INFINITE_PAST,
Duration::INFINITE
);
}
#[test]
fn time_and_duration_defaults() {
assert_eq!(MonotonicInstant::default(), MonotonicInstant::from_nanos(0));
assert_eq!(Duration::default(), MonotonicDuration::from_nanos(0));
}
}