Skip to content

Conversation

@daxian-dbw
Copy link
Member

@daxian-dbw daxian-dbw commented Jan 8, 2020

PR Summary

Fix #11418

When using Ping.SendAsync with a callback, the callback will always be invoked in the synchronization context of the pipeline thread. When the current synchronization context of the thread is null, the default context is used which is the threadpool.
In case the pipeline thread's synchronization context is set (for example, by constructing a WindowsForm object), the callback will be scheduled to run on the pipeline thread, which will result in a dead lock.
The fix is to use Ping.SendPingAsync.

Note about SendAsync and PingCompletedEventArgs

Note that, when using Ping.SendAsync, _pingCompleteArgs.Cancelled is never set to true when the task is cancelled by Ctrl+c. Instead, _pingCompleteArgs.Error is set with a PingException whose InnerException is TaskCanceledException.
I think this is a bug in the Ping.SendAsync API, but didn't spend the time to dig into it.

PR Checklist

Copy link
Collaborator

@vexx32 vexx32 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems sound to me. This looks a lot nicer!

Thanks for fixing this! 😊 💖

@ghost ghost added the Waiting on Author The PR was reviewed and requires changes or comments from the author before being accept label Jan 8, 2020
@vexx32
Copy link
Collaborator

vexx32 commented Jan 8, 2020

@daxian-dbw does this allow Ctrl+C to cancel the ping? The documentation for SendAsyncCancel() mentions that SendAsyncCancel() is meant to cancel async requests submitted with SendAsync() 🤔

Downloaded the build artifact, and it seems to cancel, but the task doesn't return until the predefined timeout either way... though it does stop. Weird. So I guess it sort of does, but it won't cause the task to return right away. Odd. I think it was already doing that before this change, though, so it must be something that the API is doing internally, a shared code path somewhere, most likely.

@ghost ghost removed the Waiting on Author The PR was reviewed and requires changes or comments from the author before being accept label Jan 8, 2020
@daxian-dbw
Copy link
Member Author

@vexx32 SendAsync is actually just a thin wrapper over SendPingAsync, see the implementation here. So SendAsyncCancel() should work for SendPingAsync as well. I did set breakpoint in the code and see the PingException being thrown with a TaskCanceledException inner exception when pressing ctrl+c. Yes, I guess the cancellation doesn't happen right away.

Copy link
Collaborator

@SeeminglyScience SeeminglyScience left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, nice catch on the inner exception 🙂

One comment

@iSazonov
Copy link
Collaborator

iSazonov commented Jan 8, 2020

When using Ping.SendAsync with a callback, the callback will always be invoked in the synchronization context of the pipeline thread.

Is it EAP behavior? I ask because SendAsync() creates an async task with ConfigureAwait(false)
https://source.dot.net/#System.Net.Ping/System/Net/NetworkInformation/Ping.cs,281
https://source.dot.net/#System.Net.Ping/System/Net/NetworkInformation/Ping.cs,328
https://source.dot.net/#System.Net.Ping/System/Net/NetworkInformation/Ping.cs,341

Also I should point may be related #11420 (comment)
There Test-Connection, traceroute and the same SendCancellablePing(). Test on Azure takes up to 40 sec! Setting Timeout parameter causes hang.
I guess it is the same issue like this.

.Net Core uses ConfigureAwait(false) everywhere. If there is something that blocking current thread I guess it is either Core bug or by-design in EAP.

@vexx32
Copy link
Collaborator

vexx32 commented Jan 8, 2020

@iSazonov yeah traceroutes take a while even on Windows, but are especially bad on Unix, since all Unix Ping APIs do nothing except report TimedOut for any non-success response (TtlExpired is not a status that Unix APIs will return under any circumstances I've found, so traceroute on Unix is currently rather busted and always waits the full Timeout value before returning). It may be worth simply skipping the traceroute tests under Unix until we get better functionality from .NET Core if we aren't already.

@SeeminglyScience
Copy link
Collaborator

@iSazonov

Is it EAP behavior? I ask because SendAsync() creates an async task with ConfigureAwait(false)

The EAP version of the API is just a wrapper around the TAP version. That wrapper uses AsyncOperationManager, which looks at the SynchronizationContext for the current thread. The TAP version doesn't use the AsyncOperationManager, instead it just uses the native API directly with a callback that sets a TaskCompletionSource.

If there is something that blocking current thread I guess it is either Core bug or by-design in EAP.

The problem is with certain sync contexts, particularly the one set by the Form constructor, it causes a deadlock. You could argue that it's by design because Form isn't being used how it's intended (e.g. from PowerShell) but still worth fixing.

@iSazonov
Copy link
Collaborator

iSazonov commented Jan 8, 2020

@SeeminglyScience Thanks! I see. Not clear is it a bug or by-design for EAP.

@SeeminglyScience
Copy link
Collaborator

I would say that it's by design that the EAP version honors the current synch context, PowerShell just doesn't play well with the windows forms sync context implementation specifically. Or really any external sync context I would guess.

@daxian-dbw
Copy link
Member Author

daxian-dbw commented Jan 8, 2020

I agree that it's by design for the EAP version. It's the right thing to invoke the callback in the current synchronization context. Library APIs themselves should always use the default sync context, but when interacting with the user code, it's the user's decision if the current sync context should be honored (though the EAP version API doesn't let you choose :)).

But I guess there is a bug in the EAP version API, where the PingCompletedEventArgs.Cancelled never gets set to true when the task is cancelled by SendAsyncCancel().

@PoshChan
Copy link
Collaborator

@daxian-dbw, successfully started retry of PowerShell-CI-Windows

@daxian-dbw
Copy link
Member Author

@SteveL-MSFT Please take another look when you have time.
@iSazonov The PR is stuck with WIP check, but I don't know why 😕

@vexx32
Copy link
Collaborator

vexx32 commented Jan 10, 2020

Seems like the bot isn't adding the status either way... interesting. Perhaps try editing the PR title / summary a bit and see if that triggers whatever webhook the bot may be using?

@daxian-dbw
Copy link
Member Author

@vexx32 Yeah, I will edit the title a bit to see if that helps.

@daxian-dbw
Copy link
Member Author

@PoshChan Please retry windows

@PoshChan
Copy link
Collaborator

@daxian-dbw, successfully started retry of PowerShell-CI-Windows

Copy link
Collaborator

@iSazonov iSazonov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With one minor comment.

@daxian-dbw
Copy link
Member Author

@SteveL-MSFT Can you please update your review? Thanks

@daxian-dbw
Copy link
Member Author

@SteveL-MSFT A gentle ping. Please take another look when you get a chance.

@daxian-dbw
Copy link
Member Author

@SteveL-MSFT ping again 👋 😃

@iSazonov
Copy link
Collaborator

I vote to have the fix in 7.0 servicing because users use often WinForms and now Out-ConsoleGridView.

@daxian-dbw
Copy link
Member Author

Moved the PR to 7.0.x-servicing-consider.

@ghost
Copy link

ghost commented Mar 26, 2020

🎉v7.1.0-preview.1 has been released which incorporates this pull request.:tada:

Handy links:

@ghost
Copy link

ghost commented May 14, 2020

🎉v7.0.1 has been released which incorporates this pull request.:tada:

Handy links:

silijon pushed a commit to SkyKick/PowerShell that referenced this pull request Jul 2, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CL-General Indicates that a PR should be marked as a general cmdlet change in the Change Log

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Test-Connection 'hangs' after New-Object System.Windows.Forms.Form

8 participants