Skip to content

Commit

Permalink
Added clock_gettime, clock_getres, clock_settime, clock_getcpuclockid
Browse files Browse the repository at this point in the history
  • Loading branch information
xonatius committed Aug 1, 2020
1 parent 96054b6 commit b3e7543
Show file tree
Hide file tree
Showing 6 changed files with 283 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased] - ReleaseDate
### Added
- Added `clock_gettime`, `clock_settime`, `clock_getres`,
`clock_getcpuclockid` functions and `ClockId` struct.
(#[1281](https://github.com/nix-rust/nix/pull/1281))
### Changed
### Fixed
### Removed
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ exclude = [
]

[dependencies]
libc = { version = "0.2.73", features = [ "extra_traits" ] }
libc = { version = "0.2.74", features = [ "extra_traits" ] }
bitflags = "1.1"
cfg-if = "0.1.10"

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ pub mod poll;
pub mod pty;
pub mod sched;
pub mod sys;
pub mod time;
// This can be implemented for other platforms as soon as libc
// provides bindings for them.
#[cfg(all(target_os = "linux",
Expand Down
239 changes: 239 additions & 0 deletions src/time.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
use crate::sys::time::TimeSpec;
use crate::unistd::Pid;
use crate::{Errno, Error, Result};
use libc::{self, clockid_t};
use std::mem::MaybeUninit;

/// Clock identifier
///
/// Newtype pattern around `clockid_t` (which is just alias). It pervents bugs caused by
/// accidentally passing wrong value.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ClockId(clockid_t);

impl ClockId {
/// Creates `ClockId` from raw `clockid_t`
pub fn from_raw(clk_id: clockid_t) -> Self {
ClockId(clk_id)
}

/// Returns `ClockId` of a `pid` CPU-time clock
#[cfg(target_os = "linux")]
pub fn pid_cpu_clock_id(pid: Pid) -> Result<Self> {
clock_getcpuclockid(pid)
}

/// Returns resolution of the clock id
pub fn res(self) -> Result<TimeSpec> {
clock_getres(self)
}

/// Returns time on the clock id
pub fn time(self) -> Result<TimeSpec> {
clock_gettime(self)
}

/// Sets time to `timespec` on the clock id
#[cfg(not(any(
target_os = "macos",
target_os = "ios",
all(
not(any(target_env = "uclibc", target_env = "newlibc")),
any(target_os = "redox", target_os = "hermit",),
),
)))]
pub fn set_time(self, timespec: TimeSpec) -> Result<()> {
clock_settime(self, timespec)
}

/// Gets the raw `clockid_t` wrapped by `self`
pub fn as_raw(self) -> clockid_t {
self.0
}

