Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added clock_gettime, clock_getres, clock_settime, clock_getcpuclockid #1281

Merged
merged 1 commit into from
Oct 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this use libc_enum!?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The enum won't work for clockid_t returned by clock_getcpuclockid, that's why I had to go with struct.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're worried that the OS may add a new clockid that Nix doesn't know about? Ok, that's a fair reason.


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)
xonatius marked this conversation as resolved.
Show resolved Hide resolved
}

/// 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