Skip to content

Windows: io::Error::from_raw_os_error(ERROR_TIMEOUT) is not io::ErrorKind::TimedOut #71646

Description

@carstenandrich

On Windows an io::Error created via from_raw_os_error() with the Windows system error code ERROR_TIMEOUT (1460) is not of the kind io::ErrorKind::TimedOut, but io::ErrorKind::Other.
This is counter-intuitive considering both (system error code and kind) have (almost) the same symbolic name.

I've tested this with Rust 1.42 on x86_64-pc-windows-gnu, but this behavior is obviously still present in git master. Unless I'm overlooking sth., this should be fairly easy to fix by defining the c::ERROR_TIMEOUT constant and adding the following after this line:

c::ERROR_TIMEOUT => return ErrorKind::TimedOut,

If you'd like me to, I can prepare a merge request implementing the suggested fix.

I tried this code:

use std::io;

// https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--500-999-
const ERROR_OPERATION_ABORTED: i32 = 995;
// https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--1300-1699-
const ERROR_TIMEOUT: i32 = 1460;

fn main() {
	assert_eq!(io::Error::from_raw_os_error(ERROR_OPERATION_ABORTED).kind(),
			io::ErrorKind::TimedOut);
	assert_eq!(io::Error::from_raw_os_error(ERROR_TIMEOUT).kind(),
			io::ErrorKind::TimedOut);
}

I expected to see this happen: Assertion passes because (should be):

io::Error::from_raw_os_error(ERROR_TIMEOUT).kind() == io::ErrorKind::TimedOut`

Instead, this happened: Assertion fails because:

io::Error::from_raw_os_error(ERROR_TIMEOUT).kind() == io::ErrorKind::Other`

Meta

rustc --version --verbose:

rustc 1.42.0 (b8cedc004 2020-03-09)
binary: rustc
commit-hash: b8cedc00407a4c56a3bda1ed605c6fc166655447
commit-date: 2020-03-09
host: x86_64-pc-windows-gnu
release: 1.42.0
LLVM version: 9.0
Backtrace

thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `Other`,
 right: `TimedOut`', src\main.rs:9:5
stack backtrace:
   0: backtrace::backtrace::dbghelp::trace
             at C:\Users\VssAdministrator\.cargo\registry\src\github.life-white.uk-1ecc6299db9ec823\backtrace-0.3.40\src\backtrace/dbghelp.rs:88
   1: backtrace::backtrace::trace_unsynchronized
             at C:\Users\VssAdministrator\.cargo\registry\src\github.life-white.uk-1ecc6299db9ec823\backtrace-0.3.40\src\backtrace/mod.rs:66
   2: std::sys_common::backtrace::_print_fmt
             at src\libstd\sys_common/backtrace.rs:77
   3: <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt
             at src\libstd\sys_common/backtrace.rs:59
   4: core::fmt::write
             at src\libcore\fmt/mod.rs:1052
   5: std::io::Write::write_fmt
             at src\libstd\io/mod.rs:1426
   6: std::sys_common::backtrace::_print
             at src\libstd\sys_common/backtrace.rs:62
   7: std::sys_common::backtrace::print
             at src\libstd\sys_common/backtrace.rs:49
   8: std::panicking::default_hook::{{closure}}
             at src\libstd/panicking.rs:204
   9: std::panicking::default_hook
             at src\libstd/panicking.rs:224
  10: std::panicking::rust_panic_with_hook
             at src\libstd/panicking.rs:472
  11: rust_begin_unwind
             at src\libstd/panicking.rs:380
  12: std::panicking::begin_panic_fmt
             at src\libstd/panicking.rs:334
  13: rust_test::main
             at src/main.rs:9
  14: std::rt::lang_start::{{closure}}
             at /rustc/b8cedc00407a4c56a3bda1ed605c6fc166655447\src\libstd/rt.rs:67
  15: std::rt::lang_start_internal::{{closure}}
             at src\libstd/rt.rs:52
  16: std::panicking::try::do_call
             at src\libstd/panicking.rs:305
  17: __rust_maybe_catch_panic
             at src\libpanic_unwind/lib.rs:86
  18: std::panicking::try
             at src\libstd/panicking.rs:281
  19: std::panic::catch_unwind
             at src\libstd/panic.rs:394
  20: std::rt::lang_start_internal
             at src\libstd/rt.rs:51
  21: std::rt::lang_start
             at /rustc/b8cedc00407a4c56a3bda1ed605c6fc166655447\src\libstd/rt.rs:67
  22: main
  23: _tmainCRTStartup
  24: mainCRTStartup
  25: unit_addrs_search
  26: unit_addrs_search
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
error: process didn't exit successfully: `target\debug\rust-test.exe` (exit code: 101)

