Skip to content

WIP: Change kevent function signature to use Timespec #838

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

Closed
wants to merge 22 commits into from
Closed
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<TimeSpec>`.
([#838](https://github.com/nix-rust/nix/pull/838))

### Fixed
- Fix compilation and tests for OpenBSD targets
Expand All @@ -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
Expand Down
142 changes: 98 additions & 44 deletions src/sys/event.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -200,20 +231,25 @@ 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<RawFd> {
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.
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 {
Expand All @@ -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<usize> {

// 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",
Expand All @@ -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<timespec>) -> Result<usize> {
/// 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::<Duration>(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<T: Into<TimeSpec>>(kq: RawFd, changelist: &[KEvent], eventlist: &mut [KEvent], timeout: Option<T>) -> Result<usize> {
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(
Expand All @@ -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;
Expand Down
38 changes: 36 additions & 2 deletions src/sys/time.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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<TimeSpec>`,
/// and can be used in any function accepting `Into<TimeSpec>`.
///
/// ```
/// use std::time::Duration;
/// use nix::libc::timespec;
/// use nix::sys::time::TimeSpec;
///
/// fn with_timeout<T: Into<TimeSpec>>(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);
Expand All @@ -58,6 +80,20 @@ const TS_MAX_SECONDS: i64 = ::std::isize::MAX as i64;

const TS_MIN_SECONDS: i64 = -TS_MAX_SECONDS;

impl From<Duration> for TimeSpec {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think there should be some doctests for TimeSpec demonstrating these conversions as they can be hard to discover unless we put them in examples.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

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 TimeSpec {
fn from(time: timespec) -> Self {
TimeSpec(time)
}
}

impl AsRef<timespec> for TimeSpec {
fn as_ref(&self) -> &timespec {
Expand Down Expand Up @@ -254,8 +290,6 @@ impl fmt::Display for TimeSpec {
}
}



#[repr(C)]
#[derive(Clone, Copy)]
pub struct TimeVal(timeval);
Expand Down
1 change: 1 addition & 0 deletions test/sys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"))]
Expand Down