Skip to content

Commit 0ef41b5

Browse files
Fix lock up that occurs when WinForms is executed on the pipeline thread (#1149)
* Ensure PSRL static constructor really only run once * Fix await thread marshalling error Co-Authored-By: Patrick Meinecke <[email protected]>
1 parent 6e52a9d commit 0ef41b5

File tree

4 files changed

+26
-21
lines changed

4 files changed

+26
-21
lines changed

src/PowerShellEditorServices/Server/PsesDebugServer.cs

+10-5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
using System;
77
using System.IO;
8+
using System.Threading;
89
using System.Threading.Tasks;
910
using Microsoft.Extensions.DependencyInjection;
1011
using Microsoft.Extensions.Logging;
@@ -21,7 +22,10 @@ namespace Microsoft.PowerShell.EditorServices.Server
2122
/// </summary>
2223
internal class PsesDebugServer : IDisposable
2324
{
24-
private static bool s_hasRunPsrlStaticCtor = false;
25+
/// <summary>
26+
/// This is a bool but must be an int, since Interlocked.Exchange can't handle a bool
27+
/// </summary>
28+
private static int s_hasRunPsrlStaticCtor = 0;
2529

2630
private readonly Stream _inputStream;
2731
private readonly Stream _outputStream;
@@ -64,20 +68,21 @@ public async Task StartAsync()
6468
options.Serializer = new DapProtocolSerializer();
6569
options.Reciever = new DapReciever();
6670
options.LoggerFactory = _loggerFactory;
67-
Extensions.Logging.ILogger logger = options.LoggerFactory.CreateLogger("DebugOptionsStartup");
71+
ILogger logger = options.LoggerFactory.CreateLogger("DebugOptionsStartup");
6872

6973
// We need to let the PowerShell Context Service know that we are in a debug session
7074
// so that it doesn't send the powerShell/startDebugger message.
7175
_powerShellContextService = ServiceProvider.GetService<PowerShellContextService>();
7276
_powerShellContextService.IsDebugServerActive = true;
7377

7478
// Needed to make sure PSReadLine's static properties are initialized in the pipeline thread.
75-
if (!s_hasRunPsrlStaticCtor && _usePSReadLine)
79+
if (_usePSReadLine && Interlocked.Exchange(ref s_hasRunPsrlStaticCtor, 1) == 0)
7680
{
77-
s_hasRunPsrlStaticCtor = true;
81+
// This must be run synchronously to ensure debugging works
7882
_powerShellContextService
7983
.ExecuteScriptStringAsync("[System.Runtime.CompilerServices.RuntimeHelpers]::RunClassConstructor([Microsoft.PowerShell.PSConsoleReadLine].TypeHandle)")
80-
.Wait();
84+
.GetAwaiter()
85+
.GetResult();
8186
}
8287

8388
options.Services = new ServiceCollection()

src/PowerShellEditorServices/Services/PowerShellContext/PowerShellContextService.cs

+12-12
Original file line numberDiff line numberDiff line change
@@ -2356,18 +2356,18 @@ private void OnDebuggerStop(object sender, DebuggerStopEventArgs e)
23562356
PowerShellExecutionResult.Stopped,
23572357
null));
23582358

2359-
// Get the session details and push the current
2360-
// runspace if the session has changed
2361-
SessionDetails sessionDetails = null;
2362-
try
2363-
{
2364-
sessionDetails = this.GetSessionDetailsInDebugger();
2365-
}
2366-
catch (InvalidOperationException)
2367-
{
2368-
this.logger.LogTrace(
2369-
"Attempting to get session details failed, most likely due to a running pipeline that is attempting to stop.");
2370-
}
2359+
// Get the session details and push the current
2360+
// runspace if the session has changed
2361+
SessionDetails sessionDetails = null;
2362+
try
2363+
{
2364+
sessionDetails = this.GetSessionDetailsInDebugger();
2365+
}
2366+
catch (InvalidOperationException)
2367+
{
2368+
this.logger.LogTrace(
2369+
"Attempting to get session details failed, most likely due to a running pipeline that is attempting to stop.");
2370+
}
23712371

23722372
if (!localThreadController.FrameExitTask.Task.IsCompleted)
23732373
{

src/PowerShellEditorServices/Services/PowerShellContext/Session/ThreadController.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ internal async Task<IEnumerable<TResult>> RequestPipelineExecutionAsync<TResult>
7878
/// A task object representing the asynchronous operation. The Result property will return
7979
/// the retrieved pipeline execution request.
8080
/// </returns>
81-
internal async Task<IPipelineExecutionRequest> TakeExecutionRequestAsync()
81+
internal Task<IPipelineExecutionRequest> TakeExecutionRequestAsync()
8282
{
83-
return await PipelineRequestQueue.DequeueAsync();
83+
return PipelineRequestQueue.DequeueAsync();
8484
}
8585

8686
/// <summary>

src/PowerShellEditorServices/Utility/AsyncQueue.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ public async Task<T> DequeueAsync(CancellationToken cancellationToken)
146146
{
147147
Task<T> requestTask;
148148

149-
using (await queueLock.LockAsync(cancellationToken))
149+
using (await queueLock.LockAsync(cancellationToken).ConfigureAwait(false))
150150
{
151151
if (this.itemQueue.Count > 0)
152152
{
@@ -171,7 +171,7 @@ public async Task<T> DequeueAsync(CancellationToken cancellationToken)
171171
}
172172

173173
// Wait for the request task to complete outside of the lock
174-
return await requestTask;
174+
return await requestTask.ConfigureAwait(false);
175175
}
176176

177177
/// <summary>

0 commit comments

Comments
 (0)