nix/sys/
aio.rs

1// vim: tw=80
2//! POSIX Asynchronous I/O
3//!
4//! The POSIX AIO interface is used for asynchronous I/O on files and disk-like
5//! devices.  It supports [`read`](struct.AioCb.html#method.read),
6//! [`write`](struct.AioCb.html#method.write), and
7//! [`fsync`](struct.AioCb.html#method.fsync) operations.  Completion
8//! notifications can optionally be delivered via
9//! [signals](../signal/enum.SigevNotify.html#variant.SigevSignal), via the
10//! [`aio_suspend`](fn.aio_suspend.html) function, or via polling.  Some
11//! platforms support other completion
12//! notifications, such as
13//! [kevent](../signal/enum.SigevNotify.html#variant.SigevKevent).
14//!
15//! Multiple operations may be submitted in a batch with
16//! [`lio_listio`](fn.lio_listio.html), though the standard does not guarantee
17//! that they will be executed atomically.
18//!
19//! Outstanding operations may be cancelled with
20//! [`cancel`](struct.AioCb.html#method.cancel) or
21//! [`aio_cancel_all`](fn.aio_cancel_all.html), though the operating system may
22//! not support this for all filesystems and devices.
23
24use crate::Result;
25use crate::errno::Errno;
26use std::os::unix::io::RawFd;
27use libc::{c_void, off_t, size_t};
28use std::fmt;
29use std::fmt::Debug;
30use std::marker::PhantomData;
31use std::mem;
32use std::pin::Pin;
33use std::ptr::{null, null_mut};
34use crate::sys::signal::*;
35use std::thread;
36use crate::sys::time::TimeSpec;
37
38libc_enum! {
39    /// Mode for `AioCb::fsync`.  Controls whether only data or both data and
40    /// metadata are synced.
41    #[repr(i32)]
42    #[non_exhaustive]
43    pub enum AioFsyncMode {
44        /// do it like `fsync`
45        O_SYNC,
46        /// on supported operating systems only, do it like `fdatasync`
47        #[cfg(any(target_os = "ios",
48                  target_os = "linux",
49                  target_os = "macos",
50                  target_os = "netbsd",
51                  target_os = "openbsd"))]
52        #[cfg_attr(docsrs, doc(cfg(all())))]
53        O_DSYNC
54    }
55}
56
57libc_enum! {
58    /// When used with [`lio_listio`](fn.lio_listio.html), determines whether a
59    /// given `aiocb` should be used for a read operation, a write operation, or
60    /// ignored.  Has no effect for any other aio functions.
61    #[repr(i32)]
62    #[non_exhaustive]
63    pub enum LioOpcode {
64        /// No operation
65        LIO_NOP,
66        /// Write data as if by a call to [`AioCb::write`]
67        LIO_WRITE,
68        /// Write data as if by a call to [`AioCb::read`]
69        LIO_READ,
70    }
71}
72
73libc_enum! {
74    /// Mode for [`lio_listio`](fn.lio_listio.html)
75    #[repr(i32)]
76    pub enum LioMode {
77        /// Requests that [`lio_listio`](fn.lio_listio.html) block until all
78        /// requested operations have been completed
79        LIO_WAIT,
80        /// Requests that [`lio_listio`](fn.lio_listio.html) return immediately
81        LIO_NOWAIT,
82    }
83}
84
85/// Return values for [`AioCb::cancel`](struct.AioCb.html#method.cancel) and
86/// [`aio_cancel_all`](fn.aio_cancel_all.html)
87#[repr(i32)]
88#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
89pub enum AioCancelStat {
90    /// All outstanding requests were canceled
91    AioCanceled = libc::AIO_CANCELED,
92    /// Some requests were not canceled.  Their status should be checked with
93    /// `AioCb::error`
94    AioNotCanceled = libc::AIO_NOTCANCELED,
95    /// All of the requests have already finished
96    AioAllDone = libc::AIO_ALLDONE,
97}
98
99/// Newtype that adds Send and Sync to libc::aiocb, which contains raw pointers
100#[repr(transparent)]
101struct LibcAiocb(libc::aiocb);
102
103unsafe impl Send for LibcAiocb {}
104unsafe impl Sync for LibcAiocb {}
105
106/// AIO Control Block.
107///
108/// The basic structure used by all aio functions.  Each `AioCb` represents one
109/// I/O request.
110pub struct AioCb<'a> {
111    aiocb: LibcAiocb,
112    /// Tracks whether the buffer pointed to by `libc::aiocb.aio_buf` is mutable
113    mutable: bool,
114    /// Could this `AioCb` potentially have any in-kernel state?
115    in_progress: bool,
116    _buffer: std::marker::PhantomData<&'a [u8]>,
117    _pin: std::marker::PhantomPinned
118}
119
120impl<'a> AioCb<'a> {
121    /// Returns the underlying file descriptor associated with the `AioCb`
122    pub fn fd(&self) -> RawFd {
123        self.aiocb.0.aio_fildes
124    }
125
126    /// Constructs a new `AioCb` with no associated buffer.
127    ///
128    /// The resulting `AioCb` structure is suitable for use with `AioCb::fsync`.
129    ///
130    /// # Parameters
131    ///
132    /// * `fd`:           File descriptor.  Required for all aio functions.
133    /// * `prio`:         If POSIX Prioritized IO is supported, then the
134    ///                   operation will be prioritized at the process's
135    ///                   priority level minus `prio`.
136    /// * `sigev_notify`: Determines how you will be notified of event
137    ///                    completion.
138    ///
139    /// # Examples
140    ///
141    /// Create an `AioCb` from a raw file descriptor and use it for an
142    /// [`fsync`](#method.fsync) operation.
143    ///
144    /// ```
145    /// # use nix::errno::Errno;
146    /// # use nix::Error;
147    /// # use nix::sys::aio::*;
148    /// # use nix::sys::signal::SigevNotify::SigevNone;
149    /// # use std::{thread, time};
150    /// # use std::os::unix::io::AsRawFd;
151    /// # use tempfile::tempfile;
152    /// let f = tempfile().unwrap();
153    /// let mut aiocb = AioCb::from_fd( f.as_raw_fd(), 0, SigevNone);
154    /// aiocb.fsync(AioFsyncMode::O_SYNC).expect("aio_fsync failed early");
155    /// while (aiocb.error() == Err(Errno::EINPROGRESS)) {
156    ///     thread::sleep(time::Duration::from_millis(10));
157    /// }
158    /// aiocb.aio_return().expect("aio_fsync failed late");
159    /// ```
160    pub fn from_fd(fd: RawFd, prio: libc::c_int,
161                    sigev_notify: SigevNotify) -> Pin<Box<AioCb<'a>>> {
162        let mut a = AioCb::common_init(fd, prio, sigev_notify);
163        a.0.aio_offset = 0;
164        a.0.aio_nbytes = 0;
165        a.0.aio_buf = null_mut();
166
167        Box::pin(AioCb {
168            aiocb: a,
169            mutable: false,
170            in_progress: false,
171            _buffer: PhantomData,
172            _pin: std::marker::PhantomPinned
173        })
174    }
175
176    // Private helper
177    #[cfg(not(any(target_os = "ios", target_os = "macos")))]
178    fn from_mut_slice_unpinned(fd: RawFd, offs: off_t, buf: &'a mut [u8],
179                          prio: libc::c_int, sigev_notify: SigevNotify,
180                          opcode: LioOpcode) -> AioCb<'a>
181    {
182        let mut a = AioCb::common_init(fd, prio, sigev_notify);
183        a.0.aio_offset = offs;
184        a.0.aio_nbytes = buf.len() as size_t;
185        a.0.aio_buf = buf.as_ptr() as *mut c_void;
186        a.0.aio_lio_opcode = opcode as libc::c_int;
187
188        AioCb {
189            aiocb: a,
190            mutable: true,
191            in_progress: false,
192            _buffer: PhantomData,
193            _pin: std::marker::PhantomPinned
194        }
195    }
196
197    /// Constructs a new `AioCb` from a mutable slice.
198    ///
199    /// The resulting `AioCb` will be suitable for both read and write
200    /// operations, but only if the borrow checker can guarantee that the slice
201    /// will outlive the `AioCb`.  That will usually be the case if the `AioCb`
202    /// is stack-allocated.
203    ///
204    /// # Parameters
205    ///
206    /// * `fd`:           File descriptor.  Required for all aio functions.
207    /// * `offs`:         File offset
208    /// * `buf`:          A memory buffer
209    /// * `prio`:         If POSIX Prioritized IO is supported, then the
210    ///                   operation will be prioritized at the process's
211    ///                   priority level minus `prio`
212    /// * `sigev_notify`: Determines how you will be notified of event
213    ///                   completion.
214    /// * `opcode`:       This field is only used for `lio_listio`.  It
215    ///                   determines which operation to use for this individual
216    ///                   aiocb
217    ///
218    /// # Examples
219    ///
220    /// Create an `AioCb` from a mutable slice and read into it.
221    ///
222    /// ```
223    /// # use nix::errno::Errno;
224    /// # use nix::Error;
225    /// # use nix::sys::aio::*;
226    /// # use nix::sys::signal::SigevNotify;
227    /// # use std::{thread, time};
228    /// # use std::io::Write;
229    /// # use std::os::unix::io::AsRawFd;
230    /// # use tempfile::tempfile;
231    /// const INITIAL: &[u8] = b"abcdef123456";
232    /// const LEN: usize = 4;
233    /// let mut rbuf = vec![0; LEN];
234    /// let mut f = tempfile().unwrap();
235    /// f.write_all(INITIAL).unwrap();
236    /// {
237    ///     let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(),
238    ///         2,   //offset
239    ///         &mut rbuf,
240    ///         0,   //priority
241    ///         SigevNotify::SigevNone,
242    ///         LioOpcode::LIO_NOP);
243    ///     aiocb.read().unwrap();
244    ///     while (aiocb.error() == Err(Errno::EINPROGRESS)) {
245    ///         thread::sleep(time::Duration::from_millis(10));
246    ///     }
247    ///     assert_eq!(aiocb.aio_return().unwrap() as usize, LEN);
248    /// }
249    /// assert_eq!(rbuf, b"cdef");
250    /// ```
251    pub fn from_mut_slice(fd: RawFd, offs: off_t, buf: &'a mut [u8],
252                          prio: libc::c_int, sigev_notify: SigevNotify,
253                          opcode: LioOpcode) -> Pin<Box<AioCb<'a>>> {
254        let mut a = AioCb::common_init(fd, prio, sigev_notify);
255        a.0.aio_offset = offs;
256        a.0.aio_nbytes = buf.len() as size_t;
257        a.0.aio_buf = buf.as_ptr() as *mut c_void;
258        a.0.aio_lio_opcode = opcode as libc::c_int;
259
260        Box::pin(AioCb {
261            aiocb: a,
262            mutable: true,
263            in_progress: false,
264            _buffer: PhantomData,
265            _pin: std::marker::PhantomPinned
266        })
267    }
268
269    /// Constructs a new `AioCb` from a mutable raw pointer
270    ///
271    /// Unlike `from_mut_slice`, this method returns a structure suitable for
272    /// placement on the heap.  It may be used for both reads and writes.  Due
273    /// to its unsafety, this method is not recommended.  It is most useful when
274    /// heap allocation is required.
275    ///
276    /// # Parameters
277    ///
278    /// * `fd`:           File descriptor.  Required for all aio functions.
279    /// * `offs`:         File offset
280    /// * `buf`:          Pointer to the memory buffer
281    /// * `len`:          Length of the buffer pointed to by `buf`
282    /// * `prio`:         If POSIX Prioritized IO is supported, then the
283    ///                   operation will be prioritized at the process's
284    ///                   priority level minus `prio`
285    /// * `sigev_notify`: Determines how you will be notified of event
286    ///                   completion.
287    /// * `opcode`:       This field is only used for `lio_listio`.  It
288    ///                   determines which operation to use for this individual
289    ///                   aiocb
290    ///
291    /// # Safety
292    ///
293    /// The caller must ensure that the storage pointed to by `buf` outlives the
294    /// `AioCb`.  The lifetime checker can't help here.
295    pub unsafe fn from_mut_ptr(fd: RawFd, offs: off_t,
296                           buf: *mut c_void, len: usize,
297                           prio: libc::c_int, sigev_notify: SigevNotify,
298                           opcode: LioOpcode) -> Pin<Box<AioCb<'a>>> {
299        let mut a = AioCb::common_init(fd, prio, sigev_notify);
300        a.0.aio_offset = offs;
301        a.0.aio_nbytes = len;
302        a.0.aio_buf = buf;
303        a.0.aio_lio_opcode = opcode as libc::c_int;
304
305        Box::pin(AioCb {
306            aiocb: a,
307            mutable: true,
308            in_progress: false,
309            _buffer: PhantomData,
310            _pin: std::marker::PhantomPinned,
311        })
312    }
313
314    /// Constructs a new `AioCb` from a raw pointer.
315    ///
316    /// Unlike `from_slice`, this method returns a structure suitable for
317    /// placement on the heap.  Due to its unsafety, this method is not
318    /// recommended.  It is most useful when heap allocation is required.
319    ///
320    /// # Parameters
321    ///
322    /// * `fd`:           File descriptor.  Required for all aio functions.
323    /// * `offs`:         File offset
324    /// * `buf`:          Pointer to the memory buffer
325    /// * `len`:          Length of the buffer pointed to by `buf`
326    /// * `prio`:         If POSIX Prioritized IO is supported, then the
327    ///                   operation will be prioritized at the process's
328    ///                   priority level minus `prio`
329    /// * `sigev_notify`: Determines how you will be notified of event
330    ///                   completion.
331    /// * `opcode`:       This field is only used for `lio_listio`.  It
332    ///                   determines which operation to use for this individual
333    ///                   aiocb
334    ///
335    /// # Safety
336    ///
337    /// The caller must ensure that the storage pointed to by `buf` outlives the
338    /// `AioCb`.  The lifetime checker can't help here.
339    pub unsafe fn from_ptr(fd: RawFd, offs: off_t,
340                           buf: *const c_void, len: usize,
341                           prio: libc::c_int, sigev_notify: SigevNotify,
342                           opcode: LioOpcode) -> Pin<Box<AioCb<'a>>> {
343        let mut a = AioCb::common_init(fd, prio, sigev_notify);
344        a.0.aio_offset = offs;
345        a.0.aio_nbytes = len;
346        // casting a const ptr to a mutable ptr here is ok, because we set the
347        // AioCb's mutable field to false
348        a.0.aio_buf = buf as *mut c_void;
349        a.0.aio_lio_opcode = opcode as libc::c_int;
350
351        Box::pin(AioCb {
352            aiocb: a,
353            mutable: false,
354            in_progress: false,
355            _buffer: PhantomData,
356            _pin: std::marker::PhantomPinned
357        })
358    }
359
360    // Private helper
361    fn from_slice_unpinned(fd: RawFd, offs: off_t, buf: &'a [u8],
362                           prio: libc::c_int, sigev_notify: SigevNotify,
363                           opcode: LioOpcode) -> AioCb
364    {
365        let mut a = AioCb::common_init(fd, prio, sigev_notify);
366        a.0.aio_offset = offs;
367        a.0.aio_nbytes = buf.len() as size_t;
368        // casting an immutable buffer to a mutable pointer looks unsafe,
369        // but technically its only unsafe to dereference it, not to create
370        // it.
371        a.0.aio_buf = buf.as_ptr() as *mut c_void;
372        assert!(opcode != LioOpcode::LIO_READ, "Can't read into an immutable buffer");
373        a.0.aio_lio_opcode = opcode as libc::c_int;
374
375        AioCb {
376            aiocb: a,
377            mutable: false,
378            in_progress: false,
379            _buffer: PhantomData,
380            _pin: std::marker::PhantomPinned
381        }
382    }
383
384    /// Like [`AioCb::from_mut_slice`], but works on constant slices rather than
385    /// mutable slices.
386    ///
387    /// An `AioCb` created this way cannot be used with `read`, and its
388    /// `LioOpcode` cannot be set to `LIO_READ`.  This method is useful when
389    /// writing a const buffer with `AioCb::write`, since `from_mut_slice` can't
390    /// work with const buffers.
391    ///
392    /// # Examples
393    ///
394    /// Construct an `AioCb` from a slice and use it for writing.
395    ///
396    /// ```
397    /// # use nix::errno::Errno;
398    /// # use nix::Error;
399    /// # use nix::sys::aio::*;
400    /// # use nix::sys::signal::SigevNotify;
401    /// # use std::{thread, time};
402    /// # use std::os::unix::io::AsRawFd;
403    /// # use tempfile::tempfile;
404    /// const WBUF: &[u8] = b"abcdef123456";
405    /// let mut f = tempfile().unwrap();
406    /// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
407    ///     2,   //offset
408    ///     WBUF,
409    ///     0,   //priority
410    ///     SigevNotify::SigevNone,
411    ///     LioOpcode::LIO_NOP);
412    /// aiocb.write().unwrap();
413    /// while (aiocb.error() == Err(Errno::EINPROGRESS)) {
414    ///     thread::sleep(time::Duration::from_millis(10));
415    /// }
416    /// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len());
417    /// ```
418    // Note: another solution to the problem of writing const buffers would be
419    // to genericize AioCb for both &mut [u8] and &[u8] buffers.  AioCb::read
420    // could take the former and AioCb::write could take the latter.  However,
421    // then lio_listio wouldn't work, because that function needs a slice of
422    // AioCb, and they must all be of the same type.
423    pub fn from_slice(fd: RawFd, offs: off_t, buf: &'a [u8],
424                      prio: libc::c_int, sigev_notify: SigevNotify,
425                      opcode: LioOpcode) -> Pin<Box<AioCb>>
426    {
427        Box::pin(AioCb::from_slice_unpinned(fd, offs, buf, prio, sigev_notify,
428                                            opcode))
429    }
430
431    fn common_init(fd: RawFd, prio: libc::c_int,
432                   sigev_notify: SigevNotify) -> LibcAiocb {
433        // Use mem::zeroed instead of explicitly zeroing each field, because the
434        // number and name of reserved fields is OS-dependent.  On some OSes,
435        // some reserved fields are used the kernel for state, and must be
436        // explicitly zeroed when allocated.
437        let mut a = unsafe { mem::zeroed::<libc::aiocb>()};
438        a.aio_fildes = fd;
439        a.aio_reqprio = prio;
440        a.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
441        LibcAiocb(a)
442    }
443
444    /// Update the notification settings for an existing `aiocb`
445    pub fn set_sigev_notify(self: &mut Pin<Box<Self>>,
446                            sigev_notify: SigevNotify)
447    {
448        // Safe because we don't move any of the data
449        let selfp = unsafe {
450            self.as_mut().get_unchecked_mut()
451        };
452        selfp.aiocb.0.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
453    }
454
455    /// Cancels an outstanding AIO request.
456    ///
457    /// The operating system is not required to implement cancellation for all
458    /// file and device types.  Even if it does, there is no guarantee that the
459    /// operation has not already completed.  So the caller must check the
460    /// result and handle operations that were not canceled or that have already
461    /// completed.
462    ///
463    /// # Examples
464    ///
465    /// Cancel an outstanding aio operation.  Note that we must still call
466    /// `aio_return` to free resources, even though we don't care about the
467    /// result.
468    ///
469    /// ```
470    /// # use nix::errno::Errno;
471    /// # use nix::Error;
472    /// # use nix::sys::aio::*;
473    /// # use nix::sys::signal::SigevNotify;
474    /// # use std::{thread, time};
475    /// # use std::io::Write;
476    /// # use std::os::unix::io::AsRawFd;
477    /// # use tempfile::tempfile;
478    /// let wbuf = b"CDEF";
479    /// let mut f = tempfile().unwrap();
480    /// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
481    ///     2,   //offset
482    ///     &wbuf[..],
483    ///     0,   //priority
484    ///     SigevNotify::SigevNone,
485    ///     LioOpcode::LIO_NOP);
486    /// aiocb.write().unwrap();
487    /// let cs = aiocb.cancel().unwrap();
488    /// if cs == AioCancelStat::AioNotCanceled {
489    ///     while (aiocb.error() == Err(Errno::EINPROGRESS)) {
490    ///         thread::sleep(time::Duration::from_millis(10));
491    ///     }
492    /// }
493    /// // Must call `aio_return`, but ignore the result
494    /// let _ = aiocb.aio_return();
495    /// ```
496    ///
497    /// # References
498    ///
499    /// [aio_cancel](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html)
500    pub fn cancel(self: &mut Pin<Box<Self>>) -> Result<AioCancelStat> {
501        let r = unsafe {
502            let selfp = self.as_mut().get_unchecked_mut();
503            libc::aio_cancel(selfp.aiocb.0.aio_fildes, &mut selfp.aiocb.0)
504        };
505        match r {
506            libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
507            libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
508            libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
509            -1 => Err(Errno::last()),
510            _ => panic!("unknown aio_cancel return value")
511        }
512    }
513
514    fn error_unpinned(&mut self) -> Result<()> {
515        let r = unsafe {
516            libc::aio_error(&mut self.aiocb.0 as *mut libc::aiocb)
517        };
518        match r {
519            0 => Ok(()),
520            num if num > 0 => Err(Errno::from_i32(num)),
521            -1 => Err(Errno::last()),
522            num => panic!("unknown aio_error return value {:?}", num)
523        }
524    }
525
526    /// Retrieve error status of an asynchronous operation.
527    ///
528    /// If the request has not yet completed, returns `EINPROGRESS`.  Otherwise,
529    /// returns `Ok` or any other error.
530    ///
531    /// # Examples
532    ///
533    /// Issue an aio operation and use `error` to poll for completion.  Polling
534    /// is an alternative to `aio_suspend`, used by most of the other examples.
535    ///
536    /// ```
537    /// # use nix::errno::Errno;
538    /// # use nix::Error;
539    /// # use nix::sys::aio::*;
540    /// # use nix::sys::signal::SigevNotify;
541    /// # use std::{thread, time};
542    /// # use std::os::unix::io::AsRawFd;
543    /// # use tempfile::tempfile;
544    /// const WBUF: &[u8] = b"abcdef123456";
545    /// let mut f = tempfile().unwrap();
546    /// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
547    ///     2,   //offset
548    ///     WBUF,
549    ///     0,   //priority
550    ///     SigevNotify::SigevNone,
551    ///     LioOpcode::LIO_NOP);
552    /// aiocb.write().unwrap();
553    /// while (aiocb.error() == Err(Errno::EINPROGRESS)) {
554    ///     thread::sleep(time::Duration::from_millis(10));
555    /// }
556    /// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len());
557    /// ```
558    ///
559    /// # References
560    ///
561    /// [aio_error](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_error.html)
562    pub fn error(self: &mut Pin<Box<Self>>) -> Result<()> {
563        // Safe because error_unpinned doesn't move the data
564        let selfp = unsafe {
565            self.as_mut().get_unchecked_mut()
566        };
567        selfp.error_unpinned()
568    }
569
570    /// An asynchronous version of `fsync(2)`.
571    ///
572    /// # References
573    ///
574    /// [aio_fsync](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_fsync.html)
575    pub fn fsync(self: &mut Pin<Box<Self>>, mode: AioFsyncMode) -> Result<()> {
576        // Safe because we don't move the libc::aiocb
577        unsafe {
578            let selfp = self.as_mut().get_unchecked_mut();
579            Errno::result({
580                let p: *mut libc::aiocb = &mut selfp.aiocb.0;
581                libc::aio_fsync(mode as libc::c_int, p)
582            }).map(|_| {
583                selfp.in_progress = true;
584            })
585        }
586    }
587
588    /// Returns the `aiocb`'s `LioOpcode` field
589    ///
590    /// If the value cannot be represented as an `LioOpcode`, returns `None`
591    /// instead.
592    pub fn lio_opcode(&self) -> Option<LioOpcode> {
593        match self.aiocb.0.aio_lio_opcode {
594            libc::LIO_READ => Some(LioOpcode::LIO_READ),
595            libc::LIO_WRITE => Some(LioOpcode::LIO_WRITE),
596            libc::LIO_NOP => Some(LioOpcode::LIO_NOP),
597            _ => None
598        }
599    }
600
601    /// Returns the requested length of the aio operation in bytes
602    ///
603    /// This method returns the *requested* length of the operation.  To get the
604    /// number of bytes actually read or written by a completed operation, use
605    /// `aio_return` instead.
606    pub fn nbytes(&self) -> usize {
607        self.aiocb.0.aio_nbytes
608    }
609
610    /// Returns the file offset stored in the `AioCb`
611    pub fn offset(&self) -> off_t {
612        self.aiocb.0.aio_offset
613    }
614
615    /// Returns the priority of the `AioCb`
616    pub fn priority(&self) -> libc::c_int {
617        self.aiocb.0.aio_reqprio
618    }
619
620    /// Asynchronously reads from a file descriptor into a buffer
621    ///
622    /// # References
623    ///
624    /// [aio_read](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_read.html)
625    pub fn read(self: &mut Pin<Box<Self>>) -> Result<()> {
626        assert!(self.mutable, "Can't read into an immutable buffer");
627        // Safe because we don't move anything
628        let selfp = unsafe {
629            self.as_mut().get_unchecked_mut()
630        };
631        Errno::result({
632            let p: *mut libc::aiocb = &mut selfp.aiocb.0;
633            unsafe { libc::aio_read(p) }
634        }).map(|_| {
635            selfp.in_progress = true;
636        })
637    }
638
639    /// Returns the `SigEvent` stored in the `AioCb`
640    pub fn sigevent(&self) -> SigEvent {
641        SigEvent::from(&self.aiocb.0.aio_sigevent)
642    }
643
644    fn aio_return_unpinned(&mut self) -> Result<isize> {
645        unsafe {
646            let p: *mut libc::aiocb = &mut self.aiocb.0;
647            self.in_progress = false;
648            Errno::result(libc::aio_return(p))
649        }
650    }
651
652    /// Retrieve return status of an asynchronous operation.
653    ///
654    /// Should only be called once for each `AioCb`, after `AioCb::error`
655    /// indicates that it has completed.  The result is the same as for the
656    /// synchronous `read(2)`, `write(2)`, of `fsync(2)` functions.
657    ///
658    /// # References
659    ///
660    /// [aio_return](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_return.html)
661    // Note: this should be just `return`, but that's a reserved word
662    pub fn aio_return(self: &mut Pin<Box<Self>>) -> Result<isize> {
663        // Safe because aio_return_unpinned does not move the data
664        let selfp = unsafe {
665            self.as_mut().get_unchecked_mut()
666        };
667        selfp.aio_return_unpinned()
668    }
669
670    /// Asynchronously writes from a buffer to a file descriptor
671    ///
672    /// # References
673    ///
674    /// [aio_write](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_write.html)
675    pub fn write(self: &mut Pin<Box<Self>>) -> Result<()> {
676        // Safe because we don't move anything
677        let selfp = unsafe {
678            self.as_mut().get_unchecked_mut()
679        };
680        Errno::result({
681            let p: *mut libc::aiocb = &mut selfp.aiocb.0;
682            unsafe{ libc::aio_write(p) }
683        }).map(|_| {
684            selfp.in_progress = true;
685        })
686    }
687}
688
689/// Cancels outstanding AIO requests for a given file descriptor.
690///
691/// # Examples
692///
693/// Issue an aio operation, then cancel all outstanding operations on that file
694/// descriptor.
695///
696/// ```
697/// # use nix::errno::Errno;
698/// # use nix::Error;
699/// # use nix::sys::aio::*;
700/// # use nix::sys::signal::SigevNotify;
701/// # use std::{thread, time};
702/// # use std::io::Write;
703/// # use std::os::unix::io::AsRawFd;
704/// # use tempfile::tempfile;
705/// let wbuf = b"CDEF";
706/// let mut f = tempfile().unwrap();
707/// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
708///     2,   //offset
709///     &wbuf[..],
710///     0,   //priority
711///     SigevNotify::SigevNone,
712///     LioOpcode::LIO_NOP);
713/// aiocb.write().unwrap();
714/// let cs = aio_cancel_all(f.as_raw_fd()).unwrap();
715/// if cs == AioCancelStat::AioNotCanceled {
716///     while (aiocb.error() == Err(Errno::EINPROGRESS)) {
717///         thread::sleep(time::Duration::from_millis(10));
718///     }
719/// }
720/// // Must call `aio_return`, but ignore the result
721/// let _ = aiocb.aio_return();
722/// ```
723///
724/// # References
725///
726/// [`aio_cancel`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html)
727pub fn aio_cancel_all(fd: RawFd) -> Result<AioCancelStat> {
728    match unsafe { libc::aio_cancel(fd, null_mut()) } {
729        libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
730        libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
731        libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
732        -1 => Err(Errno::last()),
733        _ => panic!("unknown aio_cancel return value")
734    }
735}
736
737/// Suspends the calling process until at least one of the specified `AioCb`s
738/// has completed, a signal is delivered, or the timeout has passed.
739///
740/// If `timeout` is `None`, `aio_suspend` will block indefinitely.
741///
742/// # Examples
743///
744/// Use `aio_suspend` to block until an aio operation completes.
745///
746/// ```
747/// # use nix::sys::aio::*;
748/// # use nix::sys::signal::SigevNotify;
749/// # use std::os::unix::io::AsRawFd;
750/// # use tempfile::tempfile;
751/// const WBUF: &[u8] = b"abcdef123456";
752/// let mut f = tempfile().unwrap();
753/// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
754///     2,   //offset
755///     WBUF,
756///     0,   //priority
757///     SigevNotify::SigevNone,
758///     LioOpcode::LIO_NOP);
759/// aiocb.write().unwrap();
760/// aio_suspend(&[aiocb.as_ref()], None).expect("aio_suspend failed");
761/// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len());
762/// ```
763/// # References
764///
765/// [`aio_suspend`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_suspend.html)
766pub fn aio_suspend(list: &[Pin<&AioCb>], timeout: Option<TimeSpec>) -> Result<()> {
767    let plist = list as *const [Pin<&AioCb>] as *const [*const libc::aiocb];
768    let p = plist as *const *const libc::aiocb;
769    let timep = match timeout {
770        None    => null::<libc::timespec>(),
771        Some(x) => x.as_ref() as *const libc::timespec
772    };
773    Errno::result(unsafe {
774        libc::aio_suspend(p, list.len() as i32, timep)
775    }).map(drop)
776}
777
778impl<'a> Debug for AioCb<'a> {
779    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
780        fmt.debug_struct("AioCb")
781            .field("aiocb", &self.aiocb.0)
782            .field("mutable", &self.mutable)
783            .field("in_progress", &self.in_progress)
784            .finish()
785    }
786}
787
788impl<'a> Drop for AioCb<'a> {
789    /// If the `AioCb` has no remaining state in the kernel, just drop it.
790    /// Otherwise, dropping constitutes a resource leak, which is an error
791    fn drop(&mut self) {
792        assert!(thread::panicking() || !self.in_progress,
793                "Dropped an in-progress AioCb");
794    }
795}
796
797/// LIO Control Block.
798///
799/// The basic structure used to issue multiple AIO operations simultaneously.
800#[cfg(not(any(target_os = "ios", target_os = "macos")))]
801#[cfg_attr(docsrs, doc(cfg(all())))]
802pub struct LioCb<'a> {
803    /// A collection of [`AioCb`]s.  All of these will be issued simultaneously
804    /// by the [`listio`] method.
805    ///
806    /// [`AioCb`]: struct.AioCb.html
807    /// [`listio`]: #method.listio
808    // Their locations in memory must be fixed once they are passed to the
809    // kernel.  So this field must be non-public so the user can't swap.
810    aiocbs: Box<[AioCb<'a>]>,
811
812    /// The actual list passed to `libc::lio_listio`.
813    ///
814    /// It must live for as long as any of the operations are still being
815    /// processesed, because the aio subsystem uses its address as a unique
816    /// identifier.
817    list: Vec<*mut libc::aiocb>,
818
819    /// A partial set of results.  This field will get populated by
820    /// `listio_resubmit` when an `LioCb` is resubmitted after an error
821    results: Vec<Option<Result<isize>>>
822}
823
824/// LioCb can't automatically impl Send and Sync just because of the raw
825/// pointers in list.  But that's stupid.  There's no reason that raw pointers
826/// should automatically be non-Send
827#[cfg(not(any(target_os = "ios", target_os = "macos")))]
828unsafe impl<'a> Send for LioCb<'a> {}
829#[cfg(not(any(target_os = "ios", target_os = "macos")))]
830unsafe impl<'a> Sync for LioCb<'a> {}
831
832#[cfg(not(any(target_os = "ios", target_os = "macos")))]
833#[cfg_attr(docsrs, doc(cfg(all())))]
834impl<'a> LioCb<'a> {
835    /// Are no [`AioCb`]s contained?
836    pub fn is_empty(&self) -> bool {
837        self.aiocbs.is_empty()
838    }
839
840    /// Return the number of individual [`AioCb`]s contained.
841    pub fn len(&self) -> usize {
842        self.aiocbs.len()
843    }
844
845    /// Submits multiple asynchronous I/O requests with a single system call.
846    ///
847    /// They are not guaranteed to complete atomically, and the order in which
848    /// the requests are carried out is not specified.  Reads, writes, and
849    /// fsyncs may be freely mixed.
850    ///
851    /// This function is useful for reducing the context-switch overhead of
852    /// submitting many AIO operations.  It can also be used with
853    /// `LioMode::LIO_WAIT` to block on the result of several independent
854    /// operations.  Used that way, it is often useful in programs that
855    /// otherwise make little use of AIO.
856    ///
857    /// # Examples
858    ///
859    /// Use `listio` to submit an aio operation and wait for its completion.  In
860    /// this case, there is no need to use [`aio_suspend`] to wait or
861    /// [`AioCb::error`] to poll.
862    ///
863    /// ```
864    /// # use nix::sys::aio::*;
865    /// # use nix::sys::signal::SigevNotify;
866    /// # use std::os::unix::io::AsRawFd;
867    /// # use tempfile::tempfile;
868    /// const WBUF: &[u8] = b"abcdef123456";
869    /// let mut f = tempfile().unwrap();
870    /// let mut liocb = LioCbBuilder::with_capacity(1)
871    ///     .emplace_slice(
872    ///         f.as_raw_fd(),
873    ///         2,   //offset
874    ///         WBUF,
875    ///         0,   //priority
876    ///         SigevNotify::SigevNone,
877    ///         LioOpcode::LIO_WRITE
878    ///     ).finish();
879    /// liocb.listio(LioMode::LIO_WAIT,
880    ///              SigevNotify::SigevNone).unwrap();
881    /// assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len());
882    /// ```
883    ///
884    /// # References
885    ///
886    /// [`lio_listio`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html)
887    ///
888    /// [`aio_suspend`]: fn.aio_suspend.html
889    /// [`AioCb::error`]: struct.AioCb.html#method.error
890    pub fn listio(&mut self, mode: LioMode,
891                  sigev_notify: SigevNotify) -> Result<()> {
892        let sigev = SigEvent::new(sigev_notify);
893        let sigevp = &mut sigev.sigevent() as *mut libc::sigevent;
894        self.list.clear();
895        for a in &mut self.aiocbs.iter_mut() {
896            a.in_progress = true;
897            self.list.push(a as *mut AioCb<'a>
898                             as *mut libc::aiocb);
899        }
900        let p = self.list.as_ptr();
901        Errno::result(unsafe {
902            libc::lio_listio(mode as i32, p, self.list.len() as i32, sigevp)
903        }).map(drop)
904    }
905
906    /// Resubmits any incomplete operations with [`lio_listio`].
907    ///
908    /// Sometimes, due to system resource limitations, an `lio_listio` call will
909    /// return `EIO`, or `EAGAIN`.  Or, if a signal is received, it may return
910    /// `EINTR`.  In any of these cases, only a subset of its constituent
911    /// operations will actually have been initiated.  `listio_resubmit` will
912    /// resubmit any operations that are still uninitiated.
913    ///
914    /// After calling `listio_resubmit`, results should be collected by
915    /// [`LioCb::aio_return`].
916    ///
917    /// # Examples
918    /// ```no_run
919    /// # use nix::Error;
920    /// # use nix::errno::Errno;
921    /// # use nix::sys::aio::*;
922    /// # use nix::sys::signal::SigevNotify;
923    /// # use std::os::unix::io::AsRawFd;
924    /// # use std::{thread, time};
925    /// # use tempfile::tempfile;
926    /// const WBUF: &[u8] = b"abcdef123456";
927    /// let mut f = tempfile().unwrap();
928    /// let mut liocb = LioCbBuilder::with_capacity(1)
929    ///     .emplace_slice(
930    ///         f.as_raw_fd(),
931    ///         2,   //offset
932    ///         WBUF,
933    ///         0,   //priority
934    ///         SigevNotify::SigevNone,
935    ///         LioOpcode::LIO_WRITE
936    ///     ).finish();
937    /// let mut err = liocb.listio(LioMode::LIO_WAIT, SigevNotify::SigevNone);
938    /// while err == Err(Errno::EIO) ||
939    ///       err == Err(Errno::EAGAIN) {
940    ///     thread::sleep(time::Duration::from_millis(10));
941    ///     err = liocb.listio_resubmit(LioMode::LIO_WAIT, SigevNotify::SigevNone);
942    /// }
943    /// assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len());
944    /// ```
945    ///
946    /// # References
947    ///
948    /// [`lio_listio`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html)
949    ///
950    /// [`lio_listio`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html
951    /// [`LioCb::aio_return`]: struct.LioCb.html#method.aio_return
952    // Note: the addresses of any EINPROGRESS or EOK aiocbs _must_ not be
953    // changed by this method, because the kernel relies on their addresses
954    // being stable.
955    // Note: aiocbs that are Ok(()) must be finalized by aio_return, or else the
956    // sigev_notify will immediately refire.
957    pub fn listio_resubmit(&mut self, mode:LioMode,
958                           sigev_notify: SigevNotify) -> Result<()> {
959        let sigev = SigEvent::new(sigev_notify);
960        let sigevp = &mut sigev.sigevent() as *mut libc::sigevent;
961        self.list.clear();
962
963        while self.results.len() < self.aiocbs.len() {
964            self.results.push(None);
965        }
966
967        for (i, a) in self.aiocbs.iter_mut().enumerate() {
968            if self.results[i].is_some() {
969                // Already collected final status for this operation
970                continue;
971            }
972            match a.error_unpinned() {
973                Ok(()) => {
974                    // aiocb is complete; collect its status and don't resubmit
975                    self.results[i] = Some(a.aio_return_unpinned());
976                },
977                Err(Errno::EAGAIN) => {
978                    self.list.push(a as *mut AioCb<'a> as *mut libc::aiocb);
979                },
980                Err(Errno::EINPROGRESS) => {
981                    // aiocb is was successfully queued; no need to do anything
982                },
983                Err(Errno::EINVAL) => panic!(
984                    "AioCb was never submitted, or already finalized"),
985                _ => unreachable!()
986            }
987        }
988        let p = self.list.as_ptr();
989        Errno::result(unsafe {
990            libc::lio_listio(mode as i32, p, self.list.len() as i32, sigevp)
991        }).map(drop)
992    }
993
994    /// Collect final status for an individual `AioCb` submitted as part of an
995    /// `LioCb`.
996    ///
997    /// This is just like [`AioCb::aio_return`], except it takes into account
998    /// operations that were restarted by [`LioCb::listio_resubmit`]
999    ///
1000    /// [`AioCb::aio_return`]: struct.AioCb.html#method.aio_return
1001    /// [`LioCb::listio_resubmit`]: #method.listio_resubmit
1002    pub fn aio_return(&mut self, i: usize) -> Result<isize> {
1003        if i >= self.results.len() || self.results[i].is_none() {
1004            self.aiocbs[i].aio_return_unpinned()
1005        } else {
1006            self.results[i].unwrap()
1007        }
1008    }
1009
1010    /// Retrieve error status of an individual `AioCb` submitted as part of an
1011    /// `LioCb`.
1012    ///
1013    /// This is just like [`AioCb::error`], except it takes into account
1014    /// operations that were restarted by [`LioCb::listio_resubmit`]
1015    ///
1016    /// [`AioCb::error`]: struct.AioCb.html#method.error
1017    /// [`LioCb::listio_resubmit`]: #method.listio_resubmit
1018    pub fn error(&mut self, i: usize) -> Result<()> {
1019        if i >= self.results.len() || self.results[i].is_none() {
1020            self.aiocbs[i].error_unpinned()
1021        } else {
1022            Ok(())
1023        }
1024    }
1025}
1026
1027#[cfg(not(any(target_os = "ios", target_os = "macos")))]
1028impl<'a> Debug for LioCb<'a> {
1029    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1030        fmt.debug_struct("LioCb")
1031            .field("aiocbs", &self.aiocbs)
1032            .finish()
1033    }
1034}
1035
1036/// Used to construct `LioCb`
1037// This must be a separate class from LioCb due to pinning constraints.  LioCb
1038// must use a boxed slice of AioCbs so they will have stable storage, but
1039// LioCbBuilder must use a Vec to make construction possible when the final size
1040// is unknown.
1041#[cfg(not(any(target_os = "ios", target_os = "macos")))]
1042#[cfg_attr(docsrs, doc(cfg(all())))]
1043#[derive(Debug)]
1044pub struct LioCbBuilder<'a> {
1045    /// A collection of [`AioCb`]s.
1046    ///
1047    /// [`AioCb`]: struct.AioCb.html
1048    pub aiocbs: Vec<AioCb<'a>>,
1049}
1050
1051#[cfg(not(any(target_os = "ios", target_os = "macos")))]
1052#[cfg_attr(docsrs, doc(cfg(all())))]
1053impl<'a> LioCbBuilder<'a> {
1054    /// Initialize an empty `LioCb`
1055    pub fn with_capacity(capacity: usize) -> LioCbBuilder<'a> {
1056        LioCbBuilder {
1057            aiocbs: Vec::with_capacity(capacity),
1058        }
1059    }
1060
1061    /// Add a new operation on an immutable slice to the [`LioCb`] under
1062    /// construction.
1063    ///
1064    /// Arguments are the same as for [`AioCb::from_slice`]
1065    ///
1066    /// [`LioCb`]: struct.LioCb.html
1067    /// [`AioCb::from_slice`]: struct.AioCb.html#method.from_slice
1068    #[must_use]
1069    pub fn emplace_slice(mut self, fd: RawFd, offs: off_t, buf: &'a [u8],
1070                         prio: libc::c_int, sigev_notify: SigevNotify,
1071                         opcode: LioOpcode) -> Self
1072    {
1073        self.aiocbs.push(AioCb::from_slice_unpinned(fd, offs, buf, prio,
1074                                                    sigev_notify, opcode));
1075        self
1076    }
1077
1078    /// Add a new operation on a mutable slice to the [`LioCb`] under
1079    /// construction.
1080    ///
1081    /// Arguments are the same as for [`AioCb::from_mut_slice`]
1082    ///
1083    /// [`LioCb`]: struct.LioCb.html
1084    /// [`AioCb::from_mut_slice`]: struct.AioCb.html#method.from_mut_slice
1085    #[must_use]
1086    pub fn emplace_mut_slice(mut self, fd: RawFd, offs: off_t,
1087                             buf: &'a mut [u8], prio: libc::c_int,
1088                             sigev_notify: SigevNotify, opcode: LioOpcode)
1089        -> Self
1090    {
1091        self.aiocbs.push(AioCb::from_mut_slice_unpinned(fd, offs, buf, prio,
1092                                                        sigev_notify, opcode));
1093        self
1094    }
1095
1096    /// Finalize this [`LioCb`].
1097    ///
1098    /// Afterwards it will be possible to issue the operations with
1099    /// [`LioCb::listio`].  Conversely, it will no longer be possible to add new
1100    /// operations with [`LioCbBuilder::emplace_slice`] or
1101    /// [`LioCbBuilder::emplace_mut_slice`].
1102    ///
1103    /// [`LioCb::listio`]: struct.LioCb.html#method.listio
1104    /// [`LioCb::from_mut_slice`]: struct.LioCb.html#method.from_mut_slice
1105    /// [`LioCb::from_slice`]: struct.LioCb.html#method.from_slice
1106    pub fn finish(self) -> LioCb<'a> {
1107        let len = self.aiocbs.len();
1108        LioCb {
1109            aiocbs: self.aiocbs.into(),
1110            list: Vec::with_capacity(len),
1111            results: Vec::with_capacity(len)
1112        }
1113    }
1114}
1115
1116#[cfg(not(any(target_os = "ios", target_os = "macos")))]
1117#[cfg(test)]
1118mod t {
1119    use super::*;
1120
1121    // It's important that `LioCb` be `UnPin`.  The tokio-file crate relies on
1122    // it.
1123    #[test]
1124    fn liocb_is_unpin() {
1125        use assert_impl::assert_impl;
1126
1127        assert_impl!(Unpin: LioCb);
1128    }
1129}