Closed adrianvmsft closed 2 years ago
There is no general and safe way to force cancellation of I/O that underlies a Stream-derived class as a consumer of Stream. They may or may not honor the CancellationToken. Disposing the Stream can sometimes force a Read call to return 0, but it also risks thread-safety issues to do so because Stream isn't expected to be thread-safe.
What we theoretically could do here is do a runtime type check and if the stream is a named pipe stream, we could wrap it in our own stream that implements the I/O ourselves in a cancelable way by basically copying code from https://github.com/dotnet/runtime/pull/72503 and https://github.com/dotnet/runtime/pull/72612. This seems extremely heavy though. And since the repro for this is only in a VS test, which has already worked around the problem, I'm going to close this as Won't Fix. It will work properly on .NET 7 where the named pipe class now has cancelable I/O.
This issue was encountered by internal automation - when running 2 tests using ServiceBroker in a row causes a deadlock when each of the tests calls ServiceBroker.DisposeAsync at the end.
The first async task waits on the work reported by the 2nd async task to be completed first. That never happens, despite the fact the cancellation token that is supposed to cancel is triggered.
The root cause is that the CancellationToken in Stream.ReadAsync is ignored: https://github.com/dotnet/runtime/issues/24093 https://github.com/dotnet/runtime/issues/23638 https://github.com/dotnet/runtime/issues/31390