zx/
clock.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Type-safe bindings for Zircon clock objects.
6
7use crate::{
8    AsHandleRef, BootTimeline, ClockUpdate, HandleBased, HandleRef, Instant, MonotonicTimeline,
9    NullableHandle, ObjectQuery, SyntheticTimeline, Ticks, Timeline, Topic, ok, sys,
10};
11use bitflags::bitflags;
12use std::mem::MaybeUninit;
13use std::ptr;
14use zx_status::Status;
15
16/// An object representing a kernel [clock], used to track the progress of time. A clock is a
17/// one-dimensional affine transformation of the [clock monotonic] reference timeline which may be
18/// atomically adjusted by a maintainer and observed by clients.
19///
20/// As essentially a subtype of `NullableHandle`, it can be freely interconverted.
21///
22/// [clock]: https://fuchsia.dev/fuchsia-src/reference/kernel_objects/clock
23/// [clock monotonic]: https://fuchsia.dev/fuchsia-src/reference/syscalls/clock_get_monotonic.md
24#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
25#[repr(transparent)]
26pub struct Clock<Reference = MonotonicTimeline, Output = SyntheticTimeline>(
27    NullableHandle,
28    std::marker::PhantomData<(Reference, Output)>,
29);
30
31pub type SyntheticClock = Clock<MonotonicTimeline, SyntheticTimeline>;
32pub type SyntheticClockOnBoot = Clock<BootTimeline, SyntheticTimeline>;
33
34impl<Output: Timeline> Clock<MonotonicTimeline, Output> {
35    /// Create a new clock object with the provided arguments, with the monotonic clock as the
36    /// reference timeline. Wraps the [zx_clock_create] syscall.
37    ///
38    /// [zx_clock_create]: https://fuchsia.dev/fuchsia-src/reference/syscalls/clock_create
39    pub fn create(opts: ClockOpts, backstop: Option<Instant<Output>>) -> Result<Self, Status> {
40        let mut out = 0;
41        let status = match backstop {
42            Some(backstop) => {
43                // When using backstop time, use the API v1 args struct.
44                let args = sys::zx_clock_create_args_v1_t { backstop_time: backstop.into_nanos() };
45                unsafe {
46                    sys::zx_clock_create(
47                        sys::ZX_CLOCK_ARGS_VERSION_1 | opts.bits(),
48                        std::ptr::from_ref(&args).cast::<u8>(),
49                        &mut out,
50                    )
51                }
52            }
53            None => unsafe { sys::zx_clock_create(opts.bits(), ptr::null(), &mut out) },
54        };
55        ok(status)?;
56        unsafe { Ok(Self::from(NullableHandle::from_raw(out))) }
57    }
58}
59
60impl<Output: Timeline> Clock<BootTimeline, Output> {
61    /// Create a new clock object with the provided arguments, with the boot clock as the reference
62    /// timeline. Wraps the [zx_clock_create] syscall.
63    ///
64    /// [zx_clock_create]: https://fuchsia.dev/fuchsia-src/reference/syscalls/clock_create
65    pub fn create(opts: ClockOpts, backstop: Option<Instant<Output>>) -> Result<Self, Status> {
66        let mut out = 0;
67        let opts = opts | ClockOpts::BOOT;
68        let status = match backstop {
69            Some(backstop) => {
70                // When using backstop time, use the API v1 args struct.
71                let args = sys::zx_clock_create_args_v1_t { backstop_time: backstop.into_nanos() };
72                unsafe {
73                    sys::zx_clock_create(
74                        sys::ZX_CLOCK_ARGS_VERSION_1 | opts.bits(),
75                        &args as *const _ as *const u8,
76                        &mut out,
77                    )
78                }
79            }
80            None => unsafe { sys::zx_clock_create(opts.bits(), ptr::null(), &mut out) },
81        };
82        ok(status)?;
83        unsafe { Ok(Self::from(NullableHandle::from_raw(out))) }
84    }
85}
86
87impl<Reference: Timeline, Output: Timeline> Clock<Reference, Output> {
88    /// Perform a basic read of this clock. Wraps the [zx_clock_read] syscall. Requires
89    /// `ZX_RIGHT_READ` and that the clock has had an initial time established.
90    ///
91    /// [zx_clock_read]: https://fuchsia.dev/fuchsia-src/reference/syscalls/clock_read
92    pub fn read(&self) -> Result<Instant<Output>, Status> {
93        let mut now = 0;
94        let status = unsafe { sys::zx_clock_read(self.raw_handle(), &mut now) };
95        ok(status)?;
96        Ok(Instant::<Output>::from_nanos(now))
97    }
98
99    /// Perform a basic read of a mapped clock. Wraps the [zx_clock_read_mapped] syscall.
100    ///
101    /// # Safety
102    ///
103    /// |zx_clock_get_details_mapped| must only be called using a |clock_addr| which is the address
104    /// of a clock's state currently mapped into the caller's address space using
105    /// |zx_vmar_map_clock|.  Attempting to get_details from any other address, or from a clock
106    /// mapping which has been unmapped (completely or partially) will result in undefined behavior.
107    ///
108    /// [zx_clock_read]: https://fuchsia.dev/fuchsia-src/reference/syscalls/clock_read_mapped
109    pub unsafe fn read_mapped(clock_addr: usize) -> Result<Instant<Output>, Status> {
110        let mut now = 0;
111        let status = unsafe {
112            // SAFETY: see function docs above.
113            sys::zx_clock_read_mapped(clock_addr as *const u8, &mut now)
114        };
115        ok(status)?;
116        Ok(Instant::<Output>::from_nanos(now))
117    }
118
119    /// Get low level details of this clock's current status. Wraps the
120    /// [zx_clock_get_details] syscall. Requires `ZX_RIGHT_READ`.
121    ///
122    /// [zx_clock_get_details]: https://fuchsia.dev/fuchsia-src/reference/syscalls/clock_get_details
123    pub fn get_details(&self) -> Result<ClockDetails<Reference, Output>, Status> {
124        let mut out_details = MaybeUninit::<sys::zx_clock_details_v1_t>::uninit();
125        let status = unsafe {
126            sys::zx_clock_get_details(
127                self.raw_handle(),
128                sys::ZX_CLOCK_ARGS_VERSION_1,
129                out_details.as_mut_ptr().cast::<u8>(),
130            )
131        };
132        ok(status)?;
133        let out_details = unsafe { out_details.assume_init() };
134        Ok(out_details.into())
135    }
136
137    /// Get low level details of a mapped clock's current status. Wraps the
138    /// [zx_clock_get_details_mapped] syscall.
139    ///
140    /// # Safety
141    ///
142    /// |zx_clock_get_details_mapped| must only be called using a |clock_addr| which is the address
143    /// of a clock's state currently mapped into the caller's address space using
144    /// |zx_vmar_map_clock|.  Attempting to get_details from any other address, or from a clock
145    /// mapping which has been unmapped (completely or partially) will result in undefined behavior.
146    ///
147    /// [zx_clock_get_details_mapped]: https://fuchsia.dev/fuchsia-src/reference/syscalls/clock_get_details_mapped
148    pub unsafe fn get_details_mapped(
149        clock_addr: usize,
150    ) -> Result<ClockDetails<Reference, Output>, Status> {
151        let mut out_details = MaybeUninit::<sys::zx_clock_details_v1_t>::uninit();
152        let status = unsafe {
153            // SAFETY: see safety notes in the function docs above.
154            sys::zx_clock_get_details_mapped(
155                clock_addr as *const u8,
156                sys::ZX_CLOCK_ARGS_VERSION_1,
157                out_details.as_mut_ptr().cast::<u8>(),
158            )
159        };
160        ok(status)?;
161        let out_details = unsafe { out_details.assume_init() };
162        Ok(out_details.into())
163    }
164
165    // Returns the memory size for the memory region used to store clock details.
166    pub fn get_mapped_size(clock: &Clock<Reference, Output>) -> Result<usize, Status> {
167        Ok(clock.0.get_info_single::<MappedSizeQuery>()?)
168    }
169
170    /// Make adjustments to this clock. Wraps the [zx_clock_update] syscall. Requires
171    /// `ZX_RIGHT_WRITE`.
172    ///
173    /// [zx_clock_update]: https://fuchsia.dev/fuchsia-src/reference/syscalls/clock_update
174    pub fn update(&self, update: impl Into<ClockUpdate<Reference, Output>>) -> Result<(), Status> {
175        let update = update.into();
176        let options = update.options();
177        let args = update.args();
178        let status = unsafe {
179            sys::zx_clock_update(self.raw_handle(), options, std::ptr::from_ref(&args).cast::<u8>())
180        };
181        ok(status)?;
182        Ok(())
183    }
184
185    /// Convert this clock to one on a generic synthetic timeline, erasing any user-defined
186    /// timeline.
187    pub fn downcast<NewReference: Timeline>(self) -> Clock<NewReference, SyntheticTimeline> {
188        Clock(self.0, std::marker::PhantomData)
189    }
190
191    delegated_concrete_handle_based_impls!(|h| Self(h, std::marker::PhantomData));
192}
193
194impl<Reference: Timeline> Clock<Reference, SyntheticTimeline> {
195    /// Cast a "base" clock to one with a user-defined timeline that will carry the timeline for
196    /// all transformations and reads.
197    pub fn cast<NewReference: Timeline, UserTimeline: Timeline>(
198        self,
199    ) -> Clock<NewReference, UserTimeline> {
200        Clock(self.0, std::marker::PhantomData)
201    }
202}
203
204impl<Reference: Timeline, Output: Timeline> AsHandleRef for Clock<Reference, Output> {
205    fn as_handle_ref(&self) -> HandleRef<'_> {
206        self.0.as_handle_ref()
207    }
208}
209
210impl<Reference: Timeline, Output: Timeline> From<NullableHandle> for Clock<Reference, Output> {
211    fn from(handle: NullableHandle) -> Self {
212        Clock(handle, std::marker::PhantomData)
213    }
214}
215
216impl<Reference: Timeline, Output: Timeline> From<Clock<Reference, Output>> for NullableHandle {
217    fn from(x: Clock<Reference, Output>) -> NullableHandle {
218        x.0
219    }
220}
221
222impl<Reference: Timeline, Output: Timeline> HandleBased for Clock<Reference, Output> {}
223
224struct MappedSizeQuery;
225unsafe impl ObjectQuery for MappedSizeQuery {
226    const TOPIC: Topic = Topic::CLOCK_MAPPED_SIZE;
227    type InfoTy = usize;
228}
229
230bitflags! {
231    #[repr(transparent)]
232    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
233    pub struct ClockOpts: u64 {
234        /// When set, creates a clock object which is guaranteed to never run backwards. Monotonic
235        /// clocks must always move forward.
236        const MONOTONIC = sys::ZX_CLOCK_OPT_MONOTONIC;
237
238        /// When set, creates a clock which is guaranteed to never jump either forwards or
239        /// backwards. Continuous clocks may only be maintained using frequency adjustments and are,
240        /// by definition, also monotonic.
241        const CONTINUOUS = sys::ZX_CLOCK_OPT_CONTINUOUS | Self::MONOTONIC.bits();
242
243        /// When set, creates a clock that is automatically started and is initially a clone of
244        /// clock monotonic. Users may still update the clock within the limits defined by the
245        /// other options, the handle rights, and the backstop time of the clock.
246        const AUTO_START = sys::ZX_CLOCK_OPT_AUTO_START;
247
248        /// When set, creates a clock object that uses the boot timeline as its reference timeline.
249        const BOOT = sys::ZX_CLOCK_OPT_BOOT;
250
251        /// When set, creates a clock whose state may be mapped into a VMAR, allowing the state to
252        /// be used with calls to |zx_clock_read_mapped| and |zx_clock_get_details_mapped|.
253        const MAPPABLE = sys::ZX_CLOCK_OPT_MAPPABLE;
254    }
255}
256
257/// Fine grained details of a [`Clock`] object.
258#[derive(Debug, Clone, PartialEq, Eq)]
259pub struct ClockDetails<Reference = MonotonicTimeline, Output = SyntheticTimeline> {
260    /// The minimum time the clock can ever be set to.
261    pub backstop: Instant<Output>,
262
263    /// The current ticks to clock transformation.
264    pub ticks_to_synthetic: ClockTransformation<Reference, Output>,
265
266    /// The current clock monotonic to clock transformation.
267    pub reference_to_synthetic: ClockTransformation<Reference, Output>,
268
269    /// The current symmetric error estimate (if any) for the clock, measured in nanoseconds.
270    pub error_bounds: u64,
271
272    /// An observation of the system tick counter which was taken during the observation of the
273    /// clock.
274    pub query_ticks: Ticks<Reference>,
275
276    /// The last time the clock's value was updated.
277    pub last_value_update_ticks: Ticks<Reference>,
278
279    /// The last time the clock's rate adjustment was updated.
280    pub last_rate_adjust_update_ticks: Ticks<Reference>,
281
282    /// The last time the clock's error bounds were updated.
283    pub last_error_bounds_update_ticks: Ticks<Reference>,
284
285    /// The generation nonce.
286    pub generation_counter: u32,
287}
288
289impl<Reference: Timeline, Output: Timeline> From<sys::zx_clock_details_v1_t>
290    for ClockDetails<Reference, Output>
291{
292    fn from(details: sys::zx_clock_details_v1_t) -> Self {
293        ClockDetails {
294            backstop: Instant::from_nanos(details.backstop_time),
295            ticks_to_synthetic: details.reference_ticks_to_synthetic.into(),
296            reference_to_synthetic: details.reference_to_synthetic.into(),
297            error_bounds: details.error_bound,
298            query_ticks: Ticks::from_raw(details.query_ticks),
299            last_value_update_ticks: Ticks::from_raw(details.last_value_update_ticks),
300            last_rate_adjust_update_ticks: Ticks::from_raw(details.last_rate_adjust_update_ticks),
301            last_error_bounds_update_ticks: Ticks::from_raw(details.last_error_bounds_update_ticks),
302            generation_counter: details.generation_counter,
303        }
304    }
305}
306
307/// A one-dimensional affine transformation that maps points from the reference timeline to the
308/// clock timeline. See [clock transformations].
309///
310/// [clock transformations]: https://fuchsia.dev/fuchsia-src/concepts/kernel/clock_transformations
311#[derive(Debug, Clone, Eq, PartialEq)]
312pub struct ClockTransformation<Reference = MonotonicTimeline, Output = SyntheticTimeline> {
313    /// The offset on the reference timeline, measured in reference clock ticks.
314    pub reference_offset: Instant<Reference>,
315    /// The offset on the clock timeline, measured in clock ticks (typically normalized to
316    /// nanoseconds).
317    pub synthetic_offset: Instant<Output>,
318    /// The ratio of the reference to clock rate.
319    pub rate: sys::zx_clock_rate_t,
320}
321
322impl<Reference: Timeline, Output: Timeline> From<sys::zx_clock_transformation_t>
323    for ClockTransformation<Reference, Output>
324{
325    fn from(ct: sys::zx_clock_transformation_t) -> Self {
326        ClockTransformation {
327            reference_offset: Instant::<Reference>::from_nanos(ct.reference_offset),
328            synthetic_offset: Instant::<Output>::from_nanos(ct.synthetic_offset),
329            rate: ct.rate,
330        }
331    }
332}
333
334// Apply affine transformation to convert the reference time "r" to the
335// synthetic time "c".
336//
337// All values are widened to i128 before calculations and the end result is
338// converted back to a i64. If "c" is a larger number than would fit in an i64,
339// the result saturates when cast to i64.
340//
341// # Panics
342// If `r_rate` is zero, this function will panic.
343fn transform_clock(r: i64, r_offset: i64, c_offset: i64, r_rate: u32, c_rate: u32) -> i64 {
344    assert!(r_rate != 0, "r_rate may not be zero");
345    let r = r as i128;
346    let r_offset = r_offset as i128;
347    let c_offset = c_offset as i128;
348    let r_rate = r_rate as i128;
349    let c_rate = c_rate as i128;
350    let c = (((r - r_offset) * c_rate) / r_rate) + c_offset;
351    c.try_into().unwrap_or_else(|_| if c.is_positive() { i64::MAX } else { i64::MIN })
352}
353
354/// [Clock
355/// transformations](https://fuchsia.dev/fuchsia-src/concepts/kernel/clock_transformations)
356/// can be applied to convert a time from a reference time to a synthetic time.
357///
358/// The inverse transformation can be applied to convert a synthetic time back
359/// to the reference time.
360impl<Reference: Timeline + Copy, Output: Timeline + Copy> ClockTransformation<Reference, Output> {
361    /// Apply the transformation to convert a time from the reference timeline
362    /// to the output timeline.
363    ///
364    /// # Panics
365    /// If the reference clock rate is zero, this function will panic.
366    pub fn apply(&self, time: Instant<Reference>) -> Instant<Output> {
367        let c = transform_clock(
368            time.into_nanos(),
369            self.reference_offset.into_nanos(),
370            self.synthetic_offset.into_nanos(),
371            self.rate.reference_ticks,
372            self.rate.synthetic_ticks,
373        );
374
375        Instant::from_nanos(c)
376    }
377
378    /// Apply the transformation to convert a time from the output timeline
379    /// to the reference timeline.
380    ///
381    /// # Panics
382    /// If the output clock rate is zero, this function will panic.
383    pub fn apply_inverse(&self, time: Instant<Output>) -> Instant<Reference> {
384        let r = transform_clock(
385            time.into_nanos(),
386            self.synthetic_offset.into_nanos(),
387            self.reference_offset.into_nanos(),
388            self.rate.synthetic_ticks,
389            self.rate.reference_ticks,
390        );
391
392        Instant::from_nanos(r as i64)
393    }
394}
395
396#[cfg(test)]
397mod tests {
398    use super::*;
399    use crate::{MonotonicInstant, SyntheticInstant};
400    use assert_matches::assert_matches;
401
402    #[test]
403    fn create_clocks() {
404        assert_matches!(SyntheticClock::create(ClockOpts::empty(), None), Ok(_));
405        assert_matches!(SyntheticClock::create(ClockOpts::MONOTONIC, None), Ok(_));
406        assert_matches!(SyntheticClock::create(ClockOpts::CONTINUOUS, None), Ok(_));
407        assert_matches!(
408            SyntheticClock::create(ClockOpts::AUTO_START | ClockOpts::MONOTONIC, None),
409            Ok(_)
410        );
411        assert_matches!(
412            SyntheticClock::create(ClockOpts::AUTO_START | ClockOpts::CONTINUOUS, None),
413            Ok(_)
414        );
415
416        // Now with backstop.
417        let backstop = Some(SyntheticInstant::from_nanos(5500));
418        assert_matches!(SyntheticClock::create(ClockOpts::MONOTONIC, backstop), Ok(_));
419        assert_matches!(SyntheticClock::create(ClockOpts::CONTINUOUS, backstop), Ok(_));
420        assert_matches!(
421            SyntheticClock::create(ClockOpts::AUTO_START | ClockOpts::MONOTONIC, backstop),
422            Ok(_)
423        );
424        assert_matches!(
425            SyntheticClock::create(ClockOpts::AUTO_START | ClockOpts::CONTINUOUS, backstop),
426            Ok(_)
427        );
428    }
429
430    #[test]
431    fn read_time() {
432        let clock =
433            SyntheticClock::create(ClockOpts::MONOTONIC, None).expect("failed to create clock");
434        assert_matches!(clock.read(), Ok(_));
435    }
436
437    #[test]
438    fn get_clock_details() {
439        // No backstop.
440        let clock =
441            SyntheticClock::create(ClockOpts::MONOTONIC, None).expect("failed to create clock");
442        let details = clock.get_details().expect("failed to get details");
443        assert_eq!(details.backstop, SyntheticInstant::from_nanos(0));
444
445        // With backstop.
446        let clock =
447            SyntheticClock::create(ClockOpts::MONOTONIC, Some(SyntheticInstant::from_nanos(5500)))
448                .expect("failed to create clock");
449        let details = clock.get_details().expect("failed to get details");
450        assert_eq!(details.backstop, SyntheticInstant::from_nanos(5500));
451    }
452
453    #[test]
454    fn update_clock() {
455        let clock =
456            SyntheticClock::create(ClockOpts::MONOTONIC, None).expect("failed to create clock");
457        let before_details = clock.get_details().expect("failed to get details");
458        assert_eq!(before_details.last_value_update_ticks, Ticks::from_raw(0));
459        assert_eq!(before_details.last_rate_adjust_update_ticks, Ticks::from_raw(0));
460        assert_eq!(before_details.last_error_bounds_update_ticks, Ticks::from_raw(0));
461
462        // Update all properties.
463        clock
464            .update(
465                ClockUpdate::builder()
466                    .absolute_value(
467                        MonotonicInstant::from_nanos(999),
468                        SyntheticInstant::from_nanos(42),
469                    )
470                    .rate_adjust(52)
471                    .error_bounds(52),
472            )
473            .expect("failed to update clock");
474        let after_details = clock.get_details().expect("failed to get details");
475        assert!(before_details.generation_counter < after_details.generation_counter);
476        assert!(after_details.last_value_update_ticks > before_details.last_value_update_ticks);
477        assert_eq!(
478            after_details.last_value_update_ticks,
479            after_details.last_rate_adjust_update_ticks
480        );
481        assert_eq!(
482            after_details.last_value_update_ticks,
483            after_details.last_error_bounds_update_ticks
484        );
485        assert_eq!(after_details.error_bounds, 52);
486        assert_eq!(after_details.ticks_to_synthetic.synthetic_offset.into_nanos(), 42);
487        assert_eq!(after_details.reference_to_synthetic.reference_offset.into_nanos(), 999);
488        assert_eq!(after_details.reference_to_synthetic.synthetic_offset.into_nanos(), 42);
489
490        let before_details = after_details;
491
492        // Update only one property.
493        clock.update(ClockUpdate::builder().error_bounds(100)).expect("failed to update clock");
494        let after_details = clock.get_details().expect("failed to get details");
495        assert!(before_details.generation_counter < after_details.generation_counter);
496        assert!(
497            after_details.last_error_bounds_update_ticks > before_details.last_value_update_ticks
498        );
499        assert!(
500            after_details.last_error_bounds_update_ticks
501                > after_details.last_rate_adjust_update_ticks
502        );
503        assert_eq!(
504            after_details.last_rate_adjust_update_ticks,
505            after_details.last_value_update_ticks
506        );
507        assert_eq!(after_details.error_bounds, 100);
508        assert_eq!(after_details.ticks_to_synthetic.synthetic_offset.into_nanos(), 42);
509        assert_eq!(after_details.reference_to_synthetic.synthetic_offset.into_nanos(), 42);
510    }
511
512    #[test]
513    fn clock_identity_transformation_roundtrip() {
514        let t_0 = MonotonicInstant::ZERO;
515        // Identity clock transformation
516        let xform = ClockTransformation {
517            reference_offset: MonotonicInstant::from_nanos(0),
518            synthetic_offset: SyntheticInstant::from_nanos(0),
519            rate: sys::zx_clock_rate_t { synthetic_ticks: 1, reference_ticks: 1 },
520        };
521
522        // Transformation roundtrip should be equivalent with the identity transformation.
523        let transformed_time = xform.apply(t_0);
524        let original_time = xform.apply_inverse(transformed_time);
525        assert_eq!(t_0, original_time);
526    }
527
528    #[test]
529    fn clock_trivial_transformation() {
530        let t_0 = MonotonicInstant::ZERO;
531        // Identity clock transformation
532        let xform = ClockTransformation {
533            reference_offset: MonotonicInstant::from_nanos(3),
534            synthetic_offset: SyntheticInstant::from_nanos(2),
535            rate: sys::zx_clock_rate_t { synthetic_ticks: 6, reference_ticks: 2 },
536        };
537
538        let utc_time = xform.apply(t_0);
539        let monotonic_time = xform.apply_inverse(utc_time);
540        // Verify that the math is correct.
541        assert_eq!(3 * (t_0.into_nanos() - 3) + 2, utc_time.into_nanos());
542
543        // Transformation roundtrip should be equivalent.
544        assert_eq!(t_0, monotonic_time);
545    }
546
547    #[test]
548    fn clock_transformation_roundtrip() {
549        let t_0 = MonotonicInstant::ZERO;
550        // Arbitrary clock transformation
551        let xform = ClockTransformation {
552            reference_offset: MonotonicInstant::from_nanos(196980085208),
553            synthetic_offset: SyntheticInstant::from_nanos(1616900096031887801),
554            rate: sys::zx_clock_rate_t { synthetic_ticks: 999980, reference_ticks: 1000000 },
555        };
556
557        // Transformation roundtrip should be equivalent modulo rounding error.
558        let transformed_time = xform.apply(t_0);
559        let original_time = xform.apply_inverse(transformed_time);
560        let roundtrip_diff = t_0 - original_time;
561        assert!(roundtrip_diff.into_nanos().abs() <= 1);
562    }
563
564    #[test]
565    fn clock_trailing_transformation_roundtrip() {
566        let t_0 = MonotonicInstant::ZERO;
567        // Arbitrary clock transformation where the synthetic clock is trailing behind the
568        // reference clock.
569        let xform = ClockTransformation {
570            reference_offset: MonotonicInstant::from_nanos(1616900096031887801),
571            synthetic_offset: SyntheticInstant::from_nanos(196980085208),
572            rate: sys::zx_clock_rate_t { synthetic_ticks: 1000000, reference_ticks: 999980 },
573        };
574
575        // Transformation roundtrip should be equivalent modulo rounding error.
576        let transformed_time = xform.apply(t_0);
577        let original_time = xform.apply_inverse(transformed_time);
578        let roundtrip_diff = t_0 - original_time;
579        assert!(roundtrip_diff.into_nanos().abs() <= 1);
580    }
581
582    #[test]
583    fn clock_saturating_transformations() {
584        let t_0 = MonotonicInstant::from_nanos(i64::MAX);
585        // Clock transformation which will positively overflow t_0
586        let xform = ClockTransformation {
587            reference_offset: MonotonicInstant::from_nanos(0),
588            synthetic_offset: SyntheticInstant::from_nanos(1),
589            rate: sys::zx_clock_rate_t { synthetic_ticks: 1, reference_ticks: 1 },
590        };
591
592        // Applying the transformation will lead to saturation
593        let time = xform.apply(t_0).into_nanos();
594        assert_eq!(time, i64::MAX);
595
596        let t_0 = MonotonicInstant::from_nanos(i64::MIN);
597        // Clock transformation which will negatively overflow t_0
598        let xform = ClockTransformation {
599            reference_offset: MonotonicInstant::from_nanos(1),
600            synthetic_offset: SyntheticInstant::from_nanos(0),
601            rate: sys::zx_clock_rate_t { synthetic_ticks: 1, reference_ticks: 1 },
602        };
603
604        // Applying the transformation will lead to saturation
605        let time = xform.apply(t_0).into_nanos();
606        assert_eq!(time, i64::MIN);
607    }
608}