[PATCH] Cygwin: console: Improve the performance of peek_console
Johannes Schindelin
Johannes.Schindelin@gmx.de
Sun Sep 13 11:56:38 GMT 2026
Hi Takashi,
On Fri, 4 Sep 2026, Takashi Yano wrote:
> Previously, peek_console() used PeekConsoleInput() to confirm whether
> the console input buffer has some input records. However, this need
> attaching to the console if the process does not attach to the console.
> To reduce that overhead, this patch uses WaitForSingleObject() with
> console input handle instead. WaitForSingleObject() works without
> attaching to the console, so this simplifies the peek_console()
> code.
Great! I like when Cygwin's code is taught to avoid expensive operations.
One issue, though:
>
> Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
> Reviewed-by:
> ---
> winsup/cygwin/select.cc | 9 +--------
> 1 file changed, 1 insertion(+), 8 deletions(-)
>
> diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
> index 592f6d14d..d565f4d5b 100644
> --- a/winsup/cygwin/select.cc
> +++ b/winsup/cygwin/select.cc
> @@ -1149,8 +1149,6 @@ peek_console (select_record *me, bool)
> return 1;
> }
>
> - INPUT_RECORD irec;
> - DWORD events_read;
> HANDLE h;
> set_handle_or_return_if_not_open (h, me);
>
> @@ -1161,12 +1159,7 @@ peek_console (select_record *me, bool)
> else
> {
> fh->acquire_input_mutex (mutex_timeout);
> - acquire_attach_mutex (mutex_timeout);
> - DWORD resume_pid = fh->attach_console (fh->get_owner ());
> - BOOL r = PeekConsoleInputW (h, &irec, 1, &events_read);
> - fh->detach_console (resume_pid, fh->get_owner ());
> - release_attach_mutex ();
> - if (!r || !events_read)
> + if (WaitForSingleObject (h, 0) != WAIT_OBJECT_0)
With this change, a host can report readiness without leaving any records
to process. Here is a concrete source counterexample in released
OpenConsole `v1.24.11911.0`:
1. `SetConsoleActiveScreenBufferImpl()`
(https://github.com/microsoft/terminal/blob/v1.24.11911.0/src/host/getset.cpp#L474-L498)
calls `SetActiveScreenBuffer()`
(https://github.com/microsoft/terminal/blob/v1.24.11911.0/src/host/output.cpp#L446-L476),
which invokes `FlushAllButKeys()`.
2. If the queue contained only non-key records, it becomes empty while the
input-available event remains signaled.
3. An empty subsequent peek returns before the event-reset code
(https://github.com/microsoft/terminal/blob/v1.24.11911.0/src/host/inputBuffer.cpp#L460-L470).
With no other input activity repairing that state, Cygwin's new loop
can repeatedly see "signaled handle" followed by "zero records."
In other words: changing active screen buffers can discard the remaining
non-key records without resetting the input-available event. The queue is
then empty but the handle remains signaled; an empty peek does not repair
the event. The new preliminary wait consequently succeeds repeatedly,
while `process_input_message(0)` finds zero records and returns
`input_processing` without changing readiness. `peek_console()` loops
without progress, so even a zero-timeout `select()` can hang. The old
preliminary peek explicitly broke out when its record count was zero.
This is not merely the normal distinction between a keypress and a
complete line. It is a stale host notification paired with an actually
empty queue. The source counterexample keeps Cygwin's worker paused
through Win32 input mode, with no new input or other reader repairing the
state.
I think we can keep the cheap `WaitForSingleObject()` check, but could we
let `process_input_message()` report an empty queue separately from
`input_processing`? If its `PeekConsoleInputW()` call finds no records,
`peek_console()` needs to stop retrying rather than spin on a handle that
stays signaled. Simply breaking on `input_processing` would also stop when
we consumed input but hadn't yet completed a canonical line, so we need to
keep those cases separate. Please handle input errors explicitly, too.
Ciao,
Johannes
> {
> fh->release_input_mutex ();
> break;
> --
> 2.51.0
>
>
More information about the Cygwin-patches
mailing list