summaryrefslogtreecommitdiff
path: root/src/sys/aio.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/sys/aio.rs')
-rw-r--r--src/sys/aio.rs249
1 files changed, 249 insertions, 0 deletions
diff --git a/src/sys/aio.rs b/src/sys/aio.rs
new file mode 100644
index 00000000..f0fce435
--- /dev/null
+++ b/src/sys/aio.rs
@@ -0,0 +1,249 @@
+use {Error, Errno, Result};
+use std::os::unix::io::RawFd;
+use libc::{c_void, off_t, size_t};
+use libc;
+use std::marker::PhantomData;
+use std::mem;
+use std::ptr::{null, null_mut};
+use sys::signal::*;
+use sys::time::TimeSpec;
+
+/// Mode for `aio_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
+}
+
+/// 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
+}
+
+/// 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,
+}
+
+/// Return values for `aio_cancel`
+#[repr(i32)]
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum AioCancelStat {
+ /// All outstanding requests were canceled
+ AioCanceled = libc::AIO_CANCELED,
+ /// Some requests were not canceled. Their status should be checked with
+ /// `aio_error`
+ AioNotCanceled = libc::AIO_NOTCANCELED,
+ /// All of the requests have already finished
+ AioAllDone = libc::AIO_ALLDONE,
+}
+
+/// The basic structure used by all aio functions. Each `aiocb` represents one
+/// I/O request.
+#[repr(C)]
+pub struct AioCb<'a> {
+ aiocb: libc::aiocb,
+ phantom: PhantomData<&'a mut [u8]>
+}
+
+impl<'a> AioCb<'a> {
+ /// Constructs a new `AioCb` with no associated buffer.
+ ///
+ /// The resulting `AioCb` structure is suitable for use with `aio_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.
+ pub fn from_fd(fd: RawFd, prio: ::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, phantom: PhantomData};
+ aiocb
+ }
+
+ /// Constructs a new `AioCb`.
+ ///
+ /// * `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
+ pub fn from_mut_slice(fd: RawFd, offs: off_t, buf: &'a mut [u8],
+ prio: ::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;
+ a.aio_buf = buf.as_ptr() as *mut c_void;
+ a.aio_lio_opcode = opcode as ::c_int;
+
+ let aiocb = AioCb { aiocb: a, phantom: PhantomData};
+ aiocb
+ }
+
+ /// Like `from_mut_slice`, but works on constant slices rather than
+ /// mutable slices.
+ ///
+ /// This is technically unsafe, but in practice it's fine
+ /// to use with any aio functions except `aio_read` and `lio_listio` (with
+ /// `opcode` set to `LIO_READ`). This method is useful when writing a const
+ /// buffer with `aio_write`, since from_mut_slice can't work with const
+ /// buffers.
+ // Note: another solution to the problem of writing const buffers would be
+ // to genericize AioCb for both &mut [u8] and &[u8] buffers. aio_read could
+ // take the former and aio_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.
+ pub unsafe fn from_slice(fd: RawFd, offs: off_t, buf: &'a [u8],
+ prio: ::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;
+ a.aio_buf = buf.as_ptr() as *mut c_void;
+ a.aio_lio_opcode = opcode as ::c_int;
+
+ let aiocb = AioCb { aiocb: a, phantom: PhantomData};
+ aiocb
+ }
+
+ fn common_init(fd: RawFd, prio: ::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>()};
+ a.aio_fildes = fd;
+ a.aio_reqprio = prio;
+ a.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
+ a
+ }
+
+ /// Update the notification settings for an existing `aiocb`
+ pub fn set_sigev_notify(&mut self, sigev_notify: SigevNotify) {
+ self.aiocb.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
+ }
+}
+
+/// Cancels outstanding AIO requests. If `aiocb` is `None`, then all requests
+/// for `fd` will be cancelled. Otherwise, only the given `AioCb` will be
+/// cancelled.
+pub fn aio_cancel(fd: RawFd, aiocb: Option<&mut AioCb>) -> Result<AioCancelStat> {
+ let p: *mut libc::aiocb = match aiocb {
+ None => null_mut(),
+ Some(x) => &mut x.aiocb
+ };
+ match unsafe { libc::aio_cancel(fd, p) } {
+ libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
+ libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
+ libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
+ -1 => Err(Error::last()),
+ _ => panic!("unknown aio_cancel return value")
+ }
+}
+
+/// Retrieve error status of an asynchronous operation. If the request has not
+/// yet completed, returns `EINPROGRESS`. Otherwise, returns `Ok` or any other
+/// error.
+pub fn aio_error(aiocb: &mut AioCb) -> Result<()> {
+ let p: *mut libc::aiocb = &mut aiocb.aiocb;
+ match unsafe { libc::aio_error(p) } {
+ 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)
+ }
+}
+
+/// An asynchronous version of `fsync`.
+pub fn aio_fsync(mode: AioFsyncMode, aiocb: &mut AioCb) -> Result<()> {
+ let p: *mut libc::aiocb = &mut aiocb.aiocb;
+ Errno::result(unsafe { libc::aio_fsync(mode as ::c_int, p) }).map(drop)
+}
+
+/// Asynchronously reads from a file descriptor into a buffer
+pub fn aio_read(aiocb: &mut AioCb) -> Result<()> {
+ let p: *mut libc::aiocb = &mut aiocb.aiocb;
+ Errno::result(unsafe { libc::aio_read(p) }).map(drop)
+}
+
+/// Retrieve return status of an asynchronous operation. Should only be called
+/// once for each `AioCb`, after `aio_error` indicates that it has completed.
+/// The result the same as for `read`, `write`, of `fsync`.
+pub fn aio_return(aiocb: &mut AioCb) -> Result<isize> {
+ let p: *mut libc::aiocb = &mut aiocb.aiocb;
+ Errno::result(unsafe { libc::aio_return(p) })
+}
+
+/// 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.
+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 p = plist as *const *const libc::aiocb;
+ let timep = match timeout {
+ 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)
+}
+
+/// Asynchronously writes from a buffer to a file descriptor
+pub fn aio_write(aiocb: &mut AioCb) -> Result<()> {
+ let p: *mut libc::aiocb = &mut aiocb.aiocb;
+ Errno::result(unsafe { libc::aio_write(p) }).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<()> {
+ 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 p = plist as *const *mut libc::aiocb;
+ Errno::result(unsafe {
+ libc::lio_listio(mode as i32, p, list.len() as i32, sigevp)
+ }).map(drop)
+}