Skip to content

Commit

Permalink
Merge #1281
Browse files Browse the repository at this point in the history
1281: Added clock_gettime, clock_getres, clock_settime, clock_getcpuclockid r=asomers a=xonatius

Picked up #1100 and added `clock_getcpuclockid` call as well. Credits to @kevinwern for the initial version.

https://www.man7.org/linux/man-pages/man2/clock_gettime.2.html
https://www.man7.org/linux/man-pages/man3/clock_getcpuclockid.3.html

Closes #1275 

Co-authored-by: Daniil Bondarev <[email protected]>
  • Loading branch information
bors[bot] and xonatius authored Oct 3, 2020
2 parents 2c24405 + 91effec commit fe0aa23
Show file tree
Hide file tree
Showing 7 changed files with 346 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Added
- Added Netlink protocol families to the `SockProtocol` enum
(#[1289](https://github.com/nix-rust/nix/pull/1289))
- Added `clock_gettime`, `clock_settime`, `clock_getres`,
`clock_getcpuclockid` functions and `ClockId` struct.
(#[1281](https://github.com/nix-rust/nix/pull/1281))
### Changed
- Expose `SeekData` and `SeekHole` on all Linux targets
(#[1284](https://github.com/nix-rust/nix/pull/1284))
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.77", 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
26 changes: 26 additions & 0 deletions src/sys/time.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::{cmp, fmt, ops};
use std::time::Duration;
use std::convert::From;
use libc::{c_long, timespec, timeval};
pub use libc::{time_t, suseconds_t};
Expand Down Expand Up @@ -66,6 +67,21 @@ impl From<timespec> for TimeSpec {
}
}

impl From<Duration> for TimeSpec {
fn from(duration: Duration) -> Self {
TimeSpec(timespec {
tv_sec: duration.as_secs() as time_t,
tv_nsec: duration.subsec_nanos() as c_long
})
}
}

impl From<TimeSpec> for Duration {
fn from(timespec: TimeSpec) -> Self {
Duration::new(timespec.0.tv_sec as u64, timespec.0.tv_nsec as u32)
}
}

impl AsRef<timespec> for TimeSpec {
fn as_ref(&self) -> &timespec {
&self.0
Expand Down Expand Up @@ -484,6 +500,7 @@ fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
#[cfg(test)]
mod test {
use super::{TimeSpec, TimeVal, TimeValLike};
use std::time::Duration;

#[test]
pub fn test_timespec() {
Expand All @@ -494,6 +511,15 @@ mod test {
TimeSpec::seconds(182));
}

#[test]
pub fn test_timespec_from() {
let duration = Duration::new(123, 123_456_789);
let timespec = TimeSpec::nanoseconds(123_123_456_789);

assert_eq!(TimeSpec::from(duration), timespec);
assert_eq!(Duration::from(timespec), duration);
}

#[test]
pub fn test_timespec_neg() {
let a = TimeSpec::seconds(1) + TimeSpec::nanoseconds(123);
Expand Down
258 changes: 258 additions & 0 deletions src/time.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
use crate::sys::time::TimeSpec;
#[cfg(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "linux",
target_os = "android",
target_os = "emscripten",
))]
use crate::{unistd::Pid, Error};
use crate::{Errno, 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(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "linux",
target_os = "android",
target_os = "emscripten",
))]
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 the current time on the clock id
pub fn now(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://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_getres.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://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_gettime.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://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_settime.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://pubs.opengroup.org/onlinepubs/009695399/functions/clock_getcpuclockid.html)).
#[cfg(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "linux",
target_os = "android",
target_os = "emscripten",
))]
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
Loading

0 comments on commit fe0aa23

Please sign in to comment.