summaryrefslogtreecommitdiff
path: root/src/sys
diff options
context:
space:
mode:
Diffstat (limited to 'src/sys')
-rw-r--r--src/sys/mman.rs375
-rw-r--r--src/sys/select.rs225
2 files changed, 416 insertions, 184 deletions
diff --git a/src/sys/mman.rs b/src/sys/mman.rs
index 4e422953..e1ddc38f 100644
--- a/src/sys/mman.rs
+++ b/src/sys/mman.rs
@@ -1,210 +1,220 @@
use {Errno, Error, Result, NixPath};
use fcntl::OFlag;
-use libc::{self, c_void, size_t, off_t, mode_t};
+use libc::{self, c_int, c_void, size_t, off_t};
use sys::stat::Mode;
use std::os::unix::io::RawFd;
-pub use self::consts::*;
-
libc_bitflags!{
+ /// Desired memory protection of a memory mapping.
pub flags ProtFlags: libc::c_int {
+ /// Pages cannot be accessed.
PROT_NONE,
+ /// Pages can be read.
PROT_READ,
+ /// Pages can be written.
PROT_WRITE,
+ /// Pages can be executed
PROT_EXEC,
- #[cfg(any(target_os = "linux", target_os = "android"))]
+ /// Apply protection up to the end of a mapping that grows upwards.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
PROT_GROWSDOWN,
- #[cfg(any(target_os = "linux", target_os = "android"))]
+ /// Apply protection down to the beginning of a mapping that grows downwards.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
PROT_GROWSUP,
}
}
-#[cfg(any(target_os = "linux", target_os = "android"))]
-mod consts {
- use libc::{self, c_int};
-
- libc_bitflags!{
- pub flags MapFlags: c_int {
- MAP_FILE,
- MAP_SHARED,
- MAP_PRIVATE,
- MAP_FIXED,
- MAP_ANON,
- MAP_ANONYMOUS,
- #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
- MAP_32BIT,
- MAP_GROWSDOWN,
- MAP_DENYWRITE,
- MAP_EXECUTABLE,
- MAP_LOCKED,
- MAP_NORESERVE,
- MAP_POPULATE,
- MAP_NONBLOCK,
- MAP_STACK,
- MAP_HUGETLB,
- }
- }
-
- pub type MmapAdvise = c_int;
-
- pub const MADV_NORMAL : MmapAdvise = 0; /* No further special treatment. */
- pub const MADV_RANDOM : MmapAdvise = 1; /* Expect random page references. */
- pub const MADV_SEQUENTIAL : MmapAdvise = 2; /* Expect sequential page references. */
- pub const MADV_WILLNEED : MmapAdvise = 3; /* Will need these pages. */
- pub const MADV_DONTNEED : MmapAdvise = 4; /* Don't need these pages. */
- pub const MADV_REMOVE : MmapAdvise = 9; /* Remove these pages and resources. */
- pub const MADV_DONTFORK : MmapAdvise = 10; /* Do not inherit across fork. */
- pub const MADV_DOFORK : MmapAdvise = 11; /* Do inherit across fork. */
- pub const MADV_MERGEABLE : MmapAdvise = 12; /* KSM may merge identical pages. */
- pub const MADV_UNMERGEABLE: MmapAdvise = 13; /* KSM may not merge identical pages. */
- pub const MADV_HUGEPAGE : MmapAdvise = 14; /* Worth backing with hugepages. */
- pub const MADV_NOHUGEPAGE : MmapAdvise = 15; /* Not worth backing with hugepages. */
- pub const MADV_DONTDUMP : MmapAdvise = 16; /* Explicity exclude from the core dump, overrides the coredump filter bits. */
- pub const MADV_DODUMP : MmapAdvise = 17; /* Clear the MADV_DONTDUMP flag. */
- pub const MADV_HWPOISON : MmapAdvise = 100; /* Poison a page for testing. */
-
-
- libc_bitflags!{
- pub flags MsFlags: c_int {
- MS_ASYNC,
- MS_INVALIDATE,
- MS_SYNC,
- }
- }
-
- pub const MAP_FAILED: isize = -1;
-}
-
-#[cfg(any(target_os = "macos",
- target_os = "ios"))]
-mod consts {
- use libc::{self, c_int};
-
- libc_bitflags!{
- pub flags MapFlags: c_int {
- MAP_FILE,
- MAP_SHARED,
- MAP_PRIVATE,
- MAP_FIXED,
- MAP_ANON,
- MAP_NOCACHE,
- MAP_JIT,
- }
- }
-
- pub type MmapAdvise = c_int;
-
- pub const MADV_NORMAL : MmapAdvise = 0; /* No further special treatment. */
- pub const MADV_RANDOM : MmapAdvise = 1; /* Expect random page references. */
- pub const MADV_SEQUENTIAL : MmapAdvise = 2; /* Expect sequential page references. */
- pub const MADV_WILLNEED : MmapAdvise = 3; /* Will need these pages. */
- pub const MADV_DONTNEED : MmapAdvise = 4; /* Don't need these pages. */
- pub const MADV_FREE : MmapAdvise = 5; /* pages unneeded, discard contents */
- pub const MADV_ZERO_WIRED_PAGES: MmapAdvise = 6; /* zero the wired pages that have not been unwired before the entry is deleted */
- pub const MADV_FREE_REUSABLE : MmapAdvise = 7; /* pages can be reused (by anyone) */
- pub const MADV_FREE_REUSE : MmapAdvise = 8; /* caller wants to reuse those pages */
- pub const MADV_CAN_REUSE : MmapAdvise = 9;
-
- libc_bitflags!{
- pub flags MsFlags: c_int {
- MS_ASYNC, /* [MF|SIO] return immediately */
- MS_INVALIDATE, /* [MF|SIO] invalidate all cached data */
- MS_KILLPAGES, /* invalidate pages, leave mapped */
- MS_DEACTIVATE, /* deactivate pages, leave mapped */
- MS_SYNC, /* [MF|SIO] msync synchronously */
- }
+libc_bitflags!{
+ /// Additional parameters for `mmap()`.
+ pub flags MapFlags: c_int {
+ /// Compatibility flag. Ignored.
+ MAP_FILE,
+ /// Share this mapping. Mutually exclusive with `MAP_PRIVATE`.
+ MAP_SHARED,
+ /// Create a private copy-on-write mapping. Mutually exclusive with `MAP_SHARED`.
+ MAP_PRIVATE,
+ /// Place the mapping at exactly the address specified in `addr`.
+ MAP_FIXED,
+ /// Synonym for `MAP_ANONYMOUS`.
+ MAP_ANON,
+ /// The mapping is not backed by any file.
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
+ MAP_ANONYMOUS,
+ /// Put the mapping into the first 2GB of the process address space.
+ #[cfg(any(all(any(target_os = "android", target_os = "linux"),
+ any(target_arch = "x86", target_arch = "x86_64")),
+ all(target_os = "linux", target_env = "musl", any(target_arch = "x86", target_pointer_width = "64")),
+ all(target_os = "freebsd", target_pointer_width = "64")))]
+ MAP_32BIT,
+ /// Used for stacks; indicates to the kernel that the mapping should extend downward in memory.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ MAP_GROWSDOWN,
+ /// Compatibility flag. Ignored.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ MAP_DENYWRITE,
+ /// Compatibility flag. Ignored.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ MAP_EXECUTABLE,
+ /// Mark the mmaped region to be locked in the same way as `mlock(2)`.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ MAP_LOCKED,
+ /// Do not reserve swap space for this mapping.
+ ///
+ /// This was removed in FreeBSD 11.
+ #[cfg(not(target_os = "freebsd"))]
+ MAP_NORESERVE,
+ /// Populate page tables for a mapping.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ MAP_POPULATE,
+ /// Only meaningful when used with `MAP_POPULATE`. Don't perform read-ahead.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ MAP_NONBLOCK,
+ /// Allocate the mapping using "huge pages."
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ MAP_HUGETLB,
+ /// Lock the mapped region into memory as with `mlock(2)`.
+ #[cfg(target_os = "netbsd")]
+ MAP_WIRED,
+ /// Causes dirtied data in the specified range to be flushed to disk only when necessary.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ MAP_NOSYNC,
+ /// Rename private pages to a file.
+ ///
+ /// This was removed in FreeBSD 11.
+ #[cfg(any(target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))]
+ MAP_RENAME,
+ /// Region may contain semaphores.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
+ MAP_HASSEMAPHORE,
+ /// Region grows down, like a stack.
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))]
+ MAP_STACK,
+ /// Pages in this mapping are not retained in the kernel's memory cache.
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ MAP_NOCACHE,
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ MAP_JIT,
}
-
- pub const MAP_FAILED: isize = -1;
}
-#[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd"))]
-mod consts {
- use libc::{self, c_int};
-
- libc_bitflags!{
- pub flags MapFlags: c_int {
- MAP_FILE,
- MAP_SHARED,
- MAP_PRIVATE,
- MAP_FIXED,
- MAP_RENAME,
- MAP_NORESERVE,
- MAP_HASSEMAPHORE,
- #[cfg(not(any(target_os = "openbsd", target_os = "netbsd")))]
- MAP_STACK,
- #[cfg(target_os = "netbsd")]
- MAP_WIRED,
- #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
- MAP_NOSYNC,
- MAP_ANON,
- }
+libc_enum!{
+ /// Usage information for a range of memory to allow for performance optimizations by the kernel.
+ ///
+ /// Used by [`madvise`].
+ /// [`madvise`]: ./fn.madvise.html
+ #[repr(i32)]
+ pub enum MmapAdvise {
+ /// No further special treatment. This is the default.
+ MADV_NORMAL,
+ /// Expect random page references.
+ MADV_RANDOM,
+ /// Expect sequential page references.
+ MADV_SEQUENTIAL,
+ /// Expect access in the near future.
+ MADV_WILLNEED,
+ /// Do not expect access in the near future.
+ MADV_DONTNEED,
+ /// Free up a given range of pages and its associated backing store.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ MADV_REMOVE,
+ /// Do not make pages in this range available to the child after a `fork(2)`.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ MADV_DONTFORK,
+ /// Undo the effect of `MADV_DONTFORK`.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ MADV_DOFORK,
+ /// Poison the given pages.
+ ///
+ /// Subsequent references to those pages are treated like hardware memory corruption.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ MADV_HWPOISON,
+ /// Enable Kernel Samepage Merging (KSM) for the given pages.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ MADV_MERGEABLE,
+ /// Undo the effect of `MADV_MERGEABLE`
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ MADV_UNMERGEABLE,
+ /// Preserve the memory of each page but offline the original page.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ MADV_SOFT_OFFLINE,
+ /// Enable Transparent Huge Pages (THP) for pages in the given range.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ MADV_HUGEPAGE,
+ /// Undo the effect of `MADV_HUGEPAGE`.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ MADV_NOHUGEPAGE,
+ /// Exclude the given range from a core dump.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ MADV_DONTDUMP,
+ /// Undo the effect of an earlier `MADV_DONTDUMP`.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ MADV_DODUMP,
+ /// Specify that the application no longer needs the pages in the given range.
+ MADV_FREE,
+ /// Request that the system not flush the current range to disk unless it needs to.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ MADV_NOSYNC,
+ /// Undoes the effects of `MADV_NOSYNC` for any future pages dirtied within the given range.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ MADV_AUTOSYNC,
+ /// Region is not included in a core file.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ MADV_NOCORE,
+ /// Include region in a core file
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ MADV_CORE,
+ #[cfg(any(target_os = "freebsd"))]
+ MADV_PROTECT,
+ /// Invalidate the hardware page table for the given region.
+ #[cfg(target_os = "dragonfly")]
+ MADV_INVAL,
+ /// Set the offset of the page directory page to `value` for the virtual page table.
+ #[cfg(target_os = "dragonfly")]
+ MADV_SETMAP,
+ /// Indicates that the application will not need the data in the given range.
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ MADV_ZERO_WIRED_PAGES,
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ MADV_FREE_REUSABLE,
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ MADV_FREE_REUSE,
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ MADV_CAN_REUSE,
}
-
- pub type MmapAdvise = c_int;
-
- pub const MADV_NORMAL : MmapAdvise = 0; /* No further special treatment. */
- pub const MADV_RANDOM : MmapAdvise = 1; /* Expect random page references. */
- pub const MADV_SEQUENTIAL : MmapAdvise = 2; /* Expect sequential page references. */
- pub const MADV_WILLNEED : MmapAdvise = 3; /* Will need these pages. */
- pub const MADV_DONTNEED : MmapAdvise = 4; /* Don't need these pages. */
- pub const MADV_FREE : MmapAdvise = 5; /* pages unneeded, discard contents */
- pub const MADV_NOSYNC : MmapAdvise = 6; /* try to avoid flushes to physical media*/
- pub const MADV_AUTOSYNC : MmapAdvise = 7; /* refert to default flushing strategy */
- pub const MADV_NOCORE : MmapAdvise = 8; /* do not include these pages in a core file */
- pub const MADV_CORE : MmapAdvise = 9; /* revert to including pages in a core file */
- #[cfg(not(target_os = "dragonfly"))]
- pub const MADV_PROTECT : MmapAdvise = 10; /* protect process from pageout kill */
- #[cfg(target_os = "dragonfly")]
- pub const MADV_INVAL : MmapAdvise = 10; /* virt page tables have changed, inval pmap */
- #[cfg(target_os = "dragonfly")]
- pub const MADV_SETMAP : MmapAdvise = 11; /* set page table directory page for map */
-
- bitflags!{
- pub struct MsFlags: c_int {
- const MS_ASYNC = libc::MS_ASYNC; /* [MF|SIO] return immediately */
- const MS_INVALIDATE = libc::MS_INVALIDATE; /* [MF|SIO] invalidate all cached data */
- #[cfg(not(target_os = "dragonfly"))]
- const MS_KILLPAGES = 0x0004; /* invalidate pages, leave mapped */
- #[cfg(not(target_os = "dragonfly"))]
- const MS_DEACTIVATE = 0x0004; /* deactivate pages, leave mapped */
- const MS_SYNC = libc::MS_SYNC; /* [MF|SIO] msync synchronously */
- }
- }
-
- pub const MAP_FAILED: isize = -1;
}
-mod ffi {
- use libc::{c_void, size_t, c_int, c_char, mode_t};
-
- pub use libc::{mmap, munmap};
-
- #[allow(improper_ctypes)]
- extern {
- pub fn shm_open(name: *const c_char, oflag: c_int, mode: mode_t) -> c_int;
- pub fn shm_unlink(name: *const c_char) -> c_int;
- pub fn mlock(addr: *const c_void, len: size_t) -> c_int;
- pub fn munlock(addr: *const c_void, len: size_t) -> c_int;
- pub fn madvise (addr: *const c_void, len: size_t, advice: c_int) -> c_int;
- pub fn msync (addr: *const c_void, len: size_t, flags: c_int) -> c_int;
+libc_bitflags!{
+ /// Configuration flags for `msync`.
+ pub flags MsFlags: c_int {
+ /// Schedule an update but return immediately.
+ MS_ASYNC,
+ /// Invalidate all cached data.
+ MS_INVALIDATE,
+ /// Invalidate pages, but leave them mapped.
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ MS_KILLPAGES,
+ /// Deactivate pages, but leave them mapped.
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ MS_DEACTIVATE,
+ /// Perform an update and wait for it to complete.
+ MS_SYNC,
}
}
pub unsafe fn mlock(addr: *const c_void, length: size_t) -> Result<()> {
- Errno::result(ffi::mlock(addr, length)).map(drop)
+ Errno::result(libc::mlock(addr, length)).map(drop)
}
pub unsafe fn munlock(addr: *const c_void, length: size_t) -> Result<()> {
- Errno::result(ffi::munlock(addr, length)).map(drop)
+ Errno::result(libc::munlock(addr, length)).map(drop)
}
/// Calls to mmap are inherently unsafe, so they must be made in an unsafe block. Typically
/// a higher-level abstraction will hide the unsafe interactions with the mmap'd region.
pub unsafe fn mmap(addr: *mut c_void, length: size_t, prot: ProtFlags, flags: MapFlags, fd: RawFd, offset: off_t) -> Result<*mut c_void> {
- let ret = ffi::mmap(addr, length, prot.bits(), flags.bits(), fd, offset);
+ let ret = libc::mmap(addr, length, prot.bits(), flags.bits(), fd, offset);
- if ret as isize == MAP_FAILED {
+ if ret == libc::MAP_FAILED {
Err(Error::Sys(Errno::last()))
} else {
Ok(ret)
@@ -212,30 +222,37 @@ pub unsafe fn mmap(addr: *mut c_void, length: size_t, prot: ProtFlags, flags: Ma
}
pub unsafe fn munmap(addr: *mut c_void, len: size_t) -> Result<()> {
- Errno::result(ffi::munmap(addr, len)).map(drop)
+ Errno::result(libc::munmap(addr, len)).map(drop)
}
-pub unsafe fn madvise(addr: *const c_void, length: size_t, advise: MmapAdvise) -> Result<()> {
- Errno::result(ffi::madvise(addr, length, advise)).map(drop)
+pub unsafe fn madvise(addr: *mut c_void, length: size_t, advise: MmapAdvise) -> Result<()> {
+ Errno::result(libc::madvise(addr, length, advise as i32)).map(drop)
}
-pub unsafe fn msync(addr: *const c_void, length: size_t, flags: MsFlags) -> Result<()> {
- Errno::result(ffi::msync(addr, length, flags.bits())).map(drop)
+pub unsafe fn msync(addr: *mut c_void, length: size_t, flags: MsFlags) -> Result<()> {
+ Errno::result(libc::msync(addr, length, flags.bits())).map(drop)
}
+#[cfg(not(target_os = "android"))]
pub fn shm_open<P: ?Sized + NixPath>(name: &P, flag: OFlag, mode: Mode) -> Result<RawFd> {
let ret = try!(name.with_nix_path(|cstr| {
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ unsafe {
+ libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::c_uint)
+ }
+ #[cfg(not(any(target_os = "macos", target_os = "ios")))]
unsafe {
- ffi::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as mode_t)
+ libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::mode_t)
}
}));
Errno::result(ret)
}
+#[cfg(not(target_os = "android"))]
pub fn shm_unlink<P: ?Sized + NixPath>(name: &P) -> Result<()> {
let ret = try!(name.with_nix_path(|cstr| {
- unsafe { ffi::shm_unlink(cstr.as_ptr()) }
+ unsafe { libc::shm_unlink(cstr.as_ptr()) }
}));
Errno::result(ret).map(drop)
diff --git a/src/sys/select.rs b/src/sys/select.rs
index eae191b6..82b2aad3 100644
--- a/src/sys/select.rs
+++ b/src/sys/select.rs
@@ -53,6 +53,42 @@ impl FdSet {
*bits = 0
}
}
+
+ /// Finds the highest file descriptor in the set.
+ ///
+ /// Returns `None` if the set is empty.
+ ///
+ /// This can be used to calculate the `nfds` parameter of the [`select`] function.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # extern crate nix;
+ /// # use nix::sys::select::FdSet;
+ /// # fn main() {
+ /// let mut set = FdSet::new();
+ /// set.insert(4);
+ /// set.insert(9);
+ /// assert_eq!(set.highest(), Some(9));
+ /// # }
+ /// ```
+ ///
+ /// [`select`]: fn.select.html
+ pub fn highest(&self) -> Option<RawFd> {
+ for (i, &block) in self.bits.iter().enumerate().rev() {
+ if block != 0 {
+ // Highest bit is located at `BITS - 1 - n.leading_zeros()`. Examples:
+ // 0b00000001
+ // 7 leading zeros, result should be 0 (bit at index 0 is 1)
+ // 0b001xxxxx
+ // 2 leading zeros, result should be 5 (bit at index 5 is 1) - x may be 0 or 1
+
+ return Some((i * BITS + BITS - 1 - block.leading_zeros() as usize) as RawFd);
+ }
+ }
+
+ None
+ }
}
mod ffi {
@@ -68,11 +104,52 @@ mod ffi {
}
}
-pub fn select(nfds: c_int,
- readfds: Option<&mut FdSet>,
- writefds: Option<&mut FdSet>,
- errorfds: Option<&mut FdSet>,
- timeout: Option<&mut TimeVal>) -> Result<c_int> {
+/// Monitors file descriptors for readiness (see [select(2)]).
+///
+/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
+/// file descriptors that are ready for the given operation are set.
+///
+/// When this function returns, `timeout` has an implementation-defined value.
+///
+/// # Parameters
+///
+/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
+/// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
+/// to the maximum of that.
+/// * `readfds`: File descriptors to check for being ready to read.
+/// * `writefds`: File descriptors to check for being ready to write.
+/// * `errorfds`: File descriptors to check for pending error conditions.
+/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
+/// indefinitely).
+///
+/// [select(2)]: http://man7.org/linux/man-pages/man2/select.2.html
+/// [`FdSet::highest`]: struct.FdSet.html#method.highest
+pub fn select<'a, N, R, W, E, T>(nfds: N,
+ readfds: R,
+ writefds: W,
+ errorfds: E,
+ timeout: T) -> Result<c_int>
+where
+ N: Into<Option<c_int>>,
+ R: Into<Option<&'a mut FdSet>>,
+ W: Into<Option<&'a mut FdSet>>,
+ E: Into<Option<&'a mut FdSet>>,
+ T: Into<Option<&'a mut TimeVal>>,
+{
+ let readfds = readfds.into();
+ let writefds = writefds.into();
+ let errorfds = errorfds.into();
+ let timeout = timeout.into();
+
+ let nfds = nfds.into().unwrap_or_else(|| {
+ readfds.iter()
+ .chain(writefds.iter())
+ .chain(errorfds.iter())
+ .map(|set| set.highest().unwrap_or(-1))
+ .max()
+ .unwrap_or(-1) + 1
+ });
+
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());
@@ -85,3 +162,141 @@ pub fn select(nfds: c_int,
Errno::result(res)
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use sys::time::{TimeVal, TimeValLike};
+ use unistd::{write, pipe};
+
+ #[test]
+ fn fdset_insert() {
+ let mut fd_set = FdSet::new();
+
+ for i in 0..FD_SETSIZE {
+ assert!(!fd_set.contains(i));
+ }
+
+ fd_set.insert(7);
+
+ assert!(fd_set.contains(7));
+ }
+
+ #[test]
+ fn fdset_remove() {
+ let mut fd_set = FdSet::new();
+
+ for i in 0..FD_SETSIZE {
+ assert!(!fd_set.contains(i));
+ }
+
+ fd_set.insert(7);
+ fd_set.remove(7);
+
+ for i in 0..FD_SETSIZE {
+ assert!(!fd_set.contains(i));
+ }
+ }
+
+ #[test]
+ fn fdset_clear() {
+ let mut fd_set = FdSet::new();
+ fd_set.insert(1);
+ fd_set.insert(FD_SETSIZE / 2);
+ fd_set.insert(FD_SETSIZE - 1);
+
+ fd_set.clear();
+
+ for i in 0..FD_SETSIZE {
+ assert!(!fd_set.contains(i));
+ }
+ }
+
+ #[test]
+ fn fdset_highest() {
+ let mut set = FdSet::new();
+ assert_eq!(set.highest(), None);
+ set.insert(0);
+ assert_eq!(set.highest(), Some(0));
+ set.insert(90);
+ assert_eq!(set.highest(), Some(90));
+ set.remove(0);
+ assert_eq!(set.highest(), Some(90));
+ set.remove(90);
+ assert_eq!(set.highest(), None);
+
+ set.insert(4);
+ set.insert(5);
+ set.insert(7);
+ assert_eq!(set.highest(), Some(7));
+ }
+
+ // powerpc-unknown-linux-gnu currently fails on the first `assert_eq` because
+ // `select()` returns a 0 instead of a 1. Since this test has only been run on
+ // qemu, it's unclear if this is a OS or qemu bug. Just disable it on that arch
+ // for now.
+ // FIXME: Fix tests for powerpc and mips
+ // FIXME: Add a link to an upstream qemu bug if there is one
+ #[test]
+ #[cfg_attr(any(target_arch = "powerpc", target_arch = "mips"), ignore)]
+ fn test_select() {
+ let (r1, w1) = pipe().unwrap();
+ write(w1, b"hi!").unwrap();
+ let (r2, _w2) = pipe().unwrap();
+
+ let mut fd_set = FdSet::new();
+ fd_set.insert(r1);
+ fd_set.insert(r2);
+
+ let mut timeout = TimeVal::seconds(10);
+ assert_eq!(1, select(None,
+ &mut fd_set,
+ None,
+ None,
+ &mut timeout).unwrap());
+ assert!(fd_set.contains(r1));
+ assert!(!fd_set.contains(r2));
+ }
+
+ #[test]
+ #[cfg_attr(any(target_arch = "powerpc", target_arch = "mips"), ignore)]
+ fn test_select_nfds() {
+ let (r1, w1) = pipe().unwrap();
+ write(w1, b"hi!").unwrap();
+ let (r2, _w2) = pipe().unwrap();
+
+ let mut fd_set = FdSet::new();
+ fd_set.insert(r1);
+ fd_set.insert(r2);
+
+ let mut timeout = TimeVal::seconds(10);
+ assert_eq!(1, select(Some(fd_set.highest().unwrap() + 1),
+ &mut fd_set,
+ None,
+ None,
+ &mut timeout).unwrap());
+ assert!(fd_set.contains(r1));
+ assert!(!fd_set.contains(r2));
+ }
+
+ #[test]
+ #[cfg_attr(any(target_arch = "powerpc", target_arch = "mips"), ignore)]
+ fn test_select_nfds2() {
+ let (r1, w1) = pipe().unwrap();
+ write(w1, b"hi!").unwrap();
+ let (r2, _w2) = pipe().unwrap();
+
+ let mut fd_set = FdSet::new();
+ fd_set.insert(r1);
+ fd_set.insert(r2);
+
+ let mut timeout = TimeVal::seconds(10);
+ assert_eq!(1, select(::std::cmp::max(r1, r2) + 1,
+ &mut fd_set,
+ None,
+ None,
+ &mut timeout).unwrap());
+ assert!(fd_set.contains(r1));
+ assert!(!fd_set.contains(r2));
+ }
+}