base/sys/unix/
handle_eintr.rs

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
7use std::io;
8
9use libc::EINTR;
10
11/// Trait for determining if a result indicates the operation was interrupted.
12pub trait InterruptibleResult {
13    /// Returns `true` if this result indicates the operation was interrupted and should be retried,
14    /// and `false` in all other cases.
15    fn is_interrupted(&self) -> bool;
16}
17
18impl<T> InterruptibleResult for crate::Result<T> {
19    fn is_interrupted(&self) -> bool {
20        matches!(self, Err(e) if e.errno() == EINTR)
21    }
22}
23
24impl<T> InterruptibleResult for io::Result<T> {
25    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]
118macro_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]
140macro_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]
159macro_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)]
175mod tests {
176    use super::*;
177    use crate::Error as SysError;
178
179    // Sets errno to the given error code.
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]
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]
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]
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]
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}