Skip to content

Commit 0bba217

Browse files
timeout: replace the busy loop with sigtimedwait (#9099)
1 parent 4f45042 commit 0bba217

File tree

2 files changed

+256
-268
lines changed

2 files changed

+256
-268
lines changed

src/uu/timeout/src/status.rs

Lines changed: 93 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,53 +2,105 @@
22
//
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
5+
56
//! Exit status codes produced by `timeout`.
7+
8+
use nix::errno::Errno;
9+
use std::error::Error;
10+
use std::fmt;
11+
use std::io;
12+
use std::os::unix::process::ExitStatusExt;
13+
use std::process::ExitStatus;
614
use uucore::error::UError;
15+
use uucore::translate;
16+
17+
#[derive(Debug)]
18+
pub(crate) enum TimeoutResult {
19+
/// The process exited before the timeout expired
20+
Exited(ExitStatus),
21+
/// The process was killed after the timeout expired
22+
TimedOut(ExitStatus),
23+
}
24+
25+
impl TimeoutResult {
26+
pub(crate) fn to_exit_status(&self, preserve_status: bool) -> ExitStatus {
27+
match self {
28+
Self::Exited(status) => *status,
29+
Self::TimedOut(status) => {
30+
if preserve_status {
31+
if let Some(signal) = status.signal() {
32+
// Despite the name of the option, GNU timeout does not actually fully
33+
// preserve the status of the child process if it timed out; it just sets
34+
// the exit code to the sh conventional value
35+
ExitStatus::from_raw((128 + signal) << 8)
36+
} else {
37+
*status
38+
}
39+
} else {
40+
ExitStatus::from_raw(124 << 8)
41+
}
42+
}
43+
}
44+
}
45+
}
46+
47+
#[derive(Debug)]
48+
pub(crate) enum TimeoutError {
49+
/// `timeout` itself failed
50+
Failure(Box<dyn UError>),
51+
/// Command was found but could not be invoked
52+
CommandFailedInvocation(io::Error),
53+
/// Command was not found
54+
CommandNotFound(io::Error),
55+
}
56+
57+
impl fmt::Display for TimeoutError {
58+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59+
match self {
60+
Self::Failure(err) => err.fmt(f),
61+
Self::CommandFailedInvocation(err) => {
62+
translate!("timeout-error-failed-to-execute-process", "error" => err).fmt(f)
63+
}
64+
Self::CommandNotFound(err) => {
65+
translate!("timeout-error-failed-to-execute-process", "error" => err).fmt(f)
66+
}
67+
}
68+
}
69+
}
70+
71+
impl Error for TimeoutError {}
72+
73+
impl UError for TimeoutError {
74+
fn code(&self) -> i32 {
75+
match self {
76+
Self::Failure(_) => 125,
77+
Self::CommandFailedInvocation(_) => 126,
78+
Self::CommandNotFound(_) => 127,
79+
}
80+
}
781

8-
/// Enumerates the exit statuses produced by `timeout`.
9-
///
10-
/// Use [`Into::into`] (or [`From::from`]) to convert an enumeration
11-
/// member into a numeric status code. You can also convert into a
12-
/// [`UError`].
13-
///
14-
/// # Examples
15-
///
16-
/// Convert into an [`i32`]:
17-
///
18-
/// ```rust,ignore
19-
/// assert_eq!(i32::from(ExitStatus::CommandTimedOut), 124);
20-
/// ```
21-
pub(crate) enum ExitStatus {
22-
/// When the child process times out and `--preserve-status` is not specified.
23-
CommandTimedOut,
24-
25-
/// When `timeout` itself fails.
26-
TimeoutFailed,
27-
28-
/// When a signal is sent to the child process or `timeout` itself.
29-
SignalSent(usize),
30-
31-
/// When there is a failure while waiting for the child process to terminate.
32-
WaitingFailed,
33-
34-
/// When `SIGTERM` signal received.
35-
Terminated,
36-
}
37-
38-
impl From<ExitStatus> for i32 {
39-
fn from(exit_status: ExitStatus) -> Self {
40-
match exit_status {
41-
ExitStatus::CommandTimedOut => 124,
42-
ExitStatus::TimeoutFailed => 125,
43-
ExitStatus::SignalSent(s) => 128 + s as Self,
44-
ExitStatus::WaitingFailed => 124,
45-
ExitStatus::Terminated => 143,
82+
fn usage(&self) -> bool {
83+
match self {
84+
Self::Failure(err) => err.usage(),
85+
_ => false,
4686
}
4787
}
4888
}
4989

50-
impl From<ExitStatus> for Box<dyn UError> {
51-
fn from(exit_status: ExitStatus) -> Self {
52-
Box::from(i32::from(exit_status))
90+
impl From<Box<dyn UError>> for TimeoutError {
91+
fn from(err: Box<dyn UError>) -> Self {
92+
Self::Failure(err)
93+
}
94+
}
95+
96+
impl From<io::Error> for TimeoutError {
97+
fn from(err: io::Error) -> Self {
98+
Self::Failure(err.into())
99+
}
100+
}
101+
102+
impl From<Errno> for TimeoutError {
103+
fn from(err: Errno) -> Self {
104+
Self::Failure(err.into())
53105
}
54106
}

0 commit comments

Comments
 (0)