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