#[cfg(any(
target_os = "fuchsia",
all(
not(any(target_env = "uclibc", target_env = "newlib")),
any(target_os = "linux", target_os = "android", target_os = "emscripten"),
)
))]
pub const CLOCK_BOOTTIME: ClockId = ClockId(libc::CLOCK_BOOTTIME);
#[cfg(any(
target_os = "fuchsia",
all(
not(any(target_env = "uclibc", target_env = "newlib")),
any(target_os = "linux", target_os = "android", target_os = "emscripten")
)
))]
pub const CLOCK_BOOTTIME_ALARM: ClockId = ClockId(libc::CLOCK_BOOTTIME_ALARM);
pub const CLOCK_MONOTONIC: ClockId = ClockId(libc::CLOCK_MONOTONIC);
#[cfg(any(
target_os = "fuchsia",
all(
not(any(target_env = "uclibc", target_env = "newlib")),
any(target_os = "linux", target_os = "android", target_os = "emscripten")
)
))]
pub const CLOCK_MONOTONIC_COARSE: ClockId = ClockId(libc::CLOCK_MONOTONIC_COARSE);
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
pub const CLOCK_MONOTONIC_FAST: ClockId = ClockId(libc::CLOCK_MONOTONIC_FAST);
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
pub const CLOCK_MONOTONIC_PRECISE: ClockId = ClockId(libc::CLOCK_MONOTONIC_PRECISE);
#[cfg(any(
target_os = "fuchsia",
all(
not(any(target_env = "uclibc", target_env = "newlib")),
any(target_os = "linux", target_os = "android", target_os = "emscripten")
)
))]
pub const CLOCK_MONOTONIC_RAW: ClockId = ClockId(libc::CLOCK_MONOTONIC_RAW);
#[cfg(any(
target_os = "fuchsia",
target_env = "uclibc",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly",
all(
not(target_env = "newlib"),
any(target_os = "linux", target_os = "android", target_os = "emscripten")
)
))]
pub const CLOCK_PROCESS_CPUTIME_ID: ClockId = ClockId(libc::CLOCK_PROCESS_CPUTIME_ID);
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
pub const CLOCK_PROF: ClockId = ClockId(libc::CLOCK_PROF);
pub const CLOCK_REALTIME: ClockId = ClockId(libc::CLOCK_REALTIME);
#[cfg(any(
target_os = "fuchsia",
all(
not(any(target_env = "uclibc", target_env = "newlib")),
any(target_os = "linux", target_os = "android", target_os = "emscripten")
)
))]
pub const CLOCK_REALTIME_ALARM: ClockId = ClockId(libc::CLOCK_REALTIME_ALARM);
#[cfg(any(
target_os = "fuchsia",
all(
not(any(target_env = "uclibc", target_env = "newlib")),
any(target_os = "linux", target_os = "android", target_os = "emscripten")
)
))]
pub const CLOCK_REALTIME_COARSE: ClockId = ClockId(libc::CLOCK_REALTIME_COARSE);
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
pub const CLOCK_REALTIME_FAST: ClockId = ClockId(libc::CLOCK_REALTIME_FAST);
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
pub const CLOCK_REALTIME_PRECISE: ClockId = ClockId(libc::CLOCK_REALTIME_PRECISE);
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
pub const CLOCK_SECOND: ClockId = ClockId(libc::CLOCK_SECOND);
#[cfg(any(
target_os = "fuchsia",
all(
not(any(target_env = "uclibc", target_env = "newlib")),
any(
target_os = "emscripten",
all(target_os = "linux", target_env = "musl")
)
)
))]
pub const CLOCK_SGI_CYCLE: ClockId = ClockId(libc::CLOCK_SGI_CYCLE);
#[cfg(any(
target_os = "fuchsia",
all(
not(any(target_env = "uclibc", target_env = "newlib")),
any(
target_os = "emscripten",
all(target_os = "linux", target_env = "musl")
)
)
))]
pub const CLOCK_TAI: ClockId = ClockId(libc::CLOCK_TAI);
#[cfg(any(
target_env = "uclibc",
target_os = "fuchsia",
target_os = "ios",
target_os = "macos",
target_os = "freebsd",
target_os = "dragonfly",
all(
not(target_env = "newlib"),
any(target_os = "linux", target_os = "android", target_os = "emscripten",),
),
))]
pub const CLOCK_THREAD_CPUTIME_ID: ClockId = ClockId(libc::CLOCK_THREAD_CPUTIME_ID);
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
pub const CLOCK_UPTIME: ClockId = ClockId(libc::CLOCK_UPTIME);
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
pub const CLOCK_UPTIME_FAST: ClockId = ClockId(libc::CLOCK_UPTIME_FAST);
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
pub const CLOCK_UPTIME_PRECISE: ClockId = ClockId(libc::CLOCK_UPTIME_PRECISE);
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
pub const CLOCK_VIRTUAL: ClockId = ClockId(libc::CLOCK_VIRTUAL);
}

impl Into<clockid_t> for ClockId {
fn into(self) -> clockid_t {
self.as_raw()
}
}

impl From<clockid_t> for ClockId {
fn from(clk_id: clockid_t) -> Self {
ClockId::from_raw(clk_id)
}
}

