This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[commit] Windows select improvements


This patch, written by Mark Mitchell, fixes up various problems in the
Windows select emulation.  The diff's a little hard to read, since he
wisely refactored all the cookie-cutter bits of selecting on different
serial devices into common functions.

Here's what Mark wrote about it:

> On Windows, gdb_select starts a thread corresponding to each file
> descriptor.  When something interesting happens, the threads signal
> events.  gdb_select itself waits for one of the events to be signaled.
> Then, it stops the threads.  New threads are not created every time we
> call gdb_select; once a thread is created for a descriptor it lives
> until the descriptor is closed.  So, "start" and "stop" above means
> "wakes up" and "puts to sleep".

> Issue #2 is that if select timed out, gdb_select returned early --
> before stopping the threads.  The fix for this is easy: stop the
> threads before returning.

> Issue #3 is that the threads used the following unsafe pattern:

>      SetEvent (state->have_stopped);
>      wait_events[0] = state->start_select;
>      wait_events[1] = state->exit_select;
>      if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE)
>          != WAIT_OBJECT_0) return 0;
>      ResetEvent (state->have_stopped);

> while the main program looked at HAVE_STOPPED to determine if the
> thread was in the stopped state.  There is a race condition here: if
> the main program signals START_SELECT and then quickly checks
> HAVE_STOPPED it will think that the thread has started and then
> stopped -- but, in fact, the thread may not have awaked yet.  When it
> does, it will clear HAVE_STOPPED -- but after the main program has
> already checked it.

> To fix this, I added more explicit synchronization between the main
> program and the select threads.  Because this bug appeared in all of
> the thread implementations, I also refactored common code from the
> various threads.

We've tested this code pretty much exhaustively over the last three
months.  I've checked it in for trunk.

-- 
Daniel Jacobowitz
CodeSourcery

2007-10-02  Mark Mitchell  <mark@codesourcery.com>

	* mingw-hdep.c (gdb_select): Stop helper threads before returning.
	* ser-mingw.c (enum select_thread_state): New type.
	(struct ser_console_state): Add have_started and thread_state.
	(select_thread_wait): New function.
	(thread_fn_type): New type.
	(create_select_thread): New function.
	(destroy_select_thread): Likewise.
	(start_select_thread): Likewise.
	(stop_select_thread): Likewise.
	(console_select_thread): Use new functions.
	(pipe_select_thread): Likewise.
	(file_select_thread): Likewise.
	(ser_console_wait_handle): Likewise.
	(ser_console_done_wait_handle): Likewise.
	(ser_console_close): Likewise.
	(free_pipe_state): Likewise.
	(pipe_wait_handle): Likewise.
	(pipe_done_wait_handle): Likewise.
	(struct net_windows_state): Derive from ser_console_state.
	(net_windows_select_thread): Use new functions.
	(net_windows_wait_handle): Likewise.
	(net_windows_done_wait_handle): Likewise.
	(net_windows_close): Likewise.

