diff options
author | Zach Reizner <zachr@google.com> | 2017-06-29 11:35:17 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2017-06-30 12:51:24 -0700 |
commit | 79b2a732729219e2b4b0bc5ef8ab3ac23f28e562 (patch) | |
tree | ba8547e282f23705882fc6b65fc792677d269b94 /sys_util | |
parent | fa8c6802b6a392a3dbc6300fabb47eeebf219ed6 (diff) | |
download | crosvm-79b2a732729219e2b4b0bc5ef8ab3ac23f28e562.tar crosvm-79b2a732729219e2b4b0bc5ef8ab3ac23f28e562.tar.gz crosvm-79b2a732729219e2b4b0bc5ef8ab3ac23f28e562.tar.bz2 crosvm-79b2a732729219e2b4b0bc5ef8ab3ac23f28e562.tar.lz crosvm-79b2a732729219e2b4b0bc5ef8ab3ac23f28e562.tar.xz crosvm-79b2a732729219e2b4b0bc5ef8ab3ac23f28e562.tar.zst crosvm-79b2a732729219e2b4b0bc5ef8ab3ac23f28e562.zip |
sys_util: move handle_eintr!() to sys_util so it can be reused
This CL takes the handle_intr!() macro, changes its name to handle_eintr!(), and overloads it so it can handle EINTR embedded in other kinds of Result types. BUG=None TEST=cargo test Change-Id: I920ea7d9f156137f42e9e8ea44a3e6946d06b746 Reviewed-on: https://chromium-review.googlesource.com/556348 Commit-Ready: Zach Reizner <zachr@chromium.org> Tested-by: Zach Reizner <zachr@chromium.org> Reviewed-by: Dylan Reid <dgreid@chromium.org>
Diffstat (limited to 'sys_util')
-rw-r--r-- | sys_util/src/handle_eintr.rs | 201 | ||||
-rw-r--r-- | sys_util/src/lib.rs | 2 |
2 files changed, 203 insertions, 0 deletions
diff --git a/sys_util/src/handle_eintr.rs b/sys_util/src/handle_eintr.rs new file mode 100644 index 0000000..318afec --- /dev/null +++ b/sys_util/src/handle_eintr.rs @@ -0,0 +1,201 @@ +// Copyright 2017 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//! Macro and helper trait for handling interrupted routines. + +use std::io; + +use libc::EINTR; + +/// Trait for determining if a result indicates the operation was interrupted. +pub trait InterruptibleResult { + /// Returns `true` if this result indicates the operation was interrupted and should be retried, + /// and `false` in all other cases. + fn is_interrupted(&self) -> bool; +} + +impl InterruptibleResult for i32 { + fn is_interrupted(&self) -> bool { + *self == -EINTR + } +} + +impl<T> InterruptibleResult for ::Result<T> { + fn is_interrupted(&self) -> bool { + match self { + &Err(e) if e.errno() == -EINTR => true, + _ => false, + } + } +} + +impl<T> InterruptibleResult for io::Result<T> { + fn is_interrupted(&self) -> bool { + match self { + &Err(ref e) if e.kind() == io::ErrorKind::Interrupted => true, + _ => false, + } + } +} + +/// Macro that retries the given expression every time its result indicates it was interrupted (i.e. +/// returned `-EINTR`). This is useful for operations that are prone to being interrupted by +/// signals, such as blocking syscalls. +/// +/// The given expression `$x` can return +/// +/// * `i32` in which case the expression is retried if equal to `-EINTR`. +/// * `sys_util::Result` in which case the expression is retried if the `Error::errno()` is `-EINTR`. +/// * `std::io::Result` in which case the expression is retried if the `ErrorKind` is `ErrorKind::Interrupted`. +/// +/// In all cases where the result does not indicate that the expression was interrupted, the result +/// is returned verbatim to the caller of this macro. +/// +/// See the section titled _Interruption of system calls and library functions by signal handlers_ +/// on the man page for `signal(7)` to see more information about interruptible syscalls. +/// +/// To summarize, routines that use one of these syscalls _might_ need to handle `EINTR`: +/// +/// * `accept(2)` +/// * `clock_nanosleep(2)` +/// * `connect(2)` +/// * `epoll_pwait(2)` +/// * `epoll_wait(2)` +/// * `fcntl(2)` +/// * `fifo(7)` +/// * `flock(2)` +/// * `futex(2)` +/// * `getrandom(2)` +/// * `inotify(7)` +/// * `io_getevents(2)` +/// * `ioctl(2)` +/// * `mq_receive(3)` +/// * `mq_send(3)` +/// * `mq_timedreceive(3)` +/// * `mq_timedsend(3)` +/// * `msgrcv(2)` +/// * `msgsnd(2)` +/// * `nanosleep(2)` +/// * `open(2)` +/// * `pause(2)` +/// * `poll(2)` +/// * `ppoll(2)` +/// * `pselect(2)` +/// * `pthread_cond_wait(3)` +/// * `pthread_mutex_lock(3)` +/// * `read(2)` +/// * `readv(2)` +/// * `recv(2)` +/// * `recvfrom(2)` +/// * `recvmmsg(2)` +/// * `recvmsg(2)` +/// * `select(2)` +/// * `sem_timedwait(3)` +/// * `sem_wait(3)` +/// * `semop(2)` +/// * `semtimedop(2)` +/// * `send(2)` +/// * `sendmsg(2)` +/// * `sendto(2)` +/// * `setsockopt(2)` +/// * `sigsuspend(2)` +/// * `sigtimedwait(2)` +/// * `sigwaitinfo(2)` +/// * `sleep(3)` +/// * `usleep(3)` +/// * `wait(2)` +/// * `wait3(2)` +/// * `wait4(2)` +/// * `waitid(2)` +/// * `waitpid(2)` +/// * `write(2)` +/// * `writev(2)` +/// +/// # Examples +/// +/// ``` +/// # #[macro_use] extern crate sys_util; +/// # use std::io::stdin; +/// # fn main() { +/// let mut line = String::new(); +/// let res = handle_eintr!(stdin().read_line(&mut line)); +/// # } +/// ``` +#[macro_export] +macro_rules! handle_eintr { + ($x:expr) => ( + { + use $crate::handle_eintr::InterruptibleResult; + let res; + loop { + match $x { + ref v if v.is_interrupted() => continue, + v => { + res = v; + break; + } + } + } + res + } + ) +} + + +#[cfg(test)] +mod tests { + use super::*; + + use Error as SysError; + + #[test] + fn i32_eintr() { + let mut count = 3; + { + let mut dummy = || { + count -= 1; + if count > 0 { -EINTR } else { 56 } + }; + let res = handle_eintr!(dummy()); + assert_eq!(res, 56); + } + assert_eq!(count, 0); + } + + #[test] + fn sys_eintr() { + let mut count = 7; + { + let mut dummy = || { + count -= 1; + if count > 1 { + Err(SysError::new(-EINTR)) + } else { + Ok(101) + } + }; + let res = handle_eintr!(dummy()); + assert_eq!(res, Ok(101)); + } + assert_eq!(count, 1); + } + + #[test] + fn io_eintr() { + let mut count = 108; + { + let mut dummy = || { + count -= 1; + if count > 99 { + Err(io::Error::new(io::ErrorKind::Interrupted, "interrupted again :(")) + } else { + Ok(32) + } + }; + let res = handle_eintr!(dummy()); + assert_eq!(res.unwrap(), 32); + } + assert_eq!(count, 99); + } +} diff --git a/sys_util/src/lib.rs b/sys_util/src/lib.rs index 0d652ad..a1be658 100644 --- a/sys_util/src/lib.rs +++ b/sys_util/src/lib.rs @@ -7,6 +7,8 @@ extern crate data_model; extern crate libc; +#[macro_use] +pub mod handle_eintr; mod mmap; mod eventfd; mod errno; |