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