blob: a3bbf235c34056554fbc0179bb2972a5eeb6641b [file] [log] [blame]
// Copyright 2020 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.
use fuchsia_zircon as zx;
/// Returns the time on the clock at a given monotonic reference time. This calculates the time
/// based on the clock transform definition, which only contains the most recent segment. This
/// is only useful for calculating the time for monotonic times close to the current time.
pub fn time_at_monotonic(clock: &zx::Clock, monotonic: zx::Time) -> zx::Time {
let monotonic_reference = monotonic.into_nanos() as i128;
// Clock read failures should only be caused by an invalid clock object.
let transform = clock.get_details().expect("failed to get clock details").mono_to_synthetic;
// Calculate using the transform definition underlying a zircon clock.
// Cast to i128 to avoid overflows in multiplication.
let reference_offset = transform.reference_offset as i128;
let synthetic_offset = transform.synthetic_offset as i128;
let reference_ticks = transform.rate.reference_ticks as i128;
let synthetic_ticks = transform.rate.synthetic_ticks as i128;
let time_nanos = ((monotonic_reference - reference_offset) * synthetic_ticks / reference_ticks)
+ synthetic_offset;
zx::Time::from_nanos(time_nanos as i64)
}
#[cfg(test)]
mod test {
use {
super::*,
test_util::{assert_geq, assert_leq},
};
const BACKSTOP: zx::Time = zx::Time::from_nanos(1234567890);
const TIME_DIFF: zx::Duration = zx::Duration::from_seconds(5);
const SLEW_RATE_PPM: i32 = 750;
const ONE_MILLION: i32 = 1_000_000;
#[test]
fn time_at_monotonic_clock_not_started() {
let clock = zx::Clock::create(zx::ClockOpts::empty(), Some(BACKSTOP)).unwrap();
assert_eq!(time_at_monotonic(&clock, zx::Time::get_monotonic() + TIME_DIFF), BACKSTOP);
}
#[test]
fn time_at_monotonic_clock_started() {
let clock = zx::Clock::create(zx::ClockOpts::empty(), Some(BACKSTOP)).unwrap();
let mono_before = zx::Time::get_monotonic();
clock.update(zx::ClockUpdate::new().value(BACKSTOP)).unwrap();
let mono_after = zx::Time::get_monotonic();
let mono_radius = (mono_after - mono_before) / 2;
let mono_avg = mono_before + mono_radius;
let clock_time = time_at_monotonic(&clock, mono_avg + TIME_DIFF);
assert_geq!(clock_time, BACKSTOP + TIME_DIFF - mono_radius);
assert_leq!(clock_time, BACKSTOP + TIME_DIFF + mono_radius);
}
#[test]
fn time_at_monotonic_clock_slew_fast() {
let clock = zx::Clock::create(zx::ClockOpts::empty(), Some(BACKSTOP)).unwrap();
let mono_before = zx::Time::get_monotonic();
clock.update(zx::ClockUpdate::new().value(BACKSTOP).rate_adjust(SLEW_RATE_PPM)).unwrap();
let mono_after = zx::Time::get_monotonic();
let mono_radius = (mono_after - mono_before) / 2;
let mono_avg = mono_before + mono_radius;
let clock_time = time_at_monotonic(&clock, mono_avg + TIME_DIFF);
let expected_clock_time =
BACKSTOP + TIME_DIFF * (ONE_MILLION + SLEW_RATE_PPM) / ONE_MILLION;
assert_geq!(clock_time, expected_clock_time - mono_radius);
assert_leq!(clock_time, expected_clock_time + mono_radius);
}
#[test]
fn time_at_monotonic_clock_slew_slow() {
let clock = zx::Clock::create(zx::ClockOpts::empty(), Some(BACKSTOP)).unwrap();
let mono_before = zx::Time::get_monotonic();
clock.update(zx::ClockUpdate::new().value(BACKSTOP).rate_adjust(-SLEW_RATE_PPM)).unwrap();
let mono_after = zx::Time::get_monotonic();
let mono_radius = (mono_after - mono_before) / 2;
let mono_avg = mono_before + mono_radius;
let clock_time = time_at_monotonic(&clock, mono_avg + TIME_DIFF);
let expected_clock_time =
BACKSTOP + TIME_DIFF * (ONE_MILLION - SLEW_RATE_PPM) / ONE_MILLION;
assert_geq!(clock_time, expected_clock_time - mono_radius);
assert_leq!(clock_time, expected_clock_time + mono_radius);
}
}