use crate::{ok, sys, AsHandleRef, Handle, HandleBased, HandleRef, Status};
use std::cmp::{Eq, Ord, PartialEq, PartialOrd};
use std::hash::{Hash, Hasher};
use std::{ops, time as stdtime};
pub type MonotonicInstant = Instant<MonotonicTimeline, NsUnit>;
pub type SyntheticInstant = Instant<SyntheticTimeline, NsUnit>;
pub type BootInstant = Instant<BootTimeline>;
pub type Ticks<T> = Instant<T, TicksUnit>;
pub type MonotonicTicks = Instant<MonotonicTimeline, TicksUnit>;
pub type BootTicks = Instant<BootTimeline, TicksUnit>;
pub type MonotonicDuration = Duration<MonotonicTimeline>;
pub type BootDuration = Duration<BootTimeline>;
pub type SyntheticDuration = Duration<SyntheticTimeline, NsUnit>;
pub type MonotonicDurationTicks = Duration<MonotonicTimeline, TicksUnit>;
pub type BootDurationTicks = Duration<BootTimeline, TicksUnit>;
#[repr(transparent)]
pub struct Instant<T, U = NsUnit>(sys::zx_time_t, std::marker::PhantomData<(T, U)>);
impl<T, U> Clone for Instant<T, U> {
fn clone(&self) -> Self {
*self
}
}
impl<T, U> Copy for Instant<T, U> {}
impl<T, U> Default for Instant<T, U> {
fn default() -> Self {
Instant(0, std::marker::PhantomData)
}
}
impl<T, U> Hash for Instant<T, U> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<T, U> PartialEq for Instant<T, U> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T, U> Eq for Instant<T, U> {}
impl<T, U> PartialOrd for Instant<T, U> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl<T, U> Ord for Instant<T, U> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.cmp(&other.0)
}
}
impl<T, U> std::fmt::Debug for Instant<T, U> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
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 {
pub fn get() -> Self {
unsafe { Self::from_nanos(sys::zx_clock_get_monotonic()) }
}
pub fn after(duration: MonotonicDuration) -> Self {
unsafe { Self::from_nanos(sys::zx_deadline_after(duration.0)) }
}
pub fn sleep(self) {
unsafe {
sys::zx_nanosleep(self.0);
}
}
}
impl BootInstant {
pub fn get() -> Self {
unsafe { Self::from_nanos(sys::zx_clock_get_boot()) }
}
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);
pub const fn into_nanos(self) -> i64 {
self.0
}
pub const fn from_nanos(nanos: i64) -> Self {
Instant(nanos, std::marker::PhantomData)
}
}
impl MonotonicTicks {
pub fn get() -> Self {
Self(unsafe { sys::zx_ticks_get() }, std::marker::PhantomData)
}
}
impl BootTicks {
pub fn get() -> Self {
Self(unsafe { sys::zx_ticks_get_boot() }, std::marker::PhantomData)
}
}
impl<T: Timeline> Ticks<T> {
pub const fn into_raw(self) -> i64 {
self.0
}
pub const fn from_raw(raw: i64) -> Self {
Self(raw, std::marker::PhantomData)
}
pub fn per_second() -> i64 {
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);
}
}
pub trait Timeline {}
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct MonotonicTimeline;
impl Timeline for MonotonicTimeline {}
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct BootTimeline;
impl Timeline for BootTimeline {}
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct SyntheticTimeline;
impl Timeline for SyntheticTimeline {}
pub trait TimeUnit {}
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct NsUnit;
impl TimeUnit for NsUnit {}
#[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)]
#[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);
pub const fn into_nanos(self) -> i64 {
self.0
}
pub const fn into_micros(self) -> i64 {
self.0 / 1_000
}
pub const fn into_millis(self) -> i64 {
self.into_micros() / 1_000
}
pub const fn into_seconds(self) -> i64 {
self.into_millis() / 1_000
}
pub fn into_seconds_f64(self) -> f64 {
self.into_nanos() as f64 / 1_000_000_000f64
}
pub const fn into_minutes(self) -> i64 {
self.into_seconds() / 60
}
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> {
pub const fn into_raw(self) -> i64 {
self.0
}
pub const fn from_raw(raw: i64) -> Self {
Self(raw, std::marker::PhantomData)
}
}
impl MonotonicDuration {
pub fn sleep(self) {
MonotonicInstant::after(self).sleep()
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Timer<T = MonotonicTimeline>(Handle, std::marker::PhantomData<T>);
pub type MonotonicTimer = Timer<MonotonicTimeline>;
pub type BootTimer = Timer<BootTimeline>;
impl Timer<MonotonicTimeline> {
pub fn create() -> Self {
let mut out = 0;
let opts = 0;
let status = unsafe {
sys::zx_timer_create(opts, 0 , &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> {
pub fn create() -> Self {
Self(MonotonicTimer::create().0, std::marker::PhantomData)
}
}
impl<T: Timeline> Timer<T> {
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)
}
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;
#[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();
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 timer_basic() {
let slack = Duration::from_millis(0);
let ten_ms = Duration::from_millis(10);
let five_secs = Duration::from_seconds(5);
let timer = MonotonicTimer::create();
assert_eq!(
timer.wait_handle(Signals::TIMER_SIGNALED, MonotonicInstant::after(ten_ms)),
Err(Status::TIMED_OUT)
);
assert_eq!(timer.set(MonotonicInstant::after(five_secs), slack), Ok(()));
assert_eq!(
timer.wait_handle(Signals::TIMER_SIGNALED, Instant::INFINITE),
Ok(Signals::TIMER_SIGNALED)
);
assert_eq!(timer.cancel(), Ok(()));
assert_eq!(
timer.wait_handle(Signals::TIMER_SIGNALED, MonotonicInstant::after(ten_ms)),
Err(Status::TIMED_OUT)
);
}
#[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);
let timer = BootTimer::create();
assert_eq!(
timer.wait_handle(Signals::TIMER_SIGNALED, MonotonicInstant::after(ten_ms)),
Err(Status::TIMED_OUT)
);
assert_eq!(timer.set(BootInstant::get() + five_secs, slack), Ok(()));
assert_eq!(
timer.wait_handle(Signals::TIMER_SIGNALED, Instant::INFINITE),
Ok(Signals::TIMER_SIGNALED)
);
assert_eq!(timer.cancel(), Ok(()));
assert_eq!(
timer.wait_handle(Signals::TIMER_SIGNALED, MonotonicInstant::after(ten_ms)),
Err(Status::TIMED_OUT)
);
}
#[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() {
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
);
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
);
{
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);
}
{
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() {
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
);
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);
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);
assert_eq!(MonotonicDuration::from_nanos(30) / 3, Duration::from_nanos(10));
assert_eq!(MonotonicDuration::INFINITE_PAST / -1, Duration::INFINITE);
assert_eq!(-MonotonicDuration::from_nanos(30), Duration::from_nanos(-30));
assert_eq!(-MonotonicDuration::INFINITE_PAST, Duration::INFINITE);
{
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);
}
{
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));
}
}