From c3ff37b0942f527295a7213f44eee13bf2590ddf Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Sat, 13 Jan 2018 20:47:38 -0700 Subject: aio: more documentation --- src/sys/aio.rs | 643 +++++++++++++++++++++++++++++++++++++++++++-------- test/sys/test_aio.rs | 4 +- 2 files changed, 554 insertions(+), 93 deletions(-) diff --git a/src/sys/aio.rs b/src/sys/aio.rs index 687458e5..d5561e9c 100644 --- a/src/sys/aio.rs +++ b/src/sys/aio.rs @@ -1,3 +1,25 @@ +//! POSIX Asynchronous I/O +//! +//! The POSIX AIO interface is used for asynchronous I/O on files and disk-like +//! devices. It supports [`read`](struct.AioCb.html#method.read), +//! [`write`](struct.AioCb.html#method.write), and +//! [`fsync`](struct.AioCb.html#method.fsync) operations. Completion +//! notifications can optionally be delivered via +//! [signals](../signal/enum.SigevNotify.html#variant.SigevSignal), via the +//! [`aio_suspend`](fn.aio_suspend.html) function, or via polling. Some +//! platforms support other completion +//! notifications, such as +//! [kevent](../signal/enum.SigevNotify.html#variant.SigevKevent). +//! +//! Multiple operations may be submitted in a batch with +//! [`lio_listio`](fn.lio_listio.html), though the standard does not guarantee +//! that they will be executed atomically. +//! +//! Outstanding operations may be cancelled with +//! [`cancel`](struct.AioCb.html#method.cancel) or +//! [`aio_cancel_all`](fn.aio_cancel_all.html), though the operating system may +//! not support this for all filesystems and devices. + use {Error, Result}; use bytes::{Bytes, BytesMut}; use errno::Errno; @@ -13,9 +35,9 @@ use std::ptr::{null, null_mut}; use sys::signal::*; use sys::time::TimeSpec; -/// Mode for `AioCb::fsync`. Controls whether only data or both data and -/// metadata are synced. libc_enum! { + /// Mode for `AioCb::fsync`. Controls whether only data or both data and + /// metadata are synced. #[repr(i32)] pub enum AioFsyncMode { /// do it like `fsync` @@ -31,9 +53,9 @@ libc_enum! { } libc_enum! { - /// When used with `lio_listio`, determines whether a given `aiocb` should be - /// used for a read operation, a write operation, or ignored. Has no effect for - /// any other aio functions. + /// When used with [`lio_listio`](fn.lio_listio.html), determines whether a + /// given `aiocb` should be used for a read operation, a write operation, or + /// ignored. Has no effect for any other aio functions. #[repr(i32)] pub enum LioOpcode { LIO_NOP, @@ -43,18 +65,19 @@ libc_enum! { } libc_enum! { - /// Mode for `lio_listio`. + /// Mode for [`lio_listio`](fn.lio_listio.html) #[repr(i32)] pub enum LioMode { - /// Requests that `lio_listio` block until all requested operations have - /// been completed + /// Requests that [`lio_listio`](fn.lio_listio.html) block until all + /// requested operations have been completed LIO_WAIT, - /// Requests that `lio_listio` return immediately + /// Requests that [`lio_listio`](fn.lio_listio.html) return immediately LIO_NOWAIT, } } -/// Return values for `AioCb::cancel and aio_cancel_all` +/// Return values for [`AioCb::cancel`](struct.AioCb.html#method.cancel) and +/// [`aio_cancel_all`](fn.aio_cancel_all.html) #[repr(i32)] #[derive(Clone, Copy, Debug, PartialEq)] pub enum AioCancelStat { @@ -113,15 +136,20 @@ impl<'a> Buffer<'a> { } } -/// The basic structure used by all aio functions. Each `aiocb` represents one +/// AIO Control Block. +/// +/// The basic structure used by all aio functions. Each `AioCb` represents one /// I/O request. pub struct AioCb<'a> { aiocb: libc::aiocb, - /// Tracks whether the buffer pointed to by aiocb.aio_buf is mutable + /// Tracks whether the buffer pointed to by `libc::aiocb.aio_buf` is mutable mutable: bool, /// Could this `AioCb` potentially have any in-kernel state? in_progress: bool, - /// Used to keep buffers from Drop'ing + /// Optionally keeps a reference to the data. + /// + /// Used to keep buffers from `Drop`'ing, and may be returned once the + /// `AioCb` is completed by `into_buffer`. buffer: Buffer<'a> } @@ -146,11 +174,40 @@ impl<'a> AioCb<'a> { /// /// The resulting `AioCb` structure is suitable for use with `AioCb::fsync`. /// - /// * `fd` File descriptor. Required for all aio functions. - /// * `prio` If POSIX Prioritized IO is supported, then the operation will - /// be prioritized at the process's priority level minus `prio` - /// * `sigev_notify` Determines how you will be notified of event - /// completion. + /// # Parameters + /// + /// * `fd`: File descriptor. Required for all aio functions. + /// * `prio`: If POSIX Prioritized IO is supported, then the + /// operation will be prioritized at the process's + /// priority level minus `prio`. + /// * `sigev_notify`: Determines how you will be notified of event + /// completion. + /// + /// # Examples + /// + /// Create an `AioCb` from a raw file descriptor and use it for an + /// [`fsync`](#method.from_bytes_mut) operation. + /// + /// ``` + /// # extern crate tempfile; + /// # extern crate nix; + /// # use nix::errno::Errno; + /// # use nix::Error; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify::SigevNone; + /// # use std::{thread, time}; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// # fn main() { + /// let f = tempfile().unwrap(); + /// let mut aiocb = AioCb::from_fd( f.as_raw_fd(), 0, SigevNone); + /// aiocb.fsync(AioFsyncMode::O_SYNC).expect("aio_fsync failed early"); + /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) { + /// thread::sleep(time::Duration::from_millis(10)); + /// } + /// aiocb.aio_return().expect("aio_fsync failed late"); + /// # } + /// ``` pub fn from_fd(fd: RawFd, prio: libc::c_int, sigev_notify: SigevNotify) -> AioCb<'a> { let mut a = AioCb::common_init(fd, prio, sigev_notify); @@ -166,17 +223,65 @@ impl<'a> AioCb<'a> { } } - /// Constructs a new `AioCb`. + /// Constructs a new `AioCb` from a mutable slice. + /// + /// The resulting `AioCb` will be suitable for both read and write + /// operations, but only if the borrow checker can guarantee that the slice + /// will outlive the `AioCb`. That will usually be the case if the `AioCb` + /// is stack-allocated. If the borrow checker gives you trouble, try using + /// [`from_bytes_mut`](#method.from_bytes_mut) instead. /// - /// * `fd` File descriptor. Required for all aio functions. - /// * `offs` File offset - /// * `buf` A memory buffer - /// * `prio` If POSIX Prioritized IO is supported, then the operation will - /// be prioritized at the process's priority level minus `prio` - /// * `sigev_notify` Determines how you will be notified of event - /// completion. - /// * `opcode` This field is only used for `lio_listio`. It determines - /// which operation to use for this individual aiocb + /// # Parameters + /// + /// * `fd`: File descriptor. Required for all aio functions. + /// * `offs`: File offset + /// * `buf`: A memory buffer + /// * `prio`: If POSIX Prioritized IO is supported, then the + /// operation will be prioritized at the process's + /// priority level minus `prio` + /// * `sigev_notify`: Determines how you will be notified of event + /// completion. + /// * `opcode`: This field is only used for `lio_listio`. It + /// determines which operation to use for this individual + /// aiocb + /// + /// # Examples + /// + /// Create an `AioCb` from a mutable slice and read into it. + /// + /// ``` + /// # extern crate tempfile; + /// # extern crate nix; + /// # use nix::errno::Errno; + /// # use nix::Error; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::{thread, time}; + /// # use std::io::Write; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// # fn main() { + /// const INITIAL: &[u8] = b"abcdef123456"; + /// const LEN: usize = 4; + /// let mut rbuf = vec![0; LEN]; + /// let mut f = tempfile().unwrap(); + /// f.write_all(INITIAL).unwrap(); + /// { + /// let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(), + /// 2, //offset + /// &mut rbuf, + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_NOP); + /// aiocb.read().unwrap(); + /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) { + /// thread::sleep(time::Duration::from_millis(10)); + /// } + /// assert_eq!(aiocb.aio_return().unwrap() as usize, LEN); + /// } + /// assert_eq!(rbuf, b"cdef"); + /// # } + /// ``` pub fn from_mut_slice(fd: RawFd, offs: off_t, buf: &'a mut [u8], prio: libc::c_int, sigev_notify: SigevNotify, opcode: LioOpcode) -> AioCb<'a> { @@ -194,24 +299,62 @@ impl<'a> AioCb<'a> { } } - /// Constructs a new `AioCb`. + /// Constructs a new `AioCb` from a `Bytes` object. /// - /// Unlike `from_mut_slice`, this method returns a structure suitable for + /// Unlike `from_slice`, this method returns a structure suitable for /// placement on the heap. It may be used for write operations, but not /// read operations. /// - /// * `fd` File descriptor. Required for all aio functions. - /// * `offs` File offset - /// * `buf` A shared memory buffer - /// * `prio` If POSIX Prioritized IO is supported, then the operation will - /// be prioritized at the process's priority level minus `prio` - /// * `sigev_notify` Determines how you will be notified of event - /// completion. - /// * `opcode` This field is only used for `lio_listio`. It determines - /// which operation to use for this individual aiocb + /// # Parameters + /// + /// * `fd`: File descriptor. Required for all aio functions. + /// * `offs`: File offset + /// * `buf`: A shared memory buffer + /// * `prio`: If POSIX Prioritized IO is supported, then the + /// operation will be prioritized at the process's + /// priority level minus `prio` + /// * `sigev_notify`: Determines how you will be notified of event + /// completion. + /// * `opcode`: This field is only used for `lio_listio`. It + /// determines which operation to use for this individual + /// aiocb + /// + /// # Examples + /// + /// Create an `AioCb` from a `Bytes` object and use it for writing. + /// + /// ``` + /// # extern crate bytes; + /// # extern crate tempfile; + /// # extern crate nix; + /// # use nix::errno::Errno; + /// # use nix::Error; + /// # use bytes::Bytes; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::{thread, time}; + /// # use std::io::Write; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// # fn main() { + /// let wbuf = Bytes::from(&b"CDEF"[..]); + /// let mut f = tempfile().unwrap(); + /// let mut aiocb = AioCb::from_bytes( f.as_raw_fd(), + /// 2, //offset + /// wbuf.clone(), + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_NOP); + /// aiocb.write().unwrap(); + /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) { + /// thread::sleep(time::Duration::from_millis(10)); + /// } + /// assert_eq!(aiocb.aio_return().unwrap() as usize, wbuf.len()); + /// # } + /// ``` pub fn from_bytes(fd: RawFd, offs: off_t, buf: Bytes, prio: libc::c_int, sigev_notify: SigevNotify, - opcode: LioOpcode) -> AioCb<'a> { + opcode: LioOpcode) -> AioCb<'a> { // Small BytesMuts are stored inline. Inline storage is a no-no, // because we store a pointer to the buffer in the AioCb before // returning the Buffer by move. If the buffer is too small, reallocate @@ -240,20 +383,66 @@ impl<'a> AioCb<'a> { } } - /// Constructs a new `AioCb`. + /// Constructs a new `AioCb` from a `BytesMut` object. /// /// Unlike `from_mut_slice`, this method returns a structure suitable for /// placement on the heap. It may be used for both reads and writes. /// - /// * `fd` File descriptor. Required for all aio functions. - /// * `offs` File offset - /// * `buf` A shared memory buffer - /// * `prio` If POSIX Prioritized IO is supported, then the operation will - /// be prioritized at the process's priority level minus `prio` - /// * `sigev_notify` Determines how you will be notified of event - /// completion. - /// * `opcode` This field is only used for `lio_listio`. It determines - /// which operation to use for this individual aiocb + /// # Parameters + /// + /// * `fd`: File descriptor. Required for all aio functions. + /// * `offs`: File offset + /// * `buf`: An owned memory buffer + /// * `prio`: If POSIX Prioritized IO is supported, then the + /// operation will be prioritized at the process's + /// priority level minus `prio` + /// * `sigev_notify`: Determines how you will be notified of event + /// completion. + /// * `opcode`: This field is only used for `lio_listio`. It + /// determines which operation to use for this individual + /// aiocb + /// + /// # Examples + /// + /// Create an `AioCb` from a `BytesMut` and use it for reading. In this + /// example the `AioCb` is stack-allocated, so we could've used + /// `from_mut_slice` instead. + /// + /// ``` + /// # extern crate bytes; + /// # extern crate tempfile; + /// # extern crate nix; + /// # use nix::errno::Errno; + /// # use nix::Error; + /// # use bytes::BytesMut; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::{thread, time}; + /// # use std::io::Write; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// # fn main() { + /// const INITIAL: &[u8] = b"abcdef123456"; + /// const LEN: usize = 4; + /// let rbuf = BytesMut::from(vec![0; LEN]); + /// let mut f = tempfile().unwrap(); + /// f.write_all(INITIAL).unwrap(); + /// let mut aiocb = AioCb::from_bytes_mut( f.as_raw_fd(), + /// 2, //offset + /// rbuf, + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_NOP); + /// aiocb.read().unwrap(); + /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) { + /// thread::sleep(time::Duration::from_millis(10)); + /// } + /// assert_eq!(aiocb.aio_return().unwrap() as usize, LEN); + /// let buffer = aiocb.into_buffer(); + /// const EXPECT: &[u8] = b"cdef"; + /// assert_eq!(buffer.bytes_mut().unwrap(), EXPECT); + /// # } + /// ``` pub fn from_bytes_mut(fd: RawFd, offs: off_t, buf: BytesMut, prio: libc::c_int, sigev_notify: SigevNotify, opcode: LioOpcode) -> AioCb<'a> { @@ -281,22 +470,31 @@ impl<'a> AioCb<'a> { /// Constructs a new `AioCb` from a mutable raw pointer /// - /// * `fd` File descriptor. Required for all aio functions. - /// * `offs` File offset - /// * `buf` Pointer to the memory buffer - /// * `len` Length of the buffer pointed to by `buf` - /// * `prio` If POSIX Prioritized IO is supported, then the operation will - /// be prioritized at the process's priority level minus `prio` - /// * `sigev_notify` Determines how you will be notified of event - /// completion. - /// * `opcode` This field is only used for `lio_listio`. It determines - /// which operation to use for this individual aiocb + /// Unlike `from_mut_slice`, this method returns a structure suitable for + /// placement on the heap. It may be used for both reads and writes. Due + /// to its unsafety, this method is not recommended. It is most useful when + /// heap allocation is required but for some reason the data cannot be + /// converted to a `BytesMut`. + /// + /// # Parameters + /// + /// * `fd`: File descriptor. Required for all aio functions. + /// * `offs`: File offset + /// * `buf`: Pointer to the memory buffer + /// * `len`: Length of the buffer pointed to by `buf` + /// * `prio`: If POSIX Prioritized IO is supported, then the + /// operation will be prioritized at the process's + /// priority level minus `prio` + /// * `sigev_notify`: Determines how you will be notified of event + /// completion. + /// * `opcode`: This field is only used for `lio_listio`. It + /// determines which operation to use for this individual + /// aiocb /// /// # Safety /// - /// Unsafe because using this `AioCb` will cause `libc::aio_read` or - /// `libc::aio_write` to dereference a raw pointer, without type, bounds, or - /// lifetime checking. + /// The caller must ensure that the storage pointed to by `buf` outlives the + /// `AioCb`. The lifetime checker can't help here. pub unsafe fn from_mut_ptr(fd: RawFd, offs: off_t, buf: *mut c_void, len: usize, prio: libc::c_int, sigev_notify: SigevNotify, @@ -315,23 +513,32 @@ impl<'a> AioCb<'a> { } } - /// Constructs a new `AioCb` from a raw pointer + /// Constructs a new `AioCb` from a raw pointer. + /// + /// Unlike `from_slice`, this method returns a structure suitable for + /// placement on the heap. Due to its unsafety, this method is not + /// recommended. It is most useful when heap allocation is required but for + /// some reason the data cannot be converted to a `Bytes`. /// - /// * `fd` File descriptor. Required for all aio functions. - /// * `offs` File offset - /// * `buf` Pointer to the memory buffer - /// * `len` Length of the buffer pointed to by `buf` - /// * `prio` If POSIX Prioritized IO is supported, then the operation will - /// be prioritized at the process's priority level minus `prio` - /// * `sigev_notify` Determines how you will be notified of event - /// completion. - /// * `opcode` This field is only used for `lio_listio`. It determines - /// which operation to use for this individual aiocb + /// # Parameters + /// + /// * `fd`: File descriptor. Required for all aio functions. + /// * `offs`: File offset + /// * `buf`: Pointer to the memory buffer + /// * `len`: Length of the buffer pointed to by `buf` + /// * `prio`: If POSIX Prioritized IO is supported, then the + /// operation will be prioritized at the process's + /// priority level minus `prio` + /// * `sigev_notify`: Determines how you will be notified of event + /// completion. + /// * `opcode`: This field is only used for `lio_listio`. It + /// determines which operation to use for this individual + /// aiocb /// /// # Safety /// - /// Unsafe because using this `AioCb` will cause `libc::aio_write` to - /// dereference a raw pointer, without type, bounds, or lifetime checking. + /// The caller must ensure that the storage pointed to by `buf` outlives the + /// `AioCb`. The lifetime checker can't help here. pub unsafe fn from_ptr(fd: RawFd, offs: off_t, buf: *const c_void, len: usize, prio: libc::c_int, sigev_notify: SigevNotify, @@ -357,14 +564,44 @@ impl<'a> AioCb<'a> { /// /// An `AioCb` created this way cannot be used with `read`, and its /// `LioOpcode` cannot be set to `LIO_READ`. This method is useful when - /// writing a const buffer with `AioCb::write`, since from_mut_slice can't + /// writing a const buffer with `AioCb::write`, since `from_mut_slice` can't /// work with const buffers. + /// + /// # Examples + /// + /// Construct an `AioCb` from a slice and use it for writing. + /// + /// ``` + /// # extern crate tempfile; + /// # extern crate nix; + /// # use nix::errno::Errno; + /// # use nix::Error; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::{thread, time}; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// # fn main() { + /// const WBUF: &[u8] = b"abcdef123456"; + /// let mut f = tempfile().unwrap(); + /// let mut aiocb = AioCb::from_slice( f.as_raw_fd(), + /// 2, //offset + /// WBUF, + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_NOP); + /// aiocb.write().unwrap(); + /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) { + /// thread::sleep(time::Duration::from_millis(10)); + /// } + /// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len()); + /// # } + /// ``` // Note: another solution to the problem of writing const buffers would be // to genericize AioCb for both &mut [u8] and &[u8] buffers. AioCb::read // could take the former and AioCb::write could take the latter. However, // then lio_listio wouldn't work, because that function needs a slice of - // AioCb, and they must all be the same type. We're basically stuck with - // using an unsafe function, since aio (as designed in C) is an unsafe API. + // AioCb, and they must all be of the same type. pub fn from_slice(fd: RawFd, offs: off_t, buf: &'a [u8], prio: libc::c_int, sigev_notify: SigevNotify, opcode: LioOpcode) -> AioCb { @@ -418,6 +655,56 @@ impl<'a> AioCb<'a> { } /// Cancels an outstanding AIO request. + /// + /// The operating system is not required to implement cancellation for all + /// file and device types. Even if it does, there is no guarantee that the + /// operation has not already completed. So the caller must check the + /// result and handle operations that were not canceled or that have already + /// completed. + /// + /// # Examples + /// + /// Cancel an outstanding aio operation. Note that we must still call + /// `aio_return` to free resources, even though we don't care about the + /// result. + /// + /// ``` + /// # extern crate bytes; + /// # extern crate tempfile; + /// # extern crate nix; + /// # use nix::errno::Errno; + /// # use nix::Error; + /// # use bytes::Bytes; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::{thread, time}; + /// # use std::io::Write; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// # fn main() { + /// let wbuf = Bytes::from(&b"CDEF"[..]); + /// let mut f = tempfile().unwrap(); + /// let mut aiocb = AioCb::from_bytes( f.as_raw_fd(), + /// 2, //offset + /// wbuf.clone(), + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_NOP); + /// aiocb.write().unwrap(); + /// let cs = aiocb.cancel().unwrap(); + /// if cs == AioCancelStat::AioNotCanceled { + /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) { + /// thread::sleep(time::Duration::from_millis(10)); + /// } + /// } + /// // Must call `aio_return`, but ignore the result + /// let _ = aiocb.aio_return(); + /// # } + /// ``` + /// + /// # References + /// + /// [aio_cancel](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html) pub fn cancel(&mut self) -> Result { match unsafe { libc::aio_cancel(self.aiocb.aio_fildes, &mut self.aiocb) } { libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled), @@ -428,9 +715,46 @@ impl<'a> AioCb<'a> { } } - /// Retrieve error status of an asynchronous operation. If the request has - /// not yet completed, returns `EINPROGRESS`. Otherwise, returns `Ok` or - /// any other error. + /// Retrieve error status of an asynchronous operation. + /// + /// If the request has not yet completed, returns `EINPROGRESS`. Otherwise, + /// returns `Ok` or any other error. + /// + /// # Examples + /// + /// Issue an aio operation and use `error` to poll for completion. Polling + /// is an alternative to `aio_suspend`, used by most of the other examples. + /// + /// ``` + /// # extern crate tempfile; + /// # extern crate nix; + /// # use nix::errno::Errno; + /// # use nix::Error; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::{thread, time}; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// # fn main() { + /// const WBUF: &[u8] = b"abcdef123456"; + /// let mut f = tempfile().unwrap(); + /// let mut aiocb = AioCb::from_slice( f.as_raw_fd(), + /// 2, //offset + /// WBUF, + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_NOP); + /// aiocb.write().unwrap(); + /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) { + /// thread::sleep(time::Duration::from_millis(10)); + /// } + /// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len()); + /// # } + /// ``` + /// + /// # References + /// + /// [aio_error](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_error.html) pub fn error(&mut self) -> Result<()> { match unsafe { libc::aio_error(&mut self.aiocb as *mut libc::aiocb) } { 0 => Ok(()), @@ -440,7 +764,11 @@ impl<'a> AioCb<'a> { } } - /// An asynchronous version of `fsync`. + /// An asynchronous version of `fsync(2)`. + /// + /// # References + /// + /// [aio_fsync](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_fsync.html) pub fn fsync(&mut self, mode: AioFsyncMode) -> Result<()> { let p: *mut libc::aiocb = &mut self.aiocb; Errno::result(unsafe { @@ -483,6 +811,10 @@ impl<'a> AioCb<'a> { } /// Asynchronously reads from a file descriptor into a buffer + /// + /// # References + /// + /// [aio_read](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_read.html) pub fn read(&mut self) -> Result<()> { assert!(self.mutable, "Can't read into an immutable buffer"); let p: *mut libc::aiocb = &mut self.aiocb; @@ -498,9 +830,15 @@ impl<'a> AioCb<'a> { SigEvent::from(&self.aiocb.aio_sigevent) } - /// Retrieve return status of an asynchronous operation. Should only be - /// called once for each `AioCb`, after `AioCb::error` indicates that it has - /// completed. The result is the same as for `read`, `write`, of `fsync`. + /// Retrieve return status of an asynchronous operation. + /// + /// Should only be called once for each `AioCb`, after `AioCb::error` + /// indicates that it has completed. The result is the same as for the + /// synchronous `read(2)`, `write(2)`, of `fsync(2)` functions. + /// + /// # References + /// + /// [aio_return](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_return.html) // Note: this should be just `return`, but that's a reserved word pub fn aio_return(&mut self) -> Result { let p: *mut libc::aiocb = &mut self.aiocb; @@ -509,6 +847,10 @@ impl<'a> AioCb<'a> { } /// Asynchronously writes from a buffer to a file descriptor + /// + /// # References + /// + /// [aio_write](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_write.html) pub fn write(&mut self) -> Result<()> { let p: *mut libc::aiocb = &mut self.aiocb; Errno::result(unsafe { @@ -520,7 +862,50 @@ impl<'a> AioCb<'a> { } -/// Cancels outstanding AIO requests. All requests for `fd` will be cancelled. +/// Cancels outstanding AIO requests for a given file descriptor. +/// +/// # Examples +/// +/// Issue an aio operation, then cancel all outstanding operations on that file +/// descriptor. +/// +/// ``` +/// # extern crate bytes; +/// # extern crate tempfile; +/// # extern crate nix; +/// # use nix::errno::Errno; +/// # use nix::Error; +/// # use bytes::Bytes; +/// # use nix::sys::aio::*; +/// # use nix::sys::signal::SigevNotify; +/// # use std::{thread, time}; +/// # use std::io::Write; +/// # use std::os::unix::io::AsRawFd; +/// # use tempfile::tempfile; +/// # fn main() { +/// let wbuf = Bytes::from(&b"CDEF"[..]); +/// let mut f = tempfile().unwrap(); +/// let mut aiocb = AioCb::from_bytes( f.as_raw_fd(), +/// 2, //offset +/// wbuf.clone(), +/// 0, //priority +/// SigevNotify::SigevNone, +/// LioOpcode::LIO_NOP); +/// aiocb.write().unwrap(); +/// let cs = aio_cancel_all(f.as_raw_fd()).unwrap(); +/// if cs == AioCancelStat::AioNotCanceled { +/// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) { +/// thread::sleep(time::Duration::from_millis(10)); +/// } +/// } +/// // Must call `aio_return`, but ignore the result +/// let _ = aiocb.aio_return(); +/// # } +/// ``` +/// +/// # References +/// +/// [`aio_cancel`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html) pub fn aio_cancel_all(fd: RawFd) -> Result { match unsafe { libc::aio_cancel(fd, null_mut()) } { libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled), @@ -532,8 +917,42 @@ pub fn aio_cancel_all(fd: RawFd) -> Result { } /// Suspends the calling process until at least one of the specified `AioCb`s -/// has completed, a signal is delivered, or the timeout has passed. If -/// `timeout` is `None`, `aio_suspend` will block indefinitely. +/// has completed, a signal is delivered, or the timeout has passed. +/// +/// If `timeout` is `None`, `aio_suspend` will block indefinitely. +/// +/// # Examples +/// +/// Use `aio_suspend` to block until an aio operation completes. +/// +// Disable doctest due to a known bug in FreeBSD's 32-bit emulation. The fix +// will be included in release 11.2. +// FIXME reenable the doc test when the CI machine gets upgraded to that release. +// https://svnweb.freebsd.org/base?view=revision&revision=325018 +/// ```no_run +/// # extern crate tempfile; +/// # extern crate nix; +/// # use nix::sys::aio::*; +/// # use nix::sys::signal::SigevNotify; +/// # use std::os::unix::io::AsRawFd; +/// # use tempfile::tempfile; +/// # fn main() { +/// const WBUF: &[u8] = b"abcdef123456"; +/// let mut f = tempfile().unwrap(); +/// let mut aiocb = AioCb::from_slice( f.as_raw_fd(), +/// 2, //offset +/// WBUF, +/// 0, //priority +/// SigevNotify::SigevNone, +/// LioOpcode::LIO_NOP); +/// aiocb.write().unwrap(); +/// aio_suspend(&[&aiocb], None).expect("aio_suspend failed"); +/// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len()); +/// # } +/// ``` +/// # References +/// +/// [`aio_suspend`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_suspend.html) pub fn aio_suspend(list: &[&AioCb], timeout: Option) -> Result<()> { let plist = list as *const [&AioCb] as *const [*const libc::aiocb]; let p = plist as *const *const libc::aiocb; @@ -547,8 +966,50 @@ pub fn aio_suspend(list: &[&AioCb], timeout: Option) -> Result<()> { } -/// Submits multiple asynchronous I/O requests with a single system call. The -/// order in which the requests are carried out is not specified. +/// Submits multiple asynchronous I/O requests with a single system call. +/// +/// They are not guaranteed to complete atomically, and the order in which the +/// requests are carried out is not specified. Reads, writes, and fsyncs may be +/// freely mixed. +/// +/// This function is useful for reducing the context-switch overhead of +/// submitting many AIO operations. It can also be used with +/// `LioMode::LIO_WAIT` to block on the result of several independent +/// operations. Used that way, it is often useful in programs that otherwise +/// make little use of AIO. +/// +/// # Examples +/// +/// Use `lio_listio` to submit an aio operation and wait for its completion. In +/// this case, there is no need to use `aio_suspend` to wait or `AioCb#error` to +/// poll. +/// +/// ``` +/// # extern crate tempfile; +/// # extern crate nix; +/// # use nix::sys::aio::*; +/// # use nix::sys::signal::SigevNotify; +/// # use std::os::unix::io::AsRawFd; +/// # use tempfile::tempfile; +/// # fn main() { +/// const WBUF: &[u8] = b"abcdef123456"; +/// let mut f = tempfile().unwrap(); +/// let mut aiocb = AioCb::from_slice( f.as_raw_fd(), +/// 2, //offset +/// WBUF, +/// 0, //priority +/// SigevNotify::SigevNone, +/// LioOpcode::LIO_WRITE); +/// lio_listio(LioMode::LIO_WAIT, +/// &[&mut aiocb], +/// SigevNotify::SigevNone).unwrap(); +/// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len()); +/// # } +/// ``` +/// +/// # References +/// +/// [`lio_listio`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html) #[cfg(not(any(target_os = "ios", target_os = "macos")))] pub fn lio_listio(mode: LioMode, list: &[&mut AioCb], sigev_notify: SigevNotify) -> Result<()> { diff --git a/test/sys/test_aio.rs b/test/sys/test_aio.rs index ed9d3761..f88fc268 100644 --- a/test/sys/test_aio.rs +++ b/test/sys/test_aio.rs @@ -43,8 +43,8 @@ fn test_accessors() { assert_eq!(99, sev.sigev_value.sival_ptr as i64); } -// Tests AioCb.cancel. We aren't trying to test the OS's implementation, only our -// bindings. So it's sufficient to check that AioCb.cancel returned any +// Tests AioCb.cancel. We aren't trying to test the OS's implementation, only +// our bindings. So it's sufficient to check that AioCb.cancel returned any // AioCancelStat value. #[test] #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] -- cgit v1.2.3