blob: f2f827042220698a08572f17b357e2b2f920174b [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::{AsHandleRef, ClockId, HandleBased, Handle, HandleRef, Status};
use crate::ok;
use fuchsia_zircon_sys as sys;
use std::ops;
use std::time as stdtime;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Duration(sys::zx_duration_t);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Time(sys::zx_time_t);
impl From<stdtime::Duration> for Duration {
fn from(dur: stdtime::Duration) -> Self {
Duration::from_seconds(dur.as_secs() as i64) +
Duration::from_nanos(dur.subsec_nanos() as i64)
}
}
impl ops::Add<Duration> for Time {
type Output = Time;
fn add(self, dur: Duration) -> Time {
Time::from_nanos(dur.nanos() + self.nanos())
}
}
impl ops::Add<Time> for Duration {
type Output = Time;
fn add(self, time: Time) -> Time {
Time::from_nanos(self.nanos() + time.nanos())
}
}
impl ops::Add for Duration {
type Output = Duration;
fn add(self, dur: Duration) -> Duration {
Duration::from_nanos(self.nanos() + dur.nanos())
}
}
impl ops::Sub for Duration {
type Output = Duration;
fn sub(self, dur: Duration) -> Duration {
Duration::from_nanos(self.nanos() - dur.nanos())
}
}
impl ops::Sub<Duration> for Time {
type Output = Time;
fn sub(self, dur: Duration) -> Time {
Time::from_nanos(self.nanos() - dur.nanos())
}
}
impl ops::Sub<Time> for Time {
type Output = Duration;
fn sub(self, other: Time) -> Duration {
Duration::from_nanos(self.nanos() - other.nanos())
}
}
impl ops::AddAssign for Duration {
fn add_assign(&mut self, dur: Duration) {
self.0 += dur.nanos()
}
}
impl ops::SubAssign for Duration {
fn sub_assign(&mut self, dur: Duration) {
self.0 -= dur.nanos()
}
}
impl ops::AddAssign<Duration> for Time {
fn add_assign(&mut self, dur: Duration) {
self.0 += dur.nanos()
}
}
impl ops::SubAssign<Duration> for Time {
fn sub_assign(&mut self, dur: Duration) {
self.0 -= dur.nanos()
}
}
impl<T> ops::Mul<T> for Duration
where T: Into<i64>
{
type Output = Self;
fn mul(self, mul: T) -> Self {
Duration::from_nanos(self.0 * mul.into())
}
}
impl<T> ops::Div<T> for Duration
where T: Into<i64>
{
type Output = Self;
fn div(self, div: T) -> Self {
Duration::from_nanos(self.0 / div.into())
}
}
impl Duration {
/// Sleep for the given amount of time.
pub fn sleep(self) {
Time::after(self).sleep()
}
pub fn nanos(self) -> i64 {
self.0
}
pub fn millis(self) -> i64 {
self.0 / 1_000_000
}
pub fn seconds(self) -> i64 {
self.millis() / 1_000
}
pub fn minutes(self) -> i64 {
self.seconds() / 60
}
pub fn hours(self) -> i64 {
self.minutes() / 60
}
pub fn from_nanos(nanos: i64) -> Self {
Duration(nanos)
}
pub fn from_millis(millis: i64) -> Self {
Duration(millis * 1_000_000)
}
pub fn from_seconds(secs: i64) -> Self {
Duration::from_millis(secs * 1_000)
}
pub fn from_minutes(min: i64) -> Self {
Duration::from_seconds(min * 60)
}
pub fn from_hours(hours: i64) -> Self {
Duration::from_minutes(hours * 60)
}
/// Returns a `Time` which is a `Duration` after the current time.
/// `duration.after_now()` is equivalent to `Time::after(duration)`.
pub fn after_now(self) -> Time {
Time::after(self)
}
}
pub trait DurationNum: Sized {
fn nanos(self) -> Duration;
fn millis(self) -> Duration;
fn seconds(self) -> Duration;
fn minutes(self) -> Duration;
fn hours(self) -> Duration;
// Singular versions to allow for `1.milli()` and `1.second()`, etc.
fn milli(self) -> Duration { self.millis() }
fn second(self) -> Duration { self.seconds() }
fn minute(self) -> Duration { self.minutes() }
fn hour(self) -> Duration { self.hours() }
}
// Note: this could be implemented for other unsized integer types, but it doesn't seem
// necessary to support the usual case.
impl DurationNum for i64 {
fn nanos(self) -> Duration {
Duration::from_nanos(self)
}
fn millis(self) -> Duration {
Duration::from_millis(self)
}
fn seconds(self) -> Duration {
Duration::from_seconds(self)
}
fn minutes(self) -> Duration {
Duration::from_minutes(self)
}
fn hours(self) -> Duration {
Duration::from_hours(self)
}
}
impl Time {
pub const INFINITE: Time = Time(sys::ZX_TIME_INFINITE);
pub const INFINITE_PAST: Time = Time(sys::ZX_TIME_INFINITE_PAST);
/// Get the current time, from the specific clock id.
///
/// Wraps the
/// [zx_clock_get](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/clock_get.md)
/// syscall.
pub fn get(clock_id: ClockId) -> Time {
unsafe { Time(sys::zx_clock_get(clock_id as u32)) }
}
/// Compute a deadline for the time in the future that is the given `Duration` away.
///
/// Wraps the
/// [zx_deadline_after](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/deadline_after.md)
/// syscall.
pub fn after(duration: Duration) -> Time {
unsafe { Time(sys::zx_deadline_after(duration.0)) }
}
/// Sleep until the given time.
///
/// Wraps the
/// [zx_nanosleep](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/nanosleep.md)
/// syscall.
pub fn sleep(self) {
unsafe { sys::zx_nanosleep(self.0); }
}
pub fn nanos(self) -> i64 {
self.0
}
pub fn from_nanos(nanos: i64) -> Self {
Time(nanos)
}
}
/// Read the number of high-precision timer ticks since boot. These ticks may be processor cycles,
/// high speed timer, profiling timer, etc. They are not guaranteed to continue advancing when the
/// system is asleep.
///
/// Wraps the
/// [zx_ticks_get](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/ticks_get.md)
/// syscall.
pub fn ticks_get() -> u64 {
unsafe { sys::zx_ticks_get() }
}
/// Return the number of high-precision timer ticks in a second.
///
/// Wraps the
/// [zx_ticks_per_second](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/ticks_per_second.md)
/// syscall.
pub fn ticks_per_second() -> u64 {
unsafe { sys::zx_ticks_per_second() }
}
/// An object representing a Zircon timer, such as the one returned by
/// [zx_timer_create](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/timer_create.md).
///
/// As essentially a subtype of `Handle`, it can be freely interconverted.
#[derive(Debug, Eq, PartialEq)]
#[repr(transparent)]
pub struct Timer(Handle);
impl_handle_based!(Timer);
impl Timer {
/// Create a timer, an object that can signal when a specified point in time has been reached.
/// Wraps the
/// [zx_timer_create](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/timer_create.md)
/// syscall.
pub fn create(clock_id: ClockId) -> Result<Timer, Status> {
let mut out = 0;
let opts = 0;
let status = unsafe { sys::zx_timer_create(opts, clock_id as u32, &mut out) };
ok(status)?;
unsafe {
Ok(Self::from(Handle::from_raw(out)))
}
}
/// Start a one-shot timer that will fire when `deadline` passes. Wraps the
/// [zx_timer_set](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/timer_set.md)
/// syscall.
pub fn set(&self, deadline: Time, slack: Duration) -> Result<(), Status> {
let status = unsafe {
sys::zx_timer_set(self.raw_handle(), deadline.nanos(), slack.nanos())
};
ok(status)
}
/// Cancels a pending timer that was started with start(). Wraps the
/// [zx_timer_cancel](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/timer_cancel.md)
/// syscall.
pub fn cancel(&self) -> Result<(), Status> {
let status = unsafe { sys::zx_timer_cancel(self.raw_handle()) };
ok(status)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Signals;
#[test]
fn create_timer_invalid_clock() {
assert_eq!(Timer::create(ClockId::UTC).unwrap_err(), Status::INVALID_ARGS);
assert_eq!(Timer::create(ClockId::Thread), Err(Status::INVALID_ARGS));
}
#[test]
fn from_std() {
let std_dur = stdtime::Duration::new(25, 25);
let dur = Duration::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.nanos());
}
#[test]
fn timer_basic() {
let slack = 0.millis();
let ten_ms = 10.millis();
let five_secs = 5.seconds();
let six_secs = 6.seconds();
// Create a timer
let timer = Timer::create(ClockId::Monotonic).unwrap();
// Should not signal yet.
assert_eq!(
timer.wait_handle(Signals::TIMER_SIGNALED, ten_ms.after_now()),
Err(Status::TIMED_OUT));
// Set it, and soon it should signal.
assert_eq!(timer.set(five_secs.after_now(), slack), Ok(()));
assert_eq!(
timer.wait_handle(Signals::TIMER_SIGNALED, six_secs.after_now()),
Ok(Signals::TIMER_SIGNALED));
// Cancel it, and it should stop signalling.
assert_eq!(timer.cancel(), Ok(()));
assert_eq!(
timer.wait_handle(Signals::TIMER_SIGNALED, ten_ms.after_now()),
Err(Status::TIMED_OUT));
}
#[test]
fn time_minus_time() {
let lhs = Time::from_nanos(10);
let rhs = Time::from_nanos(30);
assert_eq!(lhs - rhs, Duration::from_nanos(-20));
}
}