diff --git a/CHANGELOG.md b/CHANGELOG.md index 3eb1cf9d5b..6f98621a55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,6 +96,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#814](https://github.com/nix-rust/nix/pull/814)) - Removed return type from `pause`. ([#829](https://github.com/nix-rust/nix/pull/829)) +- Changed the API of `kevent` to accept `Into`. + ([#838](https://github.com/nix-rust/nix/pull/838)) ### Fixed - Fix compilation and tests for OpenBSD targets @@ -116,6 +118,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Removed `MS_VERBOSE`, `MS_NOSEC`, and `MS_BORN` from `MsFlags`. These are internal kernel flags and should never have been exposed. ([#814](https://github.com/nix-rust/nix/pull/814)) +- Removed `kevent_ts`, it was merged with `kevent`. + ([#838](https://github.com/nix-rust/nix/pull/838)) ## [0.9.0] 2017-07-23 diff --git a/src/sys/event.rs b/src/sys/event.rs index 4d5d18806b..dde46ba745 100644 --- a/src/sys/event.rs +++ b/src/sys/event.rs @@ -1,17 +1,31 @@ -/* TOOD: Implement for other kqueue based systems +//! Kernel event notification mechanism. +//! +//! For more information see [kqueue(2)]. +//! +//! [kqueue(2)]: https://www.freebsd.org/cgi/man.cgi?query=kqueue + +/* TODO: Implement for other kqueue based systems */ -use {Errno, Result}; +use std::mem; +use std::os::unix::io::RawFd; +use std::ptr; + #[cfg(not(target_os = "netbsd"))] -use libc::{timespec, time_t, c_int, c_long, intptr_t, uintptr_t}; +use libc::{timespec, c_int, intptr_t, uintptr_t}; #[cfg(target_os = "netbsd")] use libc::{timespec, time_t, c_long, intptr_t, uintptr_t, size_t}; use libc; -use std::os::unix::io::RawFd; -use std::ptr; -use std::mem; -// Redefine kevent in terms of programmer-friendly enums and bitfields. +use {Errno, Result}; +use sys::time::TimeSpec; + +/// Wrapper around `libc:kevent`. +/// +/// Redefined `kevent` in terms of programmer-friendly enums and bitfields. See +/// [`new`] to create a new `KEvent`. +/// +/// [`new`]: #method.new #[derive(Clone, Copy)] #[repr(C)] pub struct KEvent { @@ -35,6 +49,11 @@ type type_of_event_filter = u32; #[cfg(not(target_os = "netbsd"))] type type_of_event_filter = i16; libc_enum! { + /// Identifies the kernel filter used to process this event. + /// + /// See [`KEvent`]. + /// + /// [`KEvent`]: struct.KEvent.html #[cfg_attr(target_os = "netbsd", repr(u32))] #[cfg_attr(not(target_os = "netbsd"), repr(i16))] pub enum EventFilter { @@ -66,13 +85,20 @@ libc_enum! { } } +/// The underlying type of `EventFlag`. #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "openbsd"))] pub type type_of_event_flag = u16; #[cfg(any(target_os = "netbsd"))] pub type type_of_event_flag = u32; + libc_bitflags!{ + /// Actions to perform on the event. + /// + /// See [`KEvent`]. + /// + /// [`KEvent`]: struct.KEvent.html pub struct EventFlag: type_of_event_flag { EV_ADD; EV_CLEAR; @@ -111,6 +137,11 @@ libc_bitflags!{ } libc_bitflags!( + /// Filter-specific flags. + /// + /// See [`KEvent`]. + /// + /// [`KEvent`]: struct.KEvent.html pub struct FilterFlag: u32 { #[cfg(any(target_os = "macos", target_os = "ios"))] NOTE_ABSOLUTE; @@ -200,13 +231,17 @@ libc_bitflags!( } ); +/// The `kqueue` system call creates a new kernel event queue and returns a +/// descriptor. +/// +/// For more information see [kqueue(2)]. +/// +/// [kqueue(2)]: https://www.freebsd.org/cgi/man.cgi?query=kqueue pub fn kqueue() -> Result { let res = unsafe { libc::kqueue() }; - Errno::result(res) } - // KEvent can't derive Send because on some operating systems, udata is defined // as a void*. However, KEvent's public API always treats udata as an intptr_t, // which is safe to Send. @@ -214,6 +249,7 @@ unsafe impl Send for KEvent { } impl KEvent { + /// Create a new `KEvent`. pub fn new(ident: uintptr_t, filter: EventFilter, flags: EventFlag, fflags:FilterFlag, data: intptr_t, udata: intptr_t) -> KEvent { KEvent { kevent: libc::kevent { @@ -226,45 +262,37 @@ impl KEvent { } } } + /// Get the identifier for this event. pub fn ident(&self) -> uintptr_t { self.kevent.ident } + /// Get the kernal filter for this event. pub fn filter(&self) -> EventFilter { unsafe { mem::transmute(self.kevent.filter as type_of_event_filter) } } + /// Get the action to performan for this event. pub fn flags(&self) -> EventFlag { EventFlag::from_bits(self.kevent.flags).unwrap() } + /// Get the filter flags. pub fn fflags(&self) -> FilterFlag { FilterFlag::from_bits(self.kevent.fflags).unwrap() } + /// Get the filter data. pub fn data(&self) -> intptr_t { self.kevent.data as intptr_t } + /// Get the user data indentifier. pub fn udata(&self) -> intptr_t { self.kevent.udata as intptr_t } } -pub fn kevent(kq: RawFd, - changelist: &[KEvent], - eventlist: &mut [KEvent], - timeout_ms: usize) -> Result { - - // Convert ms to timespec - let timeout = timespec { - tv_sec: (timeout_ms / 1000) as time_t, - tv_nsec: ((timeout_ms % 1000) * 1_000_000) as c_long - }; - - kevent_ts(kq, changelist, eventlist, Some(timeout)) -} - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd", @@ -274,10 +302,51 @@ type type_of_nchanges = c_int; #[cfg(target_os = "netbsd")] type type_of_nchanges = size_t; -pub fn kevent_ts(kq: RawFd, - changelist: &[KEvent], - eventlist: &mut [KEvent], - timeout_opt: Option) -> Result { +/// The `kevent` system call is used to register events with the queue, and +/// return any pending events to the user. +/// +/// For more information see [kqueue(2)]. +/// +/// [kqueue(2)]: https://www.freebsd.org/cgi/man.cgi?query=kqueue +/// +/// # Examples +/// +/// Using `std::time::Duration`. +/// +/// ``` +/// use std::time::Duration; +/// use nix::sys::event::{kqueue, kevent}; +/// +/// let kq = kqueue().unwrap(); +/// let mut events = Vec::new(); +/// +/// // With a timeout. +/// let timeout = Duration::from_millis(100); +/// kevent(kq, &[], &mut events, Some(timeout)).unwrap(); +/// +/// // Without a timeout. +/// kevent::(kq, &[], &mut events, None).unwrap(); +/// ``` +/// +/// Using `libc::timespec` directly. +/// +/// ``` +/// use nix::libc::timespec; +/// use nix::sys::event::{kqueue, kevent}; +/// +/// let kq = kqueue().unwrap(); +/// let mut events = Vec::new(); +/// +/// let timeout = timespec { tv_sec: 0, tv_nsec: 1000}; +/// assert_eq!(kevent(kq, &[], &mut events, Some(timeout)).unwrap(), 0); +/// ``` +pub fn kevent>(kq: RawFd, changelist: &[KEvent], eventlist: &mut [KEvent], timeout: Option) -> Result { + let timeout = timeout.map(|t| t.into()); + let timeout_ptr = if let Some(ref timeout) = timeout { + timeout.as_ref() as *const timespec + } else { + ptr::null() + }; let res = unsafe { libc::kevent( @@ -286,28 +355,13 @@ pub fn kevent_ts(kq: RawFd, changelist.len() as type_of_nchanges, eventlist.as_mut_ptr() as *mut libc::kevent, eventlist.len() as type_of_nchanges, - if let Some(ref timeout) = timeout_opt {timeout as *const timespec} else {ptr::null()}) + timeout_ptr, + ) }; Errno::result(res).map(|r| r as usize) } -#[inline] -pub fn ev_set(ev: &mut KEvent, - ident: usize, - filter: EventFilter, - flags: EventFlag, - fflags: FilterFlag, - udata: intptr_t) { - - ev.kevent.ident = ident as uintptr_t; - ev.kevent.filter = filter as type_of_event_filter; - ev.kevent.flags = flags.bits(); - ev.kevent.fflags = fflags.bits(); - ev.kevent.data = 0; - ev.kevent.udata = udata as type_of_udata; -} - #[test] fn test_struct_kevent() { let udata : intptr_t = 12345; diff --git a/src/sys/time.rs b/src/sys/time.rs index 0d9770456d..73f6b06a5f 100644 --- a/src/sys/time.rs +++ b/src/sys/time.rs @@ -1,4 +1,6 @@ use std::{cmp, fmt, ops}; +use std::time::Duration; + use libc::{c_long, time_t, suseconds_t, timespec, timeval}; pub trait TimeValLike: Sized { @@ -42,6 +44,26 @@ pub trait TimeValLike: Sized { fn num_nanoseconds(&self) -> i64; } +/// A wrapper around `libc::timespec`. +/// +/// # Examples +/// +/// Both `std::time::Duration` and `libc::timespec` implement `Into`, +/// and can be used in any function accepting `Into`. +/// +/// ``` +/// use std::time::Duration; +/// use nix::libc::timespec; +/// use nix::sys::time::TimeSpec; +/// +/// fn with_timeout>(timeout: T) { +/// # drop(timeout); +/// // Code etc. +/// } +/// +/// with_timeout(Duration::from_millis(100)); +/// with_timeout(timespec { tv_sec: 0, tv_nsec: 1000}); +/// ``` #[repr(C)] #[derive(Clone, Copy)] pub struct TimeSpec(timespec); @@ -58,6 +80,20 @@ const TS_MAX_SECONDS: i64 = ::std::isize::MAX as i64; const TS_MIN_SECONDS: i64 = -TS_MAX_SECONDS; +impl From 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 for TimeSpec { + fn from(time: timespec) -> Self { + TimeSpec(time) + } +} impl AsRef for TimeSpec { fn as_ref(&self) -> ×pec { @@ -254,8 +290,6 @@ impl fmt::Display for TimeSpec { } } - - #[repr(C)] #[derive(Clone, Copy)] pub struct TimeVal(timeval); diff --git a/test/sys/mod.rs b/test/sys/mod.rs index 31cf73b1d9..eff2676497 100644 --- a/test/sys/mod.rs +++ b/test/sys/mod.rs @@ -22,6 +22,7 @@ mod test_uio; #[cfg(target_os = "linux")] mod test_epoll; + mod test_pthread; #[cfg(any(target_os = "android", target_os = "linux"))]