|
| 1 | +use {Error, Errno, Result}; |
| 2 | +use std::os::unix::io::RawFd; |
| 3 | +use libc::{c_void, off_t, size_t}; |
| 4 | +use libc; |
| 5 | +use std::marker::PhantomData; |
| 6 | +use std::mem; |
| 7 | +use std::ptr::{null, null_mut}; |
| 8 | +use sys::signal::*; |
| 9 | +use sys::time::TimeSpec; |
| 10 | + |
| 11 | +/// Mode for `aio_fsync`. Controls whether only data or both data and metadata |
| 12 | +/// are synced. |
| 13 | +#[repr(i32)] |
| 14 | +#[derive(Clone, Copy, Debug, PartialEq)] |
| 15 | +pub enum AioFsyncMode { |
| 16 | + /// do it like `fsync` |
| 17 | + O_SYNC = libc::O_SYNC, |
| 18 | + /// on supported operating systems only, do it like `fdatasync` |
| 19 | + #[cfg(any(target_os = "openbsd", target_os = "bitrig", |
| 20 | + target_os = "netbsd", target_os = "macos", target_os = "ios", |
| 21 | + target_os = "linux"))] |
| 22 | + O_DSYNC = libc::O_DSYNC |
| 23 | +} |
| 24 | + |
| 25 | +/// When used with `lio_listio`, determines whether a given `aiocb` should be |
| 26 | +/// used for a read operation, a write operation, or ignored. Has no effect for |
| 27 | +/// any other aio functions. |
| 28 | +#[repr(i32)] |
| 29 | +#[derive(Clone, Copy, Debug, PartialEq)] |
| 30 | +pub enum LioOpcode { |
| 31 | + LIO_NOP = libc::LIO_NOP, |
| 32 | + LIO_WRITE = libc::LIO_WRITE, |
| 33 | + LIO_READ = libc::LIO_READ |
| 34 | +} |
| 35 | + |
| 36 | +/// Mode for `lio_listio`. |
| 37 | +#[repr(i32)] |
| 38 | +#[derive(Clone, Copy, Debug, PartialEq)] |
| 39 | +pub enum LioMode { |
| 40 | + /// Requests that `lio_listio` block until all requested operations have |
| 41 | + /// been completed |
| 42 | + LIO_WAIT = libc::LIO_WAIT, |
| 43 | + /// Requests that `lio_listio` return immediately |
| 44 | + LIO_NOWAIT = libc::LIO_NOWAIT, |
| 45 | +} |
| 46 | + |
| 47 | +/// Return values for `aio_cancel` |
| 48 | +#[repr(i32)] |
| 49 | +#[derive(Clone, Copy, Debug, PartialEq)] |
| 50 | +pub enum AioCancelStat { |
| 51 | + /// All outstanding requests were canceled |
| 52 | + AioCanceled = libc::AIO_CANCELED, |
| 53 | + /// Some requests were not canceled. Their status should be checked with |
| 54 | + /// `aio_error` |
| 55 | + AioNotCanceled = libc::AIO_NOTCANCELED, |
| 56 | + /// All of the requests have already finished |
| 57 | + AioAllDone = libc::AIO_ALLDONE, |
| 58 | +} |
| 59 | + |
| 60 | +/// The basic structure used by all aio functions. Each `aiocb` represents one |
| 61 | +/// I/O request. |
| 62 | +#[repr(C)] |
| 63 | +pub struct AioCb<'a> { |
| 64 | + aiocb: libc::aiocb, |
| 65 | + phantom: PhantomData<&'a mut [u8]> |
| 66 | +} |
| 67 | + |
| 68 | +impl<'a> AioCb<'a> { |
| 69 | + /// Constructs a new `AioCb` with no associated buffer. |
| 70 | + /// |
| 71 | + /// The resulting `AioCb` structure is suitable for use with `aio_fsync`. |
| 72 | + /// * `fd` File descriptor. Required for all aio functions. |
| 73 | + /// * `prio` If POSIX Prioritized IO is supported, then the operation will |
| 74 | + /// be prioritized at the process's priority level minus `prio` |
| 75 | + /// * `sigev_notify` Determines how you will be notified of event |
| 76 | + /// completion. |
| 77 | + pub fn from_fd(fd: RawFd, prio: ::c_int, |
| 78 | + sigev_notify: SigevNotify) -> AioCb<'a> { |
| 79 | + let mut a = AioCb::common_init(fd, prio, sigev_notify); |
| 80 | + a.aio_offset = 0; |
| 81 | + a.aio_nbytes = 0; |
| 82 | + a.aio_buf = null_mut(); |
| 83 | + |
| 84 | + let aiocb = AioCb { aiocb: a, phantom: PhantomData}; |
| 85 | + aiocb |
| 86 | + } |
| 87 | + |
| 88 | + /// Constructs a new `AioCb`. |
| 89 | + /// |
| 90 | + /// * `fd` File descriptor. Required for all aio functions. |
| 91 | + /// * `offs` File offset |
| 92 | + /// * `buf` A memory buffer |
| 93 | + /// * `prio` If POSIX Prioritized IO is supported, then the operation will |
| 94 | + /// be prioritized at the process's priority level minus `prio` |
| 95 | + /// * `sigev_notify` Determines how you will be notified of event |
| 96 | + /// completion. |
| 97 | + /// * `opcode` This field is only used for `lio_listio`. It determines |
| 98 | + /// which operation to use for this individual aiocb |
| 99 | + pub fn from_mut_slice(fd: RawFd, offs: off_t, buf: &'a mut [u8], |
| 100 | + prio: ::c_int, sigev_notify: SigevNotify, |
| 101 | + opcode: LioOpcode) -> AioCb { |
| 102 | + let mut a = AioCb::common_init(fd, prio, sigev_notify); |
| 103 | + a.aio_offset = offs; |
| 104 | + a.aio_nbytes = buf.len() as size_t; |
| 105 | + a.aio_buf = buf.as_ptr() as *mut c_void; |
| 106 | + a.aio_lio_opcode = opcode as ::c_int; |
| 107 | + |
| 108 | + let aiocb = AioCb { aiocb: a, phantom: PhantomData}; |
| 109 | + aiocb |
| 110 | + } |
| 111 | + |
| 112 | + /// Like `from_mut_slice`, but works on constant slices rather than |
| 113 | + /// mutable slices. |
| 114 | + /// |
| 115 | + /// This is technically unsafe, but in practice it's fine |
| 116 | + /// to use with any aio functions except `aio_read` and `lio_listio` (with |
| 117 | + /// `opcode` set to `LIO_READ`). This method is useful when writing a const |
| 118 | + /// buffer with `aio_write`, since from_mut_slice can't work with const |
| 119 | + /// buffers. |
| 120 | + // Note: another solution to the problem of writing const buffers would be |
| 121 | + // to genericize AioCb for both &mut [u8] and &[u8] buffers. aio_read could |
| 122 | + // take the former and aio_write could take the latter. However, then |
| 123 | + // lio_listio wouldn't work, because that function needs a slice of AioCb, |
| 124 | + // and they must all be the same type. We're basically stuck with using an |
| 125 | + // unsafe function, since aio (as designed in C) is an unsafe API. |
| 126 | + pub unsafe fn from_slice(fd: RawFd, offs: off_t, buf: &'a [u8], |
| 127 | + prio: ::c_int, sigev_notify: SigevNotify, |
| 128 | + opcode: LioOpcode) -> AioCb { |
| 129 | + let mut a = AioCb::common_init(fd, prio, sigev_notify); |
| 130 | + a.aio_offset = offs; |
| 131 | + a.aio_nbytes = buf.len() as size_t; |
| 132 | + a.aio_buf = buf.as_ptr() as *mut c_void; |
| 133 | + a.aio_lio_opcode = opcode as ::c_int; |
| 134 | + |
| 135 | + let aiocb = AioCb { aiocb: a, phantom: PhantomData}; |
| 136 | + aiocb |
| 137 | + } |
| 138 | + |
| 139 | + fn common_init(fd: RawFd, prio: ::c_int, |
| 140 | + sigev_notify: SigevNotify) -> libc::aiocb { |
| 141 | + // Use mem::zeroed instead of explicitly zeroing each field, because the |
| 142 | + // number and name of reserved fields is OS-dependent. On some OSes, |
| 143 | + // some reserved fields are used the kernel for state, and must be |
| 144 | + // explicitly zeroed when allocated. |
| 145 | + let mut a = unsafe { mem::zeroed::<libc::aiocb>()}; |
| 146 | + a.aio_fildes = fd; |
| 147 | + a.aio_reqprio = prio; |
| 148 | + a.aio_sigevent = SigEvent::new(sigev_notify).sigevent(); |
| 149 | + a |
| 150 | + } |
| 151 | + |
| 152 | + /// Update the notification settings for an existing `aiocb` |
| 153 | + pub fn set_sigev_notify(&mut self, sigev_notify: SigevNotify) { |
| 154 | + self.aiocb.aio_sigevent = SigEvent::new(sigev_notify).sigevent(); |
| 155 | + } |
| 156 | +} |
| 157 | + |
| 158 | +/// Cancels outstanding AIO requests. If `aiocb` is `None`, then all requests |
| 159 | +/// for `fd` will be cancelled. Otherwise, only the given `AioCb` will be |
| 160 | +/// cancelled. |
| 161 | +pub fn aio_cancel(fd: RawFd, aiocb: Option<&mut AioCb>) -> Result<AioCancelStat> { |
| 162 | + let p: *mut libc::aiocb = match aiocb { |
| 163 | + None => null_mut(), |
| 164 | + Some(x) => &mut x.aiocb |
| 165 | + }; |
| 166 | + match unsafe { libc::aio_cancel(fd, p) } { |
| 167 | + libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled), |
| 168 | + libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled), |
| 169 | + libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone), |
| 170 | + -1 => Err(Error::last()), |
| 171 | + _ => panic!("unknown aio_cancel return value") |
| 172 | + } |
| 173 | +} |
| 174 | + |
| 175 | +/// Retrieve error status of an asynchronous operation. If the request has not |
| 176 | +/// yet completed, returns `EINPROGRESS`. Otherwise, returns `Ok` or any other |
| 177 | +/// error. |
| 178 | +pub fn aio_error(aiocb: &mut AioCb) -> Result<()> { |
| 179 | + let p: *mut libc::aiocb = &mut aiocb.aiocb; |
| 180 | + match unsafe { libc::aio_error(p) } { |
| 181 | + 0 => Ok(()), |
| 182 | + num if num > 0 => Err(Error::from_errno(Errno::from_i32(num))), |
| 183 | + -1 => Err(Error::last()), |
| 184 | + num => panic!("unknown aio_error return value {:?}", num) |
| 185 | + } |
| 186 | +} |
| 187 | + |
| 188 | +/// An asynchronous version of `fsync`. |
| 189 | +pub fn aio_fsync(mode: AioFsyncMode, aiocb: &mut AioCb) -> Result<()> { |
| 190 | + let p: *mut libc::aiocb = &mut aiocb.aiocb; |
| 191 | + Errno::result(unsafe { libc::aio_fsync(mode as ::c_int, p) }).map(drop) |
| 192 | +} |
| 193 | + |
| 194 | +/// Asynchronously reads from a file descriptor into a buffer |
| 195 | +pub fn aio_read(aiocb: &mut AioCb) -> Result<()> { |
| 196 | + let p: *mut libc::aiocb = &mut aiocb.aiocb; |
| 197 | + Errno::result(unsafe { libc::aio_read(p) }).map(drop) |
| 198 | +} |
| 199 | + |
| 200 | +/// Retrieve return status of an asynchronous operation. Should only be called |
| 201 | +/// once for each `AioCb`, after `aio_error` indicates that it has completed. |
| 202 | +/// The result the same as for `read`, `write`, of `fsync`. |
| 203 | +pub fn aio_return(aiocb: &mut AioCb) -> Result<isize> { |
| 204 | + let p: *mut libc::aiocb = &mut aiocb.aiocb; |
| 205 | + Errno::result(unsafe { libc::aio_return(p) }) |
| 206 | +} |
| 207 | + |
| 208 | +/// Suspends the calling process until at least one of the specified `AioCb`s |
| 209 | +/// has completed, a signal is delivered, or the timeout has passed. If |
| 210 | +/// `timeout` is `None`, `aio_suspend` will block indefinitely. |
| 211 | +pub fn aio_suspend(list: &[&AioCb], timeout: Option<TimeSpec>) -> Result<()> { |
| 212 | + // We must use transmute because Rust doesn't understand that a pointer to a |
| 213 | + // Struct is the same as a pointer to its first element. |
| 214 | + let plist = unsafe { |
| 215 | + mem::transmute::<&[&AioCb], *const [*const libc::aiocb]>(list) |
| 216 | + }; |
| 217 | + let p = plist as *const *const libc::aiocb; |
| 218 | + let timep = match timeout { |
| 219 | + None => null::<libc::timespec>(), |
| 220 | + Some(x) => x.as_ref() as *const libc::timespec |
| 221 | + }; |
| 222 | + Errno::result(unsafe { |
| 223 | + libc::aio_suspend(p, list.len() as i32, timep) |
| 224 | + }).map(drop) |
| 225 | +} |
| 226 | + |
| 227 | +/// Asynchronously writes from a buffer to a file descriptor |
| 228 | +pub fn aio_write(aiocb: &mut AioCb) -> Result<()> { |
| 229 | + let p: *mut libc::aiocb = &mut aiocb.aiocb; |
| 230 | + Errno::result(unsafe { libc::aio_write(p) }).map(drop) |
| 231 | +} |
| 232 | + |
| 233 | +/// Submits multiple asynchronous I/O requests with a single system call. The |
| 234 | +/// order in which the requests are carried out is not specified. |
| 235 | +#[cfg(not(any(target_os = "ios", target_os = "macos")))] |
| 236 | +pub fn lio_listio(mode: LioMode, list: &[&mut AioCb], |
| 237 | + sigev_notify: SigevNotify) -> Result<()> { |
| 238 | + let sigev = SigEvent::new(sigev_notify); |
| 239 | + let sigevp = &mut sigev.sigevent() as *mut libc::sigevent; |
| 240 | + // We must use transmute because Rust doesn't understand that a pointer to a |
| 241 | + // Struct is the same as a pointer to its first element. |
| 242 | + let plist = unsafe { |
| 243 | + mem::transmute::<&[&mut AioCb], *const [*mut libc::aiocb]>(list) |
| 244 | + }; |
| 245 | + let p = plist as *const *mut libc::aiocb; |
| 246 | + Errno::result(unsafe { |
| 247 | + libc::lio_listio(mode as i32, p, list.len() as i32, sigevp) |
| 248 | + }).map(drop) |
| 249 | +} |
0 commit comments