Index: mingw-hdep.c
===================================================================
RCS file: /cvs/src/src/gdb/mingw-hdep.c,v
retrieving revision 1.6
diff -u -p -r1.6 mingw-hdep.c
--- mingw-hdep.c	23 Aug 2007 18:08:36 -0000	1.6
+++ mingw-hdep.c	2 Oct 2007 16:01:36 -0000
@@ -86,12 +86,18 @@ gdb_select (int n, fd_set *readfds, fd_s
   HANDLE h;
   DWORD event;
   DWORD num_handles;
+  /* SCBS contains serial control objects corresponding to file
+     descriptors in READFDS and WRITEFDS.  */
+  struct serial *scbs[MAXIMUM_WAIT_OBJECTS];
+  /* The number of valid entries in SCBS.  */
+  size_t num_scbs;
   int fd;
   int num_ready;
-  int indx;
+  size_t indx;
 
   num_ready = 0;
   num_handles = 0;
+  num_scbs = 0;
   for (fd = 0; fd < n; ++fd)
     {
       HANDLE read = NULL, except = NULL;
@@ -105,14 +111,16 @@ gdb_select (int n, fd_set *readfds, fd_s
       if ((!readfds || !FD_ISSET (fd, readfds))
 	  && (!exceptfds || !FD_ISSET (fd, exceptfds)))
 	continue;
-      h = (HANDLE) _get_osfhandle (fd);
 
       scb = serial_for_fd (fd);
       if (scb)
-	serial_wait_handle (scb, &read, &except);
+	{
+	  serial_wait_handle (scb, &read, &except);
+	  scbs[num_scbs++] = scb;
+	}
 
       if (read == NULL)
-	read = h;
+	read = (HANDLE) _get_osfhandle (fd);
       if (except == NULL)
 	{
 	  if (!never_handle)
@@ -154,6 +162,9 @@ gdb_select (int n, fd_set *readfds, fd_s
      mutexes, that should never occur.  */
   gdb_assert (!(WAIT_ABANDONED_0 <= event
 		&& event < WAIT_ABANDONED_0 + num_handles));
+  /* We no longer need the helper threads to check for activity.  */
+  for (indx = 0; indx < num_scbs; ++indx)
+    serial_done_wait_handle (scbs[indx]);
   if (event == WAIT_FAILED)
     return -1;
   if (event == WAIT_TIMEOUT)
@@ -164,7 +175,6 @@ gdb_select (int n, fd_set *readfds, fd_s
   for (fd = 0, indx = 0; fd < n; ++fd)
     {
       HANDLE fd_h;
-      struct serial *scb;
 
       if ((!readfds || !FD_ISSET (fd, readfds))
 	  && (!exceptfds || !FD_ISSET (fd, exceptfds)))
@@ -191,12 +201,6 @@ gdb_select (int n, fd_set *readfds, fd_s
 	  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: ser-mingw.c
===================================================================
RCS file: /cvs/src/src/gdb/ser-mingw.c,v
retrieving revision 1.10
diff -u -p -r1.10 ser-mingw.c
--- ser-mingw.c	23 Aug 2007 18:08:37 -0000	1.10
+++ ser-mingw.c	2 Oct 2007 16:01:36 -0000
@@ -341,19 +341,162 @@ ser_windows_write_prim (struct serial *s
   return bytes_written;
 }
 
+/* On Windows, gdb_select is implemented using WaitForMulpleObjects.
+   A "select thread" is created for each file descriptor.  These
+   threads looks for activity on the corresponding descriptor, using
+   whatever techniques are appropriate for the descriptor type.  When
+   that activity occurs, the thread signals an appropriate event,
+   which wakes up WaitForMultipleObjects.
+
+   Each select thread is in one of two states: stopped or started.
+   Select threads begin in the stopped state.  When gdb_select is
+   called, threads corresponding to the descriptors of interest are
+   started by calling a wait_handle function.  Each thread that
+   notices activity signals the appropriate event and then reenters
+   the stopped state.  Before gdb_select returns it calls the
+   wait_handle_done functions, which return the threads to the stopped
+   state.  */
+
+enum select_thread_state {
+  STS_STARTED,
+  STS_STOPPED
+};
+
 struct ser_console_state
 {
+  /* Signaled by the select thread to indicate that data is available
+     on the file descriptor.  */
   HANDLE read_event;
+  /* Signaled by the select thread to indicate that an exception has
+     occurred on the file descriptor.  */
   HANDLE except_event;
+  /* Signaled by the select thread to indicate that it has entered the
+     started state.  HAVE_STARTED and HAVE_STOPPED are never signaled
+     simultaneously.  */
+  HANDLE have_started;
+  /* Signaled by the select thread to indicate that it has stopped,
+     either because data is available (and READ_EVENT is signaled),
+     because an exception has occurred (and EXCEPT_EVENT is signaled),
+     or because STOP_SELECT was signaled.  */
+  HANDLE have_stopped;
 
+  /* Signaled by the main program to tell the select thread to enter
+     the started state.  */
   HANDLE start_select;
+  /* Signaled by the main program to tell the select thread to enter
+     the stopped state. */
   HANDLE stop_select;
+  /* Signaled by the main program to tell the select thread to
+     exit.  */
   HANDLE exit_select;
-  HANDLE have_stopped;
 
+  /* The handle for the select thread.  */
   HANDLE thread;
+  /* The state of the select thread.  This field is only accessed in
+     the main program, never by the select thread itself.  */
+  enum select_thread_state thread_state;
 };
 
+/* Called by a select thread to enter the stopped state.  This
+   function does not return until the thread has re-entered the
+   started state.  */
+static void
+select_thread_wait (struct ser_console_state *state)
+{
+  HANDLE wait_events[2];
+
+  /* There are two things that can wake us up: a request that we enter
+     the started state, or that we exit this thread.  */
+  wait_events[0] = state->start_select;
+  wait_events[1] = state->exit_select;
+  if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) 
+      != WAIT_OBJECT_0)
+    /* Either the EXIT_SELECT event was signaled (requesting that the
+       thread exit) or an error has occurred.  In either case, we exit
+       the thread.  */
+    ExitThread (0);
+  
+  /* We are now in the started state.  */
+  SetEvent (state->have_started);
+}
+
+typedef DWORD WINAPI (*thread_fn_type)(void *);
+
+/* Create a new select thread for SCB executing THREAD_FN.  The STATE
+   will be filled in by this function before return.  */
+void
+create_select_thread (thread_fn_type thread_fn,
+		      struct serial *scb,
+		      struct ser_console_state *state)
+{
+  DWORD threadId;
+
+  /* Create all of the events.  These are all auto-reset events.  */
+  state->read_event = CreateEvent (NULL, FALSE, FALSE, NULL);
+  state->except_event = CreateEvent (NULL, FALSE, FALSE, NULL);
+  state->have_started = CreateEvent (NULL, FALSE, FALSE, NULL);
+  state->have_stopped = CreateEvent (NULL, FALSE, FALSE, NULL);
+  state->start_select = CreateEvent (NULL, FALSE, FALSE, NULL);
+  state->stop_select = CreateEvent (NULL, FALSE, FALSE, NULL);
+  state->exit_select = CreateEvent (NULL, FALSE, FALSE, NULL);
+
+  state->thread = CreateThread (NULL, 0, thread_fn, scb, 0, &threadId);
+  /* The thread begins in the stopped state.  */
+  state->thread_state = STS_STOPPED;
+}
+
+/* Destroy the select thread indicated by STATE.  */
+static void
+destroy_select_thread (struct ser_console_state *state)
+{
+  /* Ask the thread to exit.  */
+  SetEvent (state->exit_select);
+  /* Wait until it does.  */
+  WaitForSingleObject (state->thread, INFINITE);
+
+  /* Destroy the events.  */
+  CloseHandle (state->read_event);
+  CloseHandle (state->except_event);
+  CloseHandle (state->have_started);
+  CloseHandle (state->have_stopped);
+  CloseHandle (state->start_select);
+  CloseHandle (state->stop_select);
+  CloseHandle (state->exit_select);
+}
+
+/* Called by gdb_select to start the select thread indicated by STATE.
+   This function does not return until the thread has started.  */
+static void
+start_select_thread (struct ser_console_state *state)
+{
+  /* Ask the thread to start.  */
+  SetEvent (state->start_select);
+  /* Wait until it does.  */
+  WaitForSingleObject (state->have_started, INFINITE);
+  /* The thread is now started.  */
+  state->thread_state = STS_STARTED;
+}
+
+/* Called by gdb_select to stop the select thread indicated by STATE.
+   This function does not return until the thread has stopped.  */
+static void
+stop_select_thread (struct ser_console_state *state)
+{
+  /* If the thread is already in the stopped state, we have nothing to
+     do.  Some of the wait_handle functions avoid calling
+     start_select_thread if they notice activity on the relevant file
+     descriptors.  The wait_handle_done functions still call
+     stop_select_thread -- but it is already stopped.  */
+  if (state->thread_state != STS_STARTED)
+    return;
+  /* Ask the thread to stop.  */
+  SetEvent (state->stop_select);
+  /* Wait until it does.  */
+  WaitForSingleObject (state->have_stopped, INFINITE);
+  /* The thread is now stopped.  */
+  state->thread_state = STS_STOPPED;
+}
+
 static DWORD WINAPI
 console_select_thread (void *arg)
 {
@@ -371,74 +514,69 @@ 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->exit_select;
-
-      if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0)
-	return 0;
+      select_thread_wait (state);
 
-      ResetEvent (state->have_stopped);
-
-    retry:
-      wait_events[0] = state->stop_select;
-      wait_events[1] = h;
-
-      event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE);
+      while (1)
+	{
+	  wait_events[0] = state->stop_select;
+	  wait_events[1] = h;
 
-      if (event_index == WAIT_OBJECT_0
-	  || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0)
-	continue;
+	  event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE);
 
-      if (event_index != WAIT_OBJECT_0 + 1)
-	{
-	  /* Wait must have failed; assume an error has occured, e.g.
-	     the handle has been closed.  */
-	  SetEvent (state->except_event);
-	  continue;
-	}
+	  if (event_index == WAIT_OBJECT_0
+	      || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0)
+	    break;
 
-      /* We've got a pending event on the console.  See if it's
-	 of interest.  */
-      if (!PeekConsoleInput (h, &record, 1, &n_records) || n_records != 1)
-	{
-	  /* Something went wrong.  Maybe the console is gone.  */
-	  SetEvent (state->except_event);
-	  continue;
-	}
+	  if (event_index != WAIT_OBJECT_0 + 1)
+	    {
+	      /* Wait must have failed; assume an error has occured, e.g.
+		 the handle has been closed.  */
+	      SetEvent (state->except_event);
+	      break;
+	    }
 
-      if (record.EventType == KEY_EVENT && record.Event.KeyEvent.bKeyDown)
-	{
-	  WORD keycode = record.Event.KeyEvent.wVirtualKeyCode;
+	  /* We've got a pending event on the console.  See if it's
+	     of interest.  */
+	  if (!PeekConsoleInput (h, &record, 1, &n_records) || n_records != 1)
+	    {
+	      /* Something went wrong.  Maybe the console is gone.  */
+	      SetEvent (state->except_event);
+	      break;
+	    }
 
-	  /* 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)
+	  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);
+		  break;
+		}
 	    }
+
+	  /* Otherwise discard it and wait again.  */
+	  ReadConsoleInput (h, &record, 1, &n_records);
 	}
 
-      /* Otherwise discard it and wait again.  */
-      ReadConsoleInput (h, &record, 1, &n_records);
-      goto retry;
+      SetEvent(state->have_stopped);
     }
 }
 
@@ -473,38 +611,32 @@ pipe_select_thread (void *arg)
 
   while (1)
     {
-      HANDLE wait_events[2];
       DWORD n_avail;
 
-      SetEvent (state->have_stopped);
+      select_thread_wait (state);
 
-      wait_events[0] = state->start_select;
-      wait_events[1] = state->exit_select;
-
-      if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0)
-	return 0;
-
-      ResetEvent (state->have_stopped);
-
-    retry:
-      if (!PeekNamedPipe (h, NULL, 0, NULL, &n_avail, NULL))
+      /* Wait for something to happen on the pipe.  */
+      while (1)
 	{
-	  SetEvent (state->except_event);
-	  continue;
-	}
+	  if (!PeekNamedPipe (h, NULL, 0, NULL, &n_avail, NULL))
+	    {
+	      SetEvent (state->except_event);
+	      break;
+	    }
 
-      if (n_avail > 0)
-	{
-	  SetEvent (state->read_event);
-	  continue;
-	}
+	  if (n_avail > 0)
+	    {
+	      SetEvent (state->read_event);
+	      break;
+	    }
 
-      /* Delay 10ms before checking again, but allow the stop event
-	 to wake us.  */
-      if (WaitForSingleObject (state->stop_select, 10) == WAIT_OBJECT_0)
-	continue;
+	  /* Delay 10ms before checking again, but allow the stop
+	     event to wake us.  */
+	  if (WaitForSingleObject (state->stop_select, 10) == WAIT_OBJECT_0)
+	    break;
+	}
 
-      goto retry;
+      SetEvent (state->have_stopped);
     }
 }
 
