Skip to content

Commit ff4440f

Browse files
Takashi Yano via Cygwin-patchesgithub-cygwin
Takashi Yano via Cygwin-patches
authored andcommitted
Cygwin: console: Introduce new thread which handles input signal.
- Currently, Ctrl-Z, Ctrl-\ and SIGWINCH does not work in console if the process does not call read() or select(). This is because these are processed in process_input_message() which is called from read() or select(). This is a long standing issue of console. Addresses: https://cygwin.com/pipermail/cygwin/2020-May/244898.html https://cygwin.com/pipermail/cygwin/2021-February/247779.html With this patch, new thread which handles only input signals is introduced so that Crtl-Z, etc. work without calling read() or select(). Ctrl-S and Ctrl-Q are also handled in this thread.
1 parent 571e730 commit ff4440f

File tree

3 files changed

+181
-2
lines changed

3 files changed

+181
-2
lines changed

winsup/cygwin/exceptions.cc

+1
Original file line numberDiff line numberDiff line change
@@ -1163,6 +1163,7 @@ ctrl_c_handler (DWORD type)
11631163
sig = SIGQUIT;
11641164
t->last_ctrl_c = GetTickCount64 ();
11651165
t->kill_pgrp (sig);
1166+
t->output_stopped = false;
11661167
t->last_ctrl_c = GetTickCount64 ();
11671168
return TRUE;
11681169
}

winsup/cygwin/fhandler.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -2105,6 +2105,7 @@ class fhandler_console: public fhandler_termios
21052105
HANDLE input_mutex;
21062106
HANDLE output_mutex;
21072107
};
2108+
HANDLE thread_sync_event;
21082109
private:
21092110
static const unsigned MAX_WRITE_CHARS;
21102111
static console_state *shared_console_info;
@@ -2167,7 +2168,7 @@ class fhandler_console: public fhandler_termios
21672168

21682169
void __reg3 read (void *ptr, size_t& len);
21692170
ssize_t __stdcall write (const void *ptr, size_t len);
2170-
void doecho (const void *str, DWORD len) { (void) write (str, len); }
2171+
void doecho (const void *str, DWORD len);
21712172
int close ();
21722173
static bool exists () {return !!GetConsoleCP ();}
21732174

@@ -2247,6 +2248,8 @@ class fhandler_console: public fhandler_termios
22472248
static void request_xterm_mode_input (bool, const handle_set_t *p);
22482249
static void request_xterm_mode_output (bool, const handle_set_t *p);
22492250

2251+
static void cons_master_thread (handle_set_t *p, tty *ttyp);
2252+
22502253
friend tty_min * tty_list::get_cttyp ();
22512254
};
22522255

winsup/cygwin/fhandler_console.cc

+176-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ details. */
4747
con.b.srWindow.Top + con.scroll_region.Bottom)
4848
#define con_is_legacy (shared_console_info && con.is_legacy)
4949

50+
#define CONS_THREAD_SYNC "cygcons.thread_sync"
51+
5052
const unsigned fhandler_console::MAX_WRITE_CHARS = 16384;
5153

5254
fhandler_console::console_state NO_COPY *fhandler_console::shared_console_info;
@@ -170,6 +172,143 @@ console_unit::console_unit (HWND me0):
170172
api_fatal ("console device allocation failure - too many consoles in use, max consoles is 32");
171173
}
172174

175+
static DWORD WINAPI
176+
cons_master_thread (VOID *arg)
177+
{
178+
fhandler_console *fh = (fhandler_console *) arg;
179+
tty *ttyp = (tty *) fh->tc ();
180+
fhandler_console::handle_set_t handle_set;
181+
fh->get_duplicated_handle_set (&handle_set);
182+
HANDLE thread_sync_event;
183+
DuplicateHandle (GetCurrentProcess (), fh->thread_sync_event,
184+
GetCurrentProcess (), &thread_sync_event,
185+
0, FALSE, DUPLICATE_SAME_ACCESS);
186+
SetEvent (thread_sync_event);
187+
/* Do not touch class members after here because the class instance
188+
may have been destroyed. */
189+
fhandler_console::cons_master_thread (&handle_set, ttyp);
190+
fhandler_console::close_handle_set (&handle_set);
191+
SetEvent (thread_sync_event);
192+
CloseHandle (thread_sync_event);
193+
return 0;
194+
}
195+
196+
/* This thread processes signals derived from input messages.
197+
Without this thread, those signals can be handled only when
198+
the process calls read() or select(). This thread reads input
199+
records, processes signals and removes corresponding record.
200+
The other input records are kept back for read() or select(). */
201+
void
202+
fhandler_console::cons_master_thread (handle_set_t *p, tty *ttyp)
203+
{
204+
DWORD output_stopped_at = 0;
205+
while (con.owner == myself->pid)
206+
{
207+
DWORD total_read, n, i, j;
208+
INPUT_RECORD input_rec[INREC_SIZE];
209+
210+
WaitForSingleObject (p->input_mutex, INFINITE);
211+
total_read = 0;
212+
switch (cygwait (p->input_handle, (DWORD) 0))
213+
{
214+
case WAIT_OBJECT_0:
215+
ReadConsoleInputA (p->input_handle,
216+
input_rec, INREC_SIZE, &total_read);
217+
break;
218+
case WAIT_TIMEOUT:
219+
case WAIT_SIGNALED:
220+
case WAIT_CANCELED:
221+
break;
222+
default: /* Error */
223+
ReleaseMutex (p->input_mutex);
224+
return;
225+
}
226+
for (i = 0; i < total_read; i++)
227+
{
228+
const char c = input_rec[i].Event.KeyEvent.uChar.AsciiChar;
229+
bool processed = false;
230+
termios &ti = ttyp->ti;
231+
switch (input_rec[i].EventType)
232+
{
233+
case KEY_EVENT:
234+
if (ti.c_lflag & ISIG)
235+
{
236+
int sig = 0;
237+
if (CCEQ (ti.c_cc[VINTR], c))
238+
sig = SIGINT;
239+
else if (CCEQ (ti.c_cc[VQUIT], c))
240+
sig = SIGQUIT;
241+
else if (CCEQ (ti.c_cc[VSUSP], c))
242+
sig = SIGTSTP;
243+
if (sig && input_rec[i].Event.KeyEvent.bKeyDown)
244+
{
245+
ttyp->kill_pgrp (sig);
246+
ttyp->output_stopped = false;
247+
/* Discard type ahead input */
248+
goto skip_writeback;
249+
}
250+
}
251+
if (ti.c_iflag & IXON)
252+
{
253+
if (CCEQ (ti.c_cc[VSTOP], c))
254+
{
255+
if (!ttyp->output_stopped
256+
&& input_rec[i].Event.KeyEvent.bKeyDown)
257+
{
258+
ttyp->output_stopped = true;
259+
output_stopped_at = i;
260+
}
261+
processed = true;
262+
}
263+
else if (CCEQ (ti.c_cc[VSTART], c))
264+
{
265+
restart_output:
266+
if (input_rec[i].Event.KeyEvent.bKeyDown)
267+
ttyp->output_stopped = false;
268+
processed = true;
269+
}
270+
else if ((ti.c_iflag & IXANY) && ttyp->output_stopped
271+
&& c && i >= output_stopped_at)
272+
goto restart_output;
273+
}
274+
break;
275+
case WINDOW_BUFFER_SIZE_EVENT:
276+
SHORT y = con.dwWinSize.Y;
277+
SHORT x = con.dwWinSize.X;
278+
con.fillin (p->output_handle);
279+
if (y != con.dwWinSize.Y || x != con.dwWinSize.X)
280+
{
281+
con.scroll_region.Top = 0;
282+
con.scroll_region.Bottom = -1;
283+
if (wincap.has_con_24bit_colors () && !con_is_legacy)
284+
{ /* Fix tab position */
285+
/* Re-setting ENABLE_VIRTUAL_TERMINAL_PROCESSING
286+
fixes the tab position. */
287+
request_xterm_mode_output (false, p);
288+
request_xterm_mode_output (true, p);
289+
}
290+
ttyp->kill_pgrp (SIGWINCH);
291+
}
292+
processed = true;
293+
break;
294+
}
295+
if (processed)
296+
{ /* Remove corresponding record. */
297+
for (j = i; j < total_read - 1; j++)
298+
input_rec[j] = input_rec[j + 1];
299+
total_read--;
300+
i--;
301+
}
302+
}
303+
if (total_read)
304+
/* Write back input records other than interrupt. */
305+
WriteConsoleInput (p->input_handle, input_rec, total_read, &n);
306+
skip_writeback:
307+
ReleaseMutex (p->input_mutex);
308+
cygwait (40);
309+
}
310+
}
311+
173312
bool
174313
fhandler_console::set_unit ()
175314
{
@@ -1194,6 +1333,15 @@ fhandler_console::open (int flags, mode_t)
11941333
debug_printf ("opened conin$ %p, conout$ %p", get_handle (),
11951334
get_output_handle ());
11961335

1336+
if (myself->pid == con.owner)
1337+
{
1338+
char name[MAX_PATH];
1339+
shared_name (name, CONS_THREAD_SYNC, get_minor ());
1340+
thread_sync_event = CreateEvent(NULL, FALSE, FALSE, name);
1341+
new cygthread (::cons_master_thread, this, "consm");
1342+
WaitForSingleObject (thread_sync_event, INFINITE);
1343+
CloseHandle (thread_sync_event);
1344+
}
11971345
return 1;
11981346
}
11991347

