diff options
author | Daniel Jacobowitz <drow@false.org> | 2006-04-24 21:00:13 +0000 |
---|---|---|
committer | Daniel Jacobowitz <drow@false.org> | 2006-04-24 21:00:13 +0000 |
commit | c3e2b812d0111da86f66fced7b33492261db1480 (patch) | |
tree | 8215cd8205ff2c01839cb76825b748818aabed97 /gdb/ser-mingw.c | |
parent | fbefa34ecb796d084af2d1a58c9cb651e2b4a62e (diff) | |
download | binutils-gdb-c3e2b812d0111da86f66fced7b33492261db1480.tar.gz |
* ser-mingw.c: Include <conio.h>.
(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.
Diffstat (limited to 'gdb/ser-mingw.c')
-rw-r--r-- | gdb/ser-mingw.c | 277 |
1 files changed, 206 insertions, 71 deletions
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 <windows.h> +#include <conio.h> #include <fcntl.h> #include <unistd.h> @@ -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); } |