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