summary refs log tree commit diff
path: root/sys_util
diff options
context:
space:
mode:
authorZach Reizner <zachr@google.com>2017-06-29 11:35:17 -0700
committerchrome-bot <chrome-bot@chromium.org>2017-06-30 12:51:24 -0700
commit79b2a732729219e2b4b0bc5ef8ab3ac23f28e562 (patch)
treeba8547e282f23705882fc6b65fc792677d269b94 /sys_util
parentfa8c6802b6a392a3dbc6300fabb47eeebf219ed6 (diff)
downloadcrosvm-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.rs201
-rw-r--r--sys_util/src/lib.rs2
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;