vezel-dev / cathode

A terminal-centric replacement for the .NET console APIs.
https://docs.vezel.dev/cathode
BSD Zero Clause License
91 stars 7 forks source link

Mechanism to cancel reading from standard input #46

Closed alexrp closed 2 years ago

alexrp commented 2 years ago

For input processing we will need to constantly be reading standard in. But we also need a way to cancel that reading so the input thread doesn't just sit there forever.

Investigate how we can do this on each supported platform. Might be worth checking what, if anything, Notcurses does.

alexrp commented 2 years ago

Setting a flag and writing some garbage with ioctl(TIOCSTI) could potentially be an option. The equivalent on Windows should simply be WriteConsole on the input handle (probably, maybe). Or WriteConsoleInput.

alexrp commented 2 years ago

Setting a flag and writing some garbage with ioctl(TIOCSTI) could potentially be an option.

Then again, setting a flag and sending a signal should work just as well and seems less hacky. No such mechanism on Windows, though. Would also require knowing the thread ID of the reading thread.

alexrp commented 2 years ago

Here's an approach I think is workable and way less hacky.

TerminalReader.Read (and APIs that use it) should be changed to accept an optional CancellationToken.

We will create a pipe on startup. Instead of trying to read immediately, we will poll on the input FD and pipe read FD. When poll returns, we check if the pipe had data written. If yes, we drain it and throw an OperationCanceledException (leaving any potential data in the input FD unread). If no, proceed to read input as normal.

The TerminalReader will hook into the cancellation token with UnsafeRegister. The callback will simply write a byte to the pipe write FD to indicate that cancellation is requested.

This scheme should in principle work the same on Windows - just replace pipe with CreatePipe, poll with WaitForMultipleObjects, read with ReadConsole/ReadFile, etc.

alexrp commented 2 years ago

Bad news: WaitForMultipleObjects doesn't work on pipes. So the Unix approach won't translate neatly to Windows.

alexrp commented 2 years ago

Mostly done in fe53706a3c7c1c7d15481d28bcb625c9f89f8c4e (with a kinda gross but functional workaround for Windows).

Still need to see if cancellation can somehow be plumbed through for ReadLine in addition to ReadRaw.

alexrp commented 2 years ago

Still need to see if cancellation can somehow be plumbed through for ReadLine in addition to ReadRaw.

It will probably be possible in .NET 7; see:

Closing this.