| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
|
As this is a change to the core language it should go through the RFC process. |
Sorry, something went wrong.
|
Bruce Payette (@BrucePay) please see PowerShell/PowerShell-RFC#207 😄 |
Sorry, something went wrong.
|
I categorically disagree with having non-interruptable script in a shell by default. I checked ksh, bash, dash, zsh and all of those shells have trap statements which are interruptable. We can have an additional discussion about a mechanism for suppressing Crtl-C (which I think would be reasonable - something similar to stty intr ''), but by default? no way. Surely, there are times when INTR has trouble percolating down to a blocked API and doesn't affect script flow until the API returns, etc, but I deem that misbehavior rather than something we should perpetuate. |
Sorry, something went wrong.
This isn't by default. It's an explicit opt-in functionality that the author has to intentionally provide. If they want to allow interruptions, they'd just use end{}. If folks had to provide an extra command just to make it non-interruptible as well, then we're just asking them to write extra code for the same end. The majority of cases where this would be used, I'd imagine, would just do that as well. |
Sorry, something went wrong.
|
Additional thoughts; Currently finally in its existing implementation isn't quite what you describe as "guaranteed to get a chance to run" while also being cancellable. It has an unskippable state, which is reached if its try block is where the Ctrl+C was registered. So I don't know why this is suddenly a new thing where things are unskippable by default. That already exists, it's already a feature, such as it is. I agree that having a more robust / less annoying way to deal with disposable variables is also needed, but since that's not something that has really gone anywhere in the last several years either, I'm not sure it's particularly relevant to design this feature around a feature that doesn't exist yet, and may never actually get implemented. |
Sorry, something went wrong.
|
At the first stage, I would not bother with any kind of interrupts at all, since even C # applications are not always able to perform cleanup. Let's implement Cleanup block simply as End block and later think about addressing more specific user scenarios. |
Sorry, something went wrong.
|
All that already works Ilya (@iSazonov). That's not really in question here, unless someone's found an issue I haven't seen yet. |
Sorry, something went wrong.
But it becomes by default for the end users, yeah?
Maybe I didn't describe the finally behavior clearly. By "guaranteed to get a chance to run", I mean when the try block gets to run, the finally block is guaranteed to get a chance to run, no matter the try block runs to the end successfully, or it's stopped by a terminating error, or it's interrupted by Ctrl+c. Here is an example for the Ctrl+c scenario: By "but no guarantee to finish", I mean the finally block can be interrupted by another Ctrl+c, for example: So the finally block is interruptible by Ctrl+c, and I think the Clean block should be semantically the same as finally in this aspect. |
Sorry, something went wrong.
|
Dongbo Wang (@daxian-dbw) that behaviour changes if you Ctrl+C from the try and then also try to cancel the finally, is my point. try {
1..100 | % { Write-Host $_; Start-Sleep 1 }
}
finally {
Write-Host "finally"
1..100 | % { Write-Host $_; Start-Sleep 1 }
}If you ctrl+c during the try block, you cannot cancel the finally block. Somewhat related: #10457 |
Sorry, something went wrong.
It becomes the default if they are calling something that implements cleanup. This is already the case if they call something that invokes a dotnet method, or invokes one of the many binary cmdlets that don't implement StopProcessing, so it's not particularly surprising for the occasional command to refuse to cancel.
Try this: try {
Write-Host 'press ctrl + c now'
Start-Sleep 5
Write-Host 'end try'
} finally {
Write-Host 'press ctrl + c again'
Start-Sleep 5
Write-Host 'end finally'
}After pressing it a second time in the finally block, the Start-Sleep does not cancel and you'll still see end finally. |
Sorry, something went wrong.
It may not seem like the risk of skipping an efficient cleanup block is very high, but keep in mind the majority of code that would utilize it would be module code. Sometimes littered throughout a user's script. The chance to accidently miss something like closing a file handle, or popping a screen buffer, or resetting a static value is too high for library code. If it's for a module, and it's decided that cleanup can be interrupted, we'll still end up telling folks to do it in C# or not at all. |
Sorry, something went wrong.
|
Rain Sallow (/u/ta11ow) (@vexx32) Thanks for bringing up #10457, I totally missed it. I'm inclined to agree with Bruce's comment in that issue, but let me think about it more and discuss with folks in the team. Patrick Meinecke (@SeeminglyScience) Yes, we already have cases where Ctrl+c doesn't respond promptly, but that doesn't justify adding more such cases. I understand it's desired to have it deterministic from a dev's perspective. However, we are facing a tradeoff in the right of control between end user and script author, and this is for interactive use only (for automation, there is no difference because Ctrl+c is out of the picture). So the question is more about "does the difference between 'guaranteed' and 'best-effort' really matters practically in the interactive scenarios". |
Sorry, something went wrong.
Not just from a dev's perspective. As a user, if I cancel my script I want to know that the command I use for logging isn't going to leave a file locked until I close the session. I don't want to be stuck in an alternate buffer, or have some other corrupted state in an implementation detail, causing me to lose my session just because I wanted to cancel my script early.
If we force folks to use C# in these scenarios then we're going to lose more control than we'd gain as they're less likely to be able to properly implement StopProcessing.
This is nit picky, but it's really any pipeline stop request. I don't know of any custom hosts geared towards non-interactive use that are utilizing stops in some way, but PSES for instance uses stops for more than just Ctrl + C Really though, as long as there is some way to delay Stop requests, it's not the end of the world if that just has to be added to every cleanup block. I don't think it would help more than it hurts, but at least the scenario would be enabled. |
Sorry, something went wrong.
Yeah, fair... I think if the primary purpose of this block requires you use cleanup{} and an extra command, there's probably going to be room for said command to be skipped. This requirement (guarantee a block of code will be run to clean up state, etc) is the reason this PR exists at all, and there really isn't much point for the feature if it doesn't behave as expected out of the box. |
Sorry, something went wrong.
I would not use such terminology. This can be confusing for script and module developers that resources will be guaranteed released when they are not. PowerShell doesn't track disposable resources "by design" for many years and this never was a critical, and what's more, the C # API may not allow resources to be released in a predictable and guarantee way. |
Sorry, something went wrong.
|
This is not meant to be an automatic thing to release resources. The code must still be written to do so. But if such code cannot be guaranteed to be run without users accidentally cancelling it out of impatience, there isn't much point in writing it, is there? |
Sorry, something went wrong.
I don't agree with this. As I mentioned above, the very first time I heard about an ask for this, is to have a finally-like thing that covers all Begin/Process/End blocks, so that terminating errors thrown from any of those blocks can be handled without needing try/finally redundantly in those blocks with the same or similar cleanup code. So even the Clean block can be interrupted by Ctrl+c, it's meaningful and useful because it makes handling terminating errors easier for functions. When it comes to Ctrl+c and the interactive scenario, the question is "does the difference between 'guaranteed' and 'best-effort' really matters practically". After all, the finally block is interruptible since it was introduced to the language, and I personally don't know of a real world scenario where it's proven to be not sufficient (of course, Joey Aiello (@joeyaiello) would know better, and I will definitely discuss with him). As you brought up, after suspending a stopping pipeline, the finally block cannot be interrupted by subsequent Ctrl+c. From the implementation perspective, this is because ConsoleHost today simply ignores the signal when it finds there is already one stopping thread on-going, and thus the _pipelineStopper.IsStopping cannot be flipped back to true. However, in case that the stop is initiated through API PowerShell.Stop, then it's possible to call PowerShell.Stop again on a different thread to stop the finally block. Nevertheless, the behavior of finally with regarding to Ctrl+c is inconsistent today because of how ConsoleHost handles the signal. For now, I will put aside the discussion about how Clean should handle Ctrl+c (need some time to talk with other folks on team about it), and will focus more on how to fit the Clean in the code as a peer of Begin/Process/End. |
Sorry, something went wrong.
What's worse than what's happening now could happen? :-))) You are ignoring the fact that this is a shell and it should behave like a shell as indicated above by James Truher (@JamesWTruher). For some reason, you think PowerShell users are much more stupid than script writers. Aren't they more often than not the same people? :-) And won't there be a lot of modules in which this block will be used incorrectly and work so badly that it should be interrupted? Again, there is no guarantee that resources will be freed, even in C#. The situation is even worse in PowerShell. In fact, this has not been a tragedy for many years. The formulated problem looks rather contrived. |
Sorry, something went wrong.
|
I am really +1 to both Rain Sallow (/u/ta11ow) (@vexx32) and Patrick Meinecke (@SeeminglyScience) points here in that cleanup should really not be user interruptible. The whole purpose of this block is to cleanup any resources that need to be and the best person who knows what needs to be done is the script author not the end user. I don't think that I can add any more arguments to what they've already provided except that I agree with their points and believe the benefits outweigh the disadvantages. I still think having the cleanup block is a really nice feature that I would love to use, I just don't think that it should be somewhat muzzled in functionality like finally is. The main argument I can see here for being able to interrupt this with ctrl+c is that PowerShell is a shell and that everything should be cancel-able. Why is this argument being applied to a script function and not to binary cmdlets. Using the example in #9900 (comment) we can see that binary modules just plain ignore ctrl+c normally. Add-Type -OutputAssembly MyModule.dll -TypeDefinition @'
using System;
using System.Management.Automation;
using System.Threading;
namespace MyModule
{
[Cmdlet("Test" , "Cmdlet")]
public class TestCmdletCommand : PSCmdlet
{
protected override void EndProcessing()
{
try
{
Console.WriteLine("press ctrl + c");
Thread.Sleep(5000);
Console.WriteLine("end try");
}
finally {
Console.WriteLine("press ctrl + c again");
Thread.Sleep(5000);
Console.WriteLine("end finally");
}
}
}
}
'@
$module = 'MyModule'
$manifestSplat = @{
Path = 'MyModule.psd1'
NestedModules = @('MyModule.dll')
FunctionsToExport = @('Test-Cmdlet')
}
New-ModuleManifest @manifestSplat
Import-Module .\MyModule.psd1
Test-CmdletIf the argument is that people who are writing binary modules should know what they are doing and thus not create situations where this occurs then I feel you are wrong. I've seen varying qualities in both PowerShell scripts and compiled code, just because something is written in C# doesn't mean it's better quality code. What about running a process that hooks into ctrl+c and completely ignores the signal that is sent to it causing the shell to hang anyway. Removing this functionality due to the risk of poor programming just seems wrong and is hampering good functionality without addressing the problem at hand. One of the reasons why I avoid binary cmdlets is because I dislike having to deal with compiling code, shipping multiple dlls, being harder to debug as easily, and not not being able to unload an assembly. Script functions are so much simpler to write and definitely has a lower barrier for entry. By having this feature you are in fact making it possible to write better script functions now that they can cleanup resources they open in some of the more exceptional cases. Ultimately this is a really nice feature to have and if the powers that be wish to make it interruptible I would be disappointed but still excited to be able to use the cleanup block. |
Sorry, something went wrong.
|
Thanks for your input Jordan Borean (@jborean93)! Sorry for making this a bit long; it's hard to keep things short when it feels like there are multiple ongoing conversations at once, but I have tried to keep each bit as brief as I could manage. I think to some degree the conversation here really boils down to this point:
And there isn't really one answer, there's a few.
The other point being discussed here is concerns around letting folks run non-interruptible code. Most of the counterpoints here make it sound like a non-interruptible section of code is an exceptional case. At least in my experience using PowerShell, it's really not unusual at all. Ctrl+C is never as responsive as we'd all like, I'm sure. For me this really boils down to two points:
If cleanup{} doesn't provide a guarantee that necessary code will be run, I think folks that need that to work will simply turn back to using their C# solutions as Patrick mentioned and their code will end up blocking anyway, so those concerns are a bit moot to me. If folks want to block the pipeline to get things done, they can and will do it regardless. This simply enables them to do it in a bit less haphazard manner than they'd otherwise need to use.
(emphasis is mine) That's precisely why I wrote the code this way and spent all the effort making it a bit easier to handle from PowerShell. As you say, the situation in PowerShell for properly cleaning up resources or state, is quite a bit worse than C# at the moment. This provides an avenue to rectifying that disparity. Whether or not you think it's a problem largely depends on the kinds of code you interact with on a daily basis. Perhaps our spheres of operation are a bit far apart here, but I tend to end up moving to C# because PowerShell simply doesn't provide what I'm looking for these days. |
Sorry, something went wrong.
|
I don't have much to add beyond strongly echoing Rain Sallow (/u/ta11ow) (@vexx32) here. This is a feature that makes dealing with things that require deterministic cleanup in PowerShell significantly easier than it is currently. Making it interruptible greatly limits the utility of the feature, to the point that I'm not sure I can think of a case where we'd actually employ it. It would become one more PowerShell feature that would require a nuanced explanation to people who are unfamiliar with PowerShell's numerous idiosyncrasies. The cognitive load of all these gotchas is such that it is extraordinarily difficult to teach anyone, from an experienced .NET developer to a sysadmin dabbling in some scripting, how to do anything in a robust and reliable way in PowerShell. All these efforts to make bad code (e.g. a cleanup block that did some huge amount of work and would block an interactive shell for a meaningful amount of time) have fewer negative consequences end up making it fantastically difficult to write good code. It's far easier to explain "ctrl+c doesn't interrupt cleanup, so be careful about what you do there" to someone than it is to explain that "you shouldn't use cleanup unless your scenario is one where interrupting cleanup may be OK, consider the implications of that at any point in your whole function". |
Sorry, something went wrong.
I now have the prototype for an alternative design available at #15177. It's been rebased to the master, with all existing tests passed. The major change is around 2 aspects:
The prototype doesn't do anything about Ctrl+c handling, since we don't yet have an agreement there. I haven't gone through all needed changes in steppable pipeline yet. We will need the committee's review on the error handling proposal, and if it is accepted, the next step will be writing a lot tests for the error handling. Also, we will need the committee's review on Ctrl+c handling as well. |
Sorry, something went wrong.
|
This pull request has been automatically marked as Review Needed because it has been there has not been any activity for 7 days. |
Sorry, something went wrong.
|
This pull request has been automatically marked as Review Needed because it has been there has not been any activity for 7 days. |
Sorry, something went wrong.
|
This PR was superseded by PR #15177, which was merged, so closing this PR. Again, thanks Rain Sallow (/u/ta11ow) (@vexx32) for the initial hard work! |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
PR Summary
Adds a new cleanup keyword and named block for both script commands and advanced functions.
RFC: PowerShell/PowerShell-RFC#207
Changes
Cleanup { } behaviour
PR Context
For functions and script cmdlets that utilise pipeline input, there is currently no possible way to reliably utilise a resource that should be timely disposed. Resources can be disposed by the pipeline, but typically this does not occur until the completion of all commands in the pipeline.
For scripts and modules that interact with external resources, this can still become problematic. This PR attempts to address this gap and bring script cmdlets closer to parity with compiled cmdlets, which are perfectly capable of simply implementing IDisposable and taking necessary actions there.
Fix #6673
EditorSyntax PR: PowerShell/EditorSyntax#186
Open / Complicated Questions
Additional Asks
PR Checklist