Skip to content

Breaking change with timeout reading serial port in .NET 7 (Possible Overlapped I/O change) #80079

@dementedmonkey

Description

@dementedmonkey

Description

In .NET 6 and earlier, when System.IO.Ports.SerialPort.Read times out, it will throw a TimeoutException. In .NET 7, it will throw an IOException with an HResult of 0x800705B4 (1460: ERROR_TIMEOUT)

Reproduction Steps

Use the following code (substitute your own valid serial port values)

  • VS2022 or .NET 7 SDK on Windows.
  • Nuget Reference to System.IO.Ports version 7.0.0 (OK with eariler versions too)
  • Target net7.0 to see it fail, or net6.0 to see it work.
using System;
using System.IO;
using System.IO.Ports;

class Program
{
    static void Main()
    {
        var serialPort = new SerialPort("COM42", 115200, Parity.None, 8, StopBits.One)
        {
            DtrEnable = false,
            WriteTimeout = 500,
            ReadTimeout = 500,
        };
        serialPort.Open();
        var data = new byte[100];
        try
        {
            var numRead = serialPort.Read(data, 0, data.Length);
            Console.WriteLine("Read {0}", numRead);
        }
        catch (TimeoutException)
        {
            Console.WriteLine(".NET <=6 Timed out");
        }
        catch (IOException e)
        {
            Console.WriteLine(".NET 7: IO {0}  HR:0x{1:X}", e.HResult & 0xFFFF, e.HResult);
        }
    }
}

Expected behavior

Output of .NET <=6 Timed out

Actual behavior

Output of .NET 7: IO 1460 HR:0x800705B4

Regression?

Yes. Worked for .NET 6 and 5.

You can easily test this but switching target framework. If you switch to .NET 6 you get the TimeoutException, if you switch to .NET 7 you get the IOException

Known Workarounds

Code catching the TimeoutException would have to catch the IOException check the HResult and handle it the same way.

Configuration

.net 7.0.101 Windows 11, Target X64 or x86 has the problem.
Debug or release doesn't seem to matter.

Ubuntu WSL2 with 7.0.101 does not have the problem.

Other information

This seems to be caused by changed in overlapped IO introduced in .net7

The difference can be observed here:


On .net6, the errorCode is 0, on .net7 it's 1460

The call stack invoking this function is very different on .net 6 and 7.

On .net7, AsyncFSCallback is getttiong called from PortableThreadPool and the error is set here:

errorCode = Interop.NtDll.RtlNtStatusToDosError((int)ntStatus);

On .net6, it's getting called from _IOCompletionCallback.PerformIOCompletionCallback The stack stops there, and I didn't dig any deeper to see where it's getting its parameters from.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions