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