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