blob: a168e6db02b7adb815e09697d7ec991cd695aba8 [file]
// Copyright 2026 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.
//! Accessors for the `zx::Clock` representing UTC, used by common operations.
//! Standard library functions for accessing UTC or "wall clock time" all work
//! by reading this clock.
use zx::sys::{ZX_HANDLE_INVALID, zx_handle_t, zx_status_t};
use zx::{BootTimeline, Clock, NullableHandle, Status, SyntheticTimeline, Timeline, Unowned};
// <zircon/utc.h>
unsafe extern "C" {
fn _zx_utc_reference_get() -> zx_handle_t;
fn _zx_utc_reference_swap(
new_utc_reference: zx_handle_t,
prev_utc_reference_out: *mut zx_handle_t,
) -> zx_status_t;
}
/// The `zx_libc` crate does not provide the `zx::Timeline` type for UTC, but
/// the functions here are generic over a supplied `zx::Timeline` type.
pub type UtcClock<T = SyntheticTimeline> = Clock<BootTimeline, T>;
/// Returns a handle to the currently assigned UTC clock, or a null handle if
/// no such clock currently exists.
///
/// Thread safety is the responsibility of the user. In particular, if a clock
/// is fetched by a user using utc::reference_get, but then the clock is
/// swapped out using utc::reference_swap and the original clock is closed,
/// then the initial clock handle returned is now invalid and could result in a
/// use-after-close situation. It is the user's responsibility to avoid these
/// situations.
pub fn reference_get<T: Timeline>() -> Unowned<'static, UtcClock<T>> {
// SAFETY: basic FFI call.
unsafe { Unowned::from_raw_handle(_zx_utc_reference_get()) }
}
/// Atomically swap the clock handle provided with the current UTC reference.
pub fn reference_swap<T: Timeline>(new_clock: UtcClock<T>) -> Result<UtcClock<T>, Status> {
// SAFETY: basic FFI call.
Ok(unsafe {
let mut old = ZX_HANDLE_INVALID;
Status::ok(_zx_utc_reference_swap(new_clock.into_raw(), &mut old))?;
NullableHandle::from_raw(old)
}
.into())
}
#[cfg(test)]
mod tests {
use super::*;
use zx::ClockOpts;
fn fake_clock() -> UtcClock {
UtcClock::create(ClockOpts::MONOTONIC, None).expect("cannot create test clock")
}
#[test]
fn test_get() {
let test_clock = fake_clock();
let original_clock = reference_get();
assert_ne!(original_clock, test_clock.unowned())
}
#[test]
fn test_swap() {
let original_clock = reference_get();
let new_clock = fake_clock();
// SAFETY: Hidden borrow used only for comparison below.
let test_clock = unsafe { Unowned::<UtcClock>::from_raw_handle(new_clock.raw_handle()) };
let real_clock = reference_swap(new_clock).expect("cannot swap");
assert_eq!(real_clock.unowned(), original_clock);
let got_clock = reference_get();
// Put the real clock back before assertions could fail.
let swapped_clock = reference_swap(real_clock).expect("cannot swap back");
assert_ne!(original_clock, test_clock);
assert_eq!(got_clock, test_clock);
assert_eq!(swapped_clock.unowned(), test_clock);
}
}