| #![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)], |
| ); |
| } |
| } |