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