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
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
use std::thread;
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
use std::time::Duration;

use super::config::*;
use super::cpucounter::*;
use super::timestamp::*;

#[derive(Clone)]
pub struct Precision {
    pub(crate) frequency: Option<u64>,
}

impl Precision {
    /// Initialize the crate. Note that on Linux system, this will
    /// perform calibration before returning. You may want to do this
    /// only twice. The `Precision` value can then be cloned if needed.
    pub fn new(config: Config) -> Result<Self, &'static str> {
        let frequency = if config.wall_time {
            Some(Precision::guess_frequency(&config)?)
        } else {
            None
        };
        Ok(Precision { frequency })
    }

    /// Returns the current timestamp
    #[inline]
    pub fn now(&self) -> Timestamp {
        CPUCounter::current()
    }

    #[cfg(target_os = "macos")]
    fn guess_frequency(config: &Config) -> Result<u64, &'static str> {
        Self::guess_frequency_using_sysctl("machdep.tsc.frequency")
            .or_else(|_| Self::guess_frequency_with_wall_clock(config.setup_duration))
    }

    #[cfg(target_os = "freebsd")]
    fn guess_frequency(config: &Config) -> Result<u64, &'static str> {
        Self::guess_frequency_using_sysctl("machdep.tsc_freq")
            .or_else(|_| Self::guess_frequency_with_wall_clock(config.setup_duration))
    }

    #[cfg(any(target_os = "wasi", target_os = "wasix"))]
    fn guess_frequency(_config: &Config) -> Result<u64, &'static str> {
        Ok(1_000_000_000)
    }

    #[cfg(all(
        any(target_arch = "wasm32", target_arch = "wasm64"),
        target_os = "unknown"
    ))]
    fn guess_frequency(_config: &Config) -> Result<u64, &'static str> {
        Ok(1_000_000_000)
    }

    #[cfg(not(any(
        target_os = "macos",
        target_os = "freebsd",
        any(target_arch = "wasm32", target_arch = "wasm64")
    )))]
    fn guess_frequency(config: &Config) -> Result<u64, &'static str> {
        Self::guess_frequency_with_wall_clock(config.setup_duration)
    }

    #[cfg(any(target_os = "macos", target_os = "freebsd"))]
    fn guess_frequency_using_sysctl(name: &str) -> Result<u64, &'static str> {
        use std::ffi::CString;
        use std::mem;
        use std::ptr;

        use libc::{c_long, size_t};

        let sysctl_name = CString::new(name).map_err(|_| "invalid sysctl name")?;
        let mut result: c_long = 0;
        let mut result_len: size_t = mem::size_of::<c_long>() as _;
        if unsafe {
            libc::sysctlbyname(
                sysctl_name.as_ptr(),
                &mut result as *mut _ as _,
                &mut result_len as *mut _,
                ptr::null_mut(),
                0,
            )
        } != 0
            || result_len != mem::size_of::<c_long>()
            || result <= 0
        {
            return Err("sysctl() failed");
        }
        Ok(result as u64)
    }

    #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
    fn guess_frequency_with_wall_clock(setup_duration: Duration) -> Result<u64, &'static str> {
        let setup_duration = std::cmp::max(Duration::from_secs(1), setup_duration);
        let start = CPUCounter::current();
        thread::sleep(setup_duration);
        let stop = CPUCounter::current();
        let elapsed = stop - start;
        Ok(elapsed.ticks() / setup_duration.as_secs())
    }
}