@@ -521,26 +653,14 @@ file_select_thread (void *arg)
 
   while (1)
     {
-      HANDLE wait_events[2];
-      DWORD n_avail;
-
-      SetEvent (state->have_stopped);
-
-      wait_events[0] = state->start_select;
-      wait_events[1] = state->exit_select;
-
-      if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0)
-	return 0;
-
-      ResetEvent (state->have_stopped);
+      select_thread_wait (state);
 
       if (SetFilePointer (h, 0, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
-	{
-	  SetEvent (state->except_event);
-	  continue;
-	}
+	SetEvent (state->except_event);
+      else
+	SetEvent (state->read_event);
 
-      SetEvent (state->read_event);
+      SetEvent (state->have_stopped);
     }
 }
 
@@ -551,7 +671,7 @@ ser_console_wait_handle (struct serial *
 
   if (state == NULL)
     {
-      DWORD threadId;
+      thread_fn_type thread_fn;
       int is_tty;
 
       is_tty = isatty (scb->fd);
@@ -566,30 +686,14 @@ ser_console_wait_handle (struct serial *
       memset (state, 0, sizeof (struct ser_console_state));
       scb->state = state;
 
-      /* 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);
-
-      /* 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);
-
       if (is_tty)
-	state->thread = CreateThread (NULL, 0, console_select_thread, scb, 0,
-				      &threadId);
+	thread_fn = console_select_thread;
       else if (fd_is_pipe (scb->fd))
-	state->thread = CreateThread (NULL, 0, pipe_select_thread, scb, 0,
-				      &threadId);
+	thread_fn = pipe_select_thread;
       else
-	state->thread = CreateThread (NULL, 0, file_select_thread, scb, 0,
-				      &threadId);
+	thread_fn = file_select_thread;
+
+      create_select_thread (thread_fn, scb, state);
     }
 
   *read = state->read_event;
@@ -612,7 +716,7 @@ ser_console_wait_handle (struct serial *
     }
 
   /* Otherwise, start the select thread.  */
-  SetEvent (state->start_select);
+  start_select_thread (state);
 }
 
 static void
@@ -623,8 +727,7 @@ ser_console_done_wait_handle (struct ser
   if (state == NULL)
     return;
 
-  SetEvent (state->stop_select);
-  WaitForSingleObject (state->have_stopped, INFINITE);
+  stop_select_thread (state);
 }
 
 static void
@@ -634,18 +737,7 @@ ser_console_close (struct serial *scb)
 
   if (scb->state)
     {
-      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);
-
+      destroy_select_thread (state);
       xfree (scb->state);
     }
 }
