• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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