-
Notifications
You must be signed in to change notification settings - Fork 305
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
Add private contract delegate for PSES to handle idle on readline #1679
Conversation
That's not there for PSES, it needs to stay to process user registered events. |
Oh! Right, I thought @daxian-dbw meant something else. Yeah, I was under the impression that that's for event handling in normal conhost execution and we just happened to use it. |
Oh you know what, he might be talking about |
if (_handleIdleOverride != null) | ||
{ | ||
_handleIdleOverride(); | ||
continue; |
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.
Won't this continue hard stop event processing/cancellation handling?
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.
So the current wait handle will handle cancellation -- that happens above the delegate call. We also own the cancellation token, so we have a lot of control there.
Event processing is what we need to prevent, because if we invoke PSRL directly as a delegate with no pipeline running above it, event handling will fail. PSES owns the pipeline and pretty much everything else at this point, so I think it's up to us to handle events in our own way.
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.
we invoke PSRL directly as a delegate with no pipeline running above it, event handling will fail
Even if you set Runspace.DefaultRunspace
before calling PSRL? I was pretty certain Create(RunspaceMode.CurrentRunspace)
worked as long as that's set. It's definitely supposed to at least, there's code for it.
Or is it failing some other way?
so I think it's up to us to handle events in our own way.
Yeah that's fine if it comes to that, just a shame to implement the same logic in two places I suppose.
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.
Hmmm, well in fact looking into this, it may be that I'm not quite invoking the delegate the right way (i.e. not on the pipeline thread). I'll look into it and get back to you, because you may well be right! (Will keep this in draft until kinks like this are resolved)
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.
Just looked into this, and yeah the delegate is marshalled back to the pipeline thread and called there (and we have Runspace.DefaultRunspace
set). I'll see if I can try again, but that's the whole reason for the continue
, since PSRL's default event handling didn't work for us in the top-level pipeline (I spent ages debugging it...)
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.
If the event handling doesn't work, make sure TabExpansion2
and script based custom key handlers still work.
I worry that if PSRL's event handling doesn't work, I'm not 100% sure how we're going to handle that better in PSES you know? If we lose the ability to process events it might be better to ditch the delegate.
Here's where I brought it up in PowerShell/PowerShellEditorServices#1295 (comment)
An example of it getting messy is PowerShell/PowerShellEditorServices#762 (though fixed in oneget now). |
Yeah, that's the one I was talking about. I was lazy to find out the exact member 😄 |
Will we likely still need this for the PSES threading rewrite @rjmholt? |
Yes, this is required to allow us to run PSRL as a delegate rather than having to craft a secret cmdlet to run it via PowerShell |
48bc9fe
to
37debd7
Compare
I'm marking this PR as ready for review, since PowerShell/PowerShellEditorServices#1459 is getting very close and will require a version of PSReadLine with the changes in this PR to be available in order to work as a release |
/// method. This is not a public API, but it is part of a private contract | ||
/// with that project. | ||
/// </summary> | ||
private static void ForcePSEventHandling() |
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.
Woo! 🥳
The only remaining question here is whether the |
You know me, I'm a fan of cancellation tokens. |
@daxian-dbw We think we're in a place to publish a (very) preview release of PSES and the extension, but for that we'd need a build of this available. What would it look like to get this merged and a preview release on the gallery? @rjmholt says we are completely settled on this change to the internal API of PSReadLine. So while this PR updates the version to 2.3.0, I think it would be reasonable to simply include it in 2.2.0 and publish a new beta. Stable PSES is still pinned to PSReadLine 2.1.0, so nothing would break and we could start to coordinate the update to 2.2.0! |
I will review this PR this afternoon and try to release 2.2.0-beta3 PSReadLine early next week. |
// - ReadLine cancellation is requested externally | ||
handleId = WaitHandle.WaitAny(_singleton._requestKeyWaitHandles, 300); | ||
if (handleId != WaitHandle.WaitTimeout && handleId != EventProcessingRequested) | ||
break; | ||
|
||
if (_handleIdleOverride is not null) |
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.
By removing _forceEventWaitHandle
, how do you break out WaitHandle.WaitAny
immediately when you need to? Are you planning to use the cancellation token for that purpose?
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.
Yeah, we just cancel in that scenario. It's not perfect because of how PSRL handles cancellation (i.e. it returns a value rather than throwing an OperationCanceledException
) but because we know we cancelled, we can take the appropriate action.
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.
Chatted with @rjmholt offline, and we decided to remove the _forceEventWaitHandle
for now as it's not needed in the current PSES pipeline implementation. I will add it back if it turns out to be required in future.
The way how PowerShell eventing works won't play well with the design that has the pipeline thread to be a single consumer.
That means, when a event is generated and there is no running pipeline at the moment, powershell will create a new thread and use that as the pipeline thread. Here is an example: using System.Management.Automation;
using System.Management.Automation.Runspaces;
var rs = RunspaceFactory.CreateRunspace(InitialSessionState.CreateDefault2());
rs.Open();
Runspace.DefaultRunspace = rs;
var sb = ScriptBlock.Create("[Foo]::Sleep()");
// Create an OnIdle event subscriber. The OnIdle event is geneated by a timer in PowerShell
PSEventSubscriber subscriber = rs.Events.SubscribeEvent(null, null, PSEngineEvent.OnIdle, null, ScriptBlock.Create("[Foo]::EventRaised++"), false, false);
// Run a script block immeadiately
sb.Invoke();
// Check how many times the OnIdle event handler runs.
Console.WriteLine(Foo.EventRaised);
public class Foo
{
public static int EventRaised { get; set; }
public static void Sleep()
{
System.Threading.Thread.Sleep(5000);
Console.WriteLine("Done Foo.Sleep!!");
}
} Running result is shown below, and that means the OnIdle event handler runs at the same time of In order for PowerShell eventing to work with the dedicated pipeline thread design in PSES, PowerShell needs to allow disabling the pulse pipeline generation. Unfortunately, I have no idea how to achieve that without code changes in PS engine |
🎉 |
PR Summary
Adds a private delegate field for PSES (and possibly other hosts) to override the OnIdle behaviour of PSRL while it's awaiting input.
Also adds PDBs to debug builds (needed in this PR so I could debug PSRL and PSES together).
Needed for PowerShell/PowerShellEditorServices#1295.
Today PSES executes script to run PSRL's readline function and depends on ordinary PowerShell event handling within PSRL to run most of its functionality (like completions) while a prompt is shown.
However, this has several drawbacks:
Instead, a delegate allows PSES to take responsibility for all processing while a prompt is waiting; it can process events, perform completions, run commands, even cancel the current prompt and drop into the debugger
PR Checklist