microsoft / microsoft/terminal

Need a reasonable way to either cancel `ReadConsole` or poll for text input

Open
#12,143 27 comments 21 reactions 2 assignees Claimed by @zadjii-msft View on GitHub
Area-Server Issue-Question Product-Conhost
Dominant language
C++
Stars
105k
Forks
9.6k
Avg merge
3d 17h
Merged PRs (30d)
29

Description

In my [library](https://github.com/alexrp/system-terminal) I expose a terminal input API that looks like this:

```csharp
public static class Terminal
{
public static ValueTask ReadLineAsync(CancellationToken cancellationToken = default)
{
// ..
}
}
```

On Unix, implementing cancellation support was quite easy:

* Create an anonymous pipe at startup.
* When the `CancellationToken` is triggered, write a dummy value to the write end of the pipe.
* In `ReadLineAsync`, poll for input on the read end of the pipe and `stdin`.
* If the poll returns indicating input on the pipe, drain the pipe and throw `OperationCanceledException`.
* If the poll returns indicating input on `stdin`, actually read the data and return it.

(For reference, the implementation of the above can be found [here](https://github.com/alexrp/system-terminal/blob/bb7a98aeca34afa7eb19c5c154b1193831190083/src/core/Terminals/Unix/UnixCancellationPipe.cs) and [here](https://github.com/alexrp/system-terminal/blob/bb7a98aeca34afa7eb19c5c154b1193831190083/src/core/Terminals/Unix/UnixTerminalReader.cs).)

I would now like to implement something similar for Windows. Unfortunately, as far as I can tell, this is bordering on impossible at the moment.

I naïvely tried to replicate the approach above on Windows, only to discover that `WaitForMultipleObjects` does not support polling on pipes. I replaced the pipe with an event since `WaitForMultipleObjects` supports those. I then ran into another problem: Polling on a console input handle will return when *any* `INPUT_RECORD` arrives, not just `KEY_EVENT_RECORD`s. I decided to try `CancelIo` and `CancelSynchronousIo` on the off chance that they'd work instead of polling, but they just don't work on console input, apparently. At that point, things got hairy; I went back to the polling approach and tried to inspect the input buffer looking for `KEY_EVENT_RECORD`s specifically, discarding other event records, and then resuming the wait if there are no `KEY_EVENT_RECORD`s. Besides being very gross and hacky, this fell apart quickly as it turns out there's no clean way to figure out if a given `KEY_EVENT_RECORD` (or a series of them) will actually result in text input being available on the next `ReadConsole` call. Worse yet, even when I did hack together some heuristics, the behavior turned out to be different between CMD and Windows Terminal. At that point, I gave up and ripped out Windows cancellation support.

So I suppose this issue boils down to me asking: Is there a way to achieve what I'm trying to do that I just haven't realized yet? If not, could one be implemented?

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.