Skip to content

Commit 430f6f6

Browse files
committed
WIP: Testing readkey workarounds
1 parent 3667e80 commit 430f6f6

File tree

6 files changed

+42
-26
lines changed

6 files changed

+42
-26
lines changed

PowerShellEditorServices.Common.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<Company>Microsoft</Company>
66
<Copyright>© Microsoft Corporation.</Copyright>
77
<LangVersion>latest</LangVersion>
8+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
89
<PackageTags>PowerShell;editor;development;language;debugging</PackageTags>
910
<PackageLicenseUrl>https://raw.githubusercontent.com/PowerShell/PowerShellEditorServices/master/LICENSE</PackageLicenseUrl>
1011
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>

src/PowerShellEditorServices/Services/PowerShell/Console/ConsoleProxy.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System;
55
using System.Runtime.InteropServices;
66
using System.Threading;
7-
using System.Threading.Tasks;
87

98
namespace Microsoft.PowerShell.EditorServices.Services.PowerShell.Console
109
{
@@ -14,6 +13,13 @@ namespace Microsoft.PowerShell.EditorServices.Services.PowerShell.Console
1413
/// </summary>
1514
internal static class ConsoleProxy
1615
{
16+
internal static readonly ConsoleKeyInfo s_nullKeyInfo = new(
17+
keyChar: ' ',
18+
ConsoleKey.DownArrow,
19+
shift: false,
20+
alt: false,
21+
control: false);
22+
1723
private static readonly IConsoleOperations s_consoleProxy;
1824

1925
[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1810:Initialize reference type static fields inline", Justification = "Platform specific initialization")]
@@ -100,12 +106,7 @@ internal static ConsoleKeyInfo SafeReadKey(bool intercept, CancellationToken can
100106
}
101107
catch (OperationCanceledException)
102108
{
103-
return new ConsoleKeyInfo(
104-
keyChar: ' ',
105-
ConsoleKey.DownArrow,
106-
shift: false,
107-
alt: false,
108-
control: false);
109+
return s_nullKeyInfo;
109110
}
110111
}
111112
}

src/PowerShellEditorServices/Services/PowerShell/Console/LegacyReadLine.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
using Microsoft.PowerShell.EditorServices.Services.PowerShell.Execution;
54
using Microsoft.PowerShell.EditorServices.Services.PowerShell.Host;
65
using Microsoft.PowerShell.EditorServices.Services.PowerShell.Utility;
76
using System.Collections.Generic;

src/PowerShellEditorServices/Services/PowerShell/Console/UnixConsoleOperations.cs

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22
// Licensed under the MIT License.
33

44
using System;
5+
using System.Runtime.InteropServices;
6+
using System.Text;
57
using System.Threading;
8+
using System.Threading.Tasks;
69
using Microsoft.PowerShell.EditorServices.Utility;
10+
using Microsoft.Win32.SafeHandles;
711

812
namespace Microsoft.PowerShell.EditorServices.Services.PowerShell.Console
913
{
@@ -29,34 +33,44 @@ internal class UnixConsoleOperations : IConsoleOperations
2933
/// </summary>
3034
internal UnixConsoleOperations() => WaitForKeyAvailable = LongWaitForKey;
3135

32-
public ConsoleKeyInfo ReadKey(bool intercept, CancellationToken cancellationToken)
33-
{
34-
s_readKeyHandle.Wait(cancellationToken);
36+
[DllImport("libc", SetLastError = true)]
37+
private static extern unsafe int write(int fd, byte* buffer, int bufferSize);
3538

36-
// On Unix platforms System.Console.ReadKey has an internal lock on stdin. Because
37-
// of this, if a ReadKey call is pending in one thread and in another thread
38-
// Console.CursorLeft is called, both threads block until a key is pressed.
39-
try
40-
{
41-
// The WaitForKeyAvailable delegate switches between a long delay between waits and
42-
// a short timeout depending on how recently a key has been pressed. This allows us
43-
// to let the CPU enter low power mode without compromising responsiveness.
44-
while (!WaitForKeyAvailable(cancellationToken)) { }
45-
}
46-
finally
39+
private static unsafe int KillReadKey()
40+
{
41+
byte[] buffer = new byte[] { 0 }; //Encoding.ASCII.GetBytes("\n");
42+
fixed (byte* p = buffer)
4743
{
48-
s_readKeyHandle.Release();
44+
return write(0, p, buffer.Length);
4945
}
46+
}
5047

48+
public ConsoleKeyInfo ReadKey(bool intercept, CancellationToken cancellationToken)
49+
{
5150
// A key has been pressed, so acquire a lock on our internal stdin handle. This is done
5251
// so any of our calls to cursor position API's do not release ReadKey.
5352
s_stdInHandle.Wait(cancellationToken);
53+
s_readKeyHandle.Wait(cancellationToken);
5454
try
5555
{
56-
return System.Console.ReadKey(intercept);
56+
Task<ConsoleKeyInfo> keyTask = Task.Run(() => System.Console.ReadKey(intercept));
57+
keyTask.Wait(cancellationToken);
58+
return keyTask.Result;
59+
}
60+
catch (OperationCanceledException)
61+
{
62+
// TODO: Write a null byte to stdin.
63+
// Stream inputStream = System.Console.OpenStandardInput();
64+
// inputStream.Write(new byte[] { 0 }, 0, 1);
65+
// Interop.Sys.FileDescriptors.STDIN_FILENO
66+
// SafeFileHandle stdinHandle = new((IntPtr)0, false);
67+
// ConsoleStream stdinStream = new(stdinHandle, FileAccess.Write);
68+
KillReadKey();
69+
return ConsoleProxy.s_nullKeyInfo;
5770
}
5871
finally
5972
{
73+
s_readKeyHandle.Release();
6074
s_stdInHandle.Release();
6175
}
6276
}

src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -868,8 +868,7 @@ private ConsoleKeyInfo ReadKey(bool intercept)
868868
// So instead we keep track of the last key here.
869869
// This isn't functionally required,
870870
// but helps us determine when the prompt needs a newline added
871-
872-
_lastKey = ConsoleProxy.SafeReadKey(intercept, CancellationToken.None);
871+
_lastKey = ConsoleProxy.SafeReadKey(intercept, _cancellationContext.Peek().CancellationToken);
873872
return _lastKey.Value;
874873
}
875874

src/PowerShellEditorServices/Services/PowerShell/Utility/CancellationContext.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ public void CancelCurrentTask()
4949
}
5050
}
5151

52+
public CancellationScope Peek() => _cancellationSourceStack.TryPeek(out CancellationScope currentCancellationSource) ? currentCancellationSource : null;
53+
5254
public void CancelCurrentTaskStack()
5355
{
5456
foreach (CancellationScope scope in _cancellationSourceStack)

0 commit comments

Comments
 (0)