| Andrew Walbran | 12f6140 | 2020-10-14 11:10:53 +0100 | [diff] [blame^] | 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 |
| 113 | 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. |
| 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. |
| 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. |
| 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` |
| 205 | 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 | /// ``` |
| 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 | /// ``` |
| 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 |
| 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 |
| 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. |
| 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. |
| 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. |
| 658 | 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 | |
| 679 | 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` |
| 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) |
| 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) |
| 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) |
| 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. |
| 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. |
| 833 | pub fn nbytes(&self) -> usize { |
| 834 | self.aiocb.aio_nbytes |
| 835 | } |
| 836 | |
| 837 | /// Returns the file offset stored in the `AioCb` |
| 838 | pub fn offset(&self) -> off_t { |
| 839 | self.aiocb.aio_offset |
| 840 | } |
| 841 | |
| 842 | /// Returns the priority of the `AioCb` |
| 843 | 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) |
| 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` |
| 863 | 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 |
| 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) |
| 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) |
| 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) |
| 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> { |
| 993 | 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 |
| 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` |
| 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 |
| 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. |
| 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 |
| 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 |
| 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> { |
| 1230 | 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> { |
| 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 | } |