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