-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
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
NamedPipeClientStream
could hang on connection forever while doesn't respond on a cancellation token. This happens when several conditions are met:WainNamedPipe
is 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.Infinite
is resolved to the-1
integer value. Therefore in theConnectInternal()
methodint waitTime
is resolved to-1
becauseelpased
is 0 at first call. AndTryConnect(waitTime, cancellationToken)
is called withwaitTime
as-1
.If one inspects implementation of the
TryConnect
method for Windows he could discover two things:WaitNamedPipe
as integer value:But as MSDN states
WaitNamedPipe
has timeout parameter asDWORD
which 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 theCreateNamedPipe
function.NMPWAIT_WAIT_FOREVER (0xffffffff)
to wait infinite until the instance is available.The passed timeout value to the
TryConnect()
as-1
of typeint
is marshalled to0xffffffff
asDWORD
. 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
CancellationCheckInterval
as it was designed at first place.