From cc6c3167ecdbff2e482f865544569257b15ff825 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Sat, 19 Nov 2016 15:22:47 -0700 Subject: [PATCH 1/5] Opaqueify TimeVal By exposing its members, nix allowed users to create denormalized TimeVals, which don't work with the derived() Eq and Ord methods. Better to make TimeVal opaque, so it will always be normalized. --- src/sys/time.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sys/time.rs b/src/sys/time.rs index 1750481c29..31c194fb45 100644 --- a/src/sys/time.rs +++ b/src/sys/time.rs @@ -4,8 +4,8 @@ use libc::{time_t, suseconds_t}; #[repr(C)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct TimeVal { - pub tv_sec: time_t, - pub tv_usec: suseconds_t, + tv_sec: time_t, + tv_usec: suseconds_t, } const MICROS_PER_SEC: i64 = 1_000_000; From e983d20bd6be3dde43ed4a7f9f6e63eda19872b3 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Sat, 19 Nov 2016 15:45:59 -0700 Subject: [PATCH 2/5] Make TimeVal a NewType --- src/sys/select.rs | 10 +++--- src/sys/time.rs | 91 +++++++++++++++++++++++++++++++++++------------ 2 files changed, 74 insertions(+), 27 deletions(-) diff --git a/src/sys/select.rs b/src/sys/select.rs index 28b664aa75..c9e925dd35 100644 --- a/src/sys/select.rs +++ b/src/sys/select.rs @@ -1,6 +1,6 @@ use std::ptr::null_mut; use std::os::unix::io::RawFd; -use libc::c_int; +use libc::{c_int, timeval}; use {Errno, Result}; use sys::time::TimeVal; @@ -56,8 +56,7 @@ impl FdSet { } mod ffi { - use libc::c_int; - use sys::time::TimeVal; + use libc::{c_int, timeval}; use super::FdSet; extern { @@ -65,7 +64,7 @@ mod ffi { readfds: *mut FdSet, writefds: *mut FdSet, errorfds: *mut FdSet, - timeout: *mut TimeVal) -> c_int; + timeout: *mut timeval) -> c_int; } } @@ -77,7 +76,8 @@ pub fn select(nfds: c_int, let readfds = readfds.map(|set| set as *mut FdSet).unwrap_or(null_mut()); let writefds = writefds.map(|set| set as *mut FdSet).unwrap_or(null_mut()); let errorfds = errorfds.map(|set| set as *mut FdSet).unwrap_or(null_mut()); - let timeout = timeout.map(|tv| tv as *mut TimeVal).unwrap_or(null_mut()); + let timeout = timeout.map(|tv| &mut tv.timeval() as *mut timeval) + .unwrap_or(null_mut()); let res = unsafe { ffi::select(nfds, readfds, writefds, errorfds, timeout) diff --git a/src/sys/time.rs b/src/sys/time.rs index 31c194fb45..8340c9d0a4 100644 --- a/src/sys/time.rs +++ b/src/sys/time.rs @@ -1,12 +1,9 @@ -use std::{fmt, ops}; -use libc::{time_t, suseconds_t}; +use std::{cmp, fmt, ops}; +use libc::{time_t, suseconds_t, timeval}; #[repr(C)] -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub struct TimeVal { - tv_sec: time_t, - tv_usec: suseconds_t, -} +#[derive(Clone, Copy)] +pub struct TimeVal(timeval); const MICROS_PER_SEC: i64 = 1_000_000; const SECS_PER_MINUTE: i64 = 60; @@ -20,6 +17,43 @@ const MAX_SECONDS: i64 = ::std::isize::MAX as i64; const MIN_SECONDS: i64 = -MAX_SECONDS; +impl fmt::Debug for TimeVal { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("TimeVal") + .field("tv_sec", &self.tv_sec()) + .field("tv_usec", &self.tv_usec()) + .finish() + } +} + +impl cmp::PartialEq for TimeVal { + // The implementation of cmp is simplified by assuming that the struct is + // normalized. That is, tv_usec must always be within [0, 1_000_000) + fn eq(&self, other: &TimeVal) -> bool { + self.tv_sec() == other.tv_sec() && self.tv_usec() == other.tv_usec() + } +} + +impl cmp::Eq for TimeVal {} + +impl cmp::Ord for TimeVal { + // The implementation of cmp is simplified by assuming that the struct is + // normalized. That is, tv_usec must always be within [0, 1_000_000) + fn cmp(&self, other: &TimeVal) -> cmp::Ordering { + if self.tv_sec() == other.tv_sec() { + self.tv_sec().cmp(&other.tv_sec()) + } else { + self.tv_usec().cmp(&other.tv_usec()) + } + } +} + +impl cmp::PartialOrd for TimeVal { + fn partial_cmp(&self, other: &TimeVal) -> Option { + Some(self.cmp(other)) + } +} + impl TimeVal { #[inline] pub fn zero() -> TimeVal { @@ -45,7 +79,7 @@ impl TimeVal { #[inline] pub fn seconds(seconds: i64) -> TimeVal { assert!(seconds >= MIN_SECONDS && seconds <= MAX_SECONDS, "TimeVal out of bounds; seconds={}", seconds); - TimeVal { tv_sec: seconds as time_t, tv_usec: 0 } + TimeVal(timeval {tv_sec: seconds as time_t, tv_usec: 0 }) } #[inline] @@ -61,7 +95,8 @@ impl TimeVal { pub fn microseconds(microseconds: i64) -> TimeVal { let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); assert!(secs >= MIN_SECONDS && secs <= MAX_SECONDS, "TimeVal out of bounds"); - TimeVal { tv_sec: secs as time_t, tv_usec: micros as suseconds_t } + TimeVal(timeval {tv_sec: secs as time_t, + tv_usec: micros as suseconds_t }) } pub fn num_hours(&self) -> i64 { @@ -73,10 +108,10 @@ impl TimeVal { } pub fn num_seconds(&self) -> i64 { - if self.tv_sec < 0 && self.tv_usec > 0 { - (self.tv_sec + 1) as i64 + if self.tv_sec() < 0 && self.tv_usec() > 0 { + (self.tv_sec() + 1) as i64 } else { - self.tv_sec as i64 + self.tv_sec() as i64 } } @@ -91,12 +126,24 @@ impl TimeVal { } fn micros_mod_sec(&self) -> suseconds_t { - if self.tv_sec < 0 && self.tv_usec > 0 { - self.tv_usec - MICROS_PER_SEC as suseconds_t + if self.tv_sec() < 0 && self.tv_usec() > 0 { + self.tv_usec() - MICROS_PER_SEC as suseconds_t } else { - self.tv_usec + self.tv_usec() } } + + pub fn timeval(&self) -> timeval{ + self.0 + } + + pub fn tv_sec(&self) -> time_t { + self.0.tv_sec + } + + pub fn tv_usec(&self) -> suseconds_t { + self.0.tv_usec + } } impl ops::Neg for TimeVal { @@ -147,26 +194,26 @@ impl ops::Div for TimeVal { impl fmt::Display for TimeVal { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let (abs, sign) = if self.tv_sec < 0 { + let (abs, sign) = if self.tv_sec() < 0 { (-*self, "-") } else { (*self, "") }; - let sec = abs.tv_sec; + let sec = abs.tv_sec(); try!(write!(f, "{}", sign)); - if abs.tv_usec == 0 { - if abs.tv_sec == 1 { + if abs.tv_usec() == 0 { + if abs.tv_sec() == 1 { try!(write!(f, "{} second", sec)); } else { try!(write!(f, "{} seconds", sec)); } - } else if abs.tv_usec % 1000 == 0 { - try!(write!(f, "{}.{:03} seconds", sec, abs.tv_usec / 1000)); + } else if abs.tv_usec() % 1000 == 0 { + try!(write!(f, "{}.{:03} seconds", sec, abs.tv_usec() / 1000)); } else { - try!(write!(f, "{}.{:06} seconds", sec, abs.tv_usec)); + try!(write!(f, "{}.{:06} seconds", sec, abs.tv_usec())); } Ok(()) From b983240ef137c5c38a5554413381fb26d1c2e0f9 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Sat, 19 Nov 2016 19:06:13 -0700 Subject: [PATCH 3/5] Add TimeSpec, a Newtype around libc::timespec Also, add trait TimeValLike, so some code can be shared between TimeSpec and TimeVal. --- src/sys/time.rs | 356 ++++++++++++++++++++++++++++++++++------ test/sys/test_select.rs | 2 +- 2 files changed, 311 insertions(+), 47 deletions(-) diff --git a/src/sys/time.rs b/src/sys/time.rs index 8340c9d0a4..21eb87bbf3 100644 --- a/src/sys/time.rs +++ b/src/sys/time.rs @@ -1,21 +1,270 @@ use std::{cmp, fmt, ops}; -use libc::{time_t, suseconds_t, timeval}; +use libc::{c_long, time_t, suseconds_t, timespec, timeval}; + +pub trait TimeValLike: Sized { + #[inline] + fn zero() -> Self { + Self::seconds(0) + } + + #[inline] + fn hours(hours: i64) -> Self { + let secs = hours.checked_mul(SECS_PER_HOUR) + .expect("TimeVal::hours ouf of bounds"); + Self::seconds(secs) + } + + #[inline] + fn minutes(minutes: i64) -> Self { + let secs = minutes.checked_mul(SECS_PER_MINUTE) + .expect("TimeVal::minutes out of bounds"); + Self::seconds(secs) + } + + fn seconds(seconds: i64) -> Self; + fn milliseconds(milliseconds: i64) -> Self; + fn microseconds(microseconds: i64) -> Self; + fn nanoseconds(nanoseconds: i64) -> Self; + + #[inline] + fn num_hours(&self) -> i64 { + self.num_seconds() / 3600 + } + + #[inline] + fn num_minutes(&self) -> i64 { + self.num_seconds() / 60 + } + + fn num_seconds(&self) -> i64; + fn num_milliseconds(&self) -> i64; + fn num_microseconds(&self) -> i64; + fn num_nanoseconds(&self) -> i64; +} #[repr(C)] #[derive(Clone, Copy)] -pub struct TimeVal(timeval); +pub struct TimeSpec(timespec); -const MICROS_PER_SEC: i64 = 1_000_000; +const NANOS_PER_SEC: i64 = 1_000_000_000; const SECS_PER_MINUTE: i64 = 60; const SECS_PER_HOUR: i64 = 3600; #[cfg(target_pointer_width = "64")] -const MAX_SECONDS: i64 = (::std::i64::MAX / MICROS_PER_SEC) - 1; +const TS_MAX_SECONDS: i64 = (::std::i64::MAX / NANOS_PER_SEC) - 1; #[cfg(target_pointer_width = "32")] -const MAX_SECONDS: i64 = ::std::isize::MAX as i64; +const TS_MAX_SECONDS: i64 = ::std::isize::MAX as i64; + +const TS_MIN_SECONDS: i64 = -TS_MAX_SECONDS; + + +impl fmt::Debug for TimeSpec { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("TimeSpec") + .field("tv_sec", &self.tv_sec()) + .field("tv_nsec", &self.tv_nsec()) + .finish() + } +} + +impl cmp::PartialEq for TimeSpec { + // The implementation of cmp is simplified by assuming that the struct is + // normalized. That is, tv_nsec must always be within [0, 1_000_000_000) + fn eq(&self, other: &TimeSpec) -> bool { + self.tv_sec() == other.tv_sec() && self.tv_nsec() == other.tv_nsec() + } +} + +impl cmp::Eq for TimeSpec {} + +impl cmp::Ord for TimeSpec { + // The implementation of cmp is simplified by assuming that the struct is + // normalized. That is, tv_nsec must always be within [0, 1_000_000_000) + fn cmp(&self, other: &TimeSpec) -> cmp::Ordering { + if self.tv_sec() == other.tv_sec() { + self.tv_sec().cmp(&other.tv_sec()) + } else { + self.tv_nsec().cmp(&other.tv_nsec()) + } + } +} -const MIN_SECONDS: i64 = -MAX_SECONDS; +impl cmp::PartialOrd for TimeSpec { + fn partial_cmp(&self, other: &TimeSpec) -> Option { + Some(self.cmp(other)) + } +} + +impl TimeValLike for TimeSpec { + #[inline] + fn seconds(seconds: i64) -> TimeSpec { + assert!(seconds >= TS_MIN_SECONDS && seconds <= TS_MAX_SECONDS, "TimeSpec out of bounds; seconds={}", seconds); + TimeSpec(timespec {tv_sec: seconds as time_t, tv_nsec: 0 }) + } + + #[inline] + fn milliseconds(milliseconds: i64) -> TimeSpec { + let nanoseconds = milliseconds.checked_mul(1_000_000) + .expect("TimeSpec::milliseconds out of bounds"); + + TimeSpec::nanoseconds(nanoseconds) + } + + /// Makes a new `TimeSpec` with given number of microseconds. + #[inline] + fn microseconds(microseconds: i64) -> TimeSpec { + let nanoseconds = microseconds.checked_mul(1_000) + .expect("TimeSpec::milliseconds out of bounds"); + + TimeSpec::nanoseconds(nanoseconds) + } + + /// Makes a new `TimeSpec` with given number of nanoseconds. + #[inline] + fn nanoseconds(nanoseconds: i64) -> TimeSpec { + let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC); + assert!(secs >= TS_MIN_SECONDS && secs <= TS_MAX_SECONDS, "TimeSpec out of bounds"); + TimeSpec(timespec {tv_sec: secs as time_t, + tv_nsec: nanos as c_long }) + } + + fn num_seconds(&self) -> i64 { + if self.tv_sec() < 0 && self.tv_nsec() > 0 { + (self.tv_sec() + 1) as i64 + } else { + self.tv_sec() as i64 + } + } + + fn num_milliseconds(&self) -> i64 { + self.num_nanoseconds() / 1_000_000 + } + + fn num_microseconds(&self) -> i64 { + self.num_nanoseconds() / 1_000_000_000 + } + + fn num_nanoseconds(&self) -> i64 { + let secs = self.num_seconds() * 1_000_000_000; + let nsec = self.nanos_mod_sec(); + secs + nsec as i64 + } +} + +impl TimeSpec { + fn nanos_mod_sec(&self) -> c_long { + if self.tv_sec() < 0 && self.tv_nsec() > 0 { + self.tv_nsec() - NANOS_PER_SEC as c_long + } else { + self.tv_nsec() + } + } + + pub fn timespec(&self) -> timespec{ + self.0 + } + + pub fn tv_sec(&self) -> time_t { + self.0.tv_sec + } + + pub fn tv_nsec(&self) -> c_long { + self.0.tv_nsec + } +} + +impl ops::Neg for TimeSpec { + type Output = TimeSpec; + + fn neg(self) -> TimeSpec { + TimeSpec::nanoseconds(-self.num_nanoseconds()) + } +} + +impl ops::Add for TimeSpec { + type Output = TimeSpec; + + fn add(self, rhs: TimeSpec) -> TimeSpec { + TimeSpec::nanoseconds( + self.num_nanoseconds() + rhs.num_nanoseconds()) + } +} + +impl ops::Sub for TimeSpec { + type Output = TimeSpec; + + fn sub(self, rhs: TimeSpec) -> TimeSpec { + TimeSpec::nanoseconds( + self.num_nanoseconds() - rhs.num_nanoseconds()) + } +} + +impl ops::Mul for TimeSpec { + type Output = TimeSpec; + + fn mul(self, rhs: i32) -> TimeSpec { + let usec = self.num_nanoseconds().checked_mul(rhs as i64) + .expect("TimeSpec multiply out of bounds"); + + TimeSpec::nanoseconds(usec) + } +} + +impl ops::Div for TimeSpec { + type Output = TimeSpec; + + fn div(self, rhs: i32) -> TimeSpec { + let usec = self.num_nanoseconds() / rhs as i64; + TimeSpec::nanoseconds(usec) + } +} + +impl fmt::Display for TimeSpec { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let (abs, sign) = if self.tv_sec() < 0 { + (-*self, "-") + } else { + (*self, "") + }; + + let sec = abs.tv_sec(); + + try!(write!(f, "{}", sign)); + + if abs.tv_nsec() == 0 { + if abs.tv_sec() == 1 { + try!(write!(f, "{} second", sec)); + } else { + try!(write!(f, "{} seconds", sec)); + } + } else if abs.tv_nsec() % 1_000_000 == 0 { + try!(write!(f, "{}.{:03} seconds", sec, abs.tv_nsec() / 1_000_000)); + } else if abs.tv_nsec() % 1_000 == 0 { + try!(write!(f, "{}.{:06} seconds", sec, abs.tv_nsec() / 1_000)); + } else { + try!(write!(f, "{}.{:09} seconds", sec, abs.tv_nsec())); + } + + Ok(()) + } +} + + + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct TimeVal(timeval); + +const MICROS_PER_SEC: i64 = 1_000_000; + +#[cfg(target_pointer_width = "64")] +const TV_MAX_SECONDS: i64 = (::std::i64::MAX / MICROS_PER_SEC) - 1; + +#[cfg(target_pointer_width = "32")] +const TV_MAX_SECONDS: i64 = ::std::isize::MAX as i64; + +const TV_MIN_SECONDS: i64 = -TV_MAX_SECONDS; impl fmt::Debug for TimeVal { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { @@ -54,36 +303,15 @@ impl cmp::PartialOrd for TimeVal { } } -impl TimeVal { - #[inline] - pub fn zero() -> TimeVal { - TimeVal::microseconds(0) - } - - #[inline] - pub fn hours(hours: i64) -> TimeVal { - let secs = hours.checked_mul(SECS_PER_HOUR) - .expect("TimeVal::hours ouf of bounds"); - - TimeVal::seconds(secs) - } - - #[inline] - pub fn minutes(minutes: i64) -> TimeVal { - let secs = minutes.checked_mul(SECS_PER_MINUTE) - .expect("TimeVal::minutes out of bounds"); - - TimeVal::seconds(secs) - } - +impl TimeValLike for TimeVal { #[inline] - pub fn seconds(seconds: i64) -> TimeVal { - assert!(seconds >= MIN_SECONDS && seconds <= MAX_SECONDS, "TimeVal out of bounds; seconds={}", seconds); + fn seconds(seconds: i64) -> TimeVal { + assert!(seconds >= TV_MIN_SECONDS && seconds <= TV_MAX_SECONDS, "TimeVal out of bounds; seconds={}", seconds); TimeVal(timeval {tv_sec: seconds as time_t, tv_usec: 0 }) } #[inline] - pub fn milliseconds(milliseconds: i64) -> TimeVal { + fn milliseconds(milliseconds: i64) -> TimeVal { let microseconds = milliseconds.checked_mul(1_000) .expect("TimeVal::milliseconds out of bounds"); @@ -92,22 +320,25 @@ impl TimeVal { /// Makes a new `TimeVal` with given number of microseconds. #[inline] - pub fn microseconds(microseconds: i64) -> TimeVal { + fn microseconds(microseconds: i64) -> TimeVal { let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); - assert!(secs >= MIN_SECONDS && secs <= MAX_SECONDS, "TimeVal out of bounds"); + assert!(secs >= TV_MIN_SECONDS && secs <= TV_MAX_SECONDS, "TimeVal out of bounds"); TimeVal(timeval {tv_sec: secs as time_t, tv_usec: micros as suseconds_t }) } - pub fn num_hours(&self) -> i64 { - self.num_seconds() / 3600 - } - - pub fn num_minutes(&self) -> i64 { - self.num_seconds() / 60 + /// Makes a new `TimeVal` with given number of nanoseconds. Some precision + /// will be lost + #[inline] + fn nanoseconds(nanoseconds: i64) -> TimeVal { + let microseconds = nanoseconds / 1000; + let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); + assert!(secs >= TV_MIN_SECONDS && secs <= TV_MAX_SECONDS, "TimeVal out of bounds"); + TimeVal(timeval {tv_sec: secs as time_t, + tv_usec: micros as suseconds_t }) } - pub fn num_seconds(&self) -> i64 { + fn num_seconds(&self) -> i64 { if self.tv_sec() < 0 && self.tv_usec() > 0 { (self.tv_sec() + 1) as i64 } else { @@ -115,16 +346,22 @@ impl TimeVal { } } - pub fn num_milliseconds(&self) -> i64 { + fn num_milliseconds(&self) -> i64 { self.num_microseconds() / 1_000 } - pub fn num_microseconds(&self) -> i64 { + fn num_microseconds(&self) -> i64 { let secs = self.num_seconds() * 1_000_000; let usec = self.micros_mod_sec(); secs + usec as i64 } + fn num_nanoseconds(&self) -> i64 { + self.num_microseconds() * 1_000 + } +} + +impl TimeVal { fn micros_mod_sec(&self) -> suseconds_t { if self.tv_sec() < 0 && self.tv_usec() > 0 { self.tv_usec() - MICROS_PER_SEC as suseconds_t @@ -250,10 +487,36 @@ fn div_rem_64(this: i64, other: i64) -> (i64, i64) { #[cfg(test)] mod test { - use super::TimeVal; + use super::{TimeSpec, TimeVal, TimeValLike}; + + #[test] + pub fn test_timespec() { + assert!(TimeSpec::seconds(1) != TimeSpec::zero()); + assert_eq!(TimeSpec::seconds(1) + TimeSpec::seconds(2), TimeSpec::seconds(3)); + assert_eq!(TimeSpec::minutes(3) + TimeSpec::seconds(2), + TimeSpec::seconds(182)); + } + + #[test] + pub fn test_timespec_neg() { + let a = TimeSpec::seconds(1) + TimeSpec::nanoseconds(123); + let b = TimeSpec::seconds(-1) + TimeSpec::nanoseconds(-123); + + assert_eq!(a, -b); + } + + #[test] + pub fn test_timespec_fmt() { + assert_eq!(TimeSpec::zero().to_string(), "0 seconds"); + assert_eq!(TimeSpec::seconds(42).to_string(), "42 seconds"); + assert_eq!(TimeSpec::milliseconds(42).to_string(), "0.042 seconds"); + assert_eq!(TimeSpec::microseconds(42).to_string(), "0.000042 seconds"); + assert_eq!(TimeSpec::nanoseconds(42).to_string(), "0.000000042 seconds"); + assert_eq!(TimeSpec::seconds(-86401).to_string(), "-86401 seconds"); + } #[test] - pub fn test_time_val() { + pub fn test_timeval() { assert!(TimeVal::seconds(1) != TimeVal::zero()); assert_eq!(TimeVal::seconds(1) + TimeVal::seconds(2), TimeVal::seconds(3)); assert_eq!(TimeVal::minutes(3) + TimeVal::seconds(2), @@ -261,7 +524,7 @@ mod test { } #[test] - pub fn test_time_val_neg() { + pub fn test_timeval_neg() { let a = TimeVal::seconds(1) + TimeVal::microseconds(123); let b = TimeVal::seconds(-1) + TimeVal::microseconds(-123); @@ -269,11 +532,12 @@ mod test { } #[test] - pub fn test_time_val_fmt() { + pub fn test_timeval_fmt() { assert_eq!(TimeVal::zero().to_string(), "0 seconds"); assert_eq!(TimeVal::seconds(42).to_string(), "42 seconds"); assert_eq!(TimeVal::milliseconds(42).to_string(), "0.042 seconds"); assert_eq!(TimeVal::microseconds(42).to_string(), "0.000042 seconds"); + assert_eq!(TimeVal::nanoseconds(1402).to_string(), "0.000001 seconds"); assert_eq!(TimeVal::seconds(-86401).to_string(), "-86401 seconds"); } } diff --git a/test/sys/test_select.rs b/test/sys/test_select.rs index f1349923b0..53990f6546 100644 --- a/test/sys/test_select.rs +++ b/test/sys/test_select.rs @@ -1,5 +1,5 @@ use nix::sys::select::{FdSet, FD_SETSIZE, select}; -use nix::sys::time::TimeVal; +use nix::sys::time::{TimeVal, TimeValLike}; use nix::unistd::{write, pipe}; #[test] From 6466ac2e7e286e72d9379f72ee32459fa2bcbe20 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Sat, 19 Nov 2016 20:59:08 -0700 Subject: [PATCH 4/5] update CHANGELOG.md for PR #475 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19b05f7684..6dd85e1c98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added +- Added struct `TimeSpec` + ([#475](https://github.com/nix-rust/nix/pull/475)) - Added complete definitions for all kqueue-related constants on all supported OSes ([#415](https://github.com/nix-rust/nix/pull/415)) @@ -22,6 +24,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#457](https://github.com/nix-rust/nix/pull/457)) ### Changed +- Changed `TimeVal` into an opaque Newtype + ([#475](https://github.com/nix-rust/nix/pull/475)) - `kill`'s signature, defined in `::nix::sys::signal`, changed, so that the signal parameter has type `T: Into>`. `None` as an argument for that parameter will result in a 0 passed to libc's `kill`, while a From 24eefcc5b01dca9391bee5fe2efc94a69aba5ad4 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Fri, 25 Nov 2016 15:36:37 -0700 Subject: [PATCH 5/5] Fix use-after-free in select Also, fix the TimeSpec::cmp and TimeVal::cmp methods, and fix some formatting --- src/sys/select.rs | 2 +- src/sys/time.rs | 59 ++++++++++++++++++++++++++++++----------------- 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/src/sys/select.rs b/src/sys/select.rs index c9e925dd35..1d9a76c1d4 100644 --- a/src/sys/select.rs +++ b/src/sys/select.rs @@ -76,7 +76,7 @@ pub fn select(nfds: c_int, let readfds = readfds.map(|set| set as *mut FdSet).unwrap_or(null_mut()); let writefds = writefds.map(|set| set as *mut FdSet).unwrap_or(null_mut()); let errorfds = errorfds.map(|set| set as *mut FdSet).unwrap_or(null_mut()); - let timeout = timeout.map(|tv| &mut tv.timeval() as *mut timeval) + let timeout = timeout.map(|tv| tv as *mut TimeVal as *mut timeval) .unwrap_or(null_mut()); let res = unsafe { diff --git a/src/sys/time.rs b/src/sys/time.rs index 21eb87bbf3..9048a3ab41 100644 --- a/src/sys/time.rs +++ b/src/sys/time.rs @@ -10,14 +10,14 @@ pub trait TimeValLike: Sized { #[inline] fn hours(hours: i64) -> Self { let secs = hours.checked_mul(SECS_PER_HOUR) - .expect("TimeVal::hours ouf of bounds"); + .expect("TimeValLike::hours ouf of bounds"); Self::seconds(secs) } #[inline] fn minutes(minutes: i64) -> Self { let secs = minutes.checked_mul(SECS_PER_MINUTE) - .expect("TimeVal::minutes out of bounds"); + .expect("TimeValLike::minutes out of bounds"); Self::seconds(secs) } @@ -83,9 +83,9 @@ impl cmp::Ord for TimeSpec { // normalized. That is, tv_nsec must always be within [0, 1_000_000_000) fn cmp(&self, other: &TimeSpec) -> cmp::Ordering { if self.tv_sec() == other.tv_sec() { - self.tv_sec().cmp(&other.tv_sec()) - } else { self.tv_nsec().cmp(&other.tv_nsec()) + } else { + self.tv_sec().cmp(&other.tv_sec()) } } } @@ -99,7 +99,8 @@ impl cmp::PartialOrd for TimeSpec { impl TimeValLike for TimeSpec { #[inline] fn seconds(seconds: i64) -> TimeSpec { - assert!(seconds >= TS_MIN_SECONDS && seconds <= TS_MAX_SECONDS, "TimeSpec out of bounds; seconds={}", seconds); + assert!(seconds >= TS_MIN_SECONDS && seconds <= TS_MAX_SECONDS, + "TimeSpec out of bounds; seconds={}", seconds); TimeSpec(timespec {tv_sec: seconds as time_t, tv_nsec: 0 }) } @@ -124,7 +125,8 @@ impl TimeValLike for TimeSpec { #[inline] fn nanoseconds(nanoseconds: i64) -> TimeSpec { let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC); - assert!(secs >= TS_MIN_SECONDS && secs <= TS_MAX_SECONDS, "TimeSpec out of bounds"); + assert!(secs >= TS_MIN_SECONDS && secs <= TS_MAX_SECONDS, + "TimeSpec out of bounds"); TimeSpec(timespec {tv_sec: secs as time_t, tv_nsec: nanos as c_long }) } @@ -161,10 +163,6 @@ impl TimeSpec { } } - pub fn timespec(&self) -> timespec{ - self.0 - } - pub fn tv_sec(&self) -> time_t { self.0.tv_sec } @@ -290,9 +288,9 @@ impl cmp::Ord for TimeVal { // normalized. That is, tv_usec must always be within [0, 1_000_000) fn cmp(&self, other: &TimeVal) -> cmp::Ordering { if self.tv_sec() == other.tv_sec() { - self.tv_sec().cmp(&other.tv_sec()) - } else { self.tv_usec().cmp(&other.tv_usec()) + } else { + self.tv_sec().cmp(&other.tv_sec()) } } } @@ -306,7 +304,8 @@ impl cmp::PartialOrd for TimeVal { impl TimeValLike for TimeVal { #[inline] fn seconds(seconds: i64) -> TimeVal { - assert!(seconds >= TV_MIN_SECONDS && seconds <= TV_MAX_SECONDS, "TimeVal out of bounds; seconds={}", seconds); + assert!(seconds >= TV_MIN_SECONDS && seconds <= TV_MAX_SECONDS, + "TimeVal out of bounds; seconds={}", seconds); TimeVal(timeval {tv_sec: seconds as time_t, tv_usec: 0 }) } @@ -322,7 +321,8 @@ impl TimeValLike for TimeVal { #[inline] fn microseconds(microseconds: i64) -> TimeVal { let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); - assert!(secs >= TV_MIN_SECONDS && secs <= TV_MAX_SECONDS, "TimeVal out of bounds"); + assert!(secs >= TV_MIN_SECONDS && secs <= TV_MAX_SECONDS, + "TimeVal out of bounds"); TimeVal(timeval {tv_sec: secs as time_t, tv_usec: micros as suseconds_t }) } @@ -333,7 +333,8 @@ impl TimeValLike for TimeVal { fn nanoseconds(nanoseconds: i64) -> TimeVal { let microseconds = nanoseconds / 1000; let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); - assert!(secs >= TV_MIN_SECONDS && secs <= TV_MAX_SECONDS, "TimeVal out of bounds"); + assert!(secs >= TV_MIN_SECONDS && secs <= TV_MAX_SECONDS, + "TimeVal out of bounds"); TimeVal(timeval {tv_sec: secs as time_t, tv_usec: micros as suseconds_t }) } @@ -370,10 +371,6 @@ impl TimeVal { } } - pub fn timeval(&self) -> timeval{ - self.0 - } - pub fn tv_sec(&self) -> time_t { self.0.tv_sec } @@ -492,7 +489,8 @@ mod test { #[test] pub fn test_timespec() { assert!(TimeSpec::seconds(1) != TimeSpec::zero()); - assert_eq!(TimeSpec::seconds(1) + TimeSpec::seconds(2), TimeSpec::seconds(3)); + assert_eq!(TimeSpec::seconds(1) + TimeSpec::seconds(2), + TimeSpec::seconds(3)); assert_eq!(TimeSpec::minutes(3) + TimeSpec::seconds(2), TimeSpec::seconds(182)); } @@ -505,6 +503,15 @@ mod test { assert_eq!(a, -b); } + #[test] + pub fn test_timespec_ord() { + assert!(TimeSpec::seconds(1) == TimeSpec::nanoseconds(1_000_000_000)); + assert!(TimeSpec::seconds(1) < TimeSpec::nanoseconds(1_000_000_001)); + assert!(TimeSpec::seconds(1) > TimeSpec::nanoseconds(999_999_999)); + assert!(TimeSpec::seconds(-1) < TimeSpec::nanoseconds(-999_999_999)); + assert!(TimeSpec::seconds(-1) > TimeSpec::nanoseconds(-1_000_000_001)); + } + #[test] pub fn test_timespec_fmt() { assert_eq!(TimeSpec::zero().to_string(), "0 seconds"); @@ -518,11 +525,21 @@ mod test { #[test] pub fn test_timeval() { assert!(TimeVal::seconds(1) != TimeVal::zero()); - assert_eq!(TimeVal::seconds(1) + TimeVal::seconds(2), TimeVal::seconds(3)); + assert_eq!(TimeVal::seconds(1) + TimeVal::seconds(2), + TimeVal::seconds(3)); assert_eq!(TimeVal::minutes(3) + TimeVal::seconds(2), TimeVal::seconds(182)); } + #[test] + pub fn test_timeval_ord() { + assert!(TimeVal::seconds(1) == TimeVal::microseconds(1_000_000)); + assert!(TimeVal::seconds(1) < TimeVal::microseconds(1_000_001)); + assert!(TimeVal::seconds(1) > TimeVal::microseconds(999_999)); + assert!(TimeVal::seconds(-1) < TimeVal::microseconds(-999_999)); + assert!(TimeVal::seconds(-1) > TimeVal::microseconds(-1_000_001)); + } + #[test] pub fn test_timeval_neg() { let a = TimeVal::seconds(1) + TimeVal::microseconds(123);