Skip to content

Commit

Permalink
Use libc_enum! where possible
Browse files Browse the repository at this point in the history
Some enums which use different names for values than libc still set the
discriminators manually.

closes nix-rust#254
  • Loading branch information
wginolas committed Nov 5, 2017
1 parent 1b9d205 commit 94d8a4e
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 196 deletions.
188 changes: 102 additions & 86 deletions src/sys/aio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,41 @@ use sys::time::TimeSpec;

/// Mode for `AioCb::fsync`. Controls whether only data or both data and
/// metadata are synced.
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AioFsyncMode {
/// do it like `fsync`
O_SYNC = libc::O_SYNC,
/// on supported operating systems only, do it like `fdatasync`
#[cfg(any(target_os = "openbsd", target_os = "bitrig",
target_os = "netbsd", target_os = "macos", target_os = "ios",
target_os = "linux"))]
O_DSYNC = libc::O_DSYNC
libc_enum! {
#[repr(i32)]
pub enum AioFsyncMode {
/// do it like `fsync`
O_SYNC,
/// on supported operating systems only, do it like `fdatasync`
#[cfg(any(target_os = "openbsd", target_os = "bitrig",
target_os = "netbsd", target_os = "macos", target_os = "ios",
target_os = "linux"))]
O_DSYNC
}
}

/// 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.
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum LioOpcode {
LIO_NOP = libc::LIO_NOP,
LIO_WRITE = libc::LIO_WRITE,
LIO_READ = libc::LIO_READ
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.
#[repr(i32)]
pub enum LioOpcode {
LIO_NOP,
LIO_WRITE,
LIO_READ,
}
}

