Cygwin: console: Improve the code to avoid typeahead key swapping.

- The commit "Cygwin: console: Prevent the order of typeahead input
  from swapped." did not fully resolve the issue. If keys are typed
  during input buffer fix, the order of key event may be swapped.
  This patch fixes the issue again.
This commit is contained in:
Takashi Yano 2022-02-28 20:02:01 +09:00
parent f6db6f52ae
commit 8ff9f216fd
1 changed files with 42 additions and 33 deletions

View File

@ -188,12 +188,23 @@ cons_master_thread (VOID *arg)
void void
fhandler_console::cons_master_thread (handle_set_t *p, tty *ttyp) fhandler_console::cons_master_thread (handle_set_t *p, tty *ttyp)
{ {
const int additional_space = 128; /* Possible max number of incoming events
during the process. Additional space
should be left for writeback fix. */
const int inrec_size = INREC_SIZE + additional_space;
struct
{
inline static size_t bytes (size_t n)
{
return sizeof (INPUT_RECORD) * n;
}
} m;
termios &ti = ttyp->ti; termios &ti = ttyp->ti;
int processed_up_to = -1; int processed_up_to = -1;
while (con.owner == myself->pid) while (con.owner == myself->pid)
{ {
DWORD total_read, n, i; DWORD total_read, n, i;
INPUT_RECORD input_rec[INREC_SIZE]; INPUT_RECORD input_rec[inrec_size];
if (con.disable_master_thread) if (con.disable_master_thread)
{ {
@ -203,6 +214,7 @@ fhandler_console::cons_master_thread (handle_set_t *p, tty *ttyp)
WaitForSingleObject (p->input_mutex, mutex_timeout); WaitForSingleObject (p->input_mutex, mutex_timeout);
total_read = 0; total_read = 0;
bool nowait = false;
switch (cygwait (p->input_handle, (DWORD) 0)) switch (cygwait (p->input_handle, (DWORD) 0))
{ {
case WAIT_OBJECT_0: case WAIT_OBJECT_0:
@ -211,16 +223,13 @@ fhandler_console::cons_master_thread (handle_set_t *p, tty *ttyp)
if (total_read == INREC_SIZE /* Working space full */ if (total_read == INREC_SIZE /* Working space full */
&& cygwait (p->input_handle, (DWORD) 0) == WAIT_OBJECT_0) && cygwait (p->input_handle, (DWORD) 0) == WAIT_OBJECT_0)
{ {
const int incr = 1; const int incr = min (processed_up_to + 1, additional_space);
size_t bytes = sizeof (INPUT_RECORD) * (total_read - incr);
/* Discard oldest incr events. */
memmove (input_rec, input_rec + incr, bytes);
total_read -= incr;
processed_up_to =
(processed_up_to + 1 >= incr) ? processed_up_to - incr : -1;
ReadConsoleInputW (p->input_handle, ReadConsoleInputW (p->input_handle,
input_rec + total_read, incr, &n); input_rec + total_read, incr, &n);
total_read += n; /* Discard oldest n events. */
memmove (input_rec, input_rec + n, m.bytes (total_read));
processed_up_to -= n;
nowait = true;
} }
break; break;
case WAIT_TIMEOUT: case WAIT_TIMEOUT:
@ -298,7 +307,7 @@ remove_record:
{ /* Remove corresponding record. */ { /* Remove corresponding record. */
if (total_read > i + 1) if (total_read > i + 1)
memmove (input_rec + i, input_rec + i + 1, memmove (input_rec + i, input_rec + i + 1,
sizeof (INPUT_RECORD) * (total_read - i - 1)); m.bytes (total_read - i - 1));
total_read--; total_read--;
i--; i--;
} }
@ -306,44 +315,44 @@ remove_record:
processed_up_to = total_read - 1; processed_up_to = total_read - 1;
if (total_read) if (total_read)
{ {
/* Writeback input records other than interrupt. */
WriteConsoleInputW (p->input_handle, input_rec, total_read, &n);
size_t bytes = sizeof (INPUT_RECORD) * total_read;
do do
{ {
const int additional_size = 128; /* Possible max number of INPUT_RECORD tmp[inrec_size];
incoming events during /* Writeback input records other than interrupt. */
above process. */ WriteConsoleInputW (p->input_handle, input_rec, total_read, &n);
const int new_size = INREC_SIZE + additional_size;
INPUT_RECORD tmp[new_size];
/* Check if writeback was successfull. */ /* Check if writeback was successfull. */
PeekConsoleInputW (p->input_handle, tmp, new_size, &n); PeekConsoleInputW (p->input_handle, tmp, inrec_size, &n);
if (memcmp (input_rec, tmp, bytes) == 0) if (n < total_read)
break; /* Someone has read input without acquiring
input_mutex. ConEmu cygwin-connector? */
if (memcmp (input_rec, tmp, m.bytes (total_read)) == 0)
break; /* OK */ break; /* OK */
/* Try to fix */ /* Try to fix */
DWORD incr = n - total_read; DWORD incr = n - total_read;
DWORD ofst; DWORD ofst;
for (ofst = 1; ofst <= incr; ofst++) for (ofst = 1; ofst <= incr; ofst++)
if (memcmp (input_rec, tmp + ofst, bytes) == 0) if (memcmp (input_rec, tmp + ofst, m.bytes (total_read)) == 0)
{ {
ReadConsoleInputW (p->input_handle, tmp, new_size, &n); ReadConsoleInputW (p->input_handle, tmp, inrec_size, &n);
DWORD m; memcpy (input_rec, tmp + ofst, m.bytes (total_read));
WriteConsoleInputW (p->input_handle, tmp + ofst, memcpy (input_rec + total_read, tmp, m.bytes (ofst));
total_read, &m); if (n > ofst + total_read)
WriteConsoleInputW (p->input_handle, tmp, ofst, &m); memcpy (input_rec + total_read + ofst,
if ( n > ofst + total_read)
WriteConsoleInputW (p->input_handle,
tmp + ofst + total_read, tmp + ofst + total_read,
n - (ofst + total_read), &m); m.bytes (n - (ofst + total_read)));
total_read = n;
break; break;
} }
if (ofst > incr) /* Hard to fix */ if (ofst > incr)
break; /* Giving up */ break; /* Writeback was not atomic. Or someone has read
input without acquiring input_mutex.
Giving up because hard to fix. */
} }
while (true); while (true);
} }
skip_writeback: skip_writeback:
ReleaseMutex (p->input_mutex); ReleaseMutex (p->input_mutex);
if (!nowait)
cygwait (40); cygwait (40);
} }
} }