@@ -1230,6 +1378,16 @@ fhandler_console::close ()
12301378

12311379
release_output_mutex ();
12321380

1381+
if (con.owner == myself->pid)
1382+
{
1383+
char name[MAX_PATH];
1384+
shared_name (name, CONS_THREAD_SYNC, get_minor ());
1385+
thread_sync_event = OpenEvent (MAXIMUM_ALLOWED, FALSE, name);
1386+
con.owner = 0;
1387+
WaitForSingleObject (thread_sync_event, INFINITE);
1388+
CloseHandle (thread_sync_event);
1389+
}
1390+
12331391
CloseHandle (input_mutex);
12341392
input_mutex = NULL;
12351393
CloseHandle (output_mutex);
@@ -1539,7 +1697,7 @@ fhandler_console::tcgetattr (struct termios *t)
15391697
}
15401698

15411699
fhandler_console::fhandler_console (fh_devices unit) :
1542-
fhandler_termios (), input_ready (false),
1700+
fhandler_termios (), input_ready (false), thread_sync_event (NULL),
15431701
input_mutex (NULL), output_mutex (NULL)
15441702
{
15451703
if (unit > 0)
@@ -3022,6 +3180,14 @@ fhandler_console::write (const void *vsrc, size_t len)
30223180
if (bg <= bg_eof)
30233181
return (ssize_t) bg;
30243182

3183+
if (get_ttyp ()->output_stopped && is_nonblocking ())
3184+
{
3185+
set_errno (EAGAIN);
3186+
return -1;
3187+
}
3188+
while (get_ttyp ()->output_stopped)
3189+
cygwait (10);
3190+
30253191
acquire_attach_mutex (INFINITE);
30263192
push_process_state process_state (PID_TTYOU);
30273193
acquire_output_mutex (INFINITE);
@@ -3352,6 +3518,15 @@ fhandler_console::write (const void *vsrc, size_t len)
33523518
return len;
33533519
}
33543520

3521+
void
3522+
fhandler_console::doecho (const void *str, DWORD len)
3523+
{
3524+
bool stopped = get_ttyp ()->output_stopped;
3525+
get_ttyp ()->output_stopped = false;
3526+
write (str, len);
3527+
get_ttyp ()->output_stopped = stopped;
3528+
}
3529+
33553530
static const struct {
33563531
int vk;
33573532
const char *val[4];

0 commit comments

Comments
 (0)