/// Mode for `lio_listio`.
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum LioMode {
/// Requests that `lio_listio` block until all requested operations have
/// been completed
LIO_WAIT = libc::LIO_WAIT,
/// Requests that `lio_listio` return immediately
LIO_NOWAIT = libc::LIO_NOWAIT,
libc_enum! {
/// Mode for `lio_listio`.
#[repr(i32)]
pub enum LioMode {
/// Requests that `lio_listio` block until all requested operations have
/// been completed
LIO_WAIT,
/// Requests that `lio_listio` return immediately
LIO_NOWAIT,
}
}

/// Return values for `AioCb::cancel and aio_cancel_all`
Expand All @@ -68,7 +71,7 @@ enum Keeper<'a> {
/// Keeps a reference to a Boxed slice
boxed(Rc<Box<[u8]>>),
/// Keeps a reference to a slice
phantom(PhantomData<&'a mut [u8]>)
phantom(PhantomData<&'a mut [u8]>),
}

/// The basic structure used by all aio functions. Each `aiocb` represents one
Expand All @@ -80,7 +83,7 @@ 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>
keeper: Keeper<'a>,
}

impl<'a> AioCb<'a> {
Expand All @@ -97,15 +100,18 @@ impl<'a> AioCb<'a> {
/// be prioritized at the process's priority level minus `prio`
/// * `sigev_notify` Determines how you will be notified of event
/// completion.
pub fn from_fd(fd: RawFd, prio: libc::c_int,
sigev_notify: SigevNotify) -> AioCb<'a> {
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);
a.aio_offset = 0;
a.aio_nbytes = 0;
a.aio_buf = null_mut();

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

Expand All @@ -120,17 +126,26 @@ impl<'a> AioCb<'a> {
/// completion.
/// * `opcode` This field is only used for `lio_listio`. It determines
/// which operation to use for this individual aiocb
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> {
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> {
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_lio_opcode = opcode as libc::c_int;

let aiocb = AioCb { aiocb: a, mutable: true, in_progress: false,
keeper: Keeper::phantom(PhantomData)};
let aiocb = AioCb {
aiocb: a,
mutable: true,
in_progress: false,
keeper: Keeper::phantom(PhantomData),
};
aiocb
}

Expand All @@ -148,17 +163,26 @@ impl<'a> AioCb<'a> {
/// 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,
opcode: LioOpcode) -> AioCb<'a> {
pub fn from_boxed_slice(
fd: RawFd,
offs: off_t,
buf: Rc<Box<[u8]>>,
prio: libc::c_int,
sigev_notify: SigevNotify,
opcode: LioOpcode,
) -> AioCb<'a> {
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_lio_opcode = opcode as libc::c_int;

let aiocb = AioCb{ aiocb: a, mutable: true, in_progress: false,
keeper: Keeper::boxed(buf)};
let aiocb = AioCb {
aiocb: a,
mutable: true,
in_progress: false,
keeper: Keeper::boxed(buf),
};
aiocb
}

Expand All @@ -175,31 +199,42 @@ impl<'a> AioCb<'a> {
// 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.
pub fn from_slice(fd: RawFd, offs: off_t, buf: &'a [u8],
prio: libc::c_int, sigev_notify: SigevNotify,
opcode: LioOpcode) -> AioCb {
pub fn from_slice(
fd: RawFd,
offs: off_t,
buf: &'a [u8],
prio: libc::c_int,
sigev_notify: SigevNotify,
opcode: LioOpcode,
) -> AioCb {
let mut a = AioCb::common_init(fd, prio, sigev_notify);
a.aio_offset = offs;
a.aio_nbytes = buf.len() as size_t;
// casting an immutable buffer to a mutable pointer looks unsafe,
// but technically its only unsafe to dereference it, not to create
// it.
a.aio_buf = buf.as_ptr() as *mut c_void;
assert!(opcode != LioOpcode::LIO_READ, "Can't read into an immutable buffer");
assert!(
opcode != LioOpcode::LIO_READ,
"Can't read into an immutable buffer"
);
a.aio_lio_opcode = opcode as libc::c_int;

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

fn common_init(fd: RawFd, prio: libc::c_int,
sigev_notify: SigevNotify) -> libc::aiocb {
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
// number and name of reserved fields is OS-dependent. On some OSes,
// some reserved fields are used the kernel for state, and must be
// explicitly zeroed when allocated.
let mut a = unsafe { mem::zeroed::<libc::aiocb>()};
let mut a = unsafe { mem::zeroed::<libc::aiocb>() };
a.aio_fildes = fd;
a.aio_reqprio = prio;
a.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
Expand All @@ -218,7 +253,7 @@ impl<'a> AioCb<'a> {
libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
-1 => Err(Error::last()),
_ => panic!("unknown aio_cancel return value")
_ => panic!("unknown aio_cancel return value"),
}
}

Expand All @@ -230,18 +265,15 @@ impl<'a> AioCb<'a> {
0 => Ok(()),
num if num > 0 => Err(Error::from_errno(Errno::from_i32(num))),
-1 => Err(Error::last()),
num => panic!("unknown aio_error return value {:?}", num)
num => panic!("unknown aio_error return value {:?}", num),
}
}

/// An asynchronous version of `fsync`.
pub fn fsync(&mut self, mode: AioFsyncMode) -> Result<()> {
let p: *mut libc::aiocb = &mut self.aiocb;
Errno::result(unsafe {
libc::aio_fsync(mode as libc::c_int, p)
}).map(|_| {
self.in_progress = true;
})
Errno::result(unsafe { libc::aio_fsync(mode as libc::c_int, p) })
.map(|_| { self.in_progress = true; })
}

/// Returns the `aiocb`'s `LioOpcode` field
Expand All @@ -253,7 +285,7 @@ impl<'a> AioCb<'a> {
libc::LIO_READ => Some(LioOpcode::LIO_READ),
libc::LIO_WRITE => Some(LioOpcode::LIO_WRITE),
libc::LIO_NOP => Some(LioOpcode::LIO_NOP),
_ => None
_ => None,
}
}

Expand All @@ -280,11 +312,7 @@ impl<'a> AioCb<'a> {
pub fn read(&mut self) -> Result<()> {
assert!(self.mutable, "Can't read into an immutable buffer");
let p: *mut libc::aiocb = &mut self.aiocb;
Errno::result(unsafe {
libc::aio_read(p)
}).map(|_| {
self.in_progress = true;
})
Errno::result(unsafe { libc::aio_read(p) }).map(|_| { self.in_progress = true; })
}

/// Returns the `SigEvent` stored in the `AioCb`
Expand All @@ -305,13 +333,8 @@ impl<'a> AioCb<'a> {
/// Asynchronously writes from a buffer to a file descriptor
pub fn write(&mut self) -> Result<()> {
let p: *mut libc::aiocb = &mut self.aiocb;
Errno::result(unsafe {
libc::aio_write(p)
}).map(|_| {
self.in_progress = true;
})
Errno::result(unsafe { libc::aio_write(p) }).map(|_| { self.in_progress = true; })
}

}

/// Cancels outstanding AIO requests. All requests for `fd` will be cancelled.
Expand All @@ -321,7 +344,7 @@ pub fn aio_cancel_all(fd: RawFd) -> Result<AioCancelStat> {
libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
-1 => Err(Error::last()),
_ => panic!("unknown aio_cancel return value")
_ => panic!("unknown aio_cancel return value"),
}
}

Expand All @@ -331,32 +354,25 @@ pub fn aio_cancel_all(fd: RawFd) -> Result<AioCancelStat> {
pub fn aio_suspend(list: &[&AioCb], timeout: Option<TimeSpec>) -> Result<()> {
// We must use transmute because Rust doesn't understand that a pointer to a
// Struct is the same as a pointer to its first element.
let plist = unsafe {
mem::transmute::<&[&AioCb], *const [*const libc::aiocb]>(list)
};
let plist = unsafe { mem::transmute::<&[&AioCb], *const [*const libc::aiocb]>(list) };
let p = plist as *const *const libc::aiocb;
let timep = match timeout {
None => null::<libc::timespec>(),
Some(x) => x.as_ref() as *const libc::timespec
None => null::<libc::timespec>(),
Some(x) => x.as_ref() as *const libc::timespec,
};
Errno::result(unsafe {
libc::aio_suspend(p, list.len() as i32, timep)
}).map(drop)
Errno::result(unsafe { libc::aio_suspend(p, list.len() as i32, timep) }).map(drop)
}


/// Submits multiple asynchronous I/O requests with a single system call. The
/// order in which the requests are carried out is not specified.
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
pub fn lio_listio(mode: LioMode, list: &[&mut AioCb],
sigev_notify: SigevNotify) -> Result<()> {
pub fn lio_listio(mode: LioMode, list: &[&mut AioCb], sigev_notify: SigevNotify) -> Result<()> {
let sigev = SigEvent::new(sigev_notify);
let sigevp = &mut sigev.sigevent() as *mut libc::sigevent;
// We must use transmute because Rust doesn't understand that a pointer to a
// Struct is the same as a pointer to its first element.
let plist = unsafe {
mem::transmute::<&[&mut AioCb], *const [*mut libc::aiocb]>(list)
};
let plist = unsafe { mem::transmute::<&[&mut AioCb], *const [*mut libc::aiocb]>(list) };
let p = plist as *const *mut libc::aiocb;
Errno::result(unsafe {
libc::lio_listio(mode as i32, p, list.len() as i32, sigevp)
Expand Down
Loading

0 comments on commit 94d8a4e

Please sign in to comment.