Skip to content

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

@alexrp

Description

@alexrp

In my library I expose a terminal input API that looks like this:

public static class Terminal
{
    public static ValueTask<string?> 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 and here.)

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_RECORDs. 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_RECORDs specifically, discarding other event records, and then resuming the wait if there are no KEY_EVENT_RECORDs. 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?

Metadata

Metadata

Labels

Area-ServerDown in the muck of API call servicing, interprocess communication, eventing, etc.Issue-QuestionFor questions or discussionProduct-ConhostFor issues in the Console codebase

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions