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