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