@@ -703,19 +795,7 @@ free_pipe_state (struct pipe_state *ps)
   int saved_errno = errno;
 
   if (ps->wait.read_event != INVALID_HANDLE_VALUE)
-    {
-      SetEvent (ps->wait.exit_select);
-
-      WaitForSingleObject (ps->wait.thread, INFINITE);
-
-      CloseHandle (ps->wait.start_select);
-      CloseHandle (ps->wait.stop_select);
-      CloseHandle (ps->wait.exit_select);
-      CloseHandle (ps->wait.have_stopped);
-
-      CloseHandle (ps->wait.read_event);
-      CloseHandle (ps->wait.except_event);
-    }
+    destroy_select_thread (&ps->wait);
 
   /* Close the pipe to the child.  We must close the pipe before
      calling pex_free because pex_free will wait for the child to exit
@@ -870,28 +950,8 @@ pipe_wait_handle (struct serial *scb, HA
 
   /* Have we allocated our events yet?  */
   if (ps->wait.read_event == INVALID_HANDLE_VALUE)
-    {
-      DWORD threadId;
-
-      /* Create auto reset events to wake, stop, and exit the select
-	 thread.  */
-      ps->wait.start_select = CreateEvent (0, FALSE, FALSE, 0);
-      ps->wait.stop_select = CreateEvent (0, FALSE, FALSE, 0);
-      ps->wait.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.  */
-      ps->wait.have_stopped = CreateEvent (0, TRUE, FALSE, 0);
-
-      /* Create our own events to report read and exceptions separately.
-	 The exception event is currently never used.  */
-      ps->wait.read_event = CreateEvent (0, FALSE, FALSE, 0);
-      ps->wait.except_event = CreateEvent (0, FALSE, FALSE, 0);
-
-      /* Start the select thread.  */
-      CreateThread (NULL, 0, pipe_select_thread, scb, 0, &threadId);
-    }
+    /* Start the thread.  */
+    create_select_thread (pipe_select_thread, scb, &ps->wait);
 
   *read = ps->wait.read_event;
   *except = ps->wait.except_event;
