#![allow(non_camel_case_types)]
use std::io::{Error, Result};
use std::os::raw::{c_int, c_long, c_uint, c_ulong};
use std::ptr::null_mut;
type __s16 = ::std::os::raw::c_short;
type __u16 = ::std::os::raw::c_ushort;
type __u32 = ::std::os::raw::c_uint;
type __s64 = ::std::os::raw::c_longlong;
type __u64 = ::std::os::raw::c_ulonglong;
pub const IOCB_CMD_PREAD: u32 = 0;
pub const IOCB_CMD_PWRITE: u32 = 1;
pub const IOCB_CMD_FSYNC: u32 = 2;
pub const IOCB_CMD_FDSYNC: u32 = 3;
pub const IOCB_CMD_NOOP: u32 = 6;
pub const IOCB_CMD_PREADV: u32 = 7;
pub const IOCB_CMD_PWRITEV: u32 = 8;
pub const IOCB_FLAG_RESFD: u32 = 1;
pub const MAX_REQUESTS: usize = 0x10000;
#[allow(missing_docs)]
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct IoControlBlock {
pub aio_data: __u64,
pub aio_key: __u32,
pub aio_reserved1: __u32,
pub aio_lio_opcode: __u16,
pub aio_reqprio: __s16,
pub aio_fildes: __u32,
pub aio_buf: __u64,
pub aio_nbytes: __u64,
pub aio_offset: __s64,
pub aio_reserved2: __u64,
pub aio_flags: __u32,
pub aio_resfd: __u32,
}
#[allow(missing_docs)]
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct IoEvent {
pub data: __u64,
pub obj: __u64,
pub res: __s64,
pub res2: __s64,
}
#[repr(transparent)]
#[derive(Debug)]
pub struct IoContext(::std::os::raw::c_ulong);
impl IoContext {
#[allow(clippy::new_ret_no_self)]
pub fn new(nr_events: c_uint) -> Result<Self> {
if nr_events as usize > MAX_REQUESTS {
return Err(Error::from_raw_os_error(libc::EINVAL));
}
let mut ctx = IoContext(0);
let rc =
unsafe { libc::syscall(libc::SYS_io_setup, nr_events, &mut ctx as *mut Self) as c_int };
if rc < 0 {
Err(Error::last_os_error())
} else {
Ok(ctx)
}
}
pub fn submit(&self, iocbs: &[&mut IoControlBlock]) -> Result<usize> {
let rc = unsafe {
libc::syscall(
libc::SYS_io_submit,
self.0,
iocbs.len() as c_ulong,
iocbs.as_ptr(),
) as c_int
};
if rc < 0 {
Err(Error::last_os_error())
} else {
Ok(rc as usize)
}
}
pub fn cancel(&self, iocb: &IoControlBlock, result: &mut IoEvent) -> Result<()> {
let rc = unsafe {
libc::syscall(
libc::SYS_io_cancel,
self.0,
iocb as *const IoControlBlock,
result as *mut IoEvent,
) as c_int
};
if rc < 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
}
pub fn get_events(
&self,
min_nr: c_long,
events: &mut [IoEvent],
timeout: Option<&mut libc::timespec>,
) -> Result<usize> {
let to = match timeout {
Some(val) => val as *mut libc::timespec,
None => null_mut() as *mut libc::timespec,
};
let rc = unsafe {
libc::syscall(
libc::SYS_io_getevents,
self.0,
min_nr,
events.len() as c_long,
events.as_mut_ptr(),
to,
) as c_int
};
if rc < 0 {
Err(Error::last_os_error())
} else {
Ok(rc as usize)
}
}
}
impl Drop for IoContext {
fn drop(&mut self) {
if self.0 != 0 {
let _ = unsafe { libc::syscall(libc::SYS_io_destroy, self.0) as c_int };
}
}
}
#[cfg(test)]
mod test {
use super::*;
use std::fs::File;
use std::os::unix::io::AsRawFd;
#[test]
fn new_context() {
let _ = IoContext::new(0).unwrap_err();
}
#[test]
fn cancel_request() {
let file = File::open("/dev/zero").unwrap();
let ctx = IoContext::new(128).unwrap();
let mut buf: [u8; 16384] = unsafe { std::mem::uninitialized() };
let iocbs = [&mut IoControlBlock {
aio_fildes: file.as_raw_fd() as u32,
aio_lio_opcode: IOCB_CMD_PREAD as u16,
aio_buf: buf.as_mut_ptr() as u64,
aio_nbytes: buf.len() as u64,
..Default::default()
}];
let mut rc = ctx.submit(&iocbs).unwrap();
assert_eq!(rc, 1);
let mut result = Default::default();
let err = ctx
.cancel(iocbs[0], &mut result)
.unwrap_err()
.raw_os_error()
.unwrap();
assert_eq!(err, libc::EINVAL);
let mut events = [unsafe { std::mem::uninitialized::<IoEvent>() }];
rc = ctx.get_events(1, &mut events, None).unwrap();
assert_eq!(rc, 1);
assert!(events[0].res > 0);
}
#[test]
fn read_zero() {
let file = File::open("/dev/zero").unwrap();
let ctx = IoContext::new(128).unwrap();
let mut buf: [u8; 4096] = unsafe { std::mem::uninitialized() };
let iocbs = [
&mut IoControlBlock {
aio_fildes: file.as_raw_fd() as u32,
aio_lio_opcode: IOCB_CMD_PREAD as u16,
aio_buf: buf.as_mut_ptr() as u64,
aio_nbytes: buf.len() as u64,
..Default::default()
},
&mut IoControlBlock {
aio_fildes: file.as_raw_fd() as u32,
aio_lio_opcode: IOCB_CMD_PREAD as u16,
aio_buf: buf.as_mut_ptr() as u64,
aio_nbytes: buf.len() as u64,
..Default::default()
},
];
let mut rc = ctx.submit(&iocbs[..]).unwrap();
assert_eq!(rc, 2);
let mut events = [unsafe { std::mem::uninitialized::<IoEvent>() }];
rc = ctx.get_events(1, &mut events, None).unwrap();
assert_eq!(rc, 1);
assert!(events[0].res > 0);
rc = ctx.get_events(1, &mut events, None).unwrap();
assert_eq!(rc, 1);
assert!(events[0].res > 0);
}
#[test]
fn bindgen_test_layout_io_event() {
assert_eq!(
::std::mem::size_of::<IoEvent>(),
32usize,
concat!("Size of: ", stringify!(IoEvent))
);
assert_eq!(
::std::mem::align_of::<IoEvent>(),
8usize,
concat!("Alignment of", stringify!(IoEvent))
);
}
#[test]
fn bindgen_test_layout_iocb() {
assert_eq!(
::std::mem::size_of::<IoControlBlock>(),
64usize,
concat!("Size of:", stringify!(IoControlBlock))
);
assert_eq!(
::std::mem::align_of::<IoControlBlock>(),
8usize,
concat!("Alignment of", stringify!(IoControlBlock))
);
}
}