(foo.vhd is a 36+GB file)
Get-FileHash foo.vhd
{hit Ctrl+C while running}
The cmdlet should terminate "promptly" (probably with some exception)
The cmdlet continues execution for "a long time"
> $PSVersionTable
Name Value
---- -----
PSVersion 7.0.0
PSEdition Core
GitCommitId 7.0.0
OS Microsoft Windows 10.0.17763
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0鈥
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
We use .Net Core API which does not allow cancellation. You could use *-Job cmdlets as workaround.
Is your comment generic or only about Get-FileHash?
@sba923, I think what @iSazonov is saying is that Ctrl-C is ignored while a _.NET method_ is executing (as opposed to PowerShell code); since most methods execute quickly, this is usually not noticeable, but in your case it is.
You can verify the behavior as follows:
# Define a .NET class with a long-running method (effectively sleeps for 10 seconds).
Add-Type 'public class Foo { public void Bar() { for (var i = 0; i < 100; ++i) { System.Console.Write("."); System.Threading.Thread.Sleep(100); } } }'
# Ctrl-C has no effect while the .Bar() method is being executed.
[Foo]::new().Bar()
Is your comment generic or only about Get-FileHash?
My comment is about Get-FileHash.
I get the picture. But @iSazonov wrote "API _which does not_ allow cancellation." Is this a generic statement about _all_ .NET Core APIs, or (my understanding of the phrasing) about _that particular API_?
Shouldn't PowerShell wrap the calls to "potentially long-running .NET Core APIs" so that they can be aborted, whenever supported?
Good question, @sba923 - certainly, PowerShell supporting Ctrl-C consistently would make for a better user experience, but I can't speak to the technical feasibility.
@iSazonov, do we already have cases of using .NET APIs that _do_ support cancellation that we _do_ surface via Ctrl-C?
We could manually evaluate a hash by chunks and check cancellation on every iteration.
Or better run the work in another thread by Task.Run().
@iSazonov evaluating in chunks is a great idea 鉂わ笍
I'm less of a fan of just throwing it in another thread, since that gives the impression that the operation was cancelled. Also with Task.Run
in particular, the potential for thread starvation in environments with low thread pool capacity is not ideal.
@mklement0
@iSazonov, do we already have cases of using .NET APIs that _do_ support cancellation that we _do_ surface via Ctrl-C?
Yeah, that's essentially what PSCmdlet.StopProcessing
is for. When a pipeline stop is requested, that method is called in a different thread. If the cmdlet implements that method, it can then use whatever API specific logic is required to cancel (sometimes a CancellationToken
, sometimes a method has a specific Stop
operation like Ping.SendAsyncCancel()
).
@SeeminglyScience don't remind me 馃槄
I'm still not sure that method _actually_ does a proper cancellation, it seems to take the same amount of time whether you cancel it or not. 馃榿
But yeah, janky Ping APIs aside, a lot of .NET async APIs have some kind of usable cancellation mechanism.
We will get ComputeHashAsync(Stream inputStream, CancellationToken cancellationToken = default)) in .Net Core 5.0.
Most helpful comment
@mklement0
Yeah, that's essentially what
PSCmdlet.StopProcessing
is for. When a pipeline stop is requested, that method is called in a different thread. If the cmdlet implements that method, it can then use whatever API specific logic is required to cancel (sometimes aCancellationToken
, sometimes a method has a specificStop
operation likePing.SendAsyncCancel()
).