use crate::{
ok, sys, AsHandleRef, BootTimeline, ClockUpdate, Handle, HandleBased, HandleRef, Instant,
MonotonicTimeline, SyntheticTimeline, Timeline,
};
use bitflags::bitflags;
use std::mem::MaybeUninit;
use std::ptr;
use zx_status::Status;
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Clock<Reference = MonotonicTimeline, Output = SyntheticTimeline>(
Handle,
std::marker::PhantomData<(Reference, Output)>,
);
pub type SyntheticClock = Clock<MonotonicTimeline, SyntheticTimeline>;
pub type SyntheticClockOnBoot = Clock<BootTimeline, SyntheticTimeline>;
impl<Output: Timeline> Clock<MonotonicTimeline, Output> {
pub fn create(opts: ClockOpts, backstop: Option<Instant<Output>>) -> Result<Self, Status> {
let mut out = 0;
let status = match backstop {
Some(backstop) => {
let args = sys::zx_clock_create_args_v1_t { backstop_time: backstop.into_nanos() };
unsafe {
sys::zx_clock_create(
sys::ZX_CLOCK_ARGS_VERSION_1 | opts.bits(),
std::ptr::from_ref(&args).cast::<u8>(),
&mut out,
)
}
}
None => unsafe { sys::zx_clock_create(opts.bits(), ptr::null(), &mut out) },
};
ok(status)?;
unsafe { Ok(Self::from(Handle::from_raw(out))) }
}
}
impl<Output: Timeline> Clock<BootTimeline, Output> {
pub fn create(opts: ClockOpts, backstop: Option<Instant<Output>>) -> Result<Self, Status> {
let mut out = 0;
let opts = opts | ClockOpts::BOOT;
let status = match backstop {
Some(backstop) => {
let args = sys::zx_clock_create_args_v1_t { backstop_time: backstop.into_nanos() };
unsafe {
sys::zx_clock_create(
sys::ZX_CLOCK_ARGS_VERSION_1 | opts.bits(),
&args as *const _ as *const u8,
&mut out,
)
}
}
None => unsafe { sys::zx_clock_create(opts.bits(), ptr::null(), &mut out) },
};
ok(status)?;
unsafe { Ok(Self::from(Handle::from_raw(out))) }
}
}
impl<Reference: Timeline, Output: Timeline> Clock<Reference, Output> {
pub fn read(&self) -> Result<Instant<Output>, Status> {
let mut now = 0;
let status = unsafe { sys::zx_clock_read(self.raw_handle(), &mut now) };
ok(status)?;
Ok(Instant::<Output>::from_nanos(now))
}
pub fn get_details(&self) -> Result<ClockDetails<Reference, Output>, Status> {
let mut out_details = MaybeUninit::<sys::zx_clock_details_v1_t>::uninit();
let status = unsafe {
sys::zx_clock_get_details(
self.raw_handle(),
sys::ZX_CLOCK_ARGS_VERSION_1,
out_details.as_mut_ptr().cast::<u8>(),
)
};
ok(status)?;
let out_details = unsafe { out_details.assume_init() };
Ok(out_details.into())
}
pub fn update(&self, update: impl Into<ClockUpdate<Reference, Output>>) -> Result<(), Status> {
let update = update.into();
let options = update.options();
let args = update.args();
let status = unsafe {
sys::zx_clock_update(self.raw_handle(), options, std::ptr::from_ref(&args).cast::<u8>())
};
ok(status)?;
Ok(())
}
pub fn downcast<NewReference: Timeline>(self) -> Clock<NewReference, SyntheticTimeline> {
Clock(self.0, std::marker::PhantomData)
}
}
impl<Reference: Timeline> Clock<Reference, SyntheticTimeline> {
pub fn cast<NewReference: Timeline, UserTimeline: Timeline>(
self,
) -> Clock<NewReference, UserTimeline> {
Clock(self.0, std::marker::PhantomData)
}
}
impl<Reference: Timeline, Output: Timeline> AsHandleRef for Clock<Reference, Output> {
fn as_handle_ref(&self) -> HandleRef<'_> {
self.0.as_handle_ref()
}
}
impl<Reference: Timeline, Output: Timeline> From<Handle> for Clock<Reference, Output> {
fn from(handle: Handle) -> Self {
Clock(handle, std::marker::PhantomData)
}
}
impl<Reference: Timeline, Output: Timeline> From<Clock<Reference, Output>> for Handle {
fn from(x: Clock<Reference, Output>) -> Handle {
x.0
}
}
impl<Reference: Timeline, Output: Timeline> HandleBased for Clock<Reference, Output> {}
bitflags! {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ClockOpts: u64 {
const MONOTONIC = sys::ZX_CLOCK_OPT_MONOTONIC;
const CONTINUOUS = sys::ZX_CLOCK_OPT_CONTINUOUS | Self::MONOTONIC.bits();
const AUTO_START = sys::ZX_CLOCK_OPT_AUTO_START;
const BOOT = sys::ZX_CLOCK_OPT_BOOT;
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ClockDetails<Reference = MonotonicTimeline, Output = SyntheticTimeline> {
pub backstop: Instant<Output>,
pub ticks_to_synthetic: ClockTransformation<Reference, Output>,
pub reference_to_synthetic: ClockTransformation<Reference, Output>,
pub error_bounds: u64,
pub query_ticks: sys::zx_ticks_t,
pub last_value_update_ticks: sys::zx_ticks_t,
pub last_rate_adjust_update_ticks: sys::zx_ticks_t,
pub last_error_bounds_update_ticks: sys::zx_ticks_t,
pub generation_counter: u32,
}
impl<Reference: Timeline, Output: Timeline> From<sys::zx_clock_details_v1_t>
for ClockDetails<Reference, Output>
{
fn from(details: sys::zx_clock_details_v1_t) -> Self {
ClockDetails {
backstop: Instant::from_nanos(details.backstop_time),
ticks_to_synthetic: details.reference_ticks_to_synthetic.into(),
reference_to_synthetic: details.reference_to_synthetic.into(),
error_bounds: details.error_bound,
query_ticks: details.query_ticks,
last_value_update_ticks: details.last_value_update_ticks,
last_rate_adjust_update_ticks: details.last_rate_adjust_update_ticks,
last_error_bounds_update_ticks: details.last_error_bounds_update_ticks,
generation_counter: details.generation_counter,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ClockTransformation<Reference = MonotonicTimeline, Output = SyntheticTimeline> {
pub reference_offset: Instant<Reference>,
pub synthetic_offset: Instant<Output>,
pub rate: sys::zx_clock_rate_t,
}
impl<Reference: Timeline, Output: Timeline> From<sys::zx_clock_transformation_t>
for ClockTransformation<Reference, Output>
{
fn from(ct: sys::zx_clock_transformation_t) -> Self {
ClockTransformation {
reference_offset: Instant::<Reference>::from_nanos(ct.reference_offset),
synthetic_offset: Instant::<Output>::from_nanos(ct.synthetic_offset),
rate: ct.rate,
}
}
}
fn transform_clock(r: i64, r_offset: i64, c_offset: i64, r_rate: u32, c_rate: u32) -> i64 {
let r = r as i128;
let r_offset = r_offset as i128;
let c_offset = c_offset as i128;
let r_rate = r_rate as i128;
let c_rate = c_rate as i128;
let c = (((r - r_offset) * c_rate) / r_rate) + c_offset;
c.try_into().unwrap_or_else(|_| if c.is_positive() { i64::MAX } else { i64::MIN })
}
impl<Reference: Timeline + Copy, Output: Timeline + Copy> ClockTransformation<Reference, Output> {
pub fn apply(&self, time: Instant<Reference>) -> Instant<Output> {
let c = transform_clock(
time.into_nanos(),
self.reference_offset.into_nanos(),
self.synthetic_offset.into_nanos(),
self.rate.reference_ticks,
self.rate.synthetic_ticks,
);
Instant::from_nanos(c)
}
pub fn apply_inverse(&self, time: Instant<Output>) -> Instant<Reference> {
let r = transform_clock(
time.into_nanos(),
self.synthetic_offset.into_nanos(),
self.reference_offset.into_nanos(),
self.rate.synthetic_ticks,
self.rate.reference_ticks,
);
Instant::from_nanos(r as i64)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{MonotonicInstant, SyntheticInstant};
use assert_matches::assert_matches;
#[test]
fn create_clocks() {
assert_matches!(SyntheticClock::create(ClockOpts::empty(), None), Ok(_));
assert_matches!(SyntheticClock::create(ClockOpts::MONOTONIC, None), Ok(_));
assert_matches!(SyntheticClock::create(ClockOpts::CONTINUOUS, None), Ok(_));
assert_matches!(
SyntheticClock::create(ClockOpts::AUTO_START | ClockOpts::MONOTONIC, None),
Ok(_)
);
assert_matches!(
SyntheticClock::create(ClockOpts::AUTO_START | ClockOpts::CONTINUOUS, None),
Ok(_)
);
let backstop = Some(SyntheticInstant::from_nanos(5500));
assert_matches!(SyntheticClock::create(ClockOpts::MONOTONIC, backstop), Ok(_));
assert_matches!(SyntheticClock::create(ClockOpts::CONTINUOUS, backstop), Ok(_));
assert_matches!(
SyntheticClock::create(ClockOpts::AUTO_START | ClockOpts::MONOTONIC, backstop),
Ok(_)
);
assert_matches!(
SyntheticClock::create(ClockOpts::AUTO_START | ClockOpts::CONTINUOUS, backstop),
Ok(_)
);
}
#[test]
fn read_time() {
let clock =
SyntheticClock::create(ClockOpts::MONOTONIC, None).expect("failed to create clock");
assert_matches!(clock.read(), Ok(_));
}
#[test]
fn get_clock_details() {
let clock =
SyntheticClock::create(ClockOpts::MONOTONIC, None).expect("failed to create clock");
let details = clock.get_details().expect("failed to get details");
assert_eq!(details.backstop, SyntheticInstant::from_nanos(0));
let clock =
SyntheticClock::create(ClockOpts::MONOTONIC, Some(SyntheticInstant::from_nanos(5500)))
.expect("failed to create clock");
let details = clock.get_details().expect("failed to get details");
assert_eq!(details.backstop, SyntheticInstant::from_nanos(5500));
}
#[test]
fn update_clock() {
let clock =
SyntheticClock::create(ClockOpts::MONOTONIC, None).expect("failed to create clock");
let before_details = clock.get_details().expect("failed to get details");
assert_eq!(before_details.last_value_update_ticks, 0);
assert_eq!(before_details.last_rate_adjust_update_ticks, 0);
assert_eq!(before_details.last_error_bounds_update_ticks, 0);
clock
.update(
ClockUpdate::builder()
.absolute_value(
MonotonicInstant::from_nanos(999),
SyntheticInstant::from_nanos(42),
)
.rate_adjust(52)
.error_bounds(52),
)
.expect("failed to update clock");
let after_details = clock.get_details().expect("failed to get details");
assert!(before_details.generation_counter < after_details.generation_counter);
assert!(after_details.last_value_update_ticks > before_details.last_value_update_ticks);
assert_eq!(
after_details.last_value_update_ticks,
after_details.last_rate_adjust_update_ticks
);
assert_eq!(
after_details.last_value_update_ticks,
after_details.last_error_bounds_update_ticks
);
assert_eq!(after_details.error_bounds, 52);
assert_eq!(after_details.ticks_to_synthetic.synthetic_offset.into_nanos(), 42);
assert_eq!(after_details.reference_to_synthetic.reference_offset.into_nanos(), 999);
assert_eq!(after_details.reference_to_synthetic.synthetic_offset.into_nanos(), 42);
let before_details = after_details;
clock.update(ClockUpdate::builder().error_bounds(100)).expect("failed to update clock");
let after_details = clock.get_details().expect("failed to get details");
assert!(before_details.generation_counter < after_details.generation_counter);
assert!(
after_details.last_error_bounds_update_ticks > before_details.last_value_update_ticks
);
assert!(
after_details.last_error_bounds_update_ticks
> after_details.last_rate_adjust_update_ticks
);
assert_eq!(
after_details.last_rate_adjust_update_ticks,
after_details.last_value_update_ticks
);
assert_eq!(after_details.error_bounds, 100);
assert_eq!(after_details.ticks_to_synthetic.synthetic_offset.into_nanos(), 42);
assert_eq!(after_details.reference_to_synthetic.synthetic_offset.into_nanos(), 42);
}
#[test]
fn clock_identity_transformation_roundtrip() {
let t_0 = MonotonicInstant::ZERO;
let xform = ClockTransformation {
reference_offset: MonotonicInstant::from_nanos(0),
synthetic_offset: SyntheticInstant::from_nanos(0),
rate: sys::zx_clock_rate_t { synthetic_ticks: 1, reference_ticks: 1 },
};
let transformed_time = xform.apply(t_0);
let original_time = xform.apply_inverse(transformed_time);
assert_eq!(t_0, original_time);
}
#[test]
fn clock_trivial_transformation() {
let t_0 = MonotonicInstant::ZERO;
let xform = ClockTransformation {
reference_offset: MonotonicInstant::from_nanos(3),
synthetic_offset: SyntheticInstant::from_nanos(2),
rate: sys::zx_clock_rate_t { synthetic_ticks: 6, reference_ticks: 2 },
};
let utc_time = xform.apply(t_0);
let monotonic_time = xform.apply_inverse(utc_time);
assert_eq!(3 * (t_0.into_nanos() - 3) + 2, utc_time.into_nanos());
assert_eq!(t_0, monotonic_time);
}
#[test]
fn clock_transformation_roundtrip() {
let t_0 = MonotonicInstant::ZERO;
let xform = ClockTransformation {
reference_offset: MonotonicInstant::from_nanos(196980085208),
synthetic_offset: SyntheticInstant::from_nanos(1616900096031887801),
rate: sys::zx_clock_rate_t { synthetic_ticks: 999980, reference_ticks: 1000000 },
};
let transformed_time = xform.apply(t_0);
let original_time = xform.apply_inverse(transformed_time);
let roundtrip_diff = t_0 - original_time;
assert!(roundtrip_diff.into_nanos().abs() <= 1);
}
#[test]
fn clock_trailing_transformation_roundtrip() {
let t_0 = MonotonicInstant::ZERO;
let xform = ClockTransformation {
reference_offset: MonotonicInstant::from_nanos(1616900096031887801),
synthetic_offset: SyntheticInstant::from_nanos(196980085208),
rate: sys::zx_clock_rate_t { synthetic_ticks: 1000000, reference_ticks: 999980 },
};
let transformed_time = xform.apply(t_0);
let original_time = xform.apply_inverse(transformed_time);
let roundtrip_diff = t_0 - original_time;
assert!(roundtrip_diff.into_nanos().abs() <= 1);
}
#[test]
fn clock_saturating_transformations() {
let t_0 = MonotonicInstant::from_nanos(i64::MAX);
let xform = ClockTransformation {
reference_offset: MonotonicInstant::from_nanos(0),
synthetic_offset: SyntheticInstant::from_nanos(1),
rate: sys::zx_clock_rate_t { synthetic_ticks: 1, reference_ticks: 1 },
};
let time = xform.apply(t_0).into_nanos();
assert_eq!(time, i64::MAX);
let t_0 = MonotonicInstant::from_nanos(i64::MIN);
let xform = ClockTransformation {
reference_offset: MonotonicInstant::from_nanos(1),
synthetic_offset: SyntheticInstant::from_nanos(0),
rate: sys::zx_clock_rate_t { synthetic_ticks: 1, reference_ticks: 1 },
};
let time = xform.apply(t_0).into_nanos();
assert_eq!(time, i64::MIN);
}
}