Activity

  1. carstenandrich commented on Apr 29, 2020

    @carstenandrich
    ContributorAuthor

    This issue is not limited to the system error code ERROR_TIMEOUT, as Windows appears to use various error codes for what would all be ETIMEDOUT on POSIX. For example, I'm getting an ERROR_SEM_TIMEOUT (121) if a write operation on a COM port times out (on Windows 10 version 1809).

    Looking at the list of Windows system error codes, I found the following that contain TIME and appear to be timeouts:

    • ERROR_SEM_TIMEOUT (121)
    • WAIT_TIMEOUT (258)
    • ERROR_DRIVER_CANCEL_TIMEOUT (594)
    • ERROR_SERVICE_REQUEST_TIMEOUT (1053)
    • ERROR_COUNTER_TIMEOUT (1121)
    • ERROR_TIMEOUT (1460)
    • RPC_S_INVALID_TIMEOUT (1709) Thanks to @retep998 for pointing out that this error code indicates an invalid timeout and not that a timeout occurred.
    • ERROR_RESOURCE_CALL_TIMED_OUT (5910)
    • ERROR_CTX_MODEM_RESPONSE_TIMEOUT (7012)
    • ERROR_CTX_CLIENT_QUERY_TIMEOUT (7040)
    • FRS_ERR_SYSVOL_POPULATE_TIMEOUT (8014)
    • ERROR_DS_TIMELIMIT_EXCEEDED (8226)
    • DNS_ERROR_RECORD_TIMED_OUT (9705)
    • WSAETIMEDOUT (10060)
    • ERROR_IPSEC_IKE_TIMED_OUT (13805)
    • ERROR_RUNLEVEL_SWITCH_TIMEOUT (15402)
    • ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT (15403)
  2. Mark-Simulacrum commented on Apr 29, 2020

    @Mark-Simulacrum
    Member

    One concern with merging these all into ErrorKind::TimedOut is that the caller would no longer know which of the timeout occurred -- are there cases where that's important?

  3. carstenandrich commented on Apr 29, 2020

    @carstenandrich
    ContributorAuthor

    I don't think that's the case, but please correct me if I'm wrong. The following example

    use std::io;
    
    const ERROR_OPERATION_ABORTED: i32 = 995;
    const ERROR_TIMEOUT: i32 = 1460;
    
    fn main() {
            println!("{:?}", io::Error::from_raw_os_error(ERROR_OPERATION_ABORTED));
            println!("{:?}", io::Error::from_raw_os_error(ERROR_TIMEOUT));
    }

    will produce this on Windows:

    Os { code: 995, kind: TimedOut, message: "The I/O operation has been aborted because of either a thread exit or an application request." }
    Os { code: 1460, kind: Other, message: "This operation returned because the timeout period expired." }
    

    Currently, only ERROR_OPERATION_ABORTED will yield an io::Error of io::ErrorKind::TimedOut. All other timeout errors will be of io::ErrorKind::Other, which is counter-intuitive, because matching an error's kind against io::ErrorKind::TimedOut will not catch all timeout errors.

    I would suggest to only change the kind of the errors listed above to io::ErrorKind::TimedOut. The original OS error code can still be accessed via raw_os_error() if the programmer is interested in the OS-specific timeout reason. Therefore, this should not break backwards-compatibility, unless someone expects these errors to be io::ErrorKind::Other, which should be discouraged.

  4. Mark-Simulacrum commented on Apr 29, 2020

    @Mark-Simulacrum
    Member

    Ah, okay, right. I would in that case not be personally opposed to making this change, I think a PR that does so would be the best way to do so (and that can be FCP'd to libs team).

  5. carstenandrich commented on Apr 29, 2020

    @carstenandrich
    ContributorAuthor

    All right. Thanks for the quick response. I'll prepare a pull request.

  6. retep998 commented on Apr 29, 2020

    @retep998
    Contributor

    Note that RPC_S_INVALID_TIMEOUT does not indicate the operation timed out, but rather that the timeout you specified was invalid.

  7. carstenandrich commented on Apr 29, 2020

    @carstenandrich
    ContributorAuthor

    @retep998 Thanks for checking that. I'll revise the list of errors before submitting the PR, but I'm afraid I have very close to zero experience with the Windows API. I just stumbled over this issue while porting POSIX software to Windows.

  8. added 2 commits that reference this issue on Jun 21, 2020
    400b4cb
    311f99f
  9. added a commit that references this issue on Jun 22, 2020
    c9b7199
  10. added a commit that references this issue on Jun 22, 2020
    6747a11
  11. added a commit that references this issue on Jun 23, 2020
    6276c13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.O-windowsOperating system: WindowsT-libs-api[DEPRECATED; DO NOT USE]

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions