-
Notifications
You must be signed in to change notification settings - Fork 216
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
[Investigate] Fix PowerShell event handling #1591
Comments
I agree with this. I think we can move forward with the caveat that the integrated terminal does not support PowerShell or .NET events (or at least, not well). Technically the integrated terminal isn't a fully-fledged PowerShell process, it's the integration point between the editor and editor services that just so happens to let the user run code in the runspace hosting the editor's services. I know it doesn't quite look like that to end user, but it is what it is, and presumably the ISE is somewhat similar. |
Is there a way we could, like, turn off PowerShell events? Throw an error if one tries to be registered? Or would that involve having to do one of the four proposed workarounds? |
So the underlying event manager is directly available with Normally thought, |
/cc @daxian-dbw |
This seems like a problem where we could get 80% of it working (most use cases, like those two cmdlets) pretty easily (override them to queue the events on our thing), and it's just the last 20% (making all events including .NET-esque ones) is what would be very hard. |
Quoted from PowerShell/PSReadLine#1679 (comment):
I think we should do this in PowerShell engine. Other events can still be generated, and it's up to the hosting application to spin up pulse pipeline to allow event handling. Also, it's up to the hosting application to define when it is idle, and generate |
I think that'd be a great change, and it would fix this for future versions of PowerShell Core, but sadly it'll remain quite broken for Windows PowerShell 😫 |
Also, we (maybe me) need to go back review the ISE code closely to understand how it does things, for example, is it using a dedicated pipeline thread? how does it handle the PS eventing? It doesn't need to deal with PSReadLine which makes it a lot simpler, but still good to know how things work with it. |
Note that you can't actually implement [Management.Automation.PSEventManager] | Find-Member -Force -Abstract | Find-Member -Not -AccessView Child
ReflectedType: PSEventManager
Name MemberType Definition
---- ---------- ----------
AddForwardedEvent Method internal abstract void AddForwardedEvent(PSEventArgs forwardedEvent);
ForwardEvent Event internal abstract event EventHandler<PSEventArgs> ForwardEvent { add; remove; } That was probably meant to be Method 'AddForwardedEvent' in type 'TestEventManager.TestEventManager' from assembly 'TestEventManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation. |
For what it's worth I tried this in // HACK: This fools PowerShell into thinking that our top-level
PropertyInfo isNestedProperty = typeof(PowerShell).GetProperty("IsNested");
MethodInfo isNestedSetter = isNestedProperty.GetSetMethod(nonPublic: true);
isNestedSetter.Invoke(pwsh, new object[]{ true }); And while it correctly set
|
Yeah it's Did we switch back to using If we did, we could also take the PSRL approach and just invoke some quick meaningless powershell like |
@SydneyhSmith Marking this as a discussion since we're tracking the "bug" (mitigation fix) in PowerShell/vscode-powershell#3701 |
Does anyone remember what specifically they used to repro this? Seems to be working for me, it's possible I accidentally fixed this while getting attach to process working |
I remember the repro is to register an event subscriber to the |
FWIW I think the issue was here: That just creates a instance of PowerShell and assigns the runspace directly. What needed to happen was Part of the changes I made for attach to process is creating a new I'm gonna go ahead and close this |
@SeeminglyScience As far as I can remember, it was because sometimes there was not running pipeline in the default session, and when an event was triggered in that case, the event manager created a pulse pipeline. However, while that pulse pipeline was running, something happens in the extension that causes a task to be queued and executed. Since they both use the same Runsapce at that point, the "a pipeline already running" exception will be thrown. |
So I think what was happening was we create an instance of PowerShell that is not marked as But since we were doing that while the pipeline thread was technically busy, we needed the PowerShell instance to be marked |
That explains it. This is great! I thought we would have to fix how event manager firing pulse pipeline in PowerShell, but this resolves the problem perfectly! |
I swear Rob and I both tried to get it marked Nested properly and ran into issues. IDK what you did, but you did good! |
@SeeminglyScience Would be good to talk about how you did it (and maybe what was done wrong before) in the team meeting this week 😄 |
I would guess that y'all did try var oldRunspace = Runspace.DefaultRunspace;
try
{
Runspace.DefaultRunspace = theOneWeJustMade;
return PowerShell.Create(RunspaceMode.CurrentRunspace);
}
finally
{
Runspace.DefaultRunspace = oldRunspace;
}
That's a good idea but I'll have to see if I can find a way to present the information in a way that's useful. So next week! 😁 |
From PowerShell/PSReadLine#1679 (comment).
PowerShell events when run in a non-nested shell try to phone home back to the runspace in which they were registered, with the event handler calling in from another thread.
The assumption PowerShell makes is that when an action occurs and we pulse the pipeline, if the pipeline isn't nested, it must be on a different thread.
Of course in our case, the pipeline is on our thread and isn't nested, because we're running it under an application. This seems to be an oversight in the PowerShell hosting model and I think it needs to be addressed in PowerShell; any attempt to run a PowerShell pipeline on a thread not dedicated to it will have this issue.
Unfortunately for us though, this is a behaviour we're stuck with in Windows PowerShell and PS 7.0, 7.1 and 7.2, so we have to work out what mitigation we can provide.
There are a couple of possible solutions:
PSLocalEventManager
class is an internal extension of a public abstract class and most of its methods are virtual. We might conceivably be able to generate an override to this class and set it internally in the engine to redirect the eventing flow back to us. I'm not sure if this is possible, and if it is it would probably be a fair amount of work. But it would be the most "integrated" approach.EndProcessing()
block. But this would require some reimplementation inside the internal host.It's also worth noting that, as far as we can tell, the ISE doesn't have any awareness of this scenario either. So it's not clear if this is a quiet bug in the ISE that either hasn't been noticed or hasn't been large enough to come to attention.
For now, I think, event handling in PSES isn't important enough to invalidate the work here and this is something I'm hopeful can be fixed later.
The text was updated successfully, but these errors were encountered: