* ser-mingw.c: Include <conio.h>.
authorDaniel Jacobowitz <drow@false.org>
Mon, 24 Apr 2006 21:00:13 +0000 (21:00 +0000)
committerDaniel Jacobowitz <drow@false.org>
Mon, 24 Apr 2006 21:00:13 +0000 (21:00 +0000)
(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
gdb/mingw-hdep.c
gdb/ser-mingw.c
gdb/serial.c
gdb/serial.h

index 5c0a04d687f16277c0ca86684e0d5a5b7057a594..89a8e3d2bba2d13ebfff2048f9c4712aea203713 100644 (file)
@@ -1,3 +1,35 @@
+2006-04-24  Daniel Jacobowitz  <dan@codesourcery.com>
+
+       * 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.
+
 2006-04-23  Andreas Schwab  <schwab@suse.de>
 
        * rs6000-tdep.c: Include "reggroups.h" only once.
index 79b23dc2556b65669676678069298d81cb0a69c0..bb0f50aaa46f59e17d0a59d017d11bf41df44098 100644 (file)
@@ -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;
index 7a6f232379ec78a6894c405a7892a2230a0295b8..17f8320b276952b2b59e6a6c0460142544a2be94 100644 (file)
@@ -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);
 }
index fb74e1cde36e0a2b0365048f1788f6beb7fddcca..6a2c634cb345d5084e6ca8d10367449db646cbc3 100644 (file)
@@ -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
index d9a395b3fc4d1a8f75b2cd0990b4044a4be1b080..8d7ae6595bce862de0eaf0a2eea1b4908ce7ec61 100644 (file)
@@ -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 */