@@ -57,6 +57,8 @@ internal class PsesInternalHost : PSHost, IHostSupportsInteractiveSession, IRuns
57
57
58
58
private readonly IdempotentLatch _isRunningLatch = new ( ) ;
59
59
60
+ private EngineIntrinsics _mainRunspaceEngineIntrinsics ;
61
+
60
62
private bool _shouldExit = false ;
61
63
62
64
private string _localComputerName ;
@@ -347,17 +349,18 @@ public Task SetInitialWorkingDirectoryAsync(string path, CancellationToken cance
347
349
348
350
private void Run ( )
349
351
{
350
- ( PowerShell pwsh , RunspaceInfo localRunspaceInfo ) = CreateInitialPowerShellSession ( ) ;
352
+ ( PowerShell pwsh , RunspaceInfo localRunspaceInfo , EngineIntrinsics engineIntrinsics ) = CreateInitialPowerShellSession ( ) ;
353
+ _mainRunspaceEngineIntrinsics = engineIntrinsics ;
351
354
_localComputerName = localRunspaceInfo . SessionDetails . ComputerName ;
352
355
_runspaceStack . Push ( new RunspaceFrame ( pwsh . Runspace , localRunspaceInfo ) ) ;
353
356
PushPowerShellAndRunLoop ( pwsh , PowerShellFrameType . Normal , localRunspaceInfo ) ;
354
357
}
355
358
356
- private ( PowerShell , RunspaceInfo ) CreateInitialPowerShellSession ( )
359
+ private ( PowerShell , RunspaceInfo , EngineIntrinsics ) CreateInitialPowerShellSession ( )
357
360
{
358
- PowerShell pwsh = CreateInitialPowerShell ( _hostInfo , _readLineProvider ) ;
361
+ ( PowerShell pwsh , EngineIntrinsics engineIntrinsics ) = CreateInitialPowerShell ( _hostInfo , _readLineProvider ) ;
359
362
RunspaceInfo localRunspaceInfo = RunspaceInfo . CreateFromLocalPowerShell ( _logger , pwsh ) ;
360
- return ( pwsh , localRunspaceInfo ) ;
363
+ return ( pwsh , localRunspaceInfo , engineIntrinsics ) ;
361
364
}
362
365
363
366
private void PushPowerShellAndRunLoop ( PowerShell pwsh , PowerShellFrameType frameType , RunspaceInfo newRunspaceInfo = null )
@@ -613,7 +616,7 @@ private static PowerShell CreatePowerShellForRunspace(Runspace runspace)
613
616
return pwsh ;
614
617
}
615
618
616
- public PowerShell CreateInitialPowerShell (
619
+ public ( PowerShell , EngineIntrinsics ) CreateInitialPowerShell (
617
620
HostStartupInfo hostStartupInfo ,
618
621
ReadLineProvider readLineProvider )
619
622
{
@@ -649,7 +652,7 @@ public PowerShell CreateInitialPowerShell(
649
652
}
650
653
}
651
654
652
- return pwsh ;
655
+ return ( pwsh , engineIntrinsics ) ;
653
656
}
654
657
655
658
private Runspace CreateInitialRunspace ( InitialSessionState initialSessionState )
@@ -668,7 +671,27 @@ private Runspace CreateInitialRunspace(InitialSessionState initialSessionState)
668
671
669
672
private void OnPowerShellIdle ( )
670
673
{
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 )
672
695
{
673
696
return ;
674
697
}
@@ -688,9 +711,21 @@ private void OnPowerShellIdle()
688
711
return ;
689
712
}
690
713
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 ;
691
718
task . ExecuteSynchronously ( cancellationScope . CancellationToken ) ;
692
719
}
693
720
}
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
+ }
694
729
}
695
730
696
731
private void OnCancelKeyPress ( object sender , ConsoleCancelEventArgs args )
@@ -771,7 +806,8 @@ private Task PopOrReinitializeRunspaceAsync()
771
806
// If our main runspace was corrupted,
772
807
// we must re-initialize our state.
773
808
// TODO: Use runspace.ResetRunspaceState() here instead
774
- ( PowerShell pwsh , RunspaceInfo runspaceInfo ) = CreateInitialPowerShellSession ( ) ;
809
+ ( PowerShell pwsh , RunspaceInfo runspaceInfo , EngineIntrinsics engineIntrinsics ) = CreateInitialPowerShellSession ( ) ;
810
+ _mainRunspaceEngineIntrinsics = engineIntrinsics ;
775
811
PushPowerShell ( new PowerShellContextFrame ( pwsh , runspaceInfo , PowerShellFrameType . Normal ) ) ;
776
812
777
813
_logger . LogError ( $ "Top level runspace entered state '{ oldRunspaceState . State } ' for reason '{ oldRunspaceState . Reason } ' and was reinitialized."
0 commit comments