@@ -901,8 +961,7 @@ pipe_wait_handle (struct serial *scb, HA
   ResetEvent (ps->wait.except_event);
   ResetEvent (ps->wait.stop_select);
 
-  /* Start the select thread.  */
-  SetEvent (ps->wait.start_select);
+  start_select_thread (&ps->wait);
 }
 
 static void
@@ -914,8 +973,7 @@ pipe_done_wait_handle (struct serial *sc
   if (ps->wait.read_event == INVALID_HANDLE_VALUE)
     return;
 
-  SetEvent (ps->wait.stop_select);
-  WaitForSingleObject (ps->wait.have_stopped, INFINITE);
+  stop_select_thread (&ps->wait);
 }
 
 static int
@@ -931,24 +989,16 @@ pipe_avail (struct serial *scb, int fd)
 
 struct net_windows_state
 {
-  HANDLE read_event;
-  HANDLE except_event;
-
-  HANDLE start_select;
-  HANDLE stop_select;
-  HANDLE exit_select;
-  HANDLE have_stopped;
-
+  struct ser_console_state base;
+  
   HANDLE sock_event;
-
-  HANDLE thread;
 };
 
 static DWORD WINAPI
 net_windows_select_thread (void *arg)
 {
   struct serial *scb = arg;
-  struct net_windows_state *state, state_copy;
+  struct net_windows_state *state;
   int event_index;
 
   state = scb->state;
@@ -958,44 +1008,37 @@ net_windows_select_thread (void *arg)
       HANDLE wait_events[2];
       WSANETWORKEVENTS events;
 
-      SetEvent (state->have_stopped);
-
-      wait_events[0] = state->start_select;
-      wait_events[1] = state->exit_select;
-
-      if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0)
-	return 0;
+      select_thread_wait (&state->base);
 