impl std::fmt::Display for ClockId {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}

/// Get the resolution of the specified clock, (see
/// [clock_getres(2)](https://www.man7.org/linux/man-pages/man2/clock_getres.2.html)).
pub fn clock_getres(clock_id: ClockId) -> Result<TimeSpec> {
let mut c_time: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
let ret = unsafe { libc::clock_getres(clock_id.as_raw(), c_time.as_mut_ptr()) };
Errno::result(ret)?;
let res = unsafe { c_time.assume_init() };
Ok(TimeSpec::from(res))
}

/// Get the time of the specified clock, (see
/// [clock_gettime(2)](https://www.man7.org/linux/man-pages/man2/clock_gettime.2.html)).
pub fn clock_gettime(clock_id: ClockId) -> Result<TimeSpec> {
let mut c_time: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
let ret = unsafe { libc::clock_gettime(clock_id.as_raw(), c_time.as_mut_ptr()) };
Errno::result(ret)?;
let res = unsafe { c_time.assume_init() };
Ok(TimeSpec::from(res))
}

/// Set the time of the specified clock, (see
/// [clock_settime(2)](https://www.man7.org/linux/man-pages/man2/clock_settime.2.html)).
#[cfg(not(any(
target_os = "macos",
target_os = "ios",
all(
not(any(target_env = "uclibc", target_env = "newlibc")),
any(target_os = "redox", target_os = "hermit",),
),
)))]
pub fn clock_settime(clock_id: ClockId, timespec: TimeSpec) -> Result<()> {
let ret = unsafe { libc::clock_settime(clock_id.as_raw(), timespec.as_ref()) };
Errno::result(ret).map(drop)
}

/// Get the clock id of the specified process id, (see
/// [clock_getcpuclockid(3)](https://www.man7.org/linux/man-pages/man3/clock_getcpuclockid.3.html)).
#[cfg(target_os = "linux")]
pub fn clock_getcpuclockid(pid: Pid) -> Result<ClockId> {
let mut clk_id: MaybeUninit<libc::clockid_t> = MaybeUninit::uninit();
let ret = unsafe { libc::clock_getcpuclockid(pid.into(), clk_id.as_mut_ptr()) };
if ret == 0 {
let res = unsafe { clk_id.assume_init() };
Ok(ClockId::from(res))
} else {
Err(Error::Sys(Errno::from_i32(ret)))
}
}
1 change: 1 addition & 0 deletions test/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ mod test_sched;
target_os = "macos"))]
mod test_sendfile;
mod test_stat;
mod test_time;
mod test_unistd;

use std::os::unix::io::RawFd;
Expand Down
38 changes: 38 additions & 0 deletions test/test_time.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#[cfg(target_os = "linux")]
use nix::time::clock_getcpuclockid;
use nix::time::{clock_getres, clock_gettime, ClockId};

#[test]
pub fn test_clock_getres() {
assert!(clock_getres(ClockId::CLOCK_REALTIME).is_ok());
}

#[test]
pub fn test_clock_gettime() {
assert!(clock_gettime(ClockId::CLOCK_REALTIME).is_ok());
}

#[cfg(target_os = "linux")]
#[test]
pub fn test_clock_getcpuclockid() {
let clock_id = clock_getcpuclockid(nix::unistd::Pid::this()).unwrap();
assert!(clock_gettime(clock_id).is_ok());
}

#[test]
pub fn test_clock_id_res() {
assert!(ClockId::CLOCK_REALTIME.res().is_ok());
}

#[test]
pub fn test_clock_id_time() {
assert!(ClockId::CLOCK_REALTIME.time().is_ok());
}

#[cfg(target_os = "linux")]
#[test]
pub fn test_clock_id_pid_cpu_clock_id() {
assert!(ClockId::pid_cpu_clock_id(nix::unistd::Pid::this())
.map(ClockId::time)
.is_ok());
}

0 comments on commit b3e7543

Please sign in to comment.