Skip to content

WIP: Wrap ReadLine with lock and fallback to legacy when re-entering #1729

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ private IReadOnlyList<TResult> ExecuteNormally(CancellationToken cancellationTok
var invocationSettings = new PSInvocationSettings
{
AddToHistory = PowerShellExecutionOptions.AddToHistory,
Host = _psesHost
};

if (PowerShellExecutionOptions.ThrowOnError)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Microsoft.Extensions.Logging;
using System;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

namespace Microsoft.PowerShell.EditorServices.Services.PowerShell.Execution
{
Expand All @@ -23,6 +23,7 @@ internal abstract class SynchronousTask<TResult> : ISynchronousTask
private readonly TaskCompletionSource<TResult> _taskCompletionSource;

private readonly CancellationToken _taskRequesterCancellationToken;
private CancellationTokenSource _cancellationSource;

private bool _executionCanceled;

Expand Down Expand Up @@ -79,16 +80,16 @@ public void ExecuteSynchronously(CancellationToken executorCancellationToken)
return;
}

using var cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(_taskRequesterCancellationToken, executorCancellationToken);
if (cancellationSource.IsCancellationRequested)
_cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(_taskRequesterCancellationToken, executorCancellationToken);
if (executorCancellationToken.IsCancellationRequested)
{
SetCanceled();
return;
}

try
{
TResult result = Run(cancellationSource.Token);
TResult result = Run(executorCancellationToken);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright so think of a linked cancellation token source kinda like this:

static void MyMethod(CancellationToken callersToken)
{
    CancellationTokenSource myToken = new(millisecondsDelay: 5000);

    // Real method name is "Register", but bare with me
    callersToken.OnCancel(() => myToken.Cancel());
    OtherMethod(myToken.Token);
}

That's all linking does. The issue with the WIP here is instead of passing myToken.Token, you're passing callersToken. So if we call myToken.Cancel(), that's not going to do anything. That's also why it looks like it works: it's never cancelled.

I haven't played with it yet, but I would guess we can no longer cancel PSRL like this (e.g. F8/editor commands/continue). If we can still cancel PSRL, we should probably look at references for _taskRequesterCancellationToken to see if we ever even cancel it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops I meant to still send cancellationSource.Token so I clearly didn't test what I thought I was testing.

SetResult(result);
}
catch (OperationCanceledException)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -714,7 +714,22 @@ public void WriteWithPrompt(PSCommand command, CancellationToken cancellationTok

private string InvokeReadLine(CancellationToken cancellationToken)
{
return _readLineProvider.ReadLine.ReadLine(cancellationToken);
if (Monitor.TryEnter(_readLineProvider))
{
try
{
return _readLineProvider.ReadLine.ReadLine(cancellationToken);
}
finally
{
Monitor.Exit(_readLineProvider);
}
}
else
{
var legacyReadLine = new LegacyReadLine(this, ReadKey, OnPowerShellIdle);
return legacyReadLine.ReadLine(cancellationToken);
}
}

private void InvokeInput(string input, CancellationToken cancellationToken)
Expand Down