// vim: tw=80 //! POSIX Asynchronous I/O //! //! The POSIX AIO interface is used for asynchronous I/O on files and disk-like //! devices. It supports [`read`](struct.AioRead.html#method.new), //! [`write`](struct.AioWrite.html#method.new), //! [`fsync`](struct.AioFsync.html#method.new), //! [`readv`](struct.AioReadv.html#method.new), and //! [`writev`](struct.AioWritev.html#method.new), operations, subject to //! platform support. Completion //! notifications can optionally be delivered via //! [signals](../signal/enum.SigevNotify.html#variant.SigevSignal), via the //! [`aio_suspend`](fn.aio_suspend.html) function, or via polling. Some //! platforms support other completion //! notifications, such as //! [kevent](../signal/enum.SigevNotify.html#variant.SigevKevent). //! //! Multiple operations may be submitted in a batch with //! [`lio_listio`](fn.lio_listio.html), though the standard does not guarantee //! that they will be executed atomically. //! //! Outstanding operations may be cancelled with //! [`cancel`](trait.Aio.html#method.cancel) or //! [`aio_cancel_all`](fn.aio_cancel_all.html), though the operating system may //! not support this for all filesystems and devices. #[cfg(target_os = "freebsd")] use std::io::{IoSlice, IoSliceMut}; use std::{
convert::TryFrom,
fmt::{self, Debug},
marker::{PhantomData, PhantomPinned},
mem,
os::unix::io::{AsFd, AsRawFd, BorrowedFd},
pin::Pin,
ptr, thread,
};
libc_enum! { /// Mode for `AioCb::fsync`. Controls whether only data or both data and /// metadata are synced. #[repr(i32)] #[non_exhaustive] pubenum AioFsyncMode { /// do it like `fsync`
O_SYNC, /// on supported operating systems only, do it like `fdatasync` #[cfg(any(apple_targets,
target_os = "linux",
target_os = "freebsd",
netbsdlike))]
O_DSYNC
} impl TryFrom<i32>
}
libc_enum! { /// Mode for [`lio_listio`](fn.lio_listio.html) #[repr(i32)] pubenum LioMode { /// Requests that [`lio_listio`](fn.lio_listio.html) block until all /// requested operations have been completed
LIO_WAIT, /// Requests that [`lio_listio`](fn.lio_listio.html) return immediately
LIO_NOWAIT,
}
}
/// Return values for [`AioCb::cancel`](struct.AioCb.html#method.cancel) and /// [`aio_cancel_all`](fn.aio_cancel_all.html) #[repr(i32)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pubenum AioCancelStat { /// All outstanding requests were canceled
AioCanceled = libc::AIO_CANCELED, /// Some requests were not canceled. Their status should be checked with /// `AioCb::error`
AioNotCanceled = libc::AIO_NOTCANCELED, /// All of the requests have already finished
AioAllDone = libc::AIO_ALLDONE,
}
/// Newtype that adds Send and Sync to libc::aiocb, which contains raw pointers #[repr(transparent)] struct LibcAiocb(libc::aiocb);
unsafeimpl Send for LibcAiocb {} unsafeimpl Sync for LibcAiocb {}
/// Base class for all AIO operations. Should only be used directly when /// checking for completion. // We could create some kind of AsPinnedMut trait, and implement it for all aio // ops, allowing the crate's users to get pinned references to `AioCb`. That // could save some code for things like polling methods. But IMHO it would // provide polymorphism at the wrong level. Instead, the best place for // polymorphism is at the level of `Futures`. #[repr(C)] struct AioCb<'a> {
aiocb: LibcAiocb, /// Could this `AioCb` potentially have any in-kernel state? // It would be really nice to perform the in-progress check entirely at // compile time. But I can't figure out how, because: // * Future::poll takes a `Pin<&mut self>` rather than `self`, and // * Rust's lack of an equivalent of C++'s Guaranteed Copy Elision means // that there's no way to write an AioCb constructor that neither boxes // the object itself, nor moves it during return.
in_progress: bool,
_fd: PhantomData<BorrowedFd<'a>>,
}
fn aio_return(mutself: Pin<&mutSelf>) -> Result<usize> { self.in_progress = false; unsafe { let p: *mut libc::aiocb = &mutself.aiocb.0;
Errno::result(libc::aio_return(p))
}
.map(|r| r as usize)
}
fn cancel(mutself: Pin<&mutSelf>) -> Result<AioCancelStat> { let r = unsafe {
libc::aio_cancel(self.aiocb.0.aio_fildes, &mutself.aiocb.0)
}; match r {
libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
-1 => Err(Errno::last()),
_ => panic!("unknown aio_cancel return value"),
}
}
fn common_init(
fd: BorrowedFd<'a>,
prio: i32,
sigev_notify: SigevNotify,
) -> Self { // 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. letmut a = unsafe { mem::zeroed::<libc::aiocb>() };
a.aio_fildes = fd.as_raw_fd();
a.aio_reqprio = prio;
a.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
AioCb {
aiocb: LibcAiocb(a),
in_progress: false,
_fd: PhantomData,
}
}
fn error(self: Pin<&mutSelf>) -> Result<()> { let r = unsafe { libc::aio_error(&self.aiocb().0) }; match r { 0 => Ok(()),
num if num > 0 => Err(Errno::from_raw(num)),
-1 => Err(Errno::last()),
num => panic!("unknown aio_error return value {num:?}"),
}
}
/// Update the notification settings for an existing AIO operation that has /// not yet been submitted. // Takes a normal reference rather than a pinned one because this method is // normally called before the object needs to be pinned, that is, before // it's been submitted to the kernel. fn set_sigev_notify(&mutself, sigev_notify: SigevNotify) {
assert!(
!self.in_progress, "Can't change notification settings for an in-progress operation"
); self.aiocb.0.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
}
}
impl<'a> Drop for AioCb<'a> { /// If the `AioCb` has no remaining state in the kernel, just drop it. /// Otherwise, dropping constitutes a resource leak, which is an error fn drop(&mutself) {
assert!(
thread::panicking() || !self.in_progress, "Dropped an in-progress AioCb"
);
}
}
/// Methods common to all AIO operations pubtrait Aio { /// The return type of [`Aio::aio_return`]. type Output;
/// Retrieve return status of an asynchronous operation. /// /// Should only be called once for each operation, after [`Aio::error`] /// indicates that it has completed. The result is the same as for the /// synchronous `read(2)`, `write(2)`, of `fsync(2)` functions. /// /// # References /// /// [aio_return](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_return.html) fn aio_return(self: Pin<&mutSelf>) -> Result<Self::Output>;
/// Cancels an outstanding AIO request. /// /// The operating system is not required to implement cancellation for all /// file and device types. Even if it does, there is no guarantee that the /// operation has not already completed. So the caller must check the /// result and handle operations that were not canceled or that have already /// completed. /// /// # Examples /// /// Cancel an outstanding aio operation. Note that we must still call /// `aio_return` to free resources, even though we don't care about the /// result. /// /// ``` /// # use nix::errno::Errno; /// # use nix::Error; /// # use nix::sys::aio::*; /// # use nix::sys::signal::SigevNotify; /// # use std::{thread, time}; /// # use std::io::Write; /// # use std::os::unix::io::AsFd; /// # use tempfile::tempfile; /// let wbuf = b"CDEF"; /// let mut f = tempfile().unwrap(); /// let mut aiocb = Box::pin(AioWrite::new(f.as_fd(), /// 2, //offset /// &wbuf[..], /// 0, //priority /// SigevNotify::SigevNone)); /// aiocb.as_mut().submit().unwrap(); /// let cs = aiocb.as_mut().cancel().unwrap(); /// if cs == AioCancelStat::AioNotCanceled { /// while (aiocb.as_mut().error() == Err(Errno::EINPROGRESS)) { /// thread::sleep(time::Duration::from_millis(10)); /// } /// } /// // Must call `aio_return`, but ignore the result /// let _ = aiocb.as_mut().aio_return(); /// ``` /// /// # References /// /// [aio_cancel](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html) fn cancel(self: Pin<&mutSelf>) -> Result<AioCancelStat>;
/// Retrieve error status of an asynchronous operation. /// /// If the request has not yet completed, returns `EINPROGRESS`. Otherwise, /// returns `Ok` or any other error. /// /// # Examples /// /// Issue an aio operation and use `error` to poll for completion. Polling /// is an alternative to `aio_suspend`, used by most of the other examples. /// /// ``` /// # use nix::errno::Errno; /// # use nix::Error; /// # use nix::sys::aio::*; /// # use nix::sys::signal::SigevNotify; /// # use std::{thread, time}; /// # use std::os::unix::io::AsFd; /// # use tempfile::tempfile; /// const WBUF: &[u8] = b"abcdef123456"; /// let mut f = tempfile().unwrap(); /// let mut aiocb = Box::pin(AioWrite::new(f.as_fd(), /// 2, //offset /// WBUF, /// 0, //priority /// SigevNotify::SigevNone)); /// aiocb.as_mut().submit().unwrap(); /// while (aiocb.as_mut().error() == Err(Errno::EINPROGRESS)) { /// thread::sleep(time::Duration::from_millis(10)); /// } /// assert_eq!(aiocb.as_mut().aio_return().unwrap(), WBUF.len()); /// ``` /// /// # References /// /// [aio_error](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_error.html) fn error(self: Pin<&mutSelf>) -> Result<()>;
/// Returns the underlying file descriptor associated with the operation. fn fd(&self) -> BorrowedFd;
/// Does this operation currently have any in-kernel state? /// /// Dropping an operation that does have in-kernel state constitutes a /// resource leak. /// /// # Examples /// /// ``` /// # use nix::errno::Errno; /// # use nix::Error; /// # use nix::sys::aio::*; /// # use nix::sys::signal::SigevNotify::SigevNone; /// # use std::{thread, time}; /// # use std::os::unix::io::AsFd; /// # use tempfile::tempfile; /// let f = tempfile().unwrap(); /// let mut aiof = Box::pin(AioFsync::new(f.as_fd(), AioFsyncMode::O_SYNC, /// 0, SigevNone)); /// assert!(!aiof.as_mut().in_progress()); /// aiof.as_mut().submit().expect("aio_fsync failed early"); /// assert!(aiof.as_mut().in_progress()); /// while (aiof.as_mut().error() == Err(Errno::EINPROGRESS)) { /// thread::sleep(time::Duration::from_millis(10)); /// } /// aiof.as_mut().aio_return().expect("aio_fsync failed late"); /// assert!(!aiof.as_mut().in_progress()); /// ``` fn in_progress(&self) -> bool;
/// Returns the priority of the `AioCb` fn priority(&self) -> i32;
/// Update the notification settings for an existing AIO operation that has /// not yet been submitted. fn set_sigev_notify(&mutself, sev: SigevNotify);
/// Returns the `SigEvent` that will be used for notification. fn sigevent(&self) -> SigEvent;
/// Actually start the I/O operation. /// /// After calling this method and until [`Aio::aio_return`] returns `Ok`, /// the structure may not be moved in memory. fn submit(self: Pin<&mutSelf>) -> Result<()>;
}
fn fd(&self) -> BorrowedFd<'a> { // safe because self's lifetime is the same as the original file // descriptor. unsafe { BorrowedFd::borrow_raw(self.aiocb.aiocb.0.aio_fildes) }
}
/// Returns the operation's fsync mode: data and metadata or data only? pubfn mode(&self) -> AioFsyncMode {
AioFsyncMode::try_from(self.aiocb.aiocb.0.aio_lio_opcode).unwrap()
}
/// Create a new `AioFsync`. /// /// # Arguments /// /// * `fd`: File descriptor to sync. /// * `mode`: Whether to sync file metadata too, or just data. /// * `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. pubfn new(
fd: BorrowedFd<'a>,
mode: AioFsyncMode,
prio: i32,
sigev_notify: SigevNotify,
) -> Self { letmut aiocb = AioCb::common_init(fd, prio, sigev_notify); // To save some memory, store mode in an unused field of the AioCb. // True it isn't very much memory, but downstream creates will likely // create an enum containing this and other AioCb variants and pack // those enums into data structures like Vec, so it adds up.
aiocb.aiocb.0.aio_lio_opcode = mode as libc::c_int;
AioFsync {
aiocb,
_pin: PhantomPinned,
}
}
}
/// Returns the requested length of the aio operation in bytes /// /// This method returns the *requested* length of the operation. To get the /// number of bytes actually read or written by a completed operation, use /// `aio_return` instead. pubfn nbytes(&self) -> usize { self.aiocb.aiocb.0.aio_nbytes
}
/// Create a new `AioRead`, placing the data in a mutable slice. /// /// # Arguments /// /// * `fd`: File descriptor to read from /// * `offs`: File offset /// * `buf`: A memory buffer. It must outlive the `AioRead`. /// * `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. pubfn new(
fd: BorrowedFd<'a>,
offs: off_t,
buf: &'a mut [u8],
prio: i32,
sigev_notify: SigevNotify,
) -> Self { letmut aiocb = AioCb::common_init(fd, prio, sigev_notify);
aiocb.aiocb.0.aio_nbytes = buf.len();
aiocb.aiocb.0.aio_buf = buf.as_mut_ptr().cast();
aiocb.aiocb.0.aio_lio_opcode = libc::LIO_READ;
aiocb.aiocb.0.aio_offset = offs;
AioRead {
aiocb,
_data: PhantomData,
_pin: PhantomPinned,
}
}
/// Returns the file offset of the operation. pubfn offset(&self) -> off_t { self.aiocb.aiocb.0.aio_offset
}
}
impl<'a> Aio for AioRead<'a> { type Output = usize;
/// Returns the number of buffers the operation will read into. pubfn iovlen(&self) -> usize { self.aiocb.aiocb.0.aio_nbytes
}
/// Create a new `AioReadv`, placing the data in a list of mutable slices. /// /// # Arguments /// /// * `fd`: File descriptor to read from /// * `offs`: File offset /// * `bufs`: A scatter/gather list of memory buffers. They must /// outlive the `AioReadv`. /// * `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. pubfn new(
fd: BorrowedFd<'a>,
offs: off_t,
bufs: &mut [IoSliceMut<'a>],
prio: i32,
sigev_notify: SigevNotify,
) -> Self { letmut aiocb = AioCb::common_init(fd, prio, sigev_notify); // In vectored mode, aio_nbytes stores the length of the iovec array, // not the byte count.
aiocb.aiocb.0.aio_nbytes = bufs.len();
aiocb.aiocb.0.aio_buf = bufs.as_mut_ptr().cast();
aiocb.aiocb.0.aio_lio_opcode = libc::LIO_READV;
aiocb.aiocb.0.aio_offset = offs;
AioReadv {
aiocb,
_data: PhantomData,
_pin: PhantomPinned,
}
}
/// Returns the file offset of the operation. pubfn offset(&self) -> off_t { self.aiocb.aiocb.0.aio_offset
}
}
#[cfg(target_os = "freebsd")] impl<'a> Aio for AioReadv<'a> { type Output = usize;
/// Returns the requested length of the aio operation in bytes /// /// This method returns the *requested* length of the operation. To get the /// number of bytes actually read or written by a completed operation, use /// `aio_return` instead. pubfn nbytes(&self) -> usize { self.aiocb.aiocb.0.aio_nbytes
}
/// Construct a new `AioWrite`. /// /// # Arguments /// /// * `fd`: File descriptor to write to /// * `offs`: File offset /// * `buf`: A memory buffer. It must outlive the `AioWrite`. /// * `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. pubfn new(
fd: BorrowedFd<'a>,
offs: off_t,
buf: &'a [u8],
prio: i32,
sigev_notify: SigevNotify,
) -> Self { letmut aiocb = AioCb::common_init(fd, prio, sigev_notify);
aiocb.aiocb.0.aio_nbytes = buf.len(); // casting an immutable buffer to a mutable pointer looks unsafe, // but technically its only unsafe to dereference it, not to create // it. Type Safety guarantees that we'll never pass aiocb to // aio_read or aio_readv.
aiocb.aiocb.0.aio_buf = buf.as_ptr().cast_mut().cast();
aiocb.aiocb.0.aio_lio_opcode = libc::LIO_WRITE;
aiocb.aiocb.0.aio_offset = offs;
AioWrite {
aiocb,
_data: PhantomData,
_pin: PhantomPinned,
}
}
/// Returns the file offset of the operation. pubfn offset(&self) -> off_t { self.aiocb.aiocb.0.aio_offset
}
}
impl<'a> Aio for AioWrite<'a> { type Output = usize;
/// Returns the number of buffers the operation will read into. pubfn iovlen(&self) -> usize { self.aiocb.aiocb.0.aio_nbytes
}
/// Construct a new `AioWritev`. /// /// # Arguments /// /// * `fd`: File descriptor to write to /// * `offs`: File offset /// * `bufs`: A scatter/gather list of memory buffers. They must /// outlive the `AioWritev`. /// * `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. pubfn new(
fd: BorrowedFd<'a>,
offs: off_t,
bufs: &[IoSlice<'a>],
prio: i32,
sigev_notify: SigevNotify,
) -> Self { letmut aiocb = AioCb::common_init(fd, prio, sigev_notify); // In vectored mode, aio_nbytes stores the length of the iovec array, // not the byte count.
aiocb.aiocb.0.aio_nbytes = bufs.len(); // casting an immutable buffer to a mutable pointer looks unsafe, // but technically its only unsafe to dereference it, not to create // it. Type Safety guarantees that we'll never pass aiocb to // aio_read or aio_readv.
aiocb.aiocb.0.aio_buf = bufs.as_ptr().cast_mut().cast();
aiocb.aiocb.0.aio_lio_opcode = libc::LIO_WRITEV;
aiocb.aiocb.0.aio_offset = offs;
AioWritev {
aiocb,
_data: PhantomData,
_pin: PhantomPinned,
}
}
/// Returns the file offset of the operation. pubfn offset(&self) -> off_t { self.aiocb.aiocb.0.aio_offset
}
}
#[cfg(target_os = "freebsd")] impl<'a> Aio for AioWritev<'a> { type Output = usize;
/// Cancels outstanding AIO requests for a given file descriptor. /// /// # Examples /// /// Issue an aio operation, then cancel all outstanding operations on that file /// descriptor. /// /// ``` /// # use nix::errno::Errno; /// # use nix::Error; /// # use nix::sys::aio::*; /// # use nix::sys::signal::SigevNotify; /// # use std::{thread, time}; /// # use std::io::Write; /// # use std::os::unix::io::AsFd; /// # use tempfile::tempfile; /// let wbuf = b"CDEF"; /// let mut f = tempfile().unwrap(); /// let mut aiocb = Box::pin(AioWrite::new(f.as_fd(), /// 2, //offset /// &wbuf[..], /// 0, //priority /// SigevNotify::SigevNone)); /// aiocb.as_mut().submit().unwrap(); /// let cs = aio_cancel_all(f.as_fd()).unwrap(); /// if cs == AioCancelStat::AioNotCanceled { /// while (aiocb.as_mut().error() == Err(Errno::EINPROGRESS)) { /// thread::sleep(time::Duration::from_millis(10)); /// } /// } /// // Must call `aio_return`, but ignore the result /// let _ = aiocb.as_mut().aio_return(); /// ``` /// /// # References /// /// [`aio_cancel`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html) pubfn aio_cancel_all<F: AsFd>(fd: F) -> Result<AioCancelStat> { matchunsafe { libc::aio_cancel(fd.as_fd().as_raw_fd(), ptr::null_mut()) } {
libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
-1 => Err(Errno::last()),
_ => panic!("unknown aio_cancel return value"),
}
}
/// Suspends the calling process until at least one of the specified operations /// have completed, a signal is delivered, or the timeout has passed. /// /// If `timeout` is `None`, `aio_suspend` will block indefinitely. /// /// # Examples /// /// Use `aio_suspend` to block until an aio operation completes. /// /// ``` /// # use nix::sys::aio::*; /// # use nix::sys::signal::SigevNotify; /// # use std::os::unix::io::AsFd; /// # use tempfile::tempfile; /// const WBUF: &[u8] = b"abcdef123456"; /// let mut f = tempfile().unwrap(); /// let mut aiocb = Box::pin(AioWrite::new(f.as_fd(), /// 2, //offset /// WBUF, /// 0, //priority /// SigevNotify::SigevNone)); /// aiocb.as_mut().submit().unwrap(); /// aio_suspend(&[&*aiocb], None).expect("aio_suspend failed"); /// assert_eq!(aiocb.as_mut().aio_return().unwrap(), WBUF.len()); /// ``` /// # References /// /// [`aio_suspend`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_suspend.html) pubfn aio_suspend(
list: &[&dyn AsRef<libc::aiocb>],
timeout: Option<TimeSpec>,
) -> Result<()> { // Note that this allocation could be eliminated by making the argument // generic, and accepting arguments like &[AioWrite]. But that would // prevent using aio_suspend to wait on a heterogeneous list of mixed // operations. let v = list
.iter()
.map(|x| x.as_ref() as *const libc::aiocb)
.collect::<Vec<*const libc::aiocb>>(); let p = v.as_ptr(); let timep = match timeout {
None => ptr::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)
}
/// Submits multiple asynchronous I/O requests with a single system call. /// /// They are not guaranteed to complete atomically, and the order in which the /// requests are carried out is not specified. Reads, and writes may be freely /// mixed. /// /// # Examples /// /// Use `lio_listio` to submit an aio operation and wait for its completion. In /// this case, there is no need to use aio_suspend to wait or `error` to poll. /// This mode is useful for otherwise-synchronous programs that want to execute /// a handful of I/O operations in parallel. /// ``` /// # use std::os::unix::io::AsFd; /// # use nix::sys::aio::*; /// # use nix::sys::signal::SigevNotify; /// # use tempfile::tempfile; /// const WBUF: &[u8] = b"abcdef123456"; /// let mut f = tempfile().unwrap(); /// let mut aiow = Box::pin(AioWrite::new( /// f.as_fd(), /// 2, // offset /// WBUF, /// 0, // priority /// SigevNotify::SigevNone /// )); /// lio_listio(LioMode::LIO_WAIT, &mut[aiow.as_mut()], SigevNotify::SigevNone) /// .unwrap(); /// // At this point, we are guaranteed that aiow is complete. /// assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len()); /// ``` /// /// Use `lio_listio` to submit multiple asynchronous operations with a single /// syscall, but receive notification individually. This is an efficient /// technique for reducing overall context-switch overhead, especially when /// combined with kqueue. /// ``` /// # use std::os::unix::io::AsFd; /// # use std::thread; /// # use std::time; /// # use nix::errno::Errno; /// # use nix::sys::aio::*; /// # use nix::sys::signal::SigevNotify; /// # use tempfile::tempfile; /// const WBUF: &[u8] = b"abcdef123456"; /// let mut f = tempfile().unwrap(); /// let mut aiow = Box::pin(AioWrite::new( /// f.as_fd(), /// 2, // offset /// WBUF, /// 0, // priority /// SigevNotify::SigevNone /// )); /// lio_listio(LioMode::LIO_NOWAIT, &mut[aiow.as_mut()], SigevNotify::SigevNone) /// .unwrap(); /// // We must wait for the completion of each individual operation /// while (aiow.as_mut().error() == Err(Errno::EINPROGRESS)) { /// thread::sleep(time::Duration::from_millis(10)); /// } /// assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len()); /// ``` /// /// Use `lio_listio` to submit multiple operations, and receive notification /// only when all of them are complete. This can be useful when there is some /// logical relationship between the operations. But beware! Errors or system /// resource limitations may cause `lio_listio` to return `EIO`, `EAGAIN`, or /// `EINTR`, in which case some but not all operations may have been submitted. /// In that case, you must check the status of each individual operation, and /// possibly resubmit some. /// ``` /// # use libc::c_int; /// # use std::os::unix::io::AsFd; /// # use std::sync::atomic::{AtomicBool, Ordering}; /// # use std::thread; /// # use std::time; /// # use nix::errno::Errno; /// # use nix::sys::aio::*; /// # use nix::sys::signal::*; /// # use tempfile::tempfile; /// pub static SIGNALED: AtomicBool = AtomicBool::new(false); /// /// extern fn sigfunc(_: c_int) { /// SIGNALED.store(true, Ordering::Relaxed); /// } /// let sa = SigAction::new(SigHandler::Handler(sigfunc), /// SaFlags::SA_RESETHAND, /// SigSet::empty()); /// SIGNALED.store(false, Ordering::Relaxed); /// unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap(); /// /// const WBUF: &[u8] = b"abcdef123456"; /// let mut f = tempfile().unwrap(); /// let mut aiow = Box::pin(AioWrite::new( /// f.as_fd(), /// 2, // offset /// WBUF, /// 0, // priority /// SigevNotify::SigevNone /// )); /// let sev = SigevNotify::SigevSignal { signal: Signal::SIGUSR2, si_value: 0 }; /// lio_listio(LioMode::LIO_NOWAIT, &mut[aiow.as_mut()], sev).unwrap(); /// while !SIGNALED.load(Ordering::Relaxed) { /// thread::sleep(time::Duration::from_millis(10)); /// } /// // At this point, since `lio_listio` returned success and delivered its /// // notification, we know that all operations are complete. /// assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len()); /// ``` #[deprecated(
since = "0.27.0",
note = "https://github.com/nix-rust/nix/issues/2017"
)] pubfn lio_listio(
mode: LioMode,
list: &mut [Pin<&mutdyn AsMut<libc::aiocb>>],
sigev_notify: SigevNotify,
) -> Result<()> { let p = list as *mut [Pin<&mutdyn AsMut<libc::aiocb>>] as *mut [*mut libc::aiocb] as *mut *mut libc::aiocb; let sigev = SigEvent::new(sigev_notify); let sigevp = &mut sigev.sigevent() as *mut libc::sigevent;
Errno::result(unsafe {
libc::lio_listio(mode as i32, p, list.len() as i32, sigevp)
})
.map(drop)
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.33 Sekunden
(vorverarbeitet am 2026-06-18)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.