Skip to content

Commit 6747a11

Browse files
authored
Rollup merge of rust-lang#71756 - carstenandrich:master, r=dtolnay
add Windows system error codes that should map to io::ErrorKind::TimedOut closes rust-lang#71646 **Disclaimer:** The author of this pull request has a negligible amount of experience (i.e., kinda zero) with the Windows API. This PR should _definitely_ be reviewed by someone familiar with the API and its error handling. While porting POSIX software using serial ports to Windows, I found that for many Windows system error codes, an `io::Error` created via `io::Error::from_raw_os_error()` or `io::Error::last_os_error()` is not `io::ErrorKind::TimedOut`. For example, when a (non-overlapped) write to a COM port via [`WriteFile()`](https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile) times out, [`GetLastError()`](https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror) returns `ERROR_SEM_TIMEOUT` ([121](https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-)). However, an `io::Error` created from this error code will have `io::ErrorKind::Other`. Currently, only the error codes `ERROR_OPERATION_ABORTED` and `WSAETIMEDOUT` will instantiate `io::Error`s with kind `io::ErrorKind::TimedOut`. This makes `io::Error::last_os_error()` unsuitable for error handling of syscalls that could time out, because timeouts can not be caught by matching the error's kind against `io::ErrorKind::TimedOut`. Downloading the [list of Windows system error codes](https://gist.github.com/carstenandrich/c331d557520b8a0e7f44689ca257f805) and grepping anything that sounds like a timeout (`egrep -i "timed?.?(out|limit)"`), I've identified the following error codes that should also have `io::ErrorKind::TimedOut`, because they could be I/O-related: Name | Code | Description --- | --- | --- `ERROR_SEM_TIMEOUT` | [121](https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-) | The semaphore timeout period has expired. `WAIT_TIMEOUT` | [258](https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-) | The wait operation timed out. `ERROR_DRIVER_CANCEL_TIMEOUT` | [594](https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--500-999-) | The driver %hs failed to complete a cancelled I/O request in the allotted time. `ERROR_COUNTER_TIMEOUT` | [1121](https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--1000-1299-) | A serial I/O operation completed because the timeout period expired. The IOCTL_SERIAL_XOFF_COUNTER did not reach zero.) `ERROR_TIMEOUT` | [1460](https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--1300-1699-) | This operation returned because the timeout period expired. `ERROR_CTX_MODEM_RESPONSE_TIMEOUT` | [7012](https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--6000-8199-) | The modem did not respond to the command sent to it. Verify that the modem is properly cabled and powered on. `ERROR_CTX_CLIENT_QUERY_TIMEOUT` | [7040](https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--6000-8199-) | The client failed to respond to the server connect message. `ERROR_DS_TIMELIMIT_EXCEEDED` | [8226](https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--8200-8999-) | The time limit for this request was exceeded. `DNS_ERROR_RECORD_TIMED_OUT` | [9705](https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--9000-11999-) | DNS record timed out. `ERROR_IPSEC_IKE_TIMED_OUT` | [13805](https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--12000-15999-) | Negotiation timed out. The following errors are also timeouts, but they don't seem to be directly related to I/O or network operations: Name | Code | Description --- | --- | --- `ERROR_SERVICE_REQUEST_TIMEOUT` | [1053](https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--1000-1299-) | The service did not respond to the start or control request in a timely fashion. `ERROR_RESOURCE_CALL_TIMED_OUT` | [5910](https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--4000-5999-) | The call to the cluster resource DLL timed out. `FRS_ERR_SYSVOL_POPULATE_TIMEOUT` | [8014](https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--6000-8199-) | The file replication service cannot populate the system volume because of an internal timeout. The event log may have more information. `ERROR_RUNLEVEL_SWITCH_TIMEOUT` | [15402](https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--12000-15999-) | The requested run level switch cannot be completed successfully since one or more services will not stop or restart within the specified timeout. `ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT` | [15403](https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--12000-15999-) | A run level switch agent did not respond within the specified timeout. Please note that `ERROR_SEM_TIMEOUT` is the only timeout error I have [seen in action](https://gist.github.com/carstenandrich/10b3962fa1abc9e50816b6460010900b). The remainder of the error codes listed above is based purely on reading documentation. This pull request adds all of the errors listed in both tables, but I'm not sure whether adding all of them makes sense. Someone with actual Windows API experience should decide that. I expect these changes to be fairly backwards compatible, because only the error's [`.kind()`](https://doc.rust-lang.org/std/io/struct.Error.html#method.kind) will change, but matching the error's code via [`.raw_os_error()`](https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error) will not be affected. However, code expecting these errors to be `io::ErrorKind::Other` would break. Even though I personally do not think such an implementation would make sense, after all the docs say that `io::ErrorKind` is _intended to grow over time_, a residual risk remains, of course. I took the liberty to ammend the docstring of `io::ErrorKind::Other` with a remark that discourages matching against it. As per the contributing guidelines I'm adding @steveklabnik due to the changed documentation. Also @retep998 might have some valuable insights on the error codes. r? @steveklabnik cc @retep998 cc @Mark-Simulacrum
2 parents 6bb3dbf + e27a8b5 commit 6747a11

File tree

3 files changed

+38
-3
lines changed

