From d58ea5c7f6a82f5165db57d4802e4a9828011f98 Mon Sep 17 00:00:00 2001 From: Nikolay Amiantov Date: Sat, 20 Feb 2016 01:56:23 +0300 Subject: [PATCH 1/4] time: use libc::timeval for TimeVal --- src/sys/time.rs | 75 +++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/src/sys/time.rs b/src/sys/time.rs index 1750481c29..125a4f4e60 100644 --- a/src/sys/time.rs +++ b/src/sys/time.rs @@ -1,24 +1,24 @@ use std::{fmt, ops}; -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, -} +use libc::{time_t, suseconds_t, timeval}; const MICROS_PER_SEC: i64 = 1_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; +#[derive(Clone, Copy)] +pub struct TimeVal(pub timeval); -#[cfg(target_pointer_width = "32")] -const MAX_SECONDS: i64 = ::std::isize::MAX as i64; +impl AsRef for TimeVal { + fn as_ref(&self) -> &timeval { + &self.0 + } +} -const MIN_SECONDS: i64 = -MAX_SECONDS; +impl AsMut for TimeVal { + fn as_mut(&mut self) -> &mut timeval { + &mut self.0 + } +} impl TimeVal { #[inline] @@ -44,8 +44,8 @@ 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 } + assert!(seconds >= time_t::min_value() && seconds <= time_t::max_value(), "TimeVal out of bounds; seconds={}", seconds); + TimeVal(timeval { tv_sec: seconds as time_t, tv_usec: 0 }) } #[inline] @@ -60,8 +60,8 @@ impl TimeVal { #[inline] 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 } + assert!(secs >= time_t::min_value() && secs <= time_t::max_value(), "TimeVal out of bounds; seconds={}", secs); + TimeVal(timeval { tv_sec: secs as time_t, tv_usec: micros as suseconds_t }) } pub fn num_hours(&self) -> i64 { @@ -73,10 +73,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.0.tv_sec < 0 && self.0.tv_usec > 0 { + (self.0.tv_sec + 1) as i64 } else { - self.tv_sec as i64 + self.0.tv_sec as i64 } } @@ -91,10 +91,10 @@ 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.0.tv_sec < 0 && self.0.tv_usec > 0 { + self.0.tv_usec - MICROS_PER_SEC as suseconds_t } else { - self.tv_usec + self.0.tv_usec } } } @@ -147,32 +147,40 @@ 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.0.tv_sec < 0 { (-*self, "-") } else { (*self, "") }; - let sec = abs.tv_sec; + let sec = abs.0.tv_sec; try!(write!(f, "{}", sign)); - if abs.tv_usec == 0 { - if abs.tv_sec == 1 { + if abs.0.tv_usec == 0 { + if abs.0.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.0.tv_usec % 1_000 == 0 { + try!(write!(f, "{}.{:03} seconds", sec, abs.0.tv_usec / 1_000)); } else { - try!(write!(f, "{}.{:06} seconds", sec, abs.tv_usec)); + try!(write!(f, "{}.{:06} seconds", sec, abs.0.tv_usec)); } Ok(()) } } +impl PartialEq for TimeVal { + fn eq(&self, rhs: &TimeVal) -> bool { + self.0.tv_sec == rhs.0.tv_sec && self.0.tv_usec == rhs.0.tv_usec + } +} + +impl Eq for TimeVal { } + #[inline] fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) { (div_floor_64(this, other), mod_floor_64(this, other)) @@ -208,9 +216,8 @@ mod test { #[test] pub fn test_time_val() { 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), - TimeVal::seconds(182)); + assert!(TimeVal::seconds(1) + TimeVal::seconds(2) == TimeVal::seconds(3)); + assert!(TimeVal::minutes(3) + TimeVal::seconds(2) == TimeVal::seconds(182)); } #[test] @@ -218,7 +225,7 @@ mod test { let a = TimeVal::seconds(1) + TimeVal::microseconds(123); let b = TimeVal::seconds(-1) + TimeVal::microseconds(-123); - assert_eq!(a, -b); + assert!(a == -b); } #[test] From 4eea0e9fb0a924c7d40e77e5cd00d978e64d388b Mon Sep 17 00:00:00 2001 From: Nikolay Amiantov Date: Sat, 20 Feb 2016 01:57:06 +0300 Subject: [PATCH 2/4] time: add TimeSpec --- src/sys/time.rs | 215 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 213 insertions(+), 2 deletions(-) diff --git a/src/sys/time.rs b/src/sys/time.rs index 125a4f4e60..c468492f31 100644 --- a/src/sys/time.rs +++ b/src/sys/time.rs @@ -1,6 +1,7 @@ use std::{fmt, ops}; -use libc::{time_t, suseconds_t, timeval}; +use libc::{time_t, c_long, suseconds_t, timeval, timespec}; +const NANOS_PER_SEC: i64 = 1_000_000_000; const MICROS_PER_SEC: i64 = 1_000_000; const SECS_PER_MINUTE: i64 = 60; const SECS_PER_HOUR: i64 = 3600; @@ -181,6 +182,191 @@ impl PartialEq for TimeVal { impl Eq for TimeVal { } +#[derive(Clone, Copy)] +pub struct TimeSpec(pub timespec); + +impl AsRef for TimeSpec { + fn as_ref(&self) -> ×pec { + &self.0 + } +} + +impl TimeSpec { + #[inline] + pub fn zero() -> TimeSpec { + TimeSpec::nanoseconds(0) + } + + #[inline] + pub fn hours(hours: i64) -> TimeSpec { + let secs = hours.checked_mul(SECS_PER_HOUR) + .expect("TimeSpec::hours ouf of bounds"); + + TimeSpec::seconds(secs) + } + + #[inline] + pub fn minutes(minutes: i64) -> TimeSpec { + let secs = minutes.checked_mul(SECS_PER_MINUTE) + .expect("TimeSpec::minutes out of bounds"); + + TimeSpec::seconds(secs) + } + + #[inline] + pub fn seconds(seconds: i64) -> TimeSpec { + assert!(seconds >= time_t::min_value() && seconds <= time_t::max_value(), "TimeSpec out of bounds; seconds={}", seconds); + TimeSpec(timespec { tv_sec: seconds as time_t, tv_nsec: 0 }) + } + + #[inline] + pub fn milliseconds(milliseconds: i64) -> TimeSpec { + let nanoseconds = milliseconds.checked_mul(1_000_000) + .expect("TimeSpec::milliseconds out of bounds"); + + TimeSpec::nanoseconds(nanoseconds) + } + + #[inline] + pub fn microseconds(microseconds: i64) -> TimeSpec { + let nanoseconds = microseconds.checked_mul(1_000) + .expect("TimeSpec::microseconds out of bounds"); + + TimeSpec::nanoseconds(nanoseconds) + } + + + /// Makes a new `TimeSpec` with given number of nanoseconds. + #[inline] + pub fn nanoseconds(nanoseconds: i64) -> TimeSpec { + let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC); + assert!(secs >= time_t::min_value() && secs <= time_t::max_value(), "TimeSpec out of bounds; seconds={}", secs); + TimeSpec(timespec { tv_sec: secs as time_t, tv_nsec: nanos as c_long }) + } + + pub fn num_hours(&self) -> i64 { + self.num_seconds() / 3600 + } + + pub fn num_minutes(&self) -> i64 { + self.num_seconds() / 60 + } + + pub fn num_seconds(&self) -> i64 { + if self.0.tv_sec < 0 && self.0.tv_nsec > 0 { + (self.0.tv_sec + 1) as i64 + } else { + self.0.tv_sec as i64 + } + } + + pub fn num_milliseconds(&self) -> i64 { + self.num_nanoseconds() / 1_000_000 + } + + pub fn num_microseconds(&self) -> i64 { + self.num_nanoseconds() / 1_000 + } + + pub fn num_nanoseconds(&self) -> i64 { + let secs = self.num_seconds() * NANOS_PER_SEC; + let nsec = self.nanos_mod_sec(); + secs + nsec as i64 + } + + fn nanos_mod_sec(&self) -> suseconds_t { + if self.0.tv_sec < 0 && self.0.tv_nsec > 0 { + self.0.tv_nsec - NANOS_PER_SEC as c_long + } else { + 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 nsec = self.num_nanoseconds().checked_mul(rhs as i64) + .expect("TimeSpec multiply out of bounds"); + + TimeSpec::nanoseconds(nsec) + } +} + +impl ops::Div for TimeSpec { + type Output = TimeSpec; + + fn div(self, rhs: i32) -> TimeSpec { + let nsec = self.num_nanoseconds() / rhs as i64; + TimeSpec::nanoseconds(nsec) + } +} + +impl fmt::Display for TimeSpec { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let (abs, sign) = if self.0.tv_sec < 0 { + (-*self, "-") + } else { + (*self, "") + }; + + let sec = abs.0.tv_sec; + + try!(write!(f, "{}", sign)); + + if abs.0.tv_nsec == 0 { + if abs.0.tv_sec == 1 { + try!(write!(f, "{} second", sec)); + } else { + try!(write!(f, "{} seconds", sec)); + } + } else if abs.0.tv_nsec % 1_000_000 == 0 { + try!(write!(f, "{}.{:03} seconds", sec, abs.0.tv_nsec / 1_000_000)); + } else if abs.0.tv_nsec % 1_000 == 0 { + try!(write!(f, "{}.{:06} seconds", sec, abs.0.tv_nsec / 1_000)); + } else { + try!(write!(f, "{}.{:09} seconds", sec, abs.0.tv_nsec)); + } + + Ok(()) + } +} + +impl PartialEq for TimeSpec { + fn eq(&self, rhs: &TimeSpec) -> bool { + self.0.tv_sec == rhs.0.tv_sec && self.0.tv_nsec == rhs.0.tv_nsec + } +} + +impl Eq for TimeSpec { } + #[inline] fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) { (div_floor_64(this, other), mod_floor_64(this, other)) @@ -211,7 +397,7 @@ fn div_rem_64(this: i64, other: i64) -> (i64, i64) { #[cfg(test)] mod test { - use super::TimeVal; + use super::{TimeVal, TimeSpec}; #[test] pub fn test_time_val() { @@ -236,4 +422,29 @@ mod test { assert_eq!(TimeVal::microseconds(42).to_string(), "0.000042 seconds"); assert_eq!(TimeVal::seconds(-86401).to_string(), "-86401 seconds"); } + + #[test] + pub fn test_time_spec() { + assert!(TimeSpec::seconds(1) != TimeSpec::zero()); + assert!(TimeSpec::seconds(1) + TimeSpec::seconds(2) == TimeSpec::seconds(3)); + assert!(TimeSpec::minutes(3) + TimeSpec::seconds(2) == TimeSpec::seconds(182)); + } + + #[test] + pub fn test_time_spec_neg() { + let a = TimeSpec::seconds(1) + TimeSpec::nanoseconds(123); + let b = TimeSpec::seconds(-1) + TimeSpec::nanoseconds(-123); + + assert!(a == -b); + } + + #[test] + pub fn test_time_spec_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"); + } } From 980ca8d1db4a4ff4769aa3185ddaa6cf5cebad84 Mon Sep 17 00:00:00 2001 From: Nikolay Amiantov Date: Sat, 20 Feb 2016 02:14:10 +0300 Subject: [PATCH 3/4] select: use libc --- src/sys/select.rs | 86 +++++++++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 48 deletions(-) diff --git a/src/sys/select.rs b/src/sys/select.rs index 1b47d759a9..9132ee1ab8 100644 --- a/src/sys/select.rs +++ b/src/sys/select.rs @@ -1,85 +1,75 @@ -use std::ptr::null_mut; +use std::ptr::{null, null_mut}; use std::os::unix::io::RawFd; -use libc::c_int; +use std::mem; +use libc; +use libc::{fd_set, c_int, timeval}; use {Errno, Result}; use sys::time::TimeVal; +use sys::signal::SigSet; -pub const FD_SETSIZE: RawFd = 1024; - -#[cfg(any(target_os = "macos", target_os = "ios"))] -#[repr(C)] pub struct FdSet { - bits: [i32; FD_SETSIZE as usize / 32] + set: fd_set } -#[cfg(any(target_os = "macos", target_os = "ios"))] -const BITS: usize = 32; - -#[cfg(not(any(target_os = "macos", target_os = "ios")))] -#[repr(C)] -#[derive(Clone)] -pub struct FdSet { - bits: [u64; FD_SETSIZE as usize / 64] +impl AsRef for FdSet { + fn as_ref(&self) -> &fd_set { + &self.set + } } -#[cfg(not(any(target_os = "macos", target_os = "ios")))] -const BITS: usize = 64; +pub const FD_SETSIZE: RawFd = libc::FD_SETSIZE as RawFd; impl FdSet { pub fn new() -> FdSet { - FdSet { - bits: [0; FD_SETSIZE as usize / BITS] - } + let mut set = FdSet { + set: unsafe { mem::uninitialized() } + }; + set.clear(); + set } pub fn insert(&mut self, fd: RawFd) { - let fd = fd as usize; - self.bits[fd / BITS] |= 1 << (fd % BITS); + assert!(fd >= 0 && fd < FD_SETSIZE, "RawFd out of bounds"); + unsafe { + libc::FD_SET(fd, &mut self.set as *mut _); + } } pub fn remove(&mut self, fd: RawFd) { - let fd = fd as usize; - self.bits[fd / BITS] &= !(1 << (fd % BITS)); + assert!(fd >= 0 && fd < FD_SETSIZE, "RawFd out of bounds"); + unsafe { + libc::FD_CLR(fd, &mut self.set as *mut _); + } } - pub fn contains(&mut self, fd: RawFd) -> bool { - let fd = fd as usize; - self.bits[fd / BITS] & (1 << (fd % BITS)) > 0 + pub fn contains(&self, fd: RawFd) -> bool { + assert!(fd >= 0 && fd < FD_SETSIZE, "RawFd out of bounds"); + unsafe { + // We require `transmute` here because FD_ISSET wants a mutable pointer, + // when in fact it doesn't mutate. + libc::FD_ISSET(fd, mem::transmute(&self.set as *const fd_set)) + } } pub fn clear(&mut self) { - for bits in &mut self.bits { - *bits = 0 + unsafe { + libc::FD_ZERO(&mut self.set as *mut _); } } } -mod ffi { - use libc::c_int; - use sys::time::TimeVal; - use super::FdSet; - - extern { - pub fn select(nfds: c_int, - readfds: *mut FdSet, - writefds: *mut FdSet, - errorfds: *mut FdSet, - timeout: *mut TimeVal) -> c_int; - } -} - pub fn select(nfds: c_int, readfds: Option<&mut FdSet>, writefds: Option<&mut FdSet>, errorfds: Option<&mut FdSet>, timeout: Option<&mut TimeVal>) -> Result { - 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 readfds = readfds.map(|set| &mut set.set as *mut fd_set).unwrap_or(null_mut()); + let writefds = writefds.map(|set| &mut set.set as *mut fd_set).unwrap_or(null_mut()); + let errorfds = errorfds.map(|set| &mut set.set as *mut fd_set).unwrap_or(null_mut()); + let timeout = timeout.map(|tv| tv.as_mut() as *mut timeval).unwrap_or(null_mut()); let res = unsafe { - ffi::select(nfds, readfds, writefds, errorfds, timeout) + libc::select(nfds, readfds, writefds, errorfds, timeout) }; Errno::result(res) From c8b7060ccfbb3a32d8266f46f87b24fa1db6c926 Mon Sep 17 00:00:00 2001 From: Nikolay Amiantov Date: Sat, 20 Feb 2016 02:16:05 +0300 Subject: [PATCH 4/4] select: add pselect syscall --- src/sys/select.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/sys/select.rs b/src/sys/select.rs index 9132ee1ab8..56d7873461 100644 --- a/src/sys/select.rs +++ b/src/sys/select.rs @@ -2,9 +2,9 @@ use std::ptr::{null, null_mut}; use std::os::unix::io::RawFd; use std::mem; use libc; -use libc::{fd_set, c_int, timeval}; +use libc::{fd_set, c_int, timespec, timeval, sigset_t}; use {Errno, Result}; -use sys::time::TimeVal; +use sys::time::{TimeVal, TimeSpec}; use sys::signal::SigSet; pub struct FdSet { @@ -74,3 +74,22 @@ pub fn select(nfds: c_int, Errno::result(res) } + +pub fn pselect(nfds: c_int, + readfds: Option<&mut FdSet>, + writefds: Option<&mut FdSet>, + errorfds: Option<&mut FdSet>, + timeout: Option<&TimeSpec>, + sigmask: Option<&SigSet>) -> Result { + let readfds = readfds.map(|set| &mut set.set as *mut fd_set).unwrap_or(null_mut()); + let writefds = writefds.map(|set| &mut set.set as *mut fd_set).unwrap_or(null_mut()); + let errorfds = errorfds.map(|set| &mut set.set as *mut fd_set).unwrap_or(null_mut()); + let timeout = timeout.map(|ts| ts.as_ref() as *const timespec).unwrap_or(null()); + let sigmask = sigmask.map(|sm| sm.as_ref() as *const sigset_t).unwrap_or(null()); + + let res = unsafe { + libc::pselect(nfds, readfds, writefds, errorfds, timeout, sigmask) + }; + + Errno::result(res) +}