blob: e7b56e71f9034d4a284c68d0cd24e1e56e821c7a [file] [log] [blame]
#![cfg(feature = "os-poll")]
#![cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
use mio::{event::Source, Events, Interest, Poll, Registry, Token};
use std::{
fs::File,
io, mem,
os::unix::io::{AsRawFd, RawFd},
pin::Pin,
ptr,
};
mod util;
use util::{expect_events, expect_no_events, init, temp_file, ExpectEvent};
const UDATA: Token = Token(0xdead_beef);
/// A highly feature-incomplete POSIX AIO event source, suitable for testing
/// mio's handling of kqueue's EVFILT_AIO.
struct Aiocb(Pin<Box<libc::aiocb>>);
impl Aiocb {
/// Constructs a new `Aiocb` with no associated data.
///
/// The resulting `Aiocb` structure is suitable for use with `aio_fsync`
pub fn from_fd(fd: RawFd) -> 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 inner = unsafe { mem::zeroed::<libc::aiocb>() };
inner.aio_fildes = fd;
inner.aio_sigevent.sigev_notify = libc::SIGEV_NONE;
Aiocb(Box::pin(inner))
}
/// Constructs a new `Aiocb` suitable for writing to offset 0 of a file.
#[cfg(target_os = "freebsd")]
pub fn from_slice(fd: RawFd, buf: &[u8]) -> Aiocb {
let mut aiocb = Aiocb::from_fd(fd);
aiocb.0.aio_nbytes = buf.len();
aiocb.0.aio_buf = buf.as_ptr() as *mut libc::c_void;
aiocb
}
pub fn fsync(&mut self) -> io::Result<()> {
unsafe {
// Safe because we don't move the libc::aiocb
let selfp = self.0.as_mut().get_unchecked_mut();
match libc::aio_fsync(libc::O_SYNC, selfp) {
0 => Ok(()),
_ => Err(io::Error::last_os_error()),
}
}
}
}
impl Source for Aiocb {
fn register(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
) -> io::Result<()> {
assert!(interests.is_aio());
let udata = usize::from(token);
let kq = registry.as_raw_fd();
self.0.aio_sigevent.sigev_notify = libc::SIGEV_KEVENT;
self.0.aio_sigevent.sigev_signo = kq;
self.0.aio_sigevent.sigev_value.sival_ptr = udata as *mut libc::c_void;
self.0.aio_sigevent.sigev_notify_thread_id = 0;
Ok(())
}
fn reregister(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
) -> io::Result<()> {
self.register(registry, token, interests)
}
fn deregister(&mut self, _registry: &Registry) -> io::Result<()> {
self.0.aio_sigevent.sigev_notify = libc::SIGEV_NONE;
self.0.aio_sigevent.sigev_value.sival_ptr = ptr::null_mut();
Ok(())
}
}
#[cfg(target_os = "freebsd")]
struct Liocb {
_aiocbs: Box<[Aiocb]>,
/// The actual list passed to `libc::lio_listio`.
///
/// It must live for as long as any of the operations are still being
/// processesed, because the aio subsystem uses its address as a unique
/// identifier.
list: Box<[*mut libc::aiocb]>,
sev: libc::sigevent,
}
#[cfg(target_os = "freebsd")]
impl Liocb {
fn listio(&mut self) -> io::Result<()> {
unsafe {
let r = libc::lio_listio(
libc::LIO_NOWAIT,
self.list.as_ptr(),
self.list.len() as i32,
&mut self.sev as *mut libc::sigevent,
);
match r {
0 => Ok(()),
_ => Err(io::Error::last_os_error()),
}
}
}
fn new(inputs: Vec<Aiocb>) -> Liocb {
let mut aiocbs = inputs.into_boxed_slice();
for aiocb in aiocbs.iter_mut() {
aiocb.0.aio_lio_opcode = libc::LIO_WRITE;
}
let list = aiocbs
.iter_mut()
.map(|aiocb| &mut *aiocb.0 as *mut libc::aiocb)
.collect::<Vec<_>>()
.into_boxed_slice();
let sev = unsafe { mem::zeroed::<libc::sigevent>() };
Liocb {
_aiocbs: aiocbs,
list,
sev,
}
}
}
#[cfg(target_os = "freebsd")]
impl Source for Liocb {
fn register(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
) -> io::Result<()> {
assert!(interests.is_lio());
let udata = usize::from(token);
let kq = registry.as_raw_fd();
self.sev.sigev_notify = libc::SIGEV_KEVENT;
self.sev.sigev_signo = kq;
self.sev.sigev_value.sival_ptr = udata as *mut libc::c_void;
self.sev.sigev_notify_thread_id = 0;
Ok(())
}
fn reregister(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
) -> io::Result<()> {
self.register(registry, token, interests)
}
fn deregister(&mut self, _registry: &Registry) -> io::Result<()> {
self.sev.sigev_notify = libc::SIGEV_NONE;
self.sev.sigev_value.sival_ptr = ptr::null_mut();
Ok(())
}
}
mod aio {
use super::*;
#[test]
fn smoke() {
init();
let mut poll = Poll::new().unwrap();
let mut events = Events::with_capacity(8);
let f = File::create(temp_file("aio::smoke")).unwrap();
let mut aiocb = Aiocb::from_fd(f.as_raw_fd());
poll.registry()
.register(&mut aiocb, UDATA, Interest::AIO)
.unwrap();
expect_no_events(&mut poll, &mut events);
aiocb.fsync().unwrap();
expect_events(
&mut poll,
&mut events,
vec![ExpectEvent::new(UDATA, Interest::AIO)],
);
}
}
#[cfg(target_os = "freebsd")]
mod lio {
use super::*;
#[test]
fn smoke() {
init();
let data = b"hello, world!\n";
let mut poll = Poll::new().unwrap();
let mut events = Events::with_capacity(8);
let f0 = File::create(temp_file("lio::smoke0")).unwrap();
let f1 = File::create(temp_file("lio::smoke1")).unwrap();
let aiocb0 = Aiocb::from_slice(f0.as_raw_fd(), data);
let aiocb1 = Aiocb::from_slice(f1.as_raw_fd(), data);
let mut liocb = Liocb::new(vec![aiocb0, aiocb1]);
poll.registry()
.register(&mut liocb, UDATA, Interest::LIO)
.unwrap();
expect_no_events(&mut poll, &mut events);
liocb.listio().unwrap();
expect_events(
&mut poll,
&mut events,
vec![ExpectEvent::new(UDATA, Interest::LIO)],
);
}
}