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