From 1630ffc24b076ffe87c551b9086b5aefa0e6cb4c Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 1 Feb 2014 23:06:37 +0100 Subject: [PATCH] Use CLOCK_MONOTONIC_COARSE in libnative on linux. Or CLOCK_MONOTONIC if CLOCK_MONOTONIC_COARSE isn't available but prefer CLOCK_MONOTONIC_COARSE. The normal monotonic clock is fetched using a system call but the coarse clock is read directly from the vDSO and hence is a lot faster. Make sure it's high-res enough: it normally has a 1 kHz frequency on systems tuned for interactivity but on systems that are tuned for throughput, it may run as slow as 100 Hz. Whether the normal or the coarse clock is used, it should be an improvement either way. Before, libnative called gettimeofday() and that polls a clock that is subject to clock skew. gettimeofday() is still the time source on the other UNIX platforms, something that will eventually need to be addressed. --- src/libnative/io/timer_other.rs | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/libnative/io/timer_other.rs b/src/libnative/io/timer_other.rs index bc005f2fe8dd3..43916a4274e59 100644 --- a/src/libnative/io/timer_other.rs +++ b/src/libnative/io/timer_other.rs @@ -85,6 +85,7 @@ pub enum Req { } // returns the current time (in milliseconds) +#[cfg(not(target_os = "linux"))] fn now() -> u64 { unsafe { let mut now: libc::timeval = intrinsics::init(); @@ -93,6 +94,42 @@ fn now() -> u64 { } } +// Use CLOCK_MONOTONIC_COARSE when available (Linux >= 2.6.32) and accurate +// enough for millisecond resolution, else fall back to CLOCK_MONOTONIC. +// The coarse clock uses the vDSO and skips the system call entirely. +// FIXME(bnoordhuis) Not safe to enable on Android because old versions +// of the NDK don't have clock_getres() and clock_gettime(). It should +// be possible to work around that using weak linking but that requires +// weak symbol support first. (Either that or by parsing the auxiliary +// vector at start-up and looking up the vDSO from there.) +#[cfg(target_os = "linux")] +fn now() -> u64 { + use std::sync::atomics::{INIT_ATOMIC_INT, AtomicInt, SeqCst}; + use std::unstable::intrinsics; + + extern { + fn clock_getres(_: libc::c_int, _: *mut libc::timespec) -> libc::c_int; + fn clock_gettime(_: libc::c_int, _: *mut libc::timespec) -> libc::c_int; + } + + static mut clock_id: AtomicInt = INIT_ATOMIC_INT; + unsafe { + let mut ts: libc::timespec = intrinsics::uninit(); + let mut id = clock_id.load(SeqCst) as libc::c_int; + // Note: this trick doesn't work for CLOCK_REALTIME, it has clock id 0. + if id == 0 { + let rc = clock_getres(CLOCK_MONOTONIC_COARSE, &mut ts); + id = match rc == 0 && ts.tv_sec == 0 && ts.tv_nsec <= 1_000_000 { + true => CLOCK_MONOTONIC_COARSE, + false => CLOCK_MONOTONIC, + }; + clock_id.store(id as int, SeqCst); + } + assert_eq!(clock_gettime(id, &mut ts), 0); + ts.tv_sec as u64 * 1000 + ts.tv_nsec as u64 / 1_000_000 + } +} + fn helper(input: libc::c_int, messages: Port) { let mut set: imp::fd_set = unsafe { intrinsics::init() };