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