Skip to main content

zx_libc/
utc.rs

1// Copyright 2026 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//! Accessors for the `zx::Clock` representing UTC, used by common operations.
6//! Standard library functions for accessing UTC or "wall clock time" all work
7//! by reading this clock.
8
9use zx::sys::{ZX_HANDLE_INVALID, zx_handle_t, zx_status_t};
10use zx::{BootTimeline, Clock, NullableHandle, Status, SyntheticTimeline, Timeline, Unowned};
11
12// <zircon/utc.h>
13unsafe extern "C" {
14    fn _zx_utc_reference_get() -> zx_handle_t;
15
16    fn _zx_utc_reference_swap(
17        new_utc_reference: zx_handle_t,
18        prev_utc_reference_out: *mut zx_handle_t,
19    ) -> zx_status_t;
20}
21
22/// The `zx_libc` crate does not provide the `zx::Timeline` type for UTC, but
23/// the functions here are generic over a supplied `zx::Timeline` type.
24pub type UtcClock<T = SyntheticTimeline> = Clock<BootTimeline, T>;
25
26/// Returns a handle to the currently assigned UTC clock, or a null handle if
27/// no such clock currently exists.
28///
29/// Thread safety is the responsibility of the user.  In particular, if a clock
30/// is fetched by a user using utc::reference_get, but then the clock is
31/// swapped out using utc::reference_swap and the original clock is closed,
32/// then the initial clock handle returned is now invalid and could result in a
33/// use-after-close situation.  It is the user's responsibility to avoid these
34/// situations.
35pub fn reference_get<T: Timeline>() -> Unowned<'static, UtcClock<T>> {
36    // SAFETY: basic FFI call.
37    unsafe { Unowned::from_raw_handle(_zx_utc_reference_get()) }
38}
39
40/// Atomically swap the clock handle provided with the current UTC reference.
41pub fn reference_swap<T: Timeline>(new_clock: UtcClock<T>) -> Result<UtcClock<T>, Status> {
42    // SAFETY: basic FFI call.
43    Ok(unsafe {
44        let mut old = ZX_HANDLE_INVALID;
45        Status::ok(_zx_utc_reference_swap(new_clock.into_raw(), &mut old))?;
46        NullableHandle::from_raw(old)
47    }
48    .into())
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54    use zx::ClockOpts;
55
56    fn fake_clock() -> UtcClock {
57        UtcClock::create(ClockOpts::MONOTONIC, None).expect("cannot create test clock")
58    }
59
60    #[test]
61    fn test_get() {
62        let test_clock = fake_clock();
63        let original_clock = reference_get();
64        assert_ne!(original_clock, test_clock.unowned())
65    }
66
67    #[test]
68    fn test_swap() {
69        let original_clock = reference_get();
70        let new_clock = fake_clock();
71
72        // SAFETY: Hidden borrow used only for comparison below.
73        let test_clock = unsafe { Unowned::<UtcClock>::from_raw_handle(new_clock.raw_handle()) };
74
75        let real_clock = reference_swap(new_clock).expect("cannot swap");
76        assert_eq!(real_clock.unowned(), original_clock);
77
78        let got_clock = reference_get();
79
80        // Put the real clock back before assertions could fail.
81        let swapped_clock = reference_swap(real_clock).expect("cannot swap back");
82
83        assert_ne!(original_clock, test_clock);
84        assert_eq!(got_clock, test_clock);
85        assert_eq!(swapped_clock.unowned(), test_clock);
86    }
87}