aboutsummaryrefslogtreecommitdiff
path: root/gdb/ser-mingw.c
diff options
context:
space:
mode:
authorDaniel Jacobowitz <drow@false.org>2006-04-24 21:00:13 +0000
committerDaniel Jacobowitz <drow@false.org>2006-04-24 21:00:13 +0000
commitc3e2b812d0111da86f66fced7b33492261db1480 (patch)
tree8215cd8205ff2c01839cb76825b748818aabed97 /gdb/ser-mingw.c
parentfbefa34ecb796d084af2d1a58c9cb651e2b4a62e (diff)
downloadgdb-c3e2b812d0111da86f66fced7b33492261db1480.zip
gdb-c3e2b812d0111da86f66fced7b33492261db1480.tar.gz
gdb-c3e2b812d0111da86f66fced7b33492261db1480.tar.bz2
* 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.c277
1 files changed, 206 insertions, 71 deletions
diff --git a/gdb/ser-mingw.c b/gdb/ser-mingw.c
index 7a6f232..17f8320 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);
}