Skip to content

Commit c313089

Browse files
tyan0dscho
andcommitted
Cygwin: console: Correct previous NOFLSH fix
The previous fix for NOFLSH mode does not work as intended. discard_key_events(), added in "Cygwin: console: Fix NOFLSH behaviour a bit", loops on ReadConsoleInputW() until it has consumed the requested number of records, but ReadConsoleInputW() blocks while the console input buffer is empty. sigflush() calls it with a hard-coded count of one and no guarantee that a record is actually queued: in the master-thread path the signalling record has already been read out of the buffer before sigflush() runs, so the call blocks until, and then swallows, the user's next keystroke. To avoid this, this patch does not discard input when process_sigs() is called from cons_master_thread, where the value of `fh` is NULL, because discarding will be done in cons_master_thread. And because the ReadConsoleInputW() return value is unchecked, a failed read leaves the count indeterminate, so "n -= n1" can underflow and spin. Check return value of ReadConsoleInputW() and abort if it fails. Moreover, discard_key_event(1) does not work as intended if the first key event is not a bKeyDown event correspoding to the signalling key. Use discard_key_events(0) instead. This means discarding input events to the current position processed. Since the key-strokes prior to the signalling key are already in the readahead buffer, so this call discards only the signalling key. The important point here is to discard input before releasing input_mutex by release_input_mutex_if_necessary(), because, if not, cons_master_thread starts to process key events before discarding signalling key event because the thread can acquire input_mutex. This causes the signalling key is processed twice. One separate point: the `process_input_message()` caller wraps `discard_key_events()` in `acquire_attach_mutex()` + `attach_console (con.owner)`, but the `sigflush()` call site does not, so the `ReadConsoleInputW()` there runs against whatever console the calling process happens to be attached to. With the guard above the worst case is a no-op when the calling process happens not to be attached, so it would be more correct to move the attach into the helper itself. This patch also fixes two more special cases. One is done_with_debugger case. When `gdb cat` is executed and the `cat` is running, Ctrl-C discards all the key events including the events after Ctrl-C. This is because tcflush() is used for the purpose. Use discard_key_events(0) instead. The other case is not_signalled_but_done case. Previously, when `cat | non-cygwin-app` is executed and Ctrl-C is pressed, but the `Ctrl-C` is not VINTR, line_edit() wrongly returned line_edit_signalled even though `cat` is not signalled by Ctrl-C. In this case, `cat` should receive Ctrl-C as a input char, while `non-cygwin-app` has been killed by Ctrl-C. Fix this in line_edit(). In the case of not_signalled_but_done case, setting `sawsig` flag and releasing `output_stopped` has been skipped with this patch, because this (Ctrl-C) is not a signal key in the case above. Fixes: 66324ed ("Cygwin: console: Fix NOFLSH behaviour a bit") Co-authored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de> Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp> Reviewed-by: Johannes Schindelin <Johannes.Schindelin@gmx.de> (cherry picked from commit 0d516c2)
1 parent 36f40c9 commit c313089

3 files changed

Lines changed: 36 additions & 23 deletions

File tree

winsup/cygwin/fhandler/console.cc

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1665,6 +1665,7 @@ fhandler_console::process_input_message (size_t len)
16651665
continue;
16661666
}
16671667

1668+
num_input_events_processed = i + 1;
16681669
num_chars += nread;
16691670
if (toadd)
16701671
{
@@ -1695,33 +1696,37 @@ fhandler_console::process_input_message (size_t len)
16951696
/* Discard processed recored. */
16961697
DWORD discard_len = min (total_read, i + 1);
16971698
/* If input is signalled, do not discard input here because
1698-
tcflush() is already called from line_edit(). */
1699-
if (stat == input_signalled && !(ti->c_lflag & NOFLSH))
1699+
discard_key_events() is already called from line_edit(). */
1700+
if (stat == input_signalled)
17001701
discard_len = 0;
17011702
if (discard_len && (len || stat != input_ok))
1702-
{
1703-
acquire_attach_mutex (mutex_timeout);
1704-
DWORD resume_pid = attach_console (con.owner);
1705-
discard_key_events (discard_len);
1706-
detach_console (resume_pid, con.owner);
1707-
release_attach_mutex ();
1708-
}
1703+
discard_key_events (discard_len);
17091704
return stat;
17101705
}
17111706

17121707
void
17131708
fhandler_console::discard_key_events (size_t n)
17141709
{
17151710
DWORD discarded = 0;
1711+
if (n == 0)
1712+
{
1713+
n = num_input_events_processed;
1714+
num_input_events_processed = 0;
1715+
}
17161716
INPUT_RECORD input_rec[INREC_SIZE];
17171717
DWORD n1 = min (INREC_SIZE, n);
1718+
acquire_attach_mutex (mutex_timeout);
1719+
DWORD resume_pid = attach_console (con.owner);
17181720
while (n)
17191721
{
1720-
ReadConsoleInputW (get_handle (), input_rec, n1, &n1);
1722+
if (!ReadConsoleInputW (get_handle (), input_rec, n1, &n1) || !n1)
1723+
break;
17211724
n -= n1;
17221725
discarded += n1;
17231726
n1 = min (INREC_SIZE, n);
17241727
}
1728+
detach_console (resume_pid, con.owner);
1729+
release_attach_mutex ();
17251730
con.num_processed -= min (con.num_processed, discarded);
17261731
}
17271732

@@ -2305,7 +2310,8 @@ fhandler_console::tcgetattr (struct termios *t)
23052310

23062311
fhandler_console::fhandler_console (fh_devices devunit) :
23072312
fhandler_termios (), input_ready (false), thread_sync_event (NULL),
2308-
input_mutex (NULL), output_mutex (NULL), unit (MAX_CONS_DEV)
2313+
input_mutex (NULL), output_mutex (NULL), unit (MAX_CONS_DEV),
2314+
num_input_events_processed (0)
23092315
{
23102316
dev_referred_via = (dev_t) devunit;
23112317
if (devunit > 0)

winsup/cygwin/fhandler/termios.cc

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,10 @@ fhandler_termios::process_sigs (char c, tty* ttyp, fhandler_termios *fh)
353353
fhandler_pty_common::attach_console_temporarily (p->dwProcessId);
354354
if (fh && p == myself && being_debugged ())
355355
{ /* Avoid deadlock in gdb on console. */
356-
fh->tcflush(TCIFLUSH);
356+
if (fh->is_console ())
357+
fh->discard_key_events (0 /* to current position */);
358+
else
359+
fh->tcflush(TCIFLUSH);
357360
fh->release_input_mutex_if_necessary ();
358361
}
359362
/* CTRL_C_EVENT does not work for the process started with
@@ -444,10 +447,14 @@ fhandler_termios::process_sigs (char c, tty* ttyp, fhandler_termios *fh)
444447
goto not_a_sig;
445448

446449
termios_printf ("got interrupt %d, sending signal %d", c, sig);
447-
if (!(ti.c_lflag & NOFLSH) && fh)
450+
if (fh)
448451
{
449-
fh->eat_readahead (-1);
450-
fh->discard_input ();
452+
if (!(ti.c_lflag & NOFLSH))
453+
{
454+
fh->eat_readahead (-1);
455+
fh->discard_input ();
456+
}
457+
fh->discard_key_events (0 /* to current position */);
451458
}
452459
if (fh)
453460
fh->release_input_mutex_if_necessary ();
@@ -460,6 +467,8 @@ fhandler_termios::process_sigs (char c, tty* ttyp, fhandler_termios *fh)
460467
not_a_sig:
461468
if ((ti.c_lflag & ISIG) && need_discard_input)
462469
{
470+
if (need_send_sig)
471+
return not_signalled;
463472
if (!(ti.c_lflag & NOFLSH) && fh)
464473
{
465474
fh->eat_readahead (-1);
@@ -525,10 +534,11 @@ fhandler_termios::line_edit (const char *rptr, size_t nread, termios& ti,
525534
switch (process_sigs (c, get_ttyp (), this))
526535
{
527536
case signalled:
528-
case not_signalled_but_done:
529537
case done_with_debugger:
530538
sawsig = true;
531539
get_ttyp ()->output_stopped = false;
540+
fallthrough;
541+
case not_signalled_but_done:
532542
continue;
533543
case not_signalled_with_nat_reader:
534544
disable_eof_key = true;
@@ -666,13 +676,9 @@ fhandler_termios::sigflush ()
666676
be NULL while this is alive. However, we can conceivably close a
667677
ctty while exiting and that will zero this. */
668678
if ((!have_execed || have_execed_cygwin) && tc ()
669-
&& (tc ()->getpgid () == myself->pgid))
670-
{
671-
if (!(tc ()->ti.c_lflag & NOFLSH))
672-
tcflush (TCIFLUSH);
673-
else
674-
discard_key_events (1);
675-
}
679+
&& (tc ()->getpgid () == myself->pgid)
680+
&& !(tc ()->ti.c_lflag & NOFLSH))
681+
tcflush (TCIFLUSH);
676682
}
677683

678684
pid_t

winsup/cygwin/local_includes/fhandler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2200,6 +2200,7 @@ class fhandler_console: public fhandler_termios
22002200
HANDLE input_mutex, output_mutex;
22012201
handle_set_t handle_set;
22022202
_minor_t unit;
2203+
size_t num_input_events_processed;
22032204

22042205
/* Used when we encounter a truncated multi-byte sequence. The
22052206
lead bytes are stored here and revisited in the next write call. */

0 commit comments

Comments
 (0)