-      ResetEvent (state->have_stopped);
-
-      wait_events[0] = state->stop_select;
+      wait_events[0] = state->base.stop_select;
       wait_events[1] = state->sock_event;
 
       event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE);
 
       if (event_index == WAIT_OBJECT_0
-	  || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0)
-	continue;
-
-      if (event_index != WAIT_OBJECT_0 + 1)
+	  || WaitForSingleObject (state->base.stop_select, 0) == WAIT_OBJECT_0)
+	/* We have been requested to stop.  */
+	;
+      else if (event_index != WAIT_OBJECT_0 + 1)
+	/* Some error has occured.  Assume that this is an error
+	   condition.  */
+	SetEvent (state->base.except_event);
+      else
 	{
-	  /* Some error has occured.  Assume that this is an error
-	     condition.  */
-	  SetEvent (state->except_event);
-	  continue;
+	  /* Enumerate the internal network events, and reset the
+	     object that signalled us to catch the next event.  */
+	  WSAEnumNetworkEvents (scb->fd, state->sock_event, &events);
+	  
+	  gdb_assert (events.lNetworkEvents & (FD_READ | FD_CLOSE));
+	  
+	  if (events.lNetworkEvents & FD_READ)
+	    SetEvent (state->base.read_event);
+	  
+	  if (events.lNetworkEvents & FD_CLOSE)
+	    SetEvent (state->base.except_event);
 	}
 
