summaryrefslogtreecommitdiff
path: root/src/sys/aio.rs
diff options
context:
space:
mode:
authorAlan Somers <asomers@gmail.com>2018-03-06 19:28:16 -0700
committerAlan Somers <asomers@gmail.com>2018-03-22 16:53:12 -0600
commit4729935dec575d1d0353f3cdf7e318b4157d2cc3 (patch)
tree8659f8ab53d92bd4899ce27dd987398eb400626f /src/sys/aio.rs
parent6703bc8a654f4a4cff352a1959c08217f878adb0 (diff)
downloadnix-4729935dec575d1d0353f3cdf7e318b4157d2cc3.zip
Replace AioCb::from_bytes with more generic from_boxed_slice
Supporting the bytes crate was unnecessarily specific. This change replaces from_bytes and from_bytes_mut with from_boxed_slice and from_boxed_mut_slice, which can work with anything that implements Borrow<[u8]> and BorrowMut<[u8]>, respectively.
Diffstat (limited to 'src/sys/aio.rs')
-rw-r--r--src/sys/aio.rs309
1 files changed, 180 insertions, 129 deletions
diff --git a/src/sys/aio.rs b/src/sys/aio.rs
index 5cb3b818..8a958c84 100644
--- a/src/sys/aio.rs
+++ b/src/sys/aio.rs
@@ -21,16 +21,15 @@
//! not support this for all filesystems and devices.
use {Error, Result};
-use bytes::{Bytes, BytesMut};
use errno::Errno;
use std::os::unix::io::RawFd;
use libc::{c_void, off_t, size_t};
use libc;
+use std::borrow::{Borrow, BorrowMut};
use std::fmt;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::mem;
-use std::ops::Deref;
use std::ptr::{null, null_mut};
use std::thread;
use sys::signal::*;
@@ -93,46 +92,38 @@ pub enum AioCancelStat {
/// Owns (uniquely or shared) a memory buffer to keep it from `Drop`ing while
/// the kernel has a pointer to it.
-#[derive(Clone, Debug)]
pub enum Buffer<'a> {
/// No buffer to own.
///
/// Used for operations like `aio_fsync` that have no data, or for unsafe
/// operations that work with raw pointers.
None,
- /// Immutable shared ownership `Bytes` object
- // Must use out-of-line allocation so the address of the data will be
- // stable. `Bytes` and `BytesMut` sometimes dynamically allocate a buffer,
- // and sometimes inline the data within the struct itself.
- Bytes(Bytes),
- /// Mutable uniquely owned `BytesMut` object
- BytesMut(BytesMut),
/// Keeps a reference to a slice
- Phantom(PhantomData<&'a mut [u8]>)
+ Phantom(PhantomData<&'a mut [u8]>),
+ /// Generic thing that keeps a buffer from dropping
+ BoxedSlice(Box<Borrow<[u8]>>),
+ /// Generic thing that keeps a mutable buffer from dropping
+ BoxedMutSlice(Box<BorrowMut<[u8]>>),
}
-impl<'a> Buffer<'a> {
- /// Return the inner `Bytes`, if any
- pub fn bytes(&self) -> Option<&Bytes> {
- match *self {
- Buffer::Bytes(ref x) => Some(x),
- _ => None
- }
- }
-
- /// Return the inner `BytesMut`, if any
- pub fn bytes_mut(&self) -> Option<&BytesMut> {
- match *self {
- Buffer::BytesMut(ref x) => Some(x),
- _ => None
- }
- }
-
- /// Is this `Buffer` `None`?
- pub fn is_none(&self) -> bool {
- match *self {
- Buffer::None => true,
- _ => false,
+impl<'a> Debug for Buffer<'a> {
+ // Note: someday it may be possible to Derive Debug for a trait object, but
+ // not today.
+ // https://github.com/rust-lang/rust/issues/1563
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ &Buffer::None => write!(fmt, "None"),
+ &Buffer::Phantom(p) => p.fmt(fmt),
+ &Buffer::BoxedSlice(ref bs) => {
+ let borrowed : &Borrow<[u8]> = bs.borrow();
+ write!(fmt, "BoxedSlice({:?})",
+ borrowed as *const Borrow<[u8]>)
+ },
+ &Buffer::BoxedMutSlice(ref bms) => {
+ let borrowed : &BorrowMut<[u8]> = bms.borrow();
+ write!(fmt, "BoxedMutSlice({:?})",
+ borrowed as *const BorrowMut<[u8]>)
+ }
}
}
}
@@ -150,7 +141,7 @@ pub struct AioCb<'a> {
/// 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`.
+ /// `AioCb` is completed by [`buffer`](#method.buffer).
buffer: Buffer<'a>
}
@@ -166,6 +157,50 @@ impl<'a> AioCb<'a> {
x
}
+ /// Remove the inner boxed slice, if any, and return it.
+ ///
+ /// The returned value will be the argument that was passed to
+ /// `from_boxed_slice` when this `AioCb` was created.
+ ///
+ /// It is an error to call this method while the `AioCb` is still in
+ /// progress.
+ pub fn boxed_slice(&mut self) -> Option<Box<Borrow<[u8]>>> {
+ assert!(!self.in_progress, "Can't remove the buffer from an AioCb that's still in-progress. Did you forget to call aio_return?");
+ if let Buffer::BoxedSlice(_) = self.buffer {
+ let mut oldbuffer = Buffer::None;
+ mem::swap(&mut self.buffer, &mut oldbuffer);
+ if let Buffer::BoxedSlice(inner) = oldbuffer {
+ Some(inner)
+ } else {
+ unreachable!();
+ }
+ } else {
+ None
+ }
+ }
+
+ /// Remove the inner boxed mutable slice, if any, and return it.
+ ///
+ /// The returned value will be the argument that was passed to
+ /// `from_boxed_mut_slice` when this `AioCb` was created.
+ ///
+ /// It is an error to call this method while the `AioCb` is still in
+ /// progress.
+ pub fn boxed_mut_slice(&mut self) -> Option<Box<BorrowMut<[u8]>>> {
+ assert!(!self.in_progress, "Can't remove the buffer from an AioCb that's still in-progress. Did you forget to call aio_return?");
+ if let Buffer::BoxedMutSlice(_) = self.buffer {
+ let mut oldbuffer = Buffer::None;
+ mem::swap(&mut self.buffer, &mut oldbuffer);
+ if let Buffer::BoxedMutSlice(inner) = oldbuffer {
+ Some(inner)
+ } else {
+ unreachable!();
+ }
+ } else {
+ None
+ }
+ }
+
/// Returns the underlying file descriptor associated with the `AioCb`
pub fn fd(&self) -> RawFd {
self.aiocb.aio_fildes
@@ -187,7 +222,7 @@ impl<'a> AioCb<'a> {
/// # Examples
///
/// Create an `AioCb` from a raw file descriptor and use it for an
- /// [`fsync`](#method.from_bytes_mut) operation.
+ /// [`fsync`](#method.fsync) operation.
///
/// ```
/// # extern crate tempfile;
@@ -300,17 +335,19 @@ impl<'a> AioCb<'a> {
}
}
- /// Constructs a new `AioCb` from a `Bytes` object.
+ /// The safest and most flexible way to create an `AioCb`.
///
- /// Unlike `from_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.
+ /// read operations. Unlike `from_ptr`, this method will ensure that the
+ /// buffer doesn't `drop` while the kernel is still processing it. Any
+ /// object that can be borrowed as a boxed slice will work.
///
/// # Parameters
///
/// * `fd`: File descriptor. Required for all aio functions.
/// * `offs`: File offset
- /// * `buf`: A shared memory buffer
+ /// * `buf`: A boxed slice-like object
/// * `prio`: If POSIX Prioritized IO is supported, then the
/// operation will be prioritized at the process's
/// priority level minus `prio`
@@ -322,15 +359,13 @@ impl<'a> AioCb<'a> {
///
/// # Examples
///
- /// Create an `AioCb` from a `Bytes` object and use it for writing.
+ /// Create an `AioCb` from a Vector 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};
@@ -338,11 +373,12 @@ impl<'a> AioCb<'a> {
/// # use std::os::unix::io::AsRawFd;
/// # use tempfile::tempfile;
/// # fn main() {
- /// let wbuf = Bytes::from(&b"CDEF"[..]);
+ /// let wbuf = Box::new(Vec::from("CDEF"));
+ /// let expected_len = wbuf.len();
/// let mut f = tempfile().unwrap();
- /// let mut aiocb = AioCb::from_bytes( f.as_raw_fd(),
+ /// let mut aiocb = AioCb::from_boxed_slice( f.as_raw_fd(),
/// 2, //offset
- /// wbuf.clone(),
+ /// wbuf,
/// 0, //priority
/// SigevNotify::SigevNone,
/// LioOpcode::LIO_NOP);
@@ -350,72 +386,103 @@ impl<'a> AioCb<'a> {
/// 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());
+ /// assert_eq!(aiocb.aio_return().unwrap() as usize, expected_len);
+ /// # }
+ /// ```
+ ///
+ /// Create an `AioCb` from a `Bytes` object
+ ///
+ /// ```
+ /// # extern crate bytes;
+ /// # extern crate tempfile;
+ /// # extern crate nix;
+ /// # use bytes::Bytes;
+ /// # use nix::sys::aio::*;
+ /// # use nix::sys::signal::SigevNotify;
+ /// # use std::os::unix::io::AsRawFd;
+ /// # use tempfile::tempfile;
+ /// # fn main() {
+ /// let wbuf = Box::new(Bytes::from(&b"CDEF"[..]));
+ /// let mut f = tempfile().unwrap();
+ /// let mut aiocb = AioCb::from_boxed_slice( f.as_raw_fd(),
+ /// 2, //offset
+ /// wbuf,
+ /// 0, //priority
+ /// SigevNotify::SigevNone,
+ /// LioOpcode::LIO_NOP);
/// # }
/// ```
- pub fn from_bytes(fd: RawFd, offs: off_t, buf: Bytes,
+ ///
+ /// If a library needs to work with buffers that aren't `Box`ed, it can
+ /// create a `Box`ed container for use with this method. Here's an example
+ /// using an un`Box`ed `Bytes` object.
+ ///
+ /// ```
+ /// # extern crate bytes;
+ /// # extern crate tempfile;
+ /// # extern crate nix;
+ /// # use bytes::Bytes;
+ /// # use nix::sys::aio::*;
+ /// # use nix::sys::signal::SigevNotify;
+ /// # use std::borrow::Borrow;
+ /// # use std::os::unix::io::AsRawFd;
+ /// # use tempfile::tempfile;
+ /// struct BytesContainer(Bytes);
+ /// impl Borrow<[u8]> for BytesContainer {
+ /// fn borrow(&self) -> &[u8] {
+ /// self.0.as_ref()
+ /// }
+ /// }
+ /// fn main() {
+ /// let wbuf = Bytes::from(&b"CDEF"[..]);
+ /// let boxed_wbuf = Box::new(BytesContainer(wbuf));
+ /// let mut f = tempfile().unwrap();
+ /// let mut aiocb = AioCb::from_boxed_slice( f.as_raw_fd(),
+ /// 2, //offset
+ /// boxed_wbuf,
+ /// 0, //priority
+ /// SigevNotify::SigevNone,
+ /// LioOpcode::LIO_NOP);
+ /// }
+ /// ```
+ ///
+ /// [`from_slice`]: #method.from_slice
+ pub fn from_boxed_slice(fd: RawFd, offs: off_t, buf: Box<Borrow<[u8]>>,
prio: libc::c_int, sigev_notify: SigevNotify,
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
- // it to force out-of-line storage
- // TODO: Add an is_inline() method to BytesMut, and a way to explicitly
- // force out-of-line allocation.
- let buf2 = if buf.len() < 64 {
- // Reallocate to force out-of-line allocation
- let mut ool = Bytes::with_capacity(64);
- ool.extend_from_slice(buf.deref());
- ool
- } else {
- buf
- };
let mut a = AioCb::common_init(fd, prio, sigev_notify);
+ {
+ let borrowed : &Borrow<[u8]> = buf.borrow();
+ let slice : &[u8] = borrowed.borrow();
+ a.aio_nbytes = slice.len() as size_t;
+ a.aio_buf = slice.as_ptr() as *mut c_void;
+ }
a.aio_offset = offs;
- a.aio_nbytes = buf2.len() as size_t;
- a.aio_buf = buf2.as_ptr() as *mut c_void;
a.aio_lio_opcode = opcode as libc::c_int;
AioCb {
aiocb: a,
mutable: false,
in_progress: false,
- buffer: Buffer::Bytes(buf2),
+ buffer: Buffer::BoxedSlice(buf),
}
}
- /// 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.
- ///
- /// # Parameters
+ /// The safest and most flexible way to create an `AioCb` for reading.
///
- /// * `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
+ /// Like [`from_boxed_slice`], but the slice is a mutable one. More
+ /// flexible than [`from_mut_slice`], because a wide range of objects can be
+ /// used.
///
/// # 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.
+ /// Create an `AioCb` from a Vector and use it for reading
///
/// ```
- /// # 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};
@@ -425,10 +492,10 @@ impl<'a> AioCb<'a> {
/// # fn main() {
/// const INITIAL: &[u8] = b"abcdef123456";
/// const LEN: usize = 4;
- /// let rbuf = BytesMut::from(vec![0; LEN]);
+ /// let rbuf = Box::new(vec![0; LEN]);
/// let mut f = tempfile().unwrap();
/// f.write_all(INITIAL).unwrap();
- /// let mut aiocb = AioCb::from_bytes_mut( f.as_raw_fd(),
+ /// let mut aiocb = AioCb::from_boxed_mut_slice( f.as_raw_fd(),
/// 2, //offset
/// rbuf,
/// 0, //priority
@@ -439,33 +506,33 @@ impl<'a> AioCb<'a> {
/// thread::sleep(time::Duration::from_millis(10));
/// }
/// assert_eq!(aiocb.aio_return().unwrap() as usize, LEN);
- /// let buffer = aiocb.into_buffer();
+ /// let mut buffer = aiocb.boxed_mut_slice().unwrap();
/// const EXPECT: &[u8] = b"cdef";
- /// assert_eq!(buffer.bytes_mut().unwrap(), EXPECT);
+ /// assert_eq!(buffer.borrow_mut(), EXPECT);
/// # }
/// ```
- pub fn from_bytes_mut(fd: RawFd, offs: off_t, buf: BytesMut,
- prio: libc::c_int, sigev_notify: SigevNotify,
- opcode: LioOpcode) -> AioCb<'a> {
- let mut buf2 = if buf.len() < 64 {
- // Reallocate to force out-of-line allocation
- let mut ool = BytesMut::with_capacity(64);
- ool.extend_from_slice(buf.deref());
- ool
- } else {
- buf
- };
+ ///
+ /// [`from_boxed_slice`]: #method.from_boxed_slice
+ /// [`from_mut_slice`]: #method.from_mut_slice
+ pub fn from_boxed_mut_slice(fd: RawFd, offs: off_t,
+ mut buf: Box<BorrowMut<[u8]>>,
+ prio: libc::c_int, sigev_notify: SigevNotify,
+ opcode: LioOpcode) -> AioCb<'a> {
let mut a = AioCb::common_init(fd, prio, sigev_notify);
+ {
+ let borrowed : &mut BorrowMut<[u8]> = buf.borrow_mut();
+ let slice : &mut [u8] = borrowed.borrow_mut();
+ a.aio_nbytes = slice.len() as size_t;
+ a.aio_buf = slice.as_mut_ptr() as *mut c_void;
+ }
a.aio_offset = offs;
- a.aio_nbytes = buf2.len() as size_t;
- a.aio_buf = buf2.as_mut_ptr() as *mut c_void;
a.aio_lio_opcode = opcode as libc::c_int;
AioCb {
aiocb: a,
mutable: true,
in_progress: false,
- buffer: Buffer::BytesMut(buf2),
+ buffer: Buffer::BoxedMutSlice(buf),
}
}
@@ -475,7 +542,7 @@ impl<'a> AioCb<'a> {
/// 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`.
+ /// wrapped in a `struct` that implements `BorrowMut<[u8]>`
///
/// # Parameters
///
@@ -519,7 +586,8 @@ impl<'a> AioCb<'a> {
/// 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`.
+ /// some reason the data cannot be wrapped in a `struct` that implements
+ /// `Borrow<[u8]>`
///
/// # Parameters
///
@@ -624,19 +692,6 @@ impl<'a> AioCb<'a> {
}
}
- /// Consumes the `aiocb` and returns its inner `Buffer`, if any.
- ///
- /// This method is especially useful when reading into a `BytesMut`, because
- /// that type does not support shared ownership.
- pub fn into_buffer(mut self) -> Buffer<'static> {
- let buf = self.buffer();
- match buf {
- Buffer::BytesMut(x) => Buffer::BytesMut(x),
- Buffer::Bytes(x) => Buffer::Bytes(x),
- _ => Buffer::None
- }
- }
-
fn common_init(fd: RawFd, prio: libc::c_int,
sigev_notify: SigevNotify) -> libc::aiocb {
// Use mem::zeroed instead of explicitly zeroing each field, because the
@@ -670,12 +725,10 @@ impl<'a> AioCb<'a> {
/// 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};
@@ -683,11 +736,11 @@ impl<'a> AioCb<'a> {
/// # use std::os::unix::io::AsRawFd;
/// # use tempfile::tempfile;
/// # fn main() {
- /// let wbuf = Bytes::from(&b"CDEF"[..]);
+ /// let wbuf = b"CDEF";
/// let mut f = tempfile().unwrap();
- /// let mut aiocb = AioCb::from_bytes( f.as_raw_fd(),
+ /// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
/// 2, //offset
- /// wbuf.clone(),
+ /// &wbuf[..],
/// 0, //priority
/// SigevNotify::SigevNone,
/// LioOpcode::LIO_NOP);
@@ -871,12 +924,10 @@ impl<'a> AioCb<'a> {
/// 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};
@@ -884,11 +935,11 @@ impl<'a> AioCb<'a> {
/// # use std::os::unix::io::AsRawFd;
/// # use tempfile::tempfile;
/// # fn main() {
-/// let wbuf = Bytes::from(&b"CDEF"[..]);
+/// let wbuf = b"CDEF";
/// let mut f = tempfile().unwrap();
-/// let mut aiocb = AioCb::from_bytes( f.as_raw_fd(),
+/// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
/// 2, //offset
-/// wbuf.clone(),
+/// &wbuf[..],
/// 0, //priority
/// SigevNotify::SigevNone,
/// LioOpcode::LIO_NOP);