3 files changed

+38
-3
lines changed

src/libstd/io/error.rs

+5
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,11 @@ pub enum ErrorKind {
160160
#[stable(feature = "rust1", since = "1.0.0")]
161161
Interrupted,
162162
/// Any I/O error not part of this list.
163+
///
164+
/// Errors that are `Other` now may move to a different or a new
165+
/// [`ErrorKind`] variant in the future. It is not recommended to match
166+
/// an error against `Other` and to expect any additional characteristics,
167+
/// e.g., a specific [`Error::raw_os_error`] return value.
163168
#[stable(feature = "rust1", since = "1.0.0")]
164169
Other,
165170

src/libstd/sys/windows/c.rs

+17-2
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ pub const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD;
161161

162162
pub const PROGRESS_CONTINUE: DWORD = 0;
163163

164+
// List of Windows system error codes with descriptions:
165+
// https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes#system-error-codes
164166
pub const ERROR_FILE_NOT_FOUND: DWORD = 2;
165167
pub const ERROR_PATH_NOT_FOUND: DWORD = 3;
166168
pub const ERROR_ACCESS_DENIED: DWORD = 5;
@@ -171,13 +173,26 @@ pub const ERROR_FILE_EXISTS: DWORD = 80;
171173
pub const ERROR_INVALID_PARAMETER: DWORD = 87;
172174
pub const ERROR_BROKEN_PIPE: DWORD = 109;
173175
pub const ERROR_CALL_NOT_IMPLEMENTED: DWORD = 120;
176+
pub const ERROR_SEM_TIMEOUT: DWORD = 121;
174177
pub const ERROR_INSUFFICIENT_BUFFER: DWORD = 122;
175178
pub const ERROR_ALREADY_EXISTS: DWORD = 183;
176-
pub const ERROR_NO_DATA: DWORD = 232;
177179
pub const ERROR_ENVVAR_NOT_FOUND: DWORD = 203;
180+
pub const ERROR_NO_DATA: DWORD = 232;
181+
pub const ERROR_DRIVER_CANCEL_TIMEOUT: DWORD = 594;
178182
pub const ERROR_OPERATION_ABORTED: DWORD = 995;
179183
pub const ERROR_IO_PENDING: DWORD = 997;
180-
pub const ERROR_TIMEOUT: DWORD = 0x5B4;
184+
pub const ERROR_SERVICE_REQUEST_TIMEOUT: DWORD = 1053;
185+
pub const ERROR_COUNTER_TIMEOUT: DWORD = 1121;
186+
pub const ERROR_TIMEOUT: DWORD = 1460;
187+
pub const ERROR_RESOURCE_CALL_TIMED_OUT: DWORD = 5910;
188+
pub const ERROR_CTX_MODEM_RESPONSE_TIMEOUT: DWORD = 7012;
189+
pub const ERROR_CTX_CLIENT_QUERY_TIMEOUT: DWORD = 7040;
190+
pub const FRS_ERR_SYSVOL_POPULATE_TIMEOUT: DWORD = 8014;
191+
pub const ERROR_DS_TIMELIMIT_EXCEEDED: DWORD = 8226;
192+
pub const DNS_ERROR_RECORD_TIMED_OUT: DWORD = 9705;
193+
pub const ERROR_IPSEC_IKE_TIMED_OUT: DWORD = 13805;
194+
pub const ERROR_RUNLEVEL_SWITCH_TIMEOUT: DWORD = 15402;
195+
pub const ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT: DWORD = 15403;
181196

182197
pub const E_NOTIMPL: HRESULT = 0x80004001u32 as HRESULT;
183198

src/libstd/sys/windows/mod.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,22 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind {
6161
c::ERROR_FILE_NOT_FOUND => return ErrorKind::NotFound,
6262
c::ERROR_PATH_NOT_FOUND => return ErrorKind::NotFound,
6363
c::ERROR_NO_DATA => return ErrorKind::BrokenPipe,
64-
c::ERROR_OPERATION_ABORTED => return ErrorKind::TimedOut,
64+
c::ERROR_SEM_TIMEOUT
65+
| c::WAIT_TIMEOUT
66+
| c::ERROR_DRIVER_CANCEL_TIMEOUT
67+
| c::ERROR_OPERATION_ABORTED
68+
| c::ERROR_SERVICE_REQUEST_TIMEOUT
69+
| c::ERROR_COUNTER_TIMEOUT
70+
| c::ERROR_TIMEOUT
71+
| c::ERROR_RESOURCE_CALL_TIMED_OUT
72+
| c::ERROR_CTX_MODEM_RESPONSE_TIMEOUT
73+
| c::ERROR_CTX_CLIENT_QUERY_TIMEOUT
74+
| c::FRS_ERR_SYSVOL_POPULATE_TIMEOUT
75+
| c::ERROR_DS_TIMELIMIT_EXCEEDED
76+
| c::DNS_ERROR_RECORD_TIMED_OUT
77+
| c::ERROR_IPSEC_IKE_TIMED_OUT
78+
| c::ERROR_RUNLEVEL_SWITCH_TIMEOUT
79+
| c::ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT => return ErrorKind::TimedOut,
6580
_ => {}
6681
}
6782

0 commit comments

Comments
 (0)