microsoft / microsoft/terminal
Need a reasonable way to either cancel `ReadConsole` or poll for text input
- 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
Assessment
This issue has not been assessed yet.