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