2017-06-29 18:35:17 +00:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2019-03-08 23:57:49 +00:00
|
|
|
impl<T> InterruptibleResult for crate::Result<T> {
|
2017-06-29 18:35:17 +00:00
|
|
|
fn is_interrupted(&self) -> bool {
|
|
|
|
match self {
|
lint: Resolve the easier clippy lints
Hopefully the changes are self-explanatory and uncontroversial. This
eliminates much of the noise from `cargo clippy` and, for my purposes,
gives me a reasonable way to use it as a tool when writing and reviewing
code.
Here is the Clippy invocation I was using:
cargo +nightly clippy -- -W clippy::correctness -A renamed_and_removed_lints -Aclippy::{blacklisted_name,borrowed_box,cast_lossless,cast_ptr_alignment,enum_variant_names,identity_op,if_same_then_else,mut_from_ref,needless_pass_by_value,new_without_default,new_without_default_derive,or_fun_call,ptr_arg,should_implement_trait,single_match,too_many_arguments,trivially_copy_pass_by_ref,unreadable_literal,unsafe_vector_initialization,useless_transmute}
TEST=cargo check --features wl-dmabuf,gpu,usb-emulation
TEST=boot linux
Change-Id: I55eb1b4a72beb2f762480e3333a921909314a0a2
Reviewed-on: https://chromium-review.googlesource.com/1356911
Commit-Ready: David Tolnay <dtolnay@chromium.org>
Tested-by: David Tolnay <dtolnay@chromium.org>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
2018-12-02 01:49:30 +00:00
|
|
|
Err(e) if e.errno() == EINTR => true,
|
2017-06-29 18:35:17 +00:00
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> InterruptibleResult for io::Result<T> {
|
|
|
|
fn is_interrupted(&self) -> bool {
|
|
|
|
match self {
|
lint: Resolve the easier clippy lints
Hopefully the changes are self-explanatory and uncontroversial. This
eliminates much of the noise from `cargo clippy` and, for my purposes,
gives me a reasonable way to use it as a tool when writing and reviewing
code.
Here is the Clippy invocation I was using:
cargo +nightly clippy -- -W clippy::correctness -A renamed_and_removed_lints -Aclippy::{blacklisted_name,borrowed_box,cast_lossless,cast_ptr_alignment,enum_variant_names,identity_op,if_same_then_else,mut_from_ref,needless_pass_by_value,new_without_default,new_without_default_derive,or_fun_call,ptr_arg,should_implement_trait,single_match,too_many_arguments,trivially_copy_pass_by_ref,unreadable_literal,unsafe_vector_initialization,useless_transmute}
TEST=cargo check --features wl-dmabuf,gpu,usb-emulation
TEST=boot linux
Change-Id: I55eb1b4a72beb2f762480e3333a921909314a0a2
Reviewed-on: https://chromium-review.googlesource.com/1356911
Commit-Ready: David Tolnay <dtolnay@chromium.org>
Tested-by: David Tolnay <dtolnay@chromium.org>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
2018-12-02 01:49:30 +00:00
|
|
|
Err(e) if e.kind() == io::ErrorKind::Interrupted => true,
|
2017-06-29 18:35:17 +00:00
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Macro that retries the given expression every time its result indicates it was interrupted (i.e.
|
2018-02-28 00:55:17 +00:00
|
|
|
/// returned `EINTR`). This is useful for operations that are prone to being interrupted by
|
2017-06-29 18:35:17 +00:00
|
|
|
/// signals, such as blocking syscalls.
|
|
|
|
///
|
|
|
|
/// The given expression `$x` can return
|
|
|
|
///
|
2018-02-28 00:41:52 +00:00
|
|
|
/// * `sys_util::Result` in which case the expression is retried if the `Error::errno()` is `EINTR`.
|
2017-06-29 18:35:17 +00:00
|
|
|
/// * `std::io::Result` in which case the expression is retried if the `ErrorKind` is `ErrorKind::Interrupted`.
|
|
|
|
///
|
2018-02-28 00:55:17 +00:00
|
|
|
/// Note that if expression returns i32 (i.e. either -1 or error code), then handle_eintr_errno()
|
|
|
|
/// or handle_eintr_rc() should be used instead.
|
|
|
|
///
|
2017-06-29 18:35:17 +00:00
|
|
|
/// 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
|
|
|
|
///
|
|
|
|
/// ```
|
2019-04-12 18:27:37 +00:00
|
|
|
/// # use sys_util::handle_eintr;
|
2017-06-29 18:35:17 +00:00
|
|
|
/// # 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 {
|
2018-10-03 17:22:32 +00:00
|
|
|
($x:expr) => {{
|
|
|
|
use $crate::handle_eintr::InterruptibleResult;
|
|
|
|
let res;
|
|
|
|
loop {
|
|
|
|
match $x {
|
|
|
|
ref v if v.is_interrupted() => continue,
|
|
|
|
v => {
|
|
|
|
res = v;
|
|
|
|
break;
|
2017-06-29 18:35:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-03 17:22:32 +00:00
|
|
|
res
|
|
|
|
}};
|
2017-06-29 18:35:17 +00:00
|
|
|
}
|
|
|
|
|
2018-02-28 00:55:17 +00:00
|
|
|
/// Macro that retries the given expression every time its result indicates it was interrupted.
|
|
|
|
/// It is intended to use with system functions that return `EINTR` and other error codes
|
|
|
|
/// directly as their result.
|
|
|
|
/// Most of reentrant functions use this way of signalling errors.
|
|
|
|
#[macro_export]
|
|
|
|
macro_rules! handle_eintr_rc {
|
2018-10-03 17:22:32 +00:00
|
|
|
($x:expr) => {{
|
|
|
|
use libc::EINTR;
|
|
|
|
let mut res;
|
|
|
|
loop {
|
|
|
|
res = $x;
|
|
|
|
if res != EINTR {
|
|
|
|
break;
|
2018-02-28 00:55:17 +00:00
|
|
|
}
|
|
|
|
}
|
2018-10-03 17:22:32 +00:00
|
|
|
res
|
|
|
|
}};
|
2018-02-28 00:55:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Macro that retries the given expression every time its result indicates it was interrupted.
|
|
|
|
/// It is intended to use with system functions that signal error by returning `-1` and setting
|
|
|
|
/// `errno` to appropriate error code (`EINTR`, `EINVAL`, etc.)
|
|
|
|
/// Most of standard non-reentrant libc functions use this way of signalling errors.
|
|
|
|
#[macro_export]
|
|
|
|
macro_rules! handle_eintr_errno {
|
2018-10-03 17:22:32 +00:00
|
|
|
($x:expr) => {{
|
|
|
|
use libc::EINTR;
|
|
|
|
use $crate::Error;
|
|
|
|
let mut res;
|
|
|
|
loop {
|
|
|
|
res = $x;
|
|
|
|
if res != -1 || Error::last() != Error::new(EINTR) {
|
|
|
|
break;
|
2018-02-28 00:55:17 +00:00
|
|
|
}
|
|
|
|
}
|
2018-10-03 17:22:32 +00:00
|
|
|
res
|
|
|
|
}};
|
2018-02-28 00:55:17 +00:00
|
|
|
}
|
2017-06-29 18:35:17 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2019-03-08 23:57:49 +00:00
|
|
|
use crate::errno::set_errno;
|
|
|
|
use crate::Error as SysError;
|
2017-06-29 18:35:17 +00:00
|
|
|
|
|
|
|
#[test]
|
2018-02-28 00:55:17 +00:00
|
|
|
fn i32_eintr_rc() {
|
2017-06-29 18:35:17 +00:00
|
|
|
let mut count = 3;
|
2019-04-15 22:30:21 +00:00
|
|
|
let mut dummy = || {
|
|
|
|
count -= 1;
|
|
|
|
if count > 0 {
|
|
|
|
EINTR
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let res = handle_eintr_rc!(dummy());
|
|
|
|
assert_eq!(res, 0);
|
2017-06-29 18:35:17 +00:00
|
|
|
assert_eq!(count, 0);
|
|
|
|
}
|
|
|
|
|
2018-02-28 00:55:17 +00:00
|
|
|
#[test]
|
|
|
|
fn i32_eintr_errno() {
|
|
|
|
let mut count = 3;
|
2019-04-15 22:30:21 +00:00
|
|
|
let mut dummy = || {
|
|
|
|
count -= 1;
|
|
|
|
if count > 0 {
|
|
|
|
set_errno(EINTR);
|
|
|
|
-1
|
|
|
|
} else {
|
|
|
|
56
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let res = handle_eintr_errno!(dummy());
|
|
|
|
assert_eq!(res, 56);
|
2018-02-28 00:55:17 +00:00
|
|
|
assert_eq!(count, 0);
|
|
|
|
}
|
|
|
|
|
2017-06-29 18:35:17 +00:00
|
|
|
#[test]
|
|
|
|
fn sys_eintr() {
|
|
|
|
let mut count = 7;
|
2019-04-15 22:30:21 +00:00
|
|
|
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));
|
2017-06-29 18:35:17 +00:00
|
|
|
assert_eq!(count, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn io_eintr() {
|
|
|
|
let mut count = 108;
|
2019-04-15 22:30:21 +00:00
|
|
|
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);
|
2017-06-29 18:35:17 +00:00
|
|
|
assert_eq!(count, 99);
|
|
|
|
}
|
|
|
|
}
|