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