@@ -57,6 +57,8 @@ internal class PsesInternalHost : PSHost, IHostSupportsInteractiveSession, IRuns
5757
5858 private readonly IdempotentLatch _isRunningLatch = new ( ) ;
5959
60+ private EngineIntrinsics _mainRunspaceEngineIntrinsics ;
61+ 6062 private bool _shouldExit = false ;
6163
6264 private string _localComputerName ;
@@ -347,17 +349,18 @@ public Task SetInitialWorkingDirectoryAsync(string path, CancellationToken cance
347349
348350 private void Run ( )
349351 {
350- ( PowerShell pwsh , RunspaceInfo localRunspaceInfo ) = CreateInitialPowerShellSession ( ) ;
352+ ( PowerShell pwsh , RunspaceInfo localRunspaceInfo , EngineIntrinsics engineIntrinsics ) = CreateInitialPowerShellSession ( ) ;
353+ _mainRunspaceEngineIntrinsics = engineIntrinsics ;
351354 _localComputerName = localRunspaceInfo . SessionDetails . ComputerName ;
352355 _runspaceStack . Push ( new RunspaceFrame ( pwsh . Runspace , localRunspaceInfo ) ) ;
353356 PushPowerShellAndRunLoop ( pwsh , PowerShellFrameType . Normal , localRunspaceInfo ) ;
354357 }
355358
356- private ( PowerShell , RunspaceInfo ) CreateInitialPowerShellSession ( )
359+ private ( PowerShell , RunspaceInfo , EngineIntrinsics ) CreateInitialPowerShellSession ( )
357360 {
358- PowerShell pwsh = CreateInitialPowerShell ( _hostInfo , _readLineProvider ) ;
361+ ( PowerShell pwsh , EngineIntrinsics engineIntrinsics ) = CreateInitialPowerShell ( _hostInfo , _readLineProvider ) ;
359362 RunspaceInfo localRunspaceInfo = RunspaceInfo . CreateFromLocalPowerShell ( _logger , pwsh ) ;
360- return ( pwsh , localRunspaceInfo ) ;
363+ return ( pwsh , localRunspaceInfo , engineIntrinsics ) ;
361364 }
362365
363366 private void PushPowerShellAndRunLoop ( PowerShell pwsh , PowerShellFrameType frameType , RunspaceInfo newRunspaceInfo = null )
@@ -613,7 +616,7 @@ private static PowerShell CreatePowerShellForRunspace(Runspace runspace)
613616 return pwsh ;
614617 }
615618
616- public PowerShell CreateInitialPowerShell (
619+ public ( PowerShell , EngineIntrinsics ) CreateInitialPowerShell (
617620 HostStartupInfo hostStartupInfo ,
618621 ReadLineProvider readLineProvider )
619622 {
@@ -649,7 +652,7 @@ public PowerShell CreateInitialPowerShell(
649652 }
650653 }
651654
652- return pwsh ;
655+ return ( pwsh , engineIntrinsics ) ;
653656 }
654657
655658 private Runspace CreateInitialRunspace ( InitialSessionState initialSessionState )
@@ -668,7 +671,27 @@ private Runspace CreateInitialRunspace(InitialSessionState initialSessionState)
668671
669672 private void OnPowerShellIdle ( )
670673 {
671- if ( _taskQueue . Count == 0 )
674+ IReadOnlyList < PSEventSubscriber > eventSubscribers = _mainRunspaceEngineIntrinsics . Events . Subscribers ;
675+ 676+ // Go through pending event subscribers and:
677+ // - if we have any subscribers, ensure we process any events
678+ // - if we have any idle events, generate an idle event and process that
679+ bool runPipelineForEventProcessing = false ;
680+ foreach ( PSEventSubscriber subscriber in eventSubscribers )
681+ {
682+ runPipelineForEventProcessing = true ;
683+ 684+ if ( string . Equals ( subscriber . SourceIdentifier , PSEngineEvent . OnIdle , StringComparison . OrdinalIgnoreCase ) )
685+ {
686+ // We control the pipeline thread, so it's not possible for PowerShell to generate events while we're here.
687+ // But we know we're sitting waiting for the prompt, so we generate the idle event ourselves
688+ // and that will flush idle event subscribers in PowerShell so we can service them
689+ _mainRunspaceEngineIntrinsics . Events . GenerateEvent ( PSEngineEvent . OnIdle , sender : null , args : null , extraData : null ) ;
690+ break ;
691+ }
692+ }
693+ 694+ if ( ! runPipelineForEventProcessing && _taskQueue . IsEmpty )
672695 {
673696 return ;
674697 }
@@ -688,9 +711,21 @@ private void OnPowerShellIdle()
688711 return ;
689712 }
690713
714+ // If we're executing a task, we don't need to run an extra pipeline later for events
715+ // TODO: This may not be a PowerShell task, so ideally we can differentiate that here.
716+ // For now it's mostly true and an easy assumption to make.
717+ runPipelineForEventProcessing = false ;
691718 task . ExecuteSynchronously ( cancellationScope . CancellationToken ) ;
692719 }
693720 }
721+ 722+ // We didn't end up executing anything in the background,
723+ // so we need to run a small artificial pipeline instead
724+ // to force event processing
725+ if ( runPipelineForEventProcessing )
726+ {
727+ InvokePSCommand ( new PSCommand ( ) . AddScript ( "0" , useLocalScope : true ) , PowerShellExecutionOptions . Default , CancellationToken . None ) ;
728+ }
694729 }
695730
696731 private void OnCancelKeyPress ( object sender , ConsoleCancelEventArgs args )
@@ -771,7 +806,8 @@ private Task PopOrReinitializeRunspaceAsync()
771806 // If our main runspace was corrupted,
772807 // we must re-initialize our state.
773808 // TODO: Use runspace.ResetRunspaceState() here instead
774- ( PowerShell pwsh , RunspaceInfo runspaceInfo ) = CreateInitialPowerShellSession ( ) ;
809+ ( PowerShell pwsh , RunspaceInfo runspaceInfo , EngineIntrinsics engineIntrinsics ) = CreateInitialPowerShellSession ( ) ;
810+ _mainRunspaceEngineIntrinsics = engineIntrinsics ;
775811 PushPowerShell ( new PowerShellContextFrame ( pwsh , runspaceInfo , PowerShellFrameType . Normal ) ) ;
776812
777813 _logger . LogError ( $ "Top level runspace entered state '{ oldRunspaceState . State } ' for reason '{ oldRunspaceState . Reason } ' and was reinitialized."
0 commit comments