1
1
using Microsoft . Extensions . Logging ;
2
+ using Microsoft . PowerShell . EditorServices . Handlers ;
2
3
using Microsoft . PowerShell . EditorServices . Hosting ;
3
4
using Microsoft . PowerShell . EditorServices . Services . PowerShell . Console ;
4
5
using Microsoft . PowerShell . EditorServices . Services . PowerShell . Execution ;
@@ -90,6 +91,8 @@ public static PowerShellExecutionService CreateAndStart(
90
91
91
92
private readonly ConcurrentStack < CancellationTokenSource > _commandCancellationStack ;
92
93
94
+ private readonly ReaderWriterLockSlim _taskProcessingLock ;
95
+
93
96
private bool _runIdleLoop ;
94
97
95
98
private bool _isExiting ;
@@ -114,6 +117,7 @@ private PowerShellExecutionService(
114
117
_executionQueue = new BlockingCollection < ISynchronousTask > ( ) ;
115
118
_debuggingContext = new DebuggingContext ( ) ;
116
119
_psFrameStack = new Stack < PowerShellContextFrame > ( ) ;
120
+ _taskProcessingLock = new ReaderWriterLockSlim ( LockRecursionPolicy . SupportsRecursion ) ;
117
121
_hostName = hostName ;
118
122
_hostVersion = hostVersion ;
119
123
_languageMode = languageMode ;
@@ -243,8 +247,6 @@ private void RunTopLevelConsumerLoop()
243
247
{
244
248
Initialize ( ) ;
245
249
246
- _consoleRepl . StartRepl ( ) ;
247
-
248
250
var cancellationContext = LoopCancellationContext . EnterNew (
249
251
this ,
250
252
CurrentPowerShellCancellationSource ,
@@ -316,6 +318,8 @@ private void Initialize()
316
318
ImportModule ( module ) ;
317
319
}
318
320
}
321
+
322
+ _consoleRepl . StartRepl ( ) ;
319
323
}
320
324
321
325
private void SetExecutionPolicy ( )
@@ -542,7 +546,6 @@ private SMA.PowerShell CreateNestedPowerShell()
542
546
return pwsh ;
543
547
}
544
548
545
-
546
549
private void PushNonInteractivePowerShell ( )
547
550
{
548
551
PushNestedPowerShell ( PowerShellFrameType . NonInteractive ) ;
@@ -606,12 +609,14 @@ private void AddRunspaceEventHandlers(Runspace runspace)
606
609
{
607
610
runspace . Debugger . DebuggerStop += OnDebuggerStopped ;
608
611
runspace . Debugger . BreakpointUpdated += OnBreakpointUpdated ;
612
+ runspace . StateChanged += OnRunspaceStateChanged ;
609
613
}
610
614
611
615
private void RemoveRunspaceEventHandlers ( Runspace runspace )
612
616
{
613
617
runspace . Debugger . DebuggerStop -= OnDebuggerStopped ;
614
618
runspace . Debugger . BreakpointUpdated -= OnBreakpointUpdated ;
619
+ runspace . StateChanged -= OnRunspaceStateChanged ;
615
620
}
616
621
617
622
private void RunPowerShellLoop ( PowerShellFrameType powerShellFrameType )
@@ -667,6 +672,54 @@ private void OnBreakpointUpdated(object sender, BreakpointUpdatedEventArgs break
667
672
BreakpointUpdated ? . Invoke ( this , breakpointUpdatedEventArgs ) ;
668
673
}
669
674
675
+ private void OnRunspaceStateChanged ( object sender , RunspaceStateEventArgs runspaceStateEventArgs )
676
+ {
677
+ if ( ! runspaceStateEventArgs . RunspaceStateInfo . IsUsable ( ) )
678
+ {
679
+ PopOrReinitializeRunspace ( ) ;
680
+ }
681
+ }
682
+
683
+ private void PopOrReinitializeRunspace ( )
684
+ {
685
+ _consoleRepl . SetReplPop ( ) ;
686
+ CancelCurrentTask ( ) ;
687
+
688
+ RunspaceStateInfo oldRunspaceState = CurrentPowerShell . Runspace . RunspaceStateInfo ;
689
+ _taskProcessingLock . EnterWriteLock ( ) ;
690
+ try
691
+ {
692
+ while ( _psFrameStack . Count > 0
693
+ && ! _psFrameStack . Peek ( ) . PowerShell . Runspace . RunspaceStateInfo . IsUsable ( ) )
694
+ {
695
+ PopFrame ( ) ;
696
+ }
697
+
698
+ if ( _psFrameStack . Count == 0 )
699
+ {
700
+ // If our main runspace was corrupted,
701
+ // we must re-initialize our state.
702
+ // TODO: Use runspace.ResetRunspaceState() here instead
703
+ Initialize ( ) ;
704
+
705
+ _logger . LogError ( $ "Top level runspace entered state '{ oldRunspaceState . State } ' for reason '{ oldRunspaceState . Reason } ' and was reinitialized."
706
+ + " Please report this issue in the PowerShell/vscode-PowerShell GitHub repository with these logs." ) ;
707
+ EditorServicesHost . UI . WriteErrorLine ( "The main runspace encountered an error and has been reinitialized. See the PowerShell extension logs for more details." ) ;
708
+ }
709
+ else
710
+ {
711
+ _logger . LogError ( $ "Current runspace entered state '{ oldRunspaceState . State } ' for reason '{ oldRunspaceState . Reason } ' and was popped.") ;
712
+ EditorServicesHost . UI . WriteErrorLine ( $ "The current runspace entered state '{ oldRunspaceState . State } ' for reason '{ oldRunspaceState . Reason } '."
713
+ + " If this occurred when using Ctrl+C in a Windows PowerShell remoting session, this is expected behavior."
714
+ + " The session is now returning to the previous runspace." ) ;
715
+ }
716
+ }
717
+ finally
718
+ {
719
+ _taskProcessingLock . ExitWriteLock ( ) ;
720
+ }
721
+ }
722
+
670
723
internal struct PowerShellRunspaceContext
671
724
{
672
725
private readonly PowerShellExecutionService _executionService ;
@@ -783,24 +836,26 @@ public void Dispose()
783
836
{
784
837
public static TaskCancellationContext EnterNew ( PowerShellExecutionService executionService , CancellationToken loopCancellationToken )
785
838
{
839
+ executionService . _taskProcessingLock . EnterReadLock ( ) ;
786
840
var cancellationTokenSource = CancellationTokenSource . CreateLinkedTokenSource ( loopCancellationToken ) ;
787
841
executionService . _commandCancellationStack . Push ( cancellationTokenSource ) ;
788
- return new TaskCancellationContext ( executionService . _commandCancellationStack , cancellationTokenSource . Token ) ;
842
+ return new TaskCancellationContext ( executionService , cancellationTokenSource . Token ) ;
789
843
}
790
844
791
- private TaskCancellationContext ( ConcurrentStack < CancellationTokenSource > commandCancellationStack , CancellationToken cancellationToken )
845
+ private TaskCancellationContext ( PowerShellExecutionService executionService , CancellationToken cancellationToken )
792
846
{
793
- _commandCancellationStack = commandCancellationStack ;
847
+ _executionService = executionService ;
794
848
CancellationToken = cancellationToken ;
795
849
}
796
850
797
- private readonly ConcurrentStack < CancellationTokenSource > _commandCancellationStack ;
851
+ private readonly PowerShellExecutionService _executionService ;
798
852
799
853
public readonly CancellationToken CancellationToken ;
800
854
801
855
public void Dispose ( )
802
856
{
803
- if ( _commandCancellationStack . TryPop ( out CancellationTokenSource taskCancellation ) )
857
+ _executionService . _taskProcessingLock . ExitReadLock ( ) ;
858
+ if ( _executionService . _commandCancellationStack . TryPop ( out CancellationTokenSource taskCancellation ) )
804
859
{
805
860
taskCancellation . Dispose ( ) ;
806
861
}
0 commit comments