1 // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 //! Macro and helper trait for handling interrupted routines. 6 7 use std::io; 8 9 use libc::EINTR; 10 11 /// Trait for determining if a result indicates the operation was interrupted. 12 pub trait InterruptibleResult { 13 /// Returns `true` if this result indicates the operation was interrupted and should be retried, 14 /// and `false` in all other cases. is_interrupted(&self) -> bool15 fn is_interrupted(&self) -> bool; 16 } 17 18 impl<T> InterruptibleResult for crate::Result<T> { is_interrupted(&self) -> bool19 fn is_interrupted(&self) -> bool { 20 match self { 21 Err(e) if e.errno() == EINTR => true, 22 _ => false, 23 } 24 } 25 } 26 27 impl<T> InterruptibleResult for io::Result<T> { is_interrupted(&self) -> bool28 fn is_interrupted(&self) -> bool { 29 match self { 30 Err(e) if e.kind() == io::ErrorKind::Interrupted => true, 31 _ => false, 32 } 33 } 34 } 35 36 /// Macro that retries the given expression every time its result indicates it was interrupted (i.e. 37 /// returned `EINTR`). This is useful for operations that are prone to being interrupted by 38 /// signals, such as blocking syscalls. 39 /// 40 /// The given expression `$x` can return 41 /// 42 /// * `sys_util::Result` in which case the expression is retried if the `Error::errno()` is `EINTR`. 43 /// * `std::io::Result` in which case the expression is retried if the `ErrorKind` is `ErrorKind::Interrupted`. 44 /// 45 /// Note that if expression returns i32 (i.e. either -1 or error code), then handle_eintr_errno() 46 /// or handle_eintr_rc() should be used instead. 47 /// 48 /// In all cases where the result does not indicate that the expression was interrupted, the result 49 /// is returned verbatim to the caller of this macro. 50 /// 51 /// See the section titled _Interruption of system calls and library functions by signal handlers_ 52 /// on the man page for `signal(7)` to see more information about interruptible syscalls. 53 /// 54 /// To summarize, routines that use one of these syscalls _might_ need to handle `EINTR`: 55 /// 56 /// * `accept(2)` 57 /// * `clock_nanosleep(2)` 58 /// * `connect(2)` 59 /// * `epoll_pwait(2)` 60 /// * `epoll_wait(2)` 61 /// * `fcntl(2)` 62 /// * `fifo(7)` 63 /// * `flock(2)` 64 /// * `futex(2)` 65 /// * `getrandom(2)` 66 /// * `inotify(7)` 67 /// * `io_getevents(2)` 68 /// * `ioctl(2)` 69 /// * `mq_receive(3)` 70 /// * `mq_send(3)` 71 /// * `mq_timedreceive(3)` 72 /// * `mq_timedsend(3)` 73 /// * `msgrcv(2)` 74 /// * `msgsnd(2)` 75 /// * `nanosleep(2)` 76 /// * `open(2)` 77 /// * `pause(2)` 78 /// * `poll(2)` 79 /// * `ppoll(2)` 80 /// * `pselect(2)` 81 /// * `pthread_cond_wait(3)` 82 /// * `pthread_mutex_lock(3)` 83 /// * `read(2)` 84 /// * `readv(2)` 85 /// * `recv(2)` 86 /// * `recvfrom(2)` 87 /// * `recvmmsg(2)` 88 /// * `recvmsg(2)` 89 /// * `select(2)` 90 /// * `sem_timedwait(3)` 91 /// * `sem_wait(3)` 92 /// * `semop(2)` 93 /// * `semtimedop(2)` 94 /// * `send(2)` 95 /// * `sendmsg(2)` 96 /// * `sendto(2)` 97 /// * `setsockopt(2)` 98 /// * `sigsuspend(2)` 99 /// * `sigtimedwait(2)` 100 /// * `sigwaitinfo(2)` 101 /// * `sleep(3)` 102 /// * `usleep(3)` 103 /// * `wait(2)` 104 /// * `wait3(2)` 105 /// * `wait4(2)` 106 /// * `waitid(2)` 107 /// * `waitpid(2)` 108 /// * `write(2)` 109 /// * `writev(2)` 110 /// 111 /// # Examples 112 /// 113 /// ``` 114 /// # use sys_util::handle_eintr; 115 /// # use std::io::stdin; 116 /// # fn main() { 117 /// let mut line = String::new(); 118 /// let res = handle_eintr!(stdin().read_line(&mut line)); 119 /// # } 120 /// ``` 121 #[macro_export] 122 macro_rules! handle_eintr { 123 ($x:expr) => {{ 124 use $crate::handle_eintr::InterruptibleResult; 125 let res; 126 loop { 127 match $x { 128 ref v if v.is_interrupted() => continue, 129 v => { 130 res = v; 131 break; 132 } 133 } 134 } 135 res 136 }}; 137 } 138 139 /// Macro that retries the given expression every time its result indicates it was interrupted. 140 /// It is intended to use with system functions that return `EINTR` and other error codes 141 /// directly as their result. 142 /// Most of reentrant functions use this way of signalling errors. 143 #[macro_export] 144 macro_rules! handle_eintr_rc { 145 ($x:expr) => {{ 146 use libc::EINTR; 147 let mut res; 148 loop { 149 res = $x; 150 if res != EINTR { 151 break; 152 } 153 } 154 res 155 }}; 156 } 157 158 /// Macro that retries the given expression every time its result indicates it was interrupted. 159 /// It is intended to use with system functions that signal error by returning `-1` and setting 160 /// `errno` to appropriate error code (`EINTR`, `EINVAL`, etc.) 161 /// Most of standard non-reentrant libc functions use this way of signalling errors. 162 #[macro_export] 163 macro_rules! handle_eintr_errno { 164 ($x:expr) => {{ 165 use libc::EINTR; 166 use $crate::Error; 167 let mut res; 168 loop { 169 res = $x; 170 if res != -1 || Error::last() != Error::new(EINTR) { 171 break; 172 } 173 } 174 res 175 }}; 176 } 177 178 #[cfg(test)] 179 mod tests { 180 use super::*; 181 use crate::errno::set_errno; 182 use crate::Error as SysError; 183 184 #[test] i32_eintr_rc()185 fn i32_eintr_rc() { 186 let mut count = 3; 187 let mut dummy = || { 188 count -= 1; 189 if count > 0 { 190 EINTR 191 } else { 192 0 193 } 194 }; 195 let res = handle_eintr_rc!(dummy()); 196 assert_eq!(res, 0); 197 assert_eq!(count, 0); 198 } 199 200 #[test] i32_eintr_errno()201 fn i32_eintr_errno() { 202 let mut count = 3; 203 let mut dummy = || { 204 count -= 1; 205 if count > 0 { 206 set_errno(EINTR); 207 -1 208 } else { 209 56 210 } 211 }; 212 let res = handle_eintr_errno!(dummy()); 213 assert_eq!(res, 56); 214 assert_eq!(count, 0); 215 } 216 217 #[test] sys_eintr()218 fn sys_eintr() { 219 let mut count = 7; 220 let mut dummy = || { 221 count -= 1; 222 if count > 1 { 223 Err(SysError::new(EINTR)) 224 } else { 225 Ok(101) 226 } 227 }; 228 let res = handle_eintr!(dummy()); 229 assert_eq!(res, Ok(101)); 230 assert_eq!(count, 1); 231 } 232 233 #[test] io_eintr()234 fn io_eintr() { 235 let mut count = 108; 236 let mut dummy = || { 237 count -= 1; 238 if count > 99 { 239 Err(io::Error::new( 240 io::ErrorKind::Interrupted, 241 "interrupted again :(", 242 )) 243 } else { 244 Ok(32) 245 } 246 }; 247 let res = handle_eintr!(dummy()); 248 assert_eq!(res.unwrap(), 32); 249 assert_eq!(count, 99); 250 } 251 } 252