// Copyright 2017 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34//! Macro and helper trait for handling interrupted routines.56use std::io;78use libc::EINTR;910/// Trait for determining if a result indicates the operation was interrupted.11pub trait InterruptibleResult {12/// Returns `true` if this result indicates the operation was interrupted and should be retried,13/// and `false` in all other cases.14fn is_interrupted(&self) -> bool;15}1617impl<T> InterruptibleResult for crate::Result<T> {18fn is_interrupted(&self) -> bool {19matches!(self, Err(e) if e.errno() == EINTR)20}21}2223impl<T> InterruptibleResult for io::Result<T> {24fn is_interrupted(&self) -> bool {25matches!(self, Err(e) if e.kind() == io::ErrorKind::Interrupted)26}27}2829/// Macro that retries the given expression every time its result indicates it was interrupted (i.e.30/// returned `EINTR`). This is useful for operations that are prone to being interrupted by31/// signals, such as blocking syscalls.32///33/// The given expression `$x` can return34///35/// * `crate::linux::Result` in which case the expression is retried if the `Error::errno()` is36/// `EINTR`.37/// * `std::io::Result` in which case the expression is retried if the `ErrorKind` is38/// `ErrorKind::Interrupted`.39///40/// Note that if expression returns i32 (i.e. either -1 or error code), then handle_eintr_errno()41/// or handle_eintr_rc() should be used instead.42///43/// In all cases where the result does not indicate that the expression was interrupted, the result44/// is returned verbatim to the caller of this macro.45///46/// See the section titled _Interruption of system calls and library functions by signal handlers_47/// on the man page for `signal(7)` to see more information about interruptible syscalls.48///49/// To summarize, routines that use one of these syscalls _might_ need to handle `EINTR`:50///51/// * `accept(2)`52/// * `clock_nanosleep(2)`53/// * `connect(2)`54/// * `epoll_pwait(2)`55/// * `epoll_wait(2)`56/// * `fcntl(2)`57/// * `fifo(7)`58/// * `flock(2)`59/// * `futex(2)`60/// * `getrandom(2)`61/// * `inotify(7)`62/// * `io_getevents(2)`63/// * `ioctl(2)`64/// * `mq_receive(3)`65/// * `mq_send(3)`66/// * `mq_timedreceive(3)`67/// * `mq_timedsend(3)`68/// * `msgrcv(2)`69/// * `msgsnd(2)`70/// * `nanosleep(2)`71/// * `open(2)`72/// * `pause(2)`73/// * `poll(2)`74/// * `ppoll(2)`75/// * `pselect(2)`76/// * `pthread_cond_wait(3)`77/// * `pthread_mutex_lock(3)`78/// * `read(2)`79/// * `readv(2)`80/// * `recv(2)`81/// * `recvfrom(2)`82/// * `recvmmsg(2)`83/// * `recvmsg(2)`84/// * `select(2)`85/// * `sem_timedwait(3)`86/// * `sem_wait(3)`87/// * `semop(2)`88/// * `semtimedop(2)`89/// * `send(2)`90/// * `sendmsg(2)`91/// * `sendto(2)`92/// * `setsockopt(2)`93/// * `sigsuspend(2)`94/// * `sigtimedwait(2)`95/// * `sigwaitinfo(2)`96/// * `sleep(3)`97/// * `usleep(3)`98/// * `wait(2)`99/// * `wait3(2)`100/// * `wait4(2)`101/// * `waitid(2)`102/// * `waitpid(2)`103/// * `write(2)`104/// * `writev(2)`105///106/// # Examples107///108/// ```109/// # use base::handle_eintr;110/// # use std::io::stdin;111/// # fn main() {112/// let mut line = String::new();113/// let res = handle_eintr!(stdin().read_line(&mut line));114/// # }115/// ```116#[macro_export]117macro_rules! handle_eintr {118($x:expr) => {{119use $crate::unix::handle_eintr::InterruptibleResult;120let res;121loop {122match $x {123ref v if v.is_interrupted() => continue,124v => {125res = v;126break;127}128}129}130res131}};132}133134/// Macro that retries the given expression every time its result indicates it was interrupted.135/// It is intended to use with system functions that return `EINTR` and other error codes136/// directly as their result.137/// Most of reentrant functions use this way of signalling errors.138#[macro_export]139macro_rules! handle_eintr_rc {140($x:expr) => {{141use libc::EINTR;142let mut res;143loop {144res = $x;145if res != EINTR {146break;147}148}149res150}};151}152153/// Macro that retries the given expression every time its result indicates it was interrupted.154/// It is intended to use with system functions that signal error by returning `-1` and setting155/// `errno` to appropriate error code (`EINTR`, `EINVAL`, etc.)156/// Most of standard non-reentrant libc functions use this way of signalling errors.157#[macro_export]158macro_rules! handle_eintr_errno {159($x:expr) => {{160use libc::EINTR;161use $crate::Error;162let mut res;163loop {164res = $x;165if res != -1 || Error::last() != Error::new(EINTR) {166break;167}168}169res170}};171}172173#[cfg(test)]174mod tests {175use super::*;176use crate::Error as SysError;177178// Sets errno to the given error code.179fn set_errno(e: i32) {180#[cfg(target_os = "android")]181unsafe fn errno_location() -> *mut libc::c_int {182libc::__errno()183}184#[cfg(target_os = "linux")]185unsafe fn errno_location() -> *mut libc::c_int {186libc::__errno_location()187}188#[cfg(target_os = "macos")]189unsafe fn errno_location() -> *mut libc::c_int {190libc::__error()191}192193// SAFETY: trivially safe194unsafe {195*errno_location() = e;196}197}198199#[test]200fn i32_eintr_rc() {201let mut count = 3;202let mut dummy = || {203count -= 1;204if count > 0 {205EINTR206} else {2070208}209};210let res = handle_eintr_rc!(dummy());211assert_eq!(res, 0);212assert_eq!(count, 0);213}214215#[test]216fn i32_eintr_errno() {217let mut count = 3;218let mut dummy = || {219count -= 1;220if count > 0 {221set_errno(EINTR);222-1223} else {22456225}226};227let res = handle_eintr_errno!(dummy());228assert_eq!(res, 56);229assert_eq!(count, 0);230}231232#[test]233fn sys_eintr() {234let mut count = 7;235let mut dummy = || {236count -= 1;237if count > 1 {238Err(SysError::new(EINTR))239} else {240Ok(101)241}242};243let res = handle_eintr!(dummy());244assert_eq!(res, Ok(101));245assert_eq!(count, 1);246}247248#[test]249fn io_eintr() {250let mut count = 108;251let mut dummy = || {252count -= 1;253if count > 99 {254Err(io::Error::new(255io::ErrorKind::Interrupted,256"interrupted again :(",257))258} else {259Ok(32)260}261};262let res = handle_eintr!(dummy());263assert_eq!(res.unwrap(), 32);264assert_eq!(count, 99);265}266}267268269