Skip to content

Commit

Permalink
Add methods to create AioCbs from Bytes
Browse files Browse the repository at this point in the history
  • Loading branch information
asomers committed Jan 8, 2018
1 parent 9147430 commit b2ab037
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 33 deletions.
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).

### Added
- Added `AioCb::from_ptr` and `AioCb::from_mut_ptr`
([#TODO](https://github.com/nix-rust/nix/pull/TODO))
([#820](https://github.com/nix-rust/nix/pull/820))
- Added specialized wrappers: `sys::ptrace::{traceme, syscall, cont, attach}`. Using the matching routines
with `sys::ptrace::ptrace` is now deprecated.
- Added `nix::poll` module for all platforms
Expand Down Expand Up @@ -58,8 +58,6 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Changed
- Use native `pipe2` on all BSD targets. Users should notice no difference.
([#777](https://github.com/nix-rust/nix/pull/777))
- `AioCb::from_boxed_slice` no longer allows `aio_read`
([#TODO](https://github.com/nix-rust/nix/pull/TODO))
- Renamed existing `ptrace` wrappers to encourage namespacing ([#692](https://github.com/nix-rust/nix/pull/692))
- Marked `sys::ptrace::ptrace` as `unsafe`.
- Changed function signature of `socket()` and `socketpair()`. The `protocol` argument
Expand Down Expand Up @@ -107,6 +105,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
([#648](https://github.com/nix-rust/nix/pull/648))

### Removed
- `AioCb::from_boxed_slice` has been removed. It was never actually safe.
([#820](https://github.com/nix-rust/nix/pull/820))
- The syscall module has been removed. This only exposed enough functionality for
`memfd_create()` and `pivot_root()`, which are still exposed as separate functions.
([#747](https://github.com/nix-rust/nix/pull/747))
Expand Down
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ bitflags = "1.0"
cfg-if = "0.1.0"
void = "1.0.2"

# Don't include the optional serde feature
[dependencies.bytes]
version = "0.4.5"
default-features = false

[target.'cfg(target_os = "dragonfly")'.build-dependencies]
gcc = "0.3"

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#![cfg_attr(test, deny(warnings))]
#![recursion_limit = "500"]

extern crate bytes;
#[macro_use]
extern crate bitflags;

Expand Down
162 changes: 139 additions & 23 deletions src/sys/aio.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use {Error, Result};
use bytes::{Bytes, BytesMut};
use errno::Errno;
use std::os::unix::io::RawFd;
use libc::{c_void, off_t, size_t};
Expand All @@ -7,7 +8,7 @@ use std::fmt;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::mem;
use std::rc::Rc;
use std::ops::Deref;
use std::ptr::{null, null_mut};
use sys::signal::*;
use sys::time::TimeSpec;
Expand Down Expand Up @@ -66,17 +67,48 @@ pub enum AioCancelStat {
AioAllDone = libc::AIO_ALLDONE,
}

/// Private type used by nix to keep buffers from Drop'ing while the kernel has
/// a pointer to them.
/// Owns (uniquely or shared) a memory buffer to keep it from `Drop`ing while
/// the kernel has a pointer to it.
#[derive(Clone, Debug)]
enum Keeper<'a> {
pub enum Buffer<'a> {
none,
/// Keeps a reference to a Boxed slice
boxed(Rc<Box<[u8]>>),
/// 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
bytes_mut(BytesMut),
/// Keeps a reference to a slice
phantom(PhantomData<&'a mut [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::bytes_mut(ref x) => Some(x),
_ => None
}
}

/// Is this `Buffer` `None`?
pub fn is_none(&self) -> bool {
match self {
&Buffer::none => true,
_ => false,
}
}
}

/// The basic structure used by all aio functions. Each `aiocb` represents one
/// I/O request.
pub struct AioCb<'a> {
Expand All @@ -86,10 +118,21 @@ pub struct AioCb<'a> {
/// Could this `AioCb` potentially have any in-kernel state?
in_progress: bool,
/// Used to keep buffers from Drop'ing
keeper: Keeper<'a>
buffer: Buffer<'a>
}

impl<'a> AioCb<'a> {
/// Remove the inner `Buffer` and return it
///
/// It is an error to call this method while the `AioCb` is still in
/// progress.
pub fn buffer(&mut self) -> Buffer<'a> {
assert!(!self.in_progress);
let mut x = Buffer::none;
mem::swap(&mut self.buffer, &mut x);
x
}

/// Returns the underlying file descriptor associated with the `AioCb`
pub fn fd(&self) -> RawFd {
self.aiocb.aio_fildes
Expand All @@ -115,7 +158,7 @@ impl<'a> AioCb<'a> {
aiocb: a,
mutable: false,
in_progress: false,
keeper: Keeper::none
buffer: Buffer::none
}
}

Expand Down Expand Up @@ -143,38 +186,92 @@ impl<'a> AioCb<'a> {
aiocb: a,
mutable: true,
in_progress: false,
keeper: Keeper::phantom(PhantomData)
buffer: Buffer::phantom(PhantomData)
}
}

/// Constructs a new `AioCb`.
///
/// Unlike `from_mut_slice`, this method returns a structure suitable for
/// placement on the heap.
/// 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 on the heap
/// * `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
pub fn from_boxed_slice(fd: RawFd, offs: off_t, buf: Rc<Box<[u8]>>,
prio: libc::c_int, sigev_notify: SigevNotify,
pub fn from_bytes(fd: RawFd, offs: off_t, buf: Bytes,
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);
a.aio_offset = offs;
a.aio_nbytes = buf.len() as size_t;
a.aio_buf = buf.as_ptr() as *mut c_void;
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,
keeper: Keeper::boxed(buf)
buffer: Buffer::bytes(buf2)
}
}

/// Constructs a new `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.
///
/// * `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
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
};
let mut a = AioCb::common_init(fd, prio, sigev_notify);
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::bytes_mut(buf2)
}
}

Expand Down Expand Up @@ -206,9 +303,12 @@ impl<'a> AioCb<'a> {
a.aio_buf = buf;
a.aio_lio_opcode = opcode as libc::c_int;

let aiocb = AioCb { aiocb: a, mutable: true, in_progress: false,
keeper: Keeper::none};
aiocb
AioCb {
aiocb: a,
mutable: true,
in_progress: false,
buffer: Buffer::none
}
}

/// Constructs a new `AioCb` from a raw pointer
Expand Down Expand Up @@ -240,9 +340,12 @@ impl<'a> AioCb<'a> {
a.aio_buf = buf as *mut c_void;
a.aio_lio_opcode = opcode as libc::c_int;

let aiocb = AioCb { aiocb: a, mutable: false, in_progress: false,
keeper: Keeper::none};
aiocb
AioCb {
aiocb: a,
mutable: false,
in_progress: false,
buffer: Buffer::none
}
}

/// Like `from_mut_slice`, but works on constant slices rather than
Expand Down Expand Up @@ -275,7 +378,20 @@ impl<'a> AioCb<'a> {
aiocb: a,
mutable: false,
in_progress: false,
keeper: Keeper::none
buffer: Buffer::none
}
}

/// 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::bytes_mut(x) => Buffer::bytes_mut(x),
Buffer::bytes(x) => Buffer::bytes(x),
_ => Buffer::none
}
}

Expand Down
Loading

0 comments on commit b2ab037

Please sign in to comment.