-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Fix hanging of NamedPipeClientStream when connecting with infinite timeout #66877
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix hanging of NamedPipeClientStream when connecting with infinite timeout #66877
Conversation
The test includes the situation when the NamedPipeClientStream doesn't response to the cancellation token.
The test modified to highlight the place when a hang task could appear.
|
Tagging subscribers to this area: @dotnet/area-system-io Issue DetailsResolved ProblemAffected OS: Only Windows. The
After that the second client doesn't exit when passed cancellation token is passed. The situation is Explanation of the ErrorFirst, when a timeout parameter is not passed the infinite timeout is used. public Task ConnectAsync(CancellationToken cancellationToken)
{
return ConnectAsync(Timeout.Infinite, cancellationToken);
}After that The If one inspects implementation of the
if (!Interop.Kernel32.WaitNamedPipe(_normalizedPipePath, timeout))
{
errorCode = Marshal.GetLastPInvokeError();
...But as MSDN states
The passed timeout value to the Implemented SolutionIt's not correct to calculate difference between the timeout and the elapsed time when the timeout is infinite. One could do it only if the timeout is finite and grater or equals to zero. In case of infinite timeout one should use
|
|
@jeffhandley |
|
I'm linking to an issue that I opened that I believe to be relevant: In this issue, there's a lot of discussion around how difficult it is to cancel a named pipe operation. It seems you (@cranberry-knight) would like the |
|
@GSPP Thanks for linking the issue and providing more information. In my opinion, if the API provides method with cancellation token it should be possible to use at The async-over-sync pattern is implemented by dividing wait time into small pieces to be able check |
|
@adamsitnik looks like this one needs a review from IO. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cranberry-knight big thanks for your contribution and apologies for the delayed review! PTAL at my comments
src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.cs
Show resolved
Hide resolved
src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Specific.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Specific.cs
Show resolved
Hide resolved
The `WaitForConnectionAsync()` method is already returns a task.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thank you for your contribution @cranberry-knight !
|
the failures are unrelated, I am merging |
Resolved Problem
Affected OS: Only Windows.
The
NamedPipeClientStreamcould hang on connection forever while doesn't respond on a cancellation token. This happens when several conditions are met:WainNamedPipeis called by the second client.After that the second client doesn't exit when passed cancellation token is passed. The situation is
reproduces in the implemented test. One could compare test results before and after the bug fix.
Explanation of the Error
First, when a timeout parameter is not passed the infinite timeout is used.
After that
private void ConnectInternal(int timeout, CancellationToken cancellationToken, int startTime)is called.The
Timeout.Infiniteis resolved to the-1integer value. Therefore in theConnectInternal()methodint waitTimeis resolved to-1becauseelpasedis 0 at first call. AndTryConnect(waitTime, cancellationToken)is called withwaitTimeas-1.If one inspects implementation of the
TryConnectmethod for Windows he could discover two things:WaitNamedPipeas integer value:But as MSDN states
WaitNamedPipehas timeout parameter asDWORDwhich is really resolved touint. And there are two special values for the timeout parameters:NMPWAIT_USE_DEFAULT_WAIT (0x00000000)to used a timeout specified in theCreateNamedPipefunction.NMPWAIT_WAIT_FOREVER (0xffffffff)to wait infinite until the instance is available.The passed timeout value to the
TryConnect()as-1of typeintis marshalled to0xffffffffasDWORD. And theTryConnect()never returns until the pipe will be broken. And therefore passed cancellation token doesn't cancel the operation.Implemented Solution
It's not correct to calculate difference between the timeout and the elapsed time when the timeout is infinite. One could do it only if the timeout is finite and grater or equals to zero.
In case of infinite timeout one should use
CancellationCheckIntervalas it was designed at first place.