-      /* Enumerate the internal network events, and reset the object that
-	 signalled us to catch the next event.  */
-      WSAEnumNetworkEvents (scb->fd, state->sock_event, &events);
-
-      gdb_assert (events.lNetworkEvents & (FD_READ | FD_CLOSE));
-
-      if (events.lNetworkEvents & FD_READ)
-	SetEvent (state->read_event);
-
-      if (events.lNetworkEvents & FD_CLOSE)
-	SetEvent (state->except_event);
+      SetEvent (state->base.have_stopped);
     }
 }
 
@@ -1005,12 +1048,12 @@ net_windows_wait_handle (struct serial *
   struct net_windows_state *state = scb->state;
 
   /* Start from a clean slate.  */
-  ResetEvent (state->read_event);
-  ResetEvent (state->except_event);
-  ResetEvent (state->stop_select);
+  ResetEvent (state->base.read_event);
+  ResetEvent (state->base.except_event);
+  ResetEvent (state->base.stop_select);
 
-  *read = state->read_event;
-  *except = state->except_event;
+  *read = state->base.read_event;
+  *except = state->base.except_event;
 
   /* Check any pending events.  This both avoids starting the thread
      unnecessarily, and handles stray FD_READ events (see below).  */
@@ -1042,7 +1085,7 @@ net_windows_wait_handle (struct serial *
 	  if (ioctlsocket (scb->fd, FIONREAD, &available) == 0
 	      && available > 0)
 	    {
-	      SetEvent (state->read_event);
+	      SetEvent (state->base.read_event);
 	      any = 1;
 	    }
 	  else
@@ -1056,7 +1099,7 @@ net_windows_wait_handle (struct serial *
 	 still valid, and it will not be resignalled.  */
       if (events.lNetworkEvents & FD_CLOSE)
 	{
-	  SetEvent (state->except_event);
+	  SetEvent (state->base.except_event);
 	  any = 1;
 	}
 
@@ -1065,8 +1108,7 @@ net_windows_wait_handle (struct serial *
 	return;
     }
 
-  /* Start the select thread.  */
-  SetEvent (state->start_select);
+  start_select_thread (&state->base);
 }
 
 static void
@@ -1074,8 +1116,7 @@ net_windows_done_wait_handle (struct ser
 {
   struct net_windows_state *state = scb->state;
 
-  SetEvent (state->stop_select);
-  WaitForSingleObject (state->have_stopped, INFINITE);
+  stop_select_thread (&state->base);
 }
 
 static int
@@ -1093,28 +1134,12 @@ net_windows_open (struct serial *scb, co
   memset (state, 0, sizeof (struct net_windows_state));
   scb->state = state;
 
-  /* 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);
   WSAEventSelect (scb->fd, state->sock_event, FD_READ | FD_CLOSE);
 
-  /* Create our own events to report read and close separately.  */
-  state->read_event = CreateEvent (0, FALSE, FALSE, 0);
-  state->except_event = CreateEvent (0, FALSE, FALSE, 0);
-
-  /* And finally start the select thread.  */
-  state->thread = CreateThread (NULL, 0, net_windows_select_thread, scb, 0,
-				&threadId);
+  /* Start the thread.  */
+  create_select_thread (net_windows_select_thread, scb, &state->base);
 
   return 0;
 }
@@ -1125,17 +1150,7 @@ net_windows_close (struct serial *scb)
 {
   struct net_windows_state *state = scb->state;
 
-  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);
-
+  destroy_select_thread (&state->base);
   CloseHandle (state->sock_event);
 
   xfree (scb->state);


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]