Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/base/src/sys/unix/handle_eintr.rs
5394 views
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.
15
fn is_interrupted(&self) -> bool;
16
}
17
18
impl<T> InterruptibleResult for crate::Result<T> {
19
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> {
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]
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.
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
}
268
269