From c3e2b812d0111da86f66fced7b33492261db1480 Mon Sep 17 00:00:00 2001 From: Daniel Jacobowitz Date: Mon, 24 Apr 2006 21:00:13 +0000 Subject: [PATCH] * ser-mingw.c: Include . (struct ser_console_state, struct net_windows_state): Add exit_select, have_stopped, thread. (pipe_select_thread, console_select_thread) (net_windows_select_thread): Don't create a local state copy or close stop_select. Exit on exit_select instead of stop_select. Set have_stopped. (console_select_thread): Don't report control keypresses as pending input. (pipe_select_thread): Allow stop_select to interrupt sleeping. (set_console_wait_handle): Create exit_select and have_stopped. Save the thread handle. Check _kbhit before starting a thread. (ser_console_done_wait_handle): New. (ser_console_close): Close new handles. Wait for the thread to exit. (new_windows_select_thread): Assert that an event occurred. (net_windows_wait_handle): Check for pending input before starting a thread. (net_windows_done_wait_handle): New. (net_windows_open): Create exit_select and have_stopped. Save the thread handle. (net_windows_close): Close new handles. Wait for the thread to exit. (_intiialize_ser_windows): Register done_wait_handle methods. * serial.c [USE_WIN32API] (serial_done_wait_handle): New. * serial.h [USE_WIN32API] (struct serial_ops): Add done_wait_handle. [USE_WIN32API] (serial_done_wait_handle): New prototype. * mingw-hdep.c (gdb_select): Use serial_done_wait_handle. --- gdb/ChangeLog | 32 ++++++ gdb/mingw-hdep.c | 10 ++ gdb/ser-mingw.c | 277 +++++++++++++++++++++++++++++++++++------------ gdb/serial.c | 7 ++ gdb/serial.h | 4 + 5 files changed, 259 insertions(+), 71 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 5c0a04d687f..89a8e3d2bba 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,35 @@ +2006-04-24 Daniel Jacobowitz + + * ser-mingw.c: Include . + (struct ser_console_state, struct net_windows_state): Add exit_select, + have_stopped, thread. + (pipe_select_thread, console_select_thread) + (net_windows_select_thread): Don't create a local state copy or + close stop_select. Exit on exit_select instead of stop_select. Set + have_stopped. + (console_select_thread): Don't report control keypresses as pending + input. + (pipe_select_thread): Allow stop_select to interrupt sleeping. + (set_console_wait_handle): Create exit_select and have_stopped. + Save the thread handle. Check _kbhit before starting a thread. + (ser_console_done_wait_handle): New. + (ser_console_close): Close new handles. Wait for the thread to + exit. + (new_windows_select_thread): Assert that an event occurred. + (net_windows_wait_handle): Check for pending input before starting + a thread. + (net_windows_done_wait_handle): New. + (net_windows_open): Create exit_select and have_stopped. + Save the thread handle. + (net_windows_close): Close new handles. Wait for the thread to + exit. + (_intiialize_ser_windows): Register done_wait_handle methods. + + * serial.c [USE_WIN32API] (serial_done_wait_handle): New. + * serial.h [USE_WIN32API] (struct serial_ops): Add done_wait_handle. + [USE_WIN32API] (serial_done_wait_handle): New prototype. + * mingw-hdep.c (gdb_select): Use serial_done_wait_handle. + 2006-04-23 Andreas Schwab * rs6000-tdep.c: Include "reggroups.h" only once. diff --git a/gdb/mingw-hdep.c b/gdb/mingw-hdep.c index 79b23dc2556..bb0f50aaa46 100644 --- a/gdb/mingw-hdep.c +++ b/gdb/mingw-hdep.c @@ -167,6 +167,10 @@ gdb_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, for (fd = 0, indx = 0; fd < n; ++fd) { HANDLE fd_h; + struct serial *scb; + + if (!FD_ISSET (fd, readfds) && !FD_ISSET (fd, writefds)) + continue; if (FD_ISSET (fd, readfds)) { @@ -189,6 +193,12 @@ gdb_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, else num_ready++; } + + /* We created at least one event handle for this fd. Let the + device know we are finished with it. */ + scb = serial_for_fd (fd); + if (scb) + serial_done_wait_handle (scb); } return num_ready; diff --git a/gdb/ser-mingw.c b/gdb/ser-mingw.c index 7a6f232379e..17f8320b276 100644 --- a/gdb/ser-mingw.c +++ b/gdb/ser-mingw.c @@ -26,6 +26,7 @@ #include "ser-tcp.h" #include +#include #include #include @@ -350,23 +351,22 @@ struct ser_console_state HANDLE start_select; HANDLE stop_select; + HANDLE exit_select; + HANDLE have_stopped; + + HANDLE thread; }; static DWORD WINAPI console_select_thread (void *arg) { struct serial *scb = arg; - struct ser_console_state *state, state_copy; - int event_index, fd; + struct ser_console_state *state; + int event_index; HANDLE h; - /* Copy useful information out of the control block, to make sure - that we do not race with freeing it. */ - state_copy = *(struct ser_console_state *) scb->state; - state = &state_copy; - fd = scb->fd; - - h = (HANDLE) _get_osfhandle (fd); + state = scb->state; + h = (HANDLE) _get_osfhandle (scb->fd); while (1) { @@ -374,14 +374,15 @@ console_select_thread (void *arg) INPUT_RECORD record; DWORD n_records; + SetEvent (state->have_stopped); + wait_events[0] = state->start_select; - wait_events[1] = state->stop_select; + wait_events[1] = state->exit_select; if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0) - { - CloseHandle (state->stop_select); - return 0; - } + return 0; + + ResetEvent (state->have_stopped); retry: wait_events[0] = state->stop_select; @@ -391,10 +392,7 @@ console_select_thread (void *arg) if (event_index == WAIT_OBJECT_0 || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0) - { - CloseHandle (state->stop_select); - return 0; - } + continue; if (event_index != WAIT_OBJECT_0 + 1) { @@ -415,9 +413,30 @@ console_select_thread (void *arg) if (record.EventType == KEY_EVENT && record.Event.KeyEvent.bKeyDown) { - /* This is really a keypress. */ - SetEvent (state->read_event); - continue; + WORD keycode = record.Event.KeyEvent.wVirtualKeyCode; + + /* Ignore events containing only control keys. We must + recognize "enhanced" keys which we are interested in + reading via getch, if they do not map to ASCII. But we + do not want to report input available for e.g. the + control key alone. */ + + if (record.Event.KeyEvent.uChar.AsciiChar != 0 + || keycode == VK_PRIOR + || keycode == VK_NEXT + || keycode == VK_END + || keycode == VK_HOME + || keycode == VK_LEFT + || keycode == VK_UP + || keycode == VK_RIGHT + || keycode == VK_DOWN + || keycode == VK_INSERT + || keycode == VK_DELETE) + { + /* This is really a keypress. */ + SetEvent (state->read_event); + continue; + } } /* Otherwise discard it and wait again. */ @@ -439,31 +458,27 @@ static DWORD WINAPI pipe_select_thread (void *arg) { struct serial *scb = arg; - struct ser_console_state *state, state_copy; - int event_index, fd; + struct ser_console_state *state; + int event_index; HANDLE h; - /* Copy useful information out of the control block, to make sure - that we do not race with freeing it. */ - state_copy = *(struct ser_console_state *) scb->state; - state = &state_copy; - fd = scb->fd; - - h = (HANDLE) _get_osfhandle (fd); + state = scb->state; + h = (HANDLE) _get_osfhandle (scb->fd); while (1) { HANDLE wait_events[2]; DWORD n_avail; + SetEvent (state->have_stopped); + wait_events[0] = state->start_select; - wait_events[1] = state->stop_select; + wait_events[1] = state->exit_select; if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0) - { - CloseHandle (state->stop_select); - return 0; - } + return 0; + + ResetEvent (state->have_stopped); retry: if (!PeekNamedPipe (h, NULL, 0, NULL, &n_avail, NULL)) @@ -478,13 +493,11 @@ pipe_select_thread (void *arg) continue; } - if (WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0) - { - CloseHandle (state->stop_select); - return 0; - } + /* Delay 10ms before checking again, but allow the stop event + to wake us. */ + if (WaitForSingleObject (state->stop_select, 10) == WAIT_OBJECT_0) + continue; - Sleep (10); goto retry; } } @@ -511,29 +524,62 @@ ser_console_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) memset (state, 0, sizeof (struct ser_console_state)); scb->state = state; - /* Create auto reset events to wake and terminate the select thread. */ + /* Create auto reset events to wake, stop, and exit the select + thread. */ state->start_select = CreateEvent (0, FALSE, FALSE, 0); state->stop_select = CreateEvent (0, FALSE, FALSE, 0); + state->exit_select = CreateEvent (0, FALSE, FALSE, 0); - /* Create our own events to report read and exceptions separately. - The exception event is currently never used. */ + /* Create a manual reset event to signal whether the thread is + stopped. This must be manual reset, because we may wait on + it multiple times without ever starting the thread. */ + state->have_stopped = CreateEvent (0, TRUE, FALSE, 0); + + /* Create our own events to report read and exceptions separately. */ state->read_event = CreateEvent (0, FALSE, FALSE, 0); state->except_event = CreateEvent (0, FALSE, FALSE, 0); - /* And finally start the select thread. */ if (is_tty) - CreateThread (NULL, 0, console_select_thread, scb, 0, &threadId); + state->thread = CreateThread (NULL, 0, console_select_thread, scb, 0, + &threadId); else - CreateThread (NULL, 0, pipe_select_thread, scb, 0, &threadId); + state->thread = CreateThread (NULL, 0, pipe_select_thread, scb, 0, + &threadId); } + *read = state->read_event; + *except = state->except_event; + + /* Start from a blank state. */ ResetEvent (state->read_event); ResetEvent (state->except_event); + ResetEvent (state->stop_select); + + /* First check for a key already in the buffer. If there is one, + we don't need a thread. This also catches the second key of + multi-character returns from getch, for instance for arrow + keys. The second half is in a C library internal buffer, + and PeekConsoleInput will not find it. */ + if (_kbhit ()) + { + SetEvent (state->read_event); + return; + } + /* Otherwise, start the select thread. */ SetEvent (state->start_select); +} - *read = state->read_event; - *except = state->except_event; +static void +ser_console_done_wait_handle (struct serial *scb) +{ + struct ser_console_state *state = scb->state; + + if (state == NULL) + return; + + SetEvent (state->stop_select); + WaitForSingleObject (state->have_stopped, INFINITE); } static void @@ -543,7 +589,14 @@ ser_console_close (struct serial *scb) if (scb->state) { - SetEvent (state->stop_select); + SetEvent (state->exit_select); + + WaitForSingleObject (state->thread, INFINITE); + + CloseHandle (state->start_select); + CloseHandle (state->stop_select); + CloseHandle (state->exit_select); + CloseHandle (state->have_stopped); CloseHandle (state->read_event); CloseHandle (state->except_event); @@ -578,7 +631,12 @@ struct net_windows_state HANDLE start_select; HANDLE stop_select; + HANDLE exit_select; + HANDLE have_stopped; + HANDLE sock_event; + + HANDLE thread; }; static DWORD WINAPI @@ -586,27 +644,24 @@ net_windows_select_thread (void *arg) { struct serial *scb = arg; struct net_windows_state *state, state_copy; - int event_index, fd; + int event_index; - /* Copy useful information out of the control block, to make sure - that we do not race with freeing it. */ - state_copy = *(struct net_windows_state *) scb->state; - state = &state_copy; - fd = scb->fd; + state = scb->state; while (1) { HANDLE wait_events[2]; WSANETWORKEVENTS events; + SetEvent (state->have_stopped); + wait_events[0] = state->start_select; - wait_events[1] = state->stop_select; + wait_events[1] = state->exit_select; if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0) - { - CloseHandle (state->stop_select); - return 0; - } + return 0; + + ResetEvent (state->have_stopped); wait_events[0] = state->stop_select; wait_events[1] = state->sock_event; @@ -615,10 +670,7 @@ net_windows_select_thread (void *arg) if (event_index == WAIT_OBJECT_0 || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0) - { - CloseHandle (state->stop_select); - return 0; - } + continue; if (event_index != WAIT_OBJECT_0 + 1) { @@ -630,7 +682,9 @@ net_windows_select_thread (void *arg) /* Enumerate the internal network events, and reset the object that signalled us to catch the next event. */ - WSAEnumNetworkEvents (fd, state->sock_event, &events); + WSAEnumNetworkEvents (scb->fd, state->sock_event, &events); + + gdb_assert (events.lNetworkEvents & (FD_READ | FD_CLOSE)); if (events.lNetworkEvents & FD_READ) SetEvent (state->read_event); @@ -645,13 +699,78 @@ net_windows_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) { struct net_windows_state *state = scb->state; + /* Start from a clean slate. */ ResetEvent (state->read_event); ResetEvent (state->except_event); - - SetEvent (state->start_select); + ResetEvent (state->stop_select); *read = state->read_event; *except = state->except_event; + + /* Check any pending events. This both avoids starting the thread + unnecessarily, and handles stray FD_READ events (see below). */ + if (WaitForSingleObject (state->sock_event, 0) == WAIT_OBJECT_0) + { + WSANETWORKEVENTS events; + int any = 0; + + /* Enumerate the internal network events, and reset the object that + signalled us to catch the next event. */ + WSAEnumNetworkEvents (scb->fd, state->sock_event, &events); + + /* You'd think that FD_READ or FD_CLOSE would be set here. But, + sometimes, neither is. I suspect that the FD_READ is set and + the corresponding event signalled while recv is running, and + the FD_READ is then lowered when recv consumes all the data, + but there's no way to un-signal the event. This isn't a + problem for the call in net_select_thread, since any new + events after this point will not have been drained by recv. + It just means that we can't have the obvious assert here. */ + + /* If there is a read event, it might be still valid, or it might + not be - it may have been signalled before we last called + recv. Double-check that there is data. */ + if (events.lNetworkEvents & FD_READ) + { + unsigned long available; + + if (ioctlsocket (scb->fd, FIONREAD, &available) == 0 + && available > 0) + { + SetEvent (state->read_event); + any = 1; + } + else + /* Oops, no data. This call to recv will cause future + data to retrigger the event, e.g. while we are + in net_select_thread. */ + recv (scb->fd, NULL, 0, 0); + } + + /* If there's a close event, then record it - it is obviously + still valid, and it will not be resignalled. */ + if (events.lNetworkEvents & FD_CLOSE) + { + SetEvent (state->except_event); + any = 1; + } + + /* If we set either handle, there's no need to wake the thread. */ + if (any) + return; + } + + /* Start the select thread. */ + SetEvent (state->start_select); +} + +static void +net_windows_done_wait_handle (struct serial *scb) +{ + struct net_windows_state *state = scb->state; + + SetEvent (state->stop_select); + WaitForSingleObject (state->have_stopped, INFINITE); } static int @@ -669,9 +788,16 @@ net_windows_open (struct serial *scb, const char *name) memset (state, 0, sizeof (struct net_windows_state)); scb->state = state; - /* Create auto reset events to wake and terminate the select thread. */ + /* Create auto reset events to wake, stop, and exit the select + thread. */ state->start_select = CreateEvent (0, FALSE, FALSE, 0); state->stop_select = CreateEvent (0, FALSE, FALSE, 0); + state->exit_select = CreateEvent (0, FALSE, FALSE, 0); + + /* Create a manual reset event to signal whether the thread is + stopped. This must be manual reset, because we may wait on + it multiple times without ever starting the thread. */ + state->have_stopped = CreateEvent (0, TRUE, FALSE, 0); /* Associate an event with the socket. */ state->sock_event = CreateEvent (0, TRUE, FALSE, 0); @@ -682,7 +808,8 @@ net_windows_open (struct serial *scb, const char *name) state->except_event = CreateEvent (0, FALSE, FALSE, 0); /* And finally start the select thread. */ - CreateThread (NULL, 0, net_windows_select_thread, scb, 0, &threadId); + state->thread = CreateThread (NULL, 0, net_windows_select_thread, scb, 0, + &threadId); return 0; } @@ -693,11 +820,17 @@ net_windows_close (struct serial *scb) { struct net_windows_state *state = scb->state; - SetEvent (state->stop_select); + SetEvent (state->exit_select); + WaitForSingleObject (state->thread, INFINITE); CloseHandle (state->read_event); CloseHandle (state->except_event); + CloseHandle (state->start_select); + CloseHandle (state->stop_select); + CloseHandle (state->exit_select); + CloseHandle (state->have_stopped); + CloseHandle (state->sock_event); xfree (scb->state); @@ -760,6 +893,7 @@ _initialize_ser_windows (void) ops->noflush_set_tty_state = ser_base_noflush_set_tty_state; ops->drain_output = ser_base_drain_output; ops->wait_handle = ser_console_wait_handle; + ops->done_wait_handle = ser_console_done_wait_handle; serial_add_interface (ops); @@ -792,5 +926,6 @@ _initialize_ser_windows (void) ops->read_prim = net_read_prim; ops->write_prim = net_write_prim; ops->wait_handle = net_windows_wait_handle; + ops->done_wait_handle = net_windows_done_wait_handle; serial_add_interface (ops); } diff --git a/gdb/serial.c b/gdb/serial.c index fb74e1cde36..6a2c634cb34 100644 --- a/gdb/serial.c +++ b/gdb/serial.c @@ -557,6 +557,13 @@ serial_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) *except = NULL; } } + +void +serial_done_wait_handle (struct serial *scb) +{ + if (scb->ops->done_wait_handle) + scb->ops->done_wait_handle (scb); +} #endif #if 0 diff --git a/gdb/serial.h b/gdb/serial.h index d9a395b3fc4..8d7ae6595bc 100644 --- a/gdb/serial.h +++ b/gdb/serial.h @@ -253,6 +253,7 @@ struct serial_ops when signaled, in *READ. Return a handle indicating errors in *EXCEPT. */ void (*wait_handle) (struct serial *scb, HANDLE *read, HANDLE *except); + void (*done_wait_handle) (struct serial *scb); #endif /* USE_WIN32API */ }; @@ -270,6 +271,9 @@ extern void serial_log_command (const char *); serial device. */ extern void serial_wait_handle (struct serial *, HANDLE *, HANDLE *); +/* Windows-only: signal that we are done with the wait handles. */ +extern void serial_done_wait_handle (struct serial *); + #endif /* USE_WIN32API */ #endif /* SERIAL_H */ -- 2.30.2