- When an async `SerialPort` IO operation times out, it reports the timeout in the IO completion with an `NTSTATUS` value of `WAIT_TIMEOUT` (258)
- In the thread pool when using `GetQueuedCompletionStatusEx`, the `NTSTATUS` value was being checked against `STATUS_SUCCESS` to determine success, so the `WAIT_TIMEOUT` was reported as an error. This leads to a different exception being thrown, compared to before when `GetQueuedCompletionStatus` was used.
- Fixed to use similar logic to the SDK's `NT_SUCCESS` macro, which treats the `WAIT_TIMEOUT` value as a success, which is similar to what `GetQueuedCompletionStatus` does
- There are already tests that verify this behavior in `System.IO.Ports` tests, though [they are currently disabled](https://github.com/dotnet/runtime/blob/
b39d6a6eb44860746e91e5ce4f585beff33d1f63/src/libraries/System.IO.Ports/tests/Support/TCSupport.cs#L108-L118) due to instabilities. I have verified locally that the relevant failures are fixed and that there are no new failures in those tests.
Fixes https://github.com/dotnet/runtime/issues/80079
{
internal static class StatusOptions
{
+ // See the NT_SUCCESS macro in the Windows SDK, and
+ // https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/using-ntstatus-values
+ internal static bool NT_SUCCESS(uint ntStatus) => (int)ntStatus >= 0;
+
// Error codes from ntstatus.h
internal const uint STATUS_SUCCESS = 0x00000000;
internal const uint STATUS_SOME_NOT_MAPPED = 0x00000107;
// The NtStatus code for the operation is in the InternalLow field
uint ntStatus = (uint)(nint)e.nativeOverlapped->InternalLow;
uint errorCode = Interop.Errors.ERROR_SUCCESS;
- if (ntStatus != Interop.StatusOptions.STATUS_SUCCESS)
+ if (!Interop.StatusOptions.NT_SUCCESS(ntStatus))
{
errorCode = Interop.NtDll.RtlNtStatusToDosError((int)ntStatus);
}