• 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         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