1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

pub use self::inner::SteadyTime;

#[cfg(any(target_os = "macos", target_os = "ios"))]
mod inner {
    use libc;
    use time::Duration;
    use ops::Sub;
    use sync::{Once, ONCE_INIT};

    pub struct SteadyTime {
        t: u64
    }

    extern {
        pub fn mach_absolute_time() -> u64;
        pub fn mach_timebase_info(info: *mut libc::mach_timebase_info) -> libc::c_int;
    }

    impl SteadyTime {
        pub fn now() -> SteadyTime {
            SteadyTime {
                t: unsafe { mach_absolute_time() },
            }
        }

        pub fn ns(&self) -> u64 {
            let info = info();
            self.t * info.numer as u64 / info.denom as u64
        }
    }

    fn info() -> &'static libc::mach_timebase_info {
        static mut INFO: libc::mach_timebase_info = libc::mach_timebase_info {
            numer: 0,
            denom: 0,
        };
        static ONCE: Once = ONCE_INIT;

        unsafe {
            ONCE.call_once(|| {
                mach_timebase_info(&mut INFO);
            });
            &INFO
        }
    }

    impl<'a> Sub for &'a SteadyTime {
        type Output = Duration;

        fn sub(self, other: &SteadyTime) -> Duration {
            let info = info();
            let diff = self.t as i64 - other.t as i64;
            Duration::nanoseconds(diff * info.numer as i64 / info.denom as i64)
        }
    }
}

#[cfg(not(any(target_os = "macos", target_os = "ios")))]
mod inner {
    use libc;
    use time::Duration;
    use ops::Sub;

    const NSEC_PER_SEC: i64 = 1_000_000_000;

    pub struct SteadyTime {
        t: libc::timespec,
    }

    // Apparently android provides this in some other library?
    // Bitrig's RT extensions are in the C library, not a separate librt
    // OpenBSD provide it via libc
    #[cfg(not(any(target_os = "android",
                  target_os = "bitrig",
                  target_os = "openbsd")))]
    #[link(name = "rt")]
    extern {}

    extern {
        fn clock_gettime(clk_id: libc::c_int, tp: *mut libc::timespec) -> libc::c_int;
    }

    impl SteadyTime {
        pub fn now() -> SteadyTime {
            let mut t = SteadyTime {
                t: libc::timespec {
                    tv_sec: 0,
                    tv_nsec: 0,
                }
            };
            unsafe {
                assert_eq!(0, clock_gettime(libc::CLOCK_MONOTONIC, &mut t.t));
            }
            t
        }

        pub fn ns(&self) -> u64 {
            self.t.tv_sec as u64 * NSEC_PER_SEC as u64 + self.t.tv_nsec as u64
        }
    }

    impl<'a> Sub for &'a SteadyTime {
        type Output = Duration;

        fn sub(self, other: &SteadyTime) -> Duration {
            if self.t.tv_nsec >= other.t.tv_nsec {
                Duration::seconds(self.t.tv_sec as i64 - other.t.tv_sec as i64) +
                    Duration::nanoseconds(self.t.tv_nsec as i64 - other.t.tv_nsec as i64)
            } else {
                Duration::seconds(self.t.tv_sec as i64 - 1 - other.t.tv_sec as i64) +
                    Duration::nanoseconds(self.t.tv_nsec as i64 + NSEC_PER_SEC -
                                          other.t.tv_nsec as i64)
            }
        }
    }
}