RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

classic Classic list List threaded Threaded
26 messages Options
12
Reply | Threaded
Open this post in threaded view
|

RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

Daniel Jacobowitz-2
This patch is a log bigger than I wanted it to be, unfortunately - it
fixes several related issues, that all touch the same code.

Fixes, all for a mingw32-hosted GDB:

  - Windows serial support.  This definitely deserves a NEWS entry,
    included.

  - Mark's Windows-aware select wrapper is substantially improved.
    It's still a far cry from as thorough as Cygwin's, but it works
    for reads and errors on serial ports, network sockets, pipes, and
    consoles.

  - Connecting to a closed TCP socket no longer times out; instead it
    reports an error.

The primary ugly bit of this patch is the select wrapper.  Windows has
interfaces for all these things which map to Unix file descriptors, but
while the Unix interfaces are actually compatible, the Windows interfaces
are just designed along similar principles.  So you can handle a serial port
in roughly the same way you handle a console window.... but only roughly.
In fact you need to know what sort of device is behind each "file
descriptor", in order to handle it appropriately.

The one I'm least proud of is pipes - there does not appear to be a way to
sleep and have the OS wake you when data is available on a pipe.  So I poll
every 10ms in a thread.  Yuck!  The other three all have subtly different
wait mechanisms.

I've tested it.  I'm not sure what else to say about it.  Is it OK?  Are
there things blatantly wrong with it that I've missed?  Are there particular
bits that you think are just too ugly, and if so, do you have any
suggestions on how to make them less ugly?

--
Daniel Jacobowitz
CodeSourcery

2006-02-03  Daniel Jacobowitz  <[hidden email]>

        * Makefile.in (gdb_select_h, ser_tcp_h): New.
        (ALLDEPFILES): Add ser-windows.c.
        (event-loop.o, ser-base.o, ser-tcp.o): Update.
        (ser-windows.o): New rule.
        * configure: Regenerated.
        * configure.in: Add ser-windows.o for mingw32.
        * ser-windows.c: New file.
        * event-loop.c: Include "serial.h" and "gdb_select.h".
        (gdb_select): Make global.  Update comments.  Use
        serial_wait_handle.  Handle generic exception conditions.
        * ser-base.c: Include "gdb_select.h".
        (ser_base_wait_for): Use gdb_select.
        * serial.c (serial_for_fd): New function.
        (serial_fdopen): Try "terminal" before "hardwire".  Initialize
        the allocated struct serial.
        (serial_wait_handle): New function.
        * serial.h (serial_for_fd, serial_wait_handle): New prototypes.
        (struct serial_ops) [USE_WIN32API]: Add wait_handle.
        * gdb_select.h: New file.
        * ser-tcp.c: Include "ser-tcp.h".  Remove unused "ser-unix.h" include.
        (net_close, net_read_prim, net_write_prim): Make global.
        (net_open): Pass an exception set to select also.  Whitespace fix.
        (_initialize_ser_tcp) [USE_WIN32API]: Do not register TCP support here.
        * ser-tcp.h: New file.
        * inflow.c (gdb_has_a_terminal): Don't initialize stdin_serial here.
        (initialize_stdin_serial): New function.
        * terminal.h (initialize_stdin_serial): New prototype.
        * top.c (gdb_init): Call initialize_stdin_serial.

Index: src/gdb/Makefile.in
===================================================================
--- src.orig/gdb/Makefile.in 2006-02-03 15:17:30.000000000 -0500
+++ src/gdb/Makefile.in 2006-02-03 16:27:35.000000000 -0500
@@ -692,6 +692,7 @@ gdb_obstack_h = gdb_obstack.h $(obstack_
 gdb_proc_service_h = gdb_proc_service.h $(gregset_h)
 gdb_ptrace_h = gdb_ptrace.h
 gdb_regex_h = gdb_regex.h $(xregex_h)
+gdb_select_h = gdb_select.h
 gdb_stabs_h = gdb-stabs.h
 gdb_stat_h = gdb_stat.h
 gdb_string_h = gdb_string.h
@@ -764,6 +765,7 @@ scm_tags_h = scm-tags.h
 sentinel_frame_h = sentinel-frame.h
 serial_h = serial.h
 ser_base_h = ser-base.h
+ser_tcp_h = ser-tcp.h
 ser_unix_h = ser-unix.h
 shnbsd_tdep_h = shnbsd-tdep.h
 sh_tdep_h = sh-tdep.h
@@ -1440,7 +1442,7 @@ ALLDEPFILES = \
  remote-st.c remote-utils.c dcache.c \
  rs6000-nat.c rs6000-tdep.c \
  s390-tdep.c s390-nat.c \
- ser-go32.c ser-pipe.c ser-tcp.c \
+ ser-go32.c ser-pipe.c ser-tcp.c ser-windows.c \
  sh-tdep.c sh64-tdep.c shnbsd-tdep.c shnbsd-nat.c \
  sol2-tdep.c \
  solib-irix.c solib-svr4.c solib-sunos.c \
@@ -1920,7 +1922,8 @@ eval.o: eval.c $(defs_h) $(gdb_string_h)
  $(f_lang_h) $(cp_abi_h) $(infcall_h) $(objc_lang_h) $(block_h) \
  $(parser_defs_h) $(cp_support_h)
 event-loop.o: event-loop.c $(defs_h) $(event_loop_h) $(event_top_h) \
- $(gdb_string_h) $(exceptions_h) $(gdb_assert_h)
+ $(gdb_string_h) $(exceptions_h) $(gdb_assert_h) $(serial_h) \
+ $(gdb_select_h)
 event-top.o: event-top.c $(defs_h) $(top_h) $(inferior_h) $(target_h) \
  $(terminal_h) $(event_loop_h) $(event_top_h) $(interps_h) \
  $(exceptions_h) $(gdbcmd_h) $(readline_h) $(readline_history_h)
@@ -2511,13 +2514,15 @@ ser-e7kpc.o: ser-e7kpc.c $(defs_h) $(ser
 ser-go32.o: ser-go32.c $(defs_h) $(gdbcmd_h) $(serial_h) $(gdb_string_h)
 serial.o: serial.c $(defs_h) $(serial_h) $(gdb_string_h) $(gdbcmd_h)
 ser-base.o: ser-base.c $(defs_h) $(serial_h) $(ser_base_h) $(event_loop_h) \
- $(gdb_string_h)
+ $(gdb_select_h) $(gdb_string_h)
 ser-pipe.o: ser-pipe.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_unix_h) \
  $(gdb_vfork_h) $(gdb_string_h)
-ser-tcp.o: ser-tcp.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_unix_h) \
+ser-tcp.o: ser-tcp.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_tcp_h) \
  $(gdb_string_h)
 ser-unix.o: ser-unix.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_unix_h) \
  $(terminal_h) $(gdb_string_h)
+ser-windows.o: ser-windows.c $(defs_h) $(serial_h) $(ser_base_h) \
+ $(ser_tcp_h) $(gdb_assert_h) $(gdb_string_h)
 sh3-rom.o: sh3-rom.c $(defs_h) $(gdbcore_h) $(target_h) $(monitor_h) \
  $(serial_h) $(srec_h) $(arch_utils_h) $(regcache_h) $(gdb_string_h) \
  $(sh_tdep_h)
Index: src/gdb/configure.ac
===================================================================
--- src.orig/gdb/configure.ac 2006-02-03 15:17:30.000000000 -0500
+++ src/gdb/configure.ac 2006-02-03 15:24:16.000000000 -0500
@@ -1,5 +1,6 @@
 dnl Autoconf configure script for GDB, the GNU debugger.
-dnl Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+dnl Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+dnl 2005, 2006
 dnl Free Software Foundation, Inc.
 dnl
 dnl This file is part of GDB.
@@ -1198,7 +1199,7 @@ SER_HARDWIRE="ser-base.o ser-unix.o ser-
 case ${host} in
   *go32* ) SER_HARDWIRE=ser-go32.o ;;
   *djgpp* ) SER_HARDWIRE=ser-go32.o ;;
-  *mingw32*) SER_HARDWIRE="ser-base.o ser-tcp.o" ;;
+  *mingw32*) SER_HARDWIRE="ser-base.o ser-tcp.o ser-windows.o" ;;
 esac
 AC_SUBST(SER_HARDWIRE)
 
Index: src/gdb/ser-windows.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/ser-windows.c 2006-02-03 16:23:42.000000000 -0500
@@ -0,0 +1,796 @@
+/* Serial interface for local (hardwired) serial ports on Windows systems
+
+   Copyright (C) 2006
+   Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+#include "defs.h"
+#include "serial.h"
+#include "ser-base.h"
+#include "ser-tcp.h"
+
+#include <windows.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "gdb_assert.h"
+#include "gdb_string.h"
+
+void _initialize_ser_windows (void);
+
+struct ser_windows_state
+{
+  int in_progress;
+  OVERLAPPED ov;
+  DWORD lastCommMask;
+  HANDLE except_event;
+};
+
+/* Open up a real live device for serial I/O.  */
+
+static int
+ser_windows_open (struct serial *scb, const char *name)
+{
+  HANDLE h;
+  struct ser_windows_state *state;
+  COMMTIMEOUTS timeouts;
+
+  /* Only allow COM ports.  */
+  if (strncmp (name, "COM", 3) != 0)
+    {
+      errno = ENOENT;
+      return -1;
+    }
+
+  h = CreateFile (name, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+  OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+  if (h == INVALID_HANDLE_VALUE)
+    {
+      errno = ENOENT;
+      return -1;
+    }
+
+  scb->fd = _open_osfhandle ((long) h, O_RDWR);
+  if (scb->fd < 0)
+    {
+      errno = ENOENT;
+      return -1;
+    }
+
+  if (!SetCommMask (h, EV_RXCHAR))
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  timeouts.ReadIntervalTimeout = MAXDWORD;
+  timeouts.ReadTotalTimeoutConstant = 0;
+  timeouts.ReadTotalTimeoutMultiplier = 0;
+  timeouts.WriteTotalTimeoutConstant = 0;
+  timeouts.WriteTotalTimeoutMultiplier = 0;
+  if (!SetCommTimeouts (h, &timeouts))
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  state = xmalloc (sizeof (struct ser_windows_state));
+  memset (state, 0, sizeof (struct ser_windows_state));
+  scb->state = state;
+
+  /* Create a manual reset event to watch the input buffer.  */
+  state->ov.hEvent = CreateEvent (0, TRUE, FALSE, 0);
+
+  /* Create a (currently unused) handle to record exceptions.  */
+  state->except_event = CreateEvent (0, TRUE, FALSE, 0);
+
+  return 0;
+}
+
+/* Wait for the output to drain away, as opposed to flushing (discarding)
+   it.  */
+
+static int
+ser_windows_drain_output (struct serial *scb)
+{
+  HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+
+  return (FlushFileBuffers (h) != 0) ? 0 : -1;
+}
+
+static int
+ser_windows_flush_output (struct serial *scb)
+{
+  HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+
+  return (PurgeComm (h, PURGE_TXCLEAR) != 0) ? 0 : -1;
+}
+
+static int
+ser_windows_flush_input (struct serial *scb)
+{
+  HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+
+  return (PurgeComm (h, PURGE_RXCLEAR) != 0) ? 0 : -1;
+}
+
+static int
+ser_windows_send_break (struct serial *scb)
+{
+  HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+
+  if (SetCommBreak (h) == 0)
+    return -1;
+
+  /* Delay for 250 milliseconds.  */
+  Sleep (250);
+
+  if (ClearCommBreak (h))
+    return -1;
+
+  return 0;
+}
+
+static void
+ser_windows_raw (struct serial *scb)
+{
+  HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+  DCB state;
+
+  if (GetCommState (h, &state) == 0)
+    return;
+
+  state.fParity = FALSE;
+  state.fOutxCtsFlow = FALSE;
+  state.fOutxDsrFlow = FALSE;
+  state.fDtrControl = DTR_CONTROL_ENABLE;
+  state.fDsrSensitivity = FALSE;
+  state.fOutX = FALSE;
+  state.fInX = FALSE;
+  state.fNull = FALSE;
+  state.fAbortOnError = FALSE;
+  state.ByteSize = 8;
+  state.Parity = NOPARITY;
+
+  scb->current_timeout = 0;
+
+  if (SetCommState (h, &state) == 0)
+    warning (_("SetCommState failed\n"));
+}
+
+static int
+ser_windows_setstopbits (struct serial *scb, int num)
+{
+  HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+  DCB state;
+
+  if (GetCommState (h, &state) == 0)
+    return -1;
+
+  switch (num)
+    {
+    case SERIAL_1_STOPBITS:
+      state.StopBits = ONESTOPBIT;
+      break;
+    case SERIAL_1_AND_A_HALF_STOPBITS:
+      state.StopBits = ONE5STOPBITS;
+      break;
+    case SERIAL_2_STOPBITS:
+      state.StopBits = TWOSTOPBITS;
+      break;
+    default:
+      return 1;
+    }
+
+  return (SetCommState (h, &state) != 0) ? 0 : -1;
+}
+
+static int
+ser_windows_setbaudrate (struct serial *scb, int rate)
+{
+  HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+  DCB state;
+
+  if (GetCommState (h, &state) == 0)
+    return -1;
+
+  state.BaudRate = rate;
+
+  return (SetCommState (h, &state) != 0) ? 0 : -1;
+}
+
+static void
+ser_windows_close (struct serial *scb)
+{
+  struct ser_windows_state *state;
+
+  /* Stop any pending selects.  */
+  CancelIo ((HANDLE) _get_osfhandle (scb->fd));
+  state = scb->state;
+  CloseHandle (state->ov.hEvent);
+  CloseHandle (state->except_event);
+
+  if (scb->fd < 0)
+    return;
+
+  close (scb->fd);
+  scb->fd = -1;
+
+  xfree (scb->state);
+}
+
+static void
+ser_windows_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except)
+{
+  struct ser_windows_state *state;
+  COMSTAT status;
+  DWORD errors;
+  HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+
+  state = scb->state;
+
+  *except = state->except_event;
+  *read = state->ov.hEvent;
+
+  if (state->in_progress)
+    return;
+
+  /* Reset the mask - we are only interested in any characters which
+     arrive after this point, not characters which might have arrived
+     and already been read.  */
+
+  /* This really, really shouldn't be necessary - just the second one.
+     But otherwise an internal flag for EV_RXCHAR does not get
+     cleared, and we get a duplicated event, if the last batch
+     of characters included at least two arriving close together.  */
+  if (!SetCommMask (h, 0))
+    warning (_("ser_windows_wait_handle: reseting mask failed"));
+
+  if (!SetCommMask (h, EV_RXCHAR))
+    warning (_("ser_windows_wait_handle: reseting mask failed (2)"));
+
+  /* There's a potential race condition here; we must check cbInQue
+     and not wait if that's nonzero.  */
+
+  ClearCommError (h, &errors, &status);
+  if (status.cbInQue > 0)
+    {
+      SetEvent (state->ov.hEvent);
+      return;
+    }
+
+  state->in_progress = 1;
+  ResetEvent (state->ov.hEvent);
+  state->lastCommMask = -2;
+  if (WaitCommEvent (h, &state->lastCommMask, &state->ov))
+    {
+      gdb_assert (state->lastCommMask & EV_RXCHAR);
+      SetEvent (state->ov.hEvent);
+    }
+  else
+    gdb_assert (GetLastError () == ERROR_IO_PENDING);
+}
+
+static int
+ser_windows_read_prim (struct serial *scb, size_t count)
+{
+  struct ser_windows_state *state;
+  OVERLAPPED ov;
+  DWORD bytes_read, bytes_read_tmp;
+  HANDLE h;
+  gdb_byte *p;
+
+  state = scb->state;
+  if (state->in_progress)
+    {
+      WaitForSingleObject (state->ov.hEvent, INFINITE);
+      state->in_progress = 0;
+      ResetEvent (state->ov.hEvent);
+    }
+
+  memset (&ov, 0, sizeof (OVERLAPPED));
+  ov.hEvent = CreateEvent (0, FALSE, FALSE, 0);
+  h = (HANDLE) _get_osfhandle (scb->fd);
+
+  if (!ReadFile (h, scb->buf, /* count */ 1, &bytes_read, &ov))
+    {
+      if (GetLastError () != ERROR_IO_PENDING
+  || !GetOverlappedResult (h, &ov, &bytes_read, TRUE))
+ bytes_read = -1;
+    }
+
+  CloseHandle (ov.hEvent);
+  return bytes_read;
+}
+
+static int
+ser_windows_write_prim (struct serial *scb, const void *buf, size_t len)
+{
+  struct ser_windows_state *state;
+  OVERLAPPED ov;
+  DWORD bytes_written;
+  HANDLE h;
+
+  memset (&ov, 0, sizeof (OVERLAPPED));
+  ov.hEvent = CreateEvent (0, FALSE, FALSE, 0);
+  h = (HANDLE) _get_osfhandle (scb->fd);
+  if (!WriteFile (h, buf, len, &bytes_written, &ov))
+    {
+      if (GetLastError () != ERROR_IO_PENDING
+  || !GetOverlappedResult (h, &ov, &bytes_written, TRUE))
+ bytes_written = -1;
+    }
+
+  CloseHandle (ov.hEvent);
+  return bytes_written;
+}
+
+struct ser_console_state
+{
+  HANDLE read_event;
+  HANDLE except_event;
+
+  HANDLE start_select;
+  HANDLE stop_select;
+};
+
+static DWORD WINAPI
+console_select_thread (void *arg)
+{
+  struct serial *scb = arg;
+  struct ser_console_state *state, state_copy;
+  int event_index, fd;
+  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);
+
+  while (1)
+    {
+      HANDLE wait_events[2];
+      INPUT_RECORD record;
+      DWORD n_records;
+
+      wait_events[0] = state->start_select;
+      wait_events[1] = state->stop_select;
+
+      if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0)
+ {
+  CloseHandle (state->stop_select);
+  return 0;
+ }
+
+    retry:
+      wait_events[0] = state->stop_select;
+      wait_events[1] = h;
+
+      event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE);
+
+      if (event_index == WAIT_OBJECT_0
+  || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0)
+ {
+  CloseHandle (state->stop_select);
+  return 0;
+ }
+
+      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;
+ }
+
+      /* 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 (record.EventType == KEY_EVENT && record.Event.KeyEvent.bKeyDown)
+ {
+  /* This is really a keypress.  */
+  SetEvent (state->read_event);
+  continue;
+ }
+
+      /* Otherwise discard it and wait again.  */
+      ReadConsoleInput (h, &record, 1, &n_records);
+      goto retry;
+    }
+}
+
+static int
+fd_is_pipe (int fd)
+{
+  if (PeekNamedPipe ((HANDLE) _get_osfhandle (fd), NULL, 0, NULL, NULL, NULL))
+    return 1;
+  else
+    return 0;
+}
+
+static DWORD WINAPI
+pipe_select_thread (void *arg)
+{
+  struct serial *scb = arg;
+  struct ser_console_state *state, state_copy;
+  int event_index, fd;
+  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);
+
+  while (1)
+    {
+      HANDLE wait_events[2];
+      DWORD n_avail;
+
+      wait_events[0] = state->start_select;
+      wait_events[1] = state->stop_select;
+
+      if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0)
+ {
+  CloseHandle (state->stop_select);
+  return 0;
+ }
+
+    retry:
+      if (!PeekNamedPipe (h, NULL, 0, NULL, &n_avail, NULL))
+ {
+  SetEvent (state->except_event);
+  continue;
+ }
+
+      if (n_avail > 0)
+ {
+  SetEvent (state->read_event);
+  continue;
+ }
+
+      if (WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0)
+ {
+  CloseHandle (state->stop_select);
+  return 0;
+ }
+
+      Sleep (10);
+      goto retry;
+    }
+}
+
+static void
+ser_console_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except)
+{
+  struct ser_console_state *state = scb->state;
+
+  if (state == NULL)
+    {
+      DWORD threadId;
+      int is_tty;
+
+      is_tty = isatty (scb->fd);
+      if (!is_tty && !fd_is_pipe (scb->fd))
+ {
+  *read = NULL;
+  *except = NULL;
+  return;
+ }
+
+      state = xmalloc (sizeof (struct ser_console_state));
+      memset (state, 0, sizeof (struct ser_console_state));
+      scb->state = state;
+
+      /* Create auto reset events to wake and terminate the select thread.  */
+      state->start_select = CreateEvent (0, FALSE, FALSE, 0);
+      state->stop_select = CreateEvent (0, FALSE, FALSE, 0);
+
+      /* Create our own events to report read and exceptions separately.
+ The exception event is currently never used.  */
+      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);
+      else
+ CreateThread (NULL, 0, pipe_select_thread, scb, 0, &threadId);
+    }
+
+  ResetEvent (state->read_event);
+  ResetEvent (state->except_event);
+
+  SetEvent (state->start_select);
+
+  *read = state->read_event;
+  *except = state->except_event;
+}
+
+static void
+ser_console_close (struct serial *scb)
+{
+  struct ser_console_state *state = scb->state;
+
+  if (scb->state)
+    {
+      SetEvent (state->stop_select);
+
+      CloseHandle (state->read_event);
+      CloseHandle (state->except_event);
+
+      xfree (scb->state);
+    }
+}
+
+struct ser_console_ttystate
+{
+  int is_a_tty;
+};
+
+static serial_ttystate
+ser_console_get_tty_state (struct serial *scb)
+{
+  if (isatty (scb->fd))
+    {
+      struct ser_console_ttystate *state;
+      state = (struct ser_console_ttystate *) xmalloc (sizeof *state);
+      state->is_a_tty = 1;
+      return state;
+    }
+  else
+    return NULL;
+}
+
+struct net_windows_state
+{
+  HANDLE read_event;
+  HANDLE except_event;
+
+  HANDLE start_select;
+  HANDLE stop_select;
+  HANDLE sock_event;
+};
+
+static DWORD WINAPI
+net_windows_select_thread (void *arg)
+{
+  struct serial *scb = arg;
+  struct net_windows_state *state, state_copy;
+  int event_index, fd;
+
+  /* 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;
+
+  while (1)
+    {
+      HANDLE wait_events[2];
+      WSANETWORKEVENTS events;
+
+      wait_events[0] = state->start_select;
+      wait_events[1] = state->stop_select;
+
+      if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0)
+ {
+  CloseHandle (state->stop_select);
+  return 0;
+ }
+
+      wait_events[0] = state->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)
+ {
+  CloseHandle (state->stop_select);
+  return 0;
+ }
+
+      if (event_index != WAIT_OBJECT_0 + 1)
+ {
+  /* 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 (fd, state->sock_event, &events);
+
+      if (events.lNetworkEvents & FD_READ)
+ SetEvent (state->read_event);
+
+      if (events.lNetworkEvents & FD_CLOSE)
+ SetEvent (state->except_event);
+    }
+}
+
+static void
+net_windows_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except)
+{
+  struct net_windows_state *state = scb->state;
+
+  ResetEvent (state->read_event);
+  ResetEvent (state->except_event);
+
+  SetEvent (state->start_select);
+
+  *read = state->read_event;
+  *except = state->except_event;
+}
+
+static int
+net_windows_open (struct serial *scb, const char *name)
+{
+  struct net_windows_state *state;
+  int ret;
+  DWORD threadId;
+
+  ret = net_open (scb, name);
+  if (ret != 0)
+    return ret;
+
+  state = xmalloc (sizeof (struct net_windows_state));
+  memset (state, 0, sizeof (struct net_windows_state));
+  scb->state = state;
+
+  /* Create auto reset events to wake and terminate the select thread.  */
+  state->start_select = CreateEvent (0, FALSE, FALSE, 0);
+  state->stop_select = CreateEvent (0, FALSE, 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.  */
+  CreateThread (NULL, 0, net_windows_select_thread, scb, 0, &threadId);
+
+  return 0;
+}
+
+
+static void
+net_windows_close (struct serial *scb)
+{
+  struct net_windows_state *state = scb->state;
+
+  SetEvent (state->stop_select);
+
+  CloseHandle (state->read_event);
+  CloseHandle (state->except_event);
+  CloseHandle (state->start_select);
+  CloseHandle (state->sock_event);
+
+  xfree (scb->state);
+
+  net_close (scb);
+}
+
+void
+_initialize_ser_windows (void)
+{
+  WSADATA wsa_data;
+  struct serial_ops *ops;
+
+  /* First register the serial port driver.  */
+
+  ops = XMALLOC (struct serial_ops);
+  memset (ops, 0, sizeof (struct serial_ops));
+  ops->name = "hardwire";
+  ops->next = 0;
+  ops->open = ser_windows_open;
+  ops->close = ser_windows_close;
+
+  ops->flush_output = ser_windows_flush_output;
+  ops->flush_input = ser_windows_flush_input;
+  ops->send_break = ser_windows_send_break;
+
+  /* These are only used for stdin; we do not need them for serial
+     ports, so supply the standard dummies.  */
+  ops->get_tty_state = ser_base_get_tty_state;
+  ops->set_tty_state = ser_base_set_tty_state;
+  ops->print_tty_state = ser_base_print_tty_state;
+  ops->noflush_set_tty_state = ser_base_noflush_set_tty_state;
+
+  ops->go_raw = ser_windows_raw;
+  ops->setbaudrate = ser_windows_setbaudrate;
+  ops->setstopbits = ser_windows_setstopbits;
+  ops->drain_output = ser_windows_drain_output;
+  ops->readchar = ser_base_readchar;
+  ops->write = ser_base_write;
+  ops->async = ser_base_async;
+  ops->read_prim = ser_windows_read_prim;
+  ops->write_prim = ser_windows_write_prim;
+  ops->wait_handle = ser_windows_wait_handle;
+
+  serial_add_interface (ops);
+
+  /* Next create the dummy serial driver used for terminals.  We only
+     provide the TTY-related methods.  */
+
+  ops = XMALLOC (struct serial_ops);
+  memset (ops, 0, sizeof (struct serial_ops));
+
+  ops->name = "terminal";
+  ops->next = 0;
+
+  ops->close = ser_console_close;
+  ops->get_tty_state = ser_console_get_tty_state;
+  ops->set_tty_state = ser_base_set_tty_state;
+  ops->print_tty_state = ser_base_print_tty_state;
+  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;
+
+  serial_add_interface (ops);
+
+  /* If WinSock works, register the TCP/UDP socket driver.  */
+
+  if (WSAStartup (MAKEWORD (1, 0), &wsa_data) != 0)
+    /* WinSock is unavailable.  */
+    return;
+
+  ops = XMALLOC (struct serial_ops);
+  memset (ops, 0, sizeof (struct serial_ops));
+  ops->name = "tcp";
+  ops->next = 0;
+  ops->open = net_windows_open;
+  ops->close = net_windows_close;
+  ops->readchar = ser_base_readchar;
+  ops->write = ser_base_write;
+  ops->flush_output = ser_base_flush_output;
+  ops->flush_input = ser_base_flush_input;
+  ops->send_break = ser_base_send_break;
+  ops->go_raw = ser_base_raw;
+  ops->get_tty_state = ser_base_get_tty_state;
+  ops->set_tty_state = ser_base_set_tty_state;
+  ops->print_tty_state = ser_base_print_tty_state;
+  ops->noflush_set_tty_state = ser_base_noflush_set_tty_state;
+  ops->setbaudrate = ser_base_setbaudrate;
+  ops->setstopbits = ser_base_setstopbits;
+  ops->drain_output = ser_base_drain_output;
+  ops->async = ser_base_async;
+  ops->read_prim = net_read_prim;
+  ops->write_prim = net_write_prim;
+  ops->wait_handle = net_windows_wait_handle;
+  serial_add_interface (ops);
+}
Index: src/gdb/event-loop.c
===================================================================
--- src.orig/gdb/event-loop.c 2006-02-03 15:17:30.000000000 -0500
+++ src/gdb/event-loop.c 2006-02-03 16:25:46.000000000 -0500
@@ -1,5 +1,6 @@
 /* Event loop machinery for GDB, the GNU debugger.
-   Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2000, 2001, 2002, 2005, 2006
+   Free Software Foundation, Inc.
    Written by Elena Zannoni <[hidden email]> of Cygnus Solutions.
 
    This file is part of GDB.
@@ -22,6 +23,7 @@
 #include "defs.h"
 #include "event-loop.h"
 #include "event-top.h"
+#include "serial.h"
 
 #ifdef HAVE_POLL
 #if defined (HAVE_POLL_H)
@@ -37,6 +39,7 @@
 #include <sys/time.h>
 #include "exceptions.h"
 #include "gdb_assert.h"
+#include "gdb_select.h"
 
 typedef struct gdb_event gdb_event;
 typedef void (event_handler_func) (int);
@@ -731,13 +734,11 @@ handle_file_event (int event_file_desc)
     }
 }
 
-/* Wrapper for select.  This function is not yet exported from this
-   file because it is not sufficiently general.  For example,
-   ser-base.c uses select to check for socket activity, and this
-   function does not support sockets under Windows, so we do not want
-   to use gdb_select in ser-base.c.  */
+/* Wrapper for select.  On Windows systems, where the select interface
+   only works for sockets, this uses the GDB serial abstraction to
+   handle sockets, consoles, pipes, and serial ports.  */
 
-static int
+int
 gdb_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
     struct timeval *timeout)
 {
@@ -748,40 +749,55 @@ gdb_select (int n, fd_set *readfds, fd_s
   DWORD num_handles;
   int fd;
   int num_ready;
+  int indx;
+  static HANDLE never_handle;
 
   num_ready = 0;
   num_handles = 0;
   for (fd = 0; fd < n; ++fd)
     {
+      HANDLE read = NULL, except = NULL;
+      struct serial *scb;
+
       /* There is no support yet for WRITEFDS.  At present, this isn't
  used by GDB -- but we do not want to silently ignore WRITEFDS
  if something starts using it.  */
-      gdb_assert (!FD_ISSET (fd, writefds));
+      gdb_assert (!writefds || !FD_ISSET (fd, writefds));
+
       if (!FD_ISSET (fd, readfds)
   && !FD_ISSET (fd, exceptfds))
  continue;
       h = (HANDLE) _get_osfhandle (fd);
-      if (h == INVALID_HANDLE_VALUE)
+
+      scb = serial_for_fd (fd);
+      if (scb)
+ serial_wait_handle (scb, &read, &except);
+
+      if (read == NULL)
+ read = h;
+      if (except == NULL)
  {
-  /* If the underlying handle is INVALID_HANDLE_VALUE, then
-     this descriptor is no more.  */
-  if (FD_ISSET (fd, exceptfds))
-    ++num_ready;
-  continue;
- }
-      /* The only exceptional condition we recognize is a closed file
- descriptor.  Since we have already checked for that
- condition, clear the exceptional bit for this descriptor.  */
-      FD_CLR (fd, exceptfds);
+  if (!never_handle)
+    never_handle = CreateEvent (0, FALSE, FALSE, 0);
+
+  except = never_handle;
+ }
+
       if (FD_ISSET (fd, readfds))
-      {
- gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS);
- handles[num_handles++] = h;
-      }
+ {
+  gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS);
+  handles[num_handles++] = read;
+ }
+
+      if (FD_ISSET (fd, exceptfds))
+ {
+  gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS);
+  handles[num_handles++] = except;
+ }
     }
   /* If we don't need to wait for any handles, we are done.  */
   if (!num_handles)
-    return num_ready;
+    return 0;
   event = WaitForMultipleObjects (num_handles,
   handles,
   FALSE,
@@ -796,23 +812,34 @@ gdb_select (int n, fd_set *readfds, fd_s
   if (event == WAIT_FAILED)
     return -1;
   if (event == WAIT_TIMEOUT)
-    return num_ready;
+    return 0;
   /* Run through the READFDS, clearing bits corresponding to descriptors
      for which input is unavailable.  */
-  num_ready += num_handles;
   h = handles[event - WAIT_OBJECT_0];
-  for (fd = 0; fd < n; ++fd)
+  for (fd = 0, indx = 0; fd < n; ++fd)
     {
       HANDLE fd_h;
-      if (!FD_ISSET (fd, readfds))
- continue;
-      fd_h = (HANDLE) _get_osfhandle (fd);
-      /* This handle might be ready, even though it wasn't the handle
- returned by WaitForMultipleObjects.  */
-      if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0)
+
+      if (FD_ISSET (fd, readfds))
  {
-  FD_CLR (fd, readfds);
-  --num_ready;
+  fd_h = handles[indx++];
+  /* This handle might be ready, even though it wasn't the handle
+     returned by WaitForMultipleObjects.  */
+  if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0)
+    FD_CLR (fd, readfds);
+  else
+    num_ready++;
+ }
+
+      if (FD_ISSET (fd, exceptfds))
+ {
+  fd_h = handles[indx++];
+  /* This handle might be ready, even though it wasn't the handle
+     returned by WaitForMultipleObjects.  */
+  if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0)
+    FD_CLR (fd, exceptfds);
+  else
+    num_ready++;
  }
     }
 
Index: src/gdb/ser-base.c
===================================================================
--- src.orig/gdb/ser-base.c 2006-02-03 15:17:30.000000000 -0500
+++ src/gdb/ser-base.c 2006-02-03 16:26:09.000000000 -0500
@@ -1,7 +1,7 @@
 /* Generic serial interface functions.
 
    Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
-   2003, 2004, 2005 Free Software Foundation, Inc.
+   2003, 2004, 2005, 2006 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -25,6 +25,7 @@
 #include "ser-base.h"
 #include "event-loop.h"
 
+#include "gdb_select.h"
 #include "gdb_string.h"
 #include <sys/time.h>
 #ifdef USE_WIN32API
@@ -202,9 +203,9 @@ ser_base_wait_for (struct serial *scb, i
       FD_SET (scb->fd, &exceptfds);
 
       if (timeout >= 0)
- numfds = select (scb->fd + 1, &readfds, 0, &exceptfds, &tv);
+ numfds = gdb_select (scb->fd + 1, &readfds, 0, &exceptfds, &tv);
       else
- numfds = select (scb->fd + 1, &readfds, 0, &exceptfds, 0);
+ numfds = gdb_select (scb->fd + 1, &readfds, 0, &exceptfds, 0);
 
       if (numfds <= 0)
  {
Index: src/gdb/serial.c
===================================================================
--- src.orig/gdb/serial.c 2006-02-03 15:17:30.000000000 -0500
+++ src/gdb/serial.c 2006-02-03 16:26:43.000000000 -0500
@@ -1,7 +1,7 @@
 /* Generic serial interface routines
 
    Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
-   2001, 2002 Free Software Foundation, Inc.
+   2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -233,6 +233,22 @@ serial_open (const char *name)
   return scb;
 }
 
+/* Return the open serial device for FD, if found, or NULL if FD
+   is not already opened.  */
+
+struct serial *
+serial_for_fd (int fd)
+{
+  struct serial *scb;
+  struct serial_ops *ops;
+
+  for (scb = scb_base; scb; scb = scb->next)
+    if (scb->fd == fd)
+      return scb;
+
+  return NULL;
+}
+
 struct serial *
 serial_fdopen (const int fd)
 {
@@ -246,12 +262,14 @@ serial_fdopen (const int fd)
  return scb;
       }
 
-  ops = serial_interface_lookup ("hardwire");
+  ops = serial_interface_lookup ("terminal");
+  if (!ops)
+    ops = serial_interface_lookup ("hardwire");
 
   if (!ops)
     return NULL;
 
-  scb = XMALLOC (struct serial);
+  scb = XCALLOC (1, struct serial);
 
   scb->ops = ops;
 
@@ -524,6 +542,19 @@ serial_debug_p (struct serial *scb)
   return scb->debug_p || global_serial_debug_p;
 }
 
+#ifdef USE_WIN32API
+void
+serial_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except)
+{
+  if (scb->ops->wait_handle)
+    scb->ops->wait_handle (scb, read, except);
+  else
+    {
+      *read = (HANDLE) _get_osfhandle (scb->fd);
+      *except = NULL;
+    }
+}
+#endif
 
 #if 0
 /* The connect command is #if 0 because I hadn't thought of an elegant
Index: src/gdb/serial.h
===================================================================
--- src.orig/gdb/serial.h 2006-02-03 15:17:30.000000000 -0500
+++ src/gdb/serial.h 2006-02-03 16:27:11.000000000 -0500
@@ -1,5 +1,6 @@
 /* Remote serial support interface definitions for GDB, the GNU Debugger.
-   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000
+   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+   2004, 2005, 2006
    Free Software Foundation, Inc.
 
    This file is part of GDB.
@@ -22,6 +23,10 @@
 #ifndef SERIAL_H
 #define SERIAL_H
 
+#ifdef USE_WIN32API
+#include <windows.h>
+#endif
+
 struct ui_file;
 
 /* For most routines, if a failure is indicated, then errno should be
@@ -41,6 +46,10 @@ struct serial;
 
 extern struct serial *serial_open (const char *name);
 
+/* Find an already opened serial stream using a file handle.  */
+
+extern struct serial *serial_for_fd (int fd);
+
 /* Open a new serial stream using a file handle.  */
 
 extern struct serial *serial_fdopen (const int fd);
@@ -238,6 +247,13 @@ struct serial_ops
     /* Perform a low-level write operation, writing (at most) COUNT
        bytes from BUF.  */
     int (*write_prim)(struct serial *scb, const void *buf, size_t count);
+
+#ifdef USE_WIN32API
+    /* Return a handle to wait on, indicating available data from SCB
+       when signaled, in *READ.  Return a handle indicating errors
+       in *EXCEPT.  */
+    void (*wait_handle) (struct serial *scb, HANDLE *read, HANDLE *except);
+#endif /* USE_WIN32API */
   };
 
 /* Add a new serial interface to the interface list */
@@ -248,4 +264,12 @@ extern void serial_add_interface (struct
 
 extern void serial_log_command (const char *);
 
+#ifdef USE_WIN32API
+
+/* Windows-only: find or create handles that we can wait on for this
+   serial device.  */
+extern void serial_wait_handle (struct serial *, HANDLE *, HANDLE *);
+
+#endif /* USE_WIN32API */
+
 #endif /* SERIAL_H */
Index: src/gdb/gdb_select.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/gdb_select.h 2006-02-03 15:19:42.000000000 -0500
@@ -0,0 +1,37 @@
+/* Slightly more portable version of <sys/select.h>.
+
+   Copyright (C) 2006
+   Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+#if !defined(GDB_SELECT_H)
+#define GDB_SELECT_H
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#ifdef USE_WIN32API
+#include <windows.h>
+#endif
+
+extern int gdb_select (int n, fd_set *readfds, fd_set *writefds,
+       fd_set *exceptfds, struct timeval *timeout);
+
+#endif /* !defined(GDB_STRING_H) */
Index: src/gdb/ser-tcp.c
===================================================================
--- src.orig/gdb/ser-tcp.c 2006-02-03 15:17:30.000000000 -0500
+++ src/gdb/ser-tcp.c 2006-02-03 16:27:18.000000000 -0500
@@ -1,6 +1,6 @@
 /* Serial interface for raw TCP connections on Un*x like systems.
 
-   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2001, 2005
+   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2001, 2005, 2006
    Free Software Foundation, Inc.
 
    This file is part of GDB.
@@ -23,7 +23,7 @@
 #include "defs.h"
 #include "serial.h"
 #include "ser-base.h"
-#include "ser-unix.h"
+#include "ser-tcp.h"
 
 #include <sys/types.h>
 
@@ -56,8 +56,6 @@
 typedef int socklen_t;
 #endif
 
-static int net_open (struct serial *scb, const char *name);
-static void net_close (struct serial *scb);
 void _initialize_ser_tcp (void);
 
 /* seconds to wait for connect */
@@ -67,7 +65,7 @@ void _initialize_ser_tcp (void);
 
 /* Open a tcp socket */
 
-static int
+int
 net_open (struct serial *scb, const char *name)
 {
   char *port_str, hostname[100];
@@ -153,7 +151,7 @@ net_open (struct serial *scb, const char
     {
       /* looks like we need to wait for the connect */
       struct timeval t;
-      fd_set rset, wset;
+      fd_set rset, wset, eset;
       int polls = 0;
       FD_ZERO (&rset);
 
@@ -174,10 +172,13 @@ net_open (struct serial *scb, const char
   
   FD_SET (scb->fd, &rset);
   wset = rset;
+  eset = rset;
   t.tv_sec = 0;
   t.tv_usec = 1000000 / POLL_INTERVAL;
   
-  n = select (scb->fd + 1, &rset, &wset, NULL, &t);
+  /* Windows returns connection failure in eset rather than wset.
+     How perverse.  */
+  n = select (scb->fd + 1, &rset, &wset, &eset, &t);
   polls++;
  }
       while (n == 0 && polls <= TIMEOUT * POLL_INTERVAL);
@@ -194,7 +195,7 @@ net_open (struct serial *scb, const char
   {
     int res, err;
     socklen_t len;
-    len = sizeof(err);
+    len = sizeof (err);
     /* On Windows, the fourth parameter to getsockopt is a "char *";
        on UNIX systems it is generally "void *".  The cast to "void *"
        is OK everywhere, since in C "void *" can be implicitly
@@ -230,7 +231,7 @@ net_open (struct serial *scb, const char
   return 0;
 }
 
-static void
+void
 net_close (struct serial *scb)
 {
   if (scb->fd < 0)
@@ -240,13 +241,13 @@ net_close (struct serial *scb)
   scb->fd = -1;
 }
 
-static int
+int
 net_read_prim (struct serial *scb, size_t count)
 {
   return recv (scb->fd, scb->buf, count, 0);
 }
 
-static int
+int
 net_write_prim (struct serial *scb, const void *buf, size_t count)
 {
   return send (scb->fd, buf, count, 0);
@@ -255,13 +256,12 @@ net_write_prim (struct serial *scb, cons
 void
 _initialize_ser_tcp (void)
 {
-  struct serial_ops *ops;
 #ifdef USE_WIN32API
-  WSADATA wsa_data;
-  if (WSAStartup (MAKEWORD (1, 0), &wsa_data) != 0)
-    /* WinSock is unavailable.  */
-    return;
-#endif
+  /* Do nothing; the TCP serial operations will be initialized in
+     ser-windows.c.  */
+  return;
+#else
+  struct serial_ops *ops;
   ops = XMALLOC (struct serial_ops);
   memset (ops, 0, sizeof (struct serial_ops));
   ops->name = "tcp";
@@ -285,4 +285,5 @@ _initialize_ser_tcp (void)
   ops->read_prim = net_read_prim;
   ops->write_prim = net_write_prim;
   serial_add_interface (ops);
+#endif /* USE_WIN32API */
 }
Index: src/gdb/ser-tcp.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/ser-tcp.h 2006-02-03 16:34:52.000000000 -0500
@@ -0,0 +1,32 @@
+/* Serial interface for raw TCP connections on Un*x like systems.
+
+   Copyright (C) 2006 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+#ifndef SER_TCP_H
+#define SER_TCP_H
+
+struct serial;
+
+extern int net_open (struct serial *scb, const char *name);
+extern void net_close (struct serial *scb);
+extern int net_read_prim (struct serial *scb, size_t count);
+extern int net_write_prim (struct serial *scb, const void *buf, size_t count);
+
+#endif
Index: src/gdb/inflow.c
===================================================================
--- src.orig/gdb/inflow.c 2006-02-03 15:17:30.000000000 -0500
+++ src/gdb/inflow.c 2006-02-03 16:28:04.000000000 -0500
@@ -1,6 +1,6 @@
 /* Low level interface to ptrace, for GDB when running under Unix.
    Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995,
-   1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+   1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
    Free Software Foundation, Inc.
 
    This file is part of GDB.
@@ -129,7 +129,6 @@ gdb_has_a_terminal (void)
 #endif
 
       gdb_has_a_terminal_flag = no;
-      stdin_serial = serial_fdopen (0);
       if (stdin_serial != NULL)
  {
   our_ttystate = serial_get_tty_state (stdin_serial);
@@ -730,6 +729,18 @@ gdb_setpgid (void)
   return retval;
 }
 
+/* Get all the current tty settings (including whether we have a
+   tty at all!).  We can't do this in _initialize_inflow because
+   serial_fdopen() won't work until the serial_ops_list is
+   initialized, but we don't want to do it lazily either, so
+   that we can guarantee stdin_serial is opened if there is
+   a terminal.  */
+void
+initialize_stdin_serial (void)
+{
+  stdin_serial = serial_fdopen (0);
+}
+
 void
 _initialize_inflow (void)
 {
Index: src/gdb/terminal.h
===================================================================
--- src.orig/gdb/terminal.h 2006-02-03 15:17:30.000000000 -0500
+++ src/gdb/terminal.h 2006-02-03 16:28:30.000000000 -0500
@@ -1,5 +1,6 @@
 /* Terminal interface definitions for GDB, the GNU Debugger.
-   Copyright (C) 1986, 1989, 1990, 1991, 1992, 1993, 1995, 1996, 1999, 2000
+   Copyright (C) 1986, 1989, 1990, 1991, 1992, 1993, 1995, 1996, 1999, 2000,
+   2006
    Free Software Foundation, Inc.
 
    This file is part of GDB.
@@ -88,4 +89,7 @@ extern int job_control;
    we lack job control.  */
 extern int gdb_setpgid (void);
 
+/* Set up a serial structure describing standard input.  In inflow.c.  */
+extern void initialize_stdin_serial (void);
+
 #endif /* !defined (TERMINAL_H) */
Index: src/gdb/top.c
===================================================================
--- src.orig/gdb/top.c 2006-02-03 15:17:30.000000000 -0500
+++ src/gdb/top.c 2006-02-03 15:19:42.000000000 -0500
@@ -1550,6 +1550,8 @@ gdb_init (char *argv0)
   init_cli_cmds();
   init_main (); /* But that omits this file!  Do it now */
 
+  initialize_stdin_serial ();
+
   async_init_signals ();
 
   /* We need a default language for parsing expressions, so simple things like
Reply | Threaded
Open this post in threaded view
|

Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

Daniel Jacobowitz-2
On Fri, Feb 03, 2006 at 05:05:29PM -0500, Daniel Jacobowitz wrote:

> Fixes, all for a mingw32-hosted GDB:
>
>   - Windows serial support.  This definitely deserves a NEWS entry,
>     included.
>
>   - Mark's Windows-aware select wrapper is substantially improved.
>     It's still a far cry from as thorough as Cygwin's, but it works
>     for reads and errors on serial ports, network sockets, pipes, and
>     consoles.
>
>   - Connecting to a closed TCP socket no longer times out; instead it
>     reports an error.

Oh - I missed one.  gdb_has_a_terminal() now works, so query() prints
questions instead of assuming yes.  That makes a big usability
difference.

Unfortunately, this is true for Windows consoles, but not for a
Windows-native GDB run from a Cygwin SSH terminal - there stdin
is just a pipe.  I checked with Chris, and there really doesn't seem to
be a useful way around this.  So if you want to use a native Windows
GDB, use it in a native Windows console (or via a pipe explicitly -
e.g. Eclipse).


--
Daniel Jacobowitz
CodeSourcery
Reply | Threaded
Open this post in threaded view
|

Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

Ian Lance Taylor
In reply to this post by Daniel Jacobowitz-2
Daniel Jacobowitz <[hidden email]> writes:

> The one I'm least proud of is pipes - there does not appear to be a way to
> sleep and have the OS wake you when data is available on a pipe.  So I poll
> every 10ms in a thread.  Yuck!  The other three all have subtly different
> wait mechanisms.

Why do you have to poll?  You should be able to have a thread which
just sleeps on reading the pipe.  When the thread reads something, it
can signal the main thread, passing it the character which it read.

The cleanest portable event loop which I know of is Tcl.  The Tcl code
has solved pretty much all the event loop issues for Unix, Windows,
and MacOS 9.

Ian
Reply | Threaded
Open this post in threaded view
|

Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

Eli Zaretskii
> Cc: [hidden email]
> From: Ian Lance Taylor <[hidden email]>
> Date: 03 Feb 2006 22:28:16 -0800
>
> The cleanest portable event loop which I know of is Tcl.  The Tcl code
> has solved pretty much all the event loop issues for Unix, Windows,
> and MacOS 9.

Can you, or someone else, please show a URL where I can see that event
loop, without having to download the whole package?
Reply | Threaded
Open this post in threaded view
|

Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

Eli Zaretskii
In reply to this post by Daniel Jacobowitz-2
> Date: Fri, 3 Feb 2006 17:05:29 -0500
> From: Daniel Jacobowitz <[hidden email]>
>
> The primary ugly bit of this patch is the select wrapper.  Windows has
> interfaces for all these things which map to Unix file descriptors, but
> while the Unix interfaces are actually compatible, the Windows interfaces
> are just designed along similar principles.  So you can handle a serial port
> in roughly the same way you handle a console window.... but only roughly.
> In fact you need to know what sort of device is behind each "file
> descriptor", in order to handle it appropriately.

Could you elaborate a bit?  I cannot easily see where that device
knowledge is present in the patch; it all looks to me like several
instances of the same code with almost identical flow.

In particular, I thought WaitForMultipleObjects could handle any kind
of handle, be it a pipe, a socket, a console, a process, or a file.

Even if the code is slightly different in that it calls different OS
API functions, cannot it all be expressed as the same code that uses a
function table indexed by the interface type?

> The one I'm least proud of is pipes - there does not appear to be a way to
> sleep and have the OS wake you when data is available on a pipe.

??? Again, doesn't WaitForMultipleObjects fit the bill?

> I've tested it.  I'm not sure what else to say about it.  Is it OK?  Are
> there things blatantly wrong with it that I've missed?  Are there particular
> bits that you think are just too ugly, and if so, do you have any
> suggestions on how to make them less ugly?

I'd like an explanation about this paradigm:

> +      wait_events[0] = state->stop_select;
> +      wait_events[1] = h;
> +
> +      event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE);
> +
> +      if (event_index == WAIT_OBJECT_0
> +  || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0)
> + {
> +  CloseHandle (state->stop_select);
> +  return 0;
> + }

(You have similar code in gdb_select and elsewhere.)  Why do you need
to wait for an object again with WaitForSingleObject, after you've
just waited for it in WaitForMultipleObjects?

Otherwise, I'm okay with these patches, except that...

>  - Windows serial support.  This definitely deserves a NEWS entry,
>    included.

...I don't think you included this entry.
Reply | Threaded
Open this post in threaded view
|

Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

Eli Zaretskii
In reply to this post by Daniel Jacobowitz-2
> Date: Fri, 3 Feb 2006 17:08:11 -0500
> From: Daniel Jacobowitz <[hidden email]>
>
> Oh - I missed one.  gdb_has_a_terminal() now works, so query() prints
> questions instead of assuming yes.  That makes a big usability
> difference.

Btw, as long as you are fixing annoyances in the MinGW port, how about
this one: whenever GDB has a large amount of data to display via
printf_filtered and its ilk, if I type `q' in response to its question
whether to continue or quit after displaying a screenful of text, I
get this bogus message:

  Quit (expect signal SIGINT when the program is resumed)

That's a lie, of course: no SIGINT will be coming my way any time
soon.
Reply | Threaded
Open this post in threaded view
|

Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

Daniel Jacobowitz-2
In reply to this post by Ian Lance Taylor
On Fri, Feb 03, 2006 at 10:28:16PM -0800, Ian Lance Taylor wrote:

> Daniel Jacobowitz <[hidden email]> writes:
>
> > The one I'm least proud of is pipes - there does not appear to be a way to
> > sleep and have the OS wake you when data is available on a pipe.  So I poll
> > every 10ms in a thread.  Yuck!  The other three all have subtly different
> > wait mechanisms.
>
> Why do you have to poll?  You should be able to have a thread which
> just sleeps on reading the pipe.  When the thread reads something, it
> can signal the main thread, passing it the character which it read.

I am glad you asked.  Oh, so glad.  I can't read from the pipe, because
I can't pass the data to the main thread.

There's a dissimilarity between the console and pipe support, and the
serial and socket support.  I used the serial.c interface for both
pairs, but the serial and socket devices are accessed exclusively from
the serial interface.  On the other hand, that interface is only used
for managing TTY state (and now for select) for the other two.  The
pipe I was interested in supporting was on stdin, and that gets
read from in all sorts of interesting places - like inside the
bowels of readline.  If I read a character from the pipe, it's going
to get lost.

Pity there's no ungetc equivalent under the read() layer.

--
Daniel Jacobowitz
CodeSourcery
Reply | Threaded
Open this post in threaded view
|

Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

Daniel Jacobowitz-2
In reply to this post by Eli Zaretskii
On Sat, Feb 04, 2006 at 02:38:56PM +0200, Eli Zaretskii wrote:

> > Date: Fri, 3 Feb 2006 17:05:29 -0500
> > From: Daniel Jacobowitz <[hidden email]>
> >
> > The primary ugly bit of this patch is the select wrapper.  Windows has
> > interfaces for all these things which map to Unix file descriptors, but
> > while the Unix interfaces are actually compatible, the Windows interfaces
> > are just designed along similar principles.  So you can handle a serial port
> > in roughly the same way you handle a console window.... but only roughly.
> > In fact you need to know what sort of device is behind each "file
> > descriptor", in order to handle it appropriately.
>
> Could you elaborate a bit?  I cannot easily see where that device
> knowledge is present in the patch; it all looks to me like several
> instances of the same code with almost identical flow.
>
> In particular, I thought WaitForMultipleObjects could handle any kind
> of handle, be it a pipe, a socket, a console, a process, or a file.
>
> Even if the code is slightly different in that it calls different OS
> API functions, cannot it all be expressed as the same code that uses a
> function table indexed by the interface type?

Nope.  Well, in a sense, that is what I've done - the
serial_wait_handle interface feeds back to a central loop using
WaitForMultipleObjects.

The problem is that, yes, all these objects are HANDLEs, and
WaitForMultipleObjects can wait for many kinds of HANDLEs.  But there's
different things that "waiting for a handle" might mean, and it doesn't
happen to pick the right one.  So what I'm doing is using other
handles, controlled by threads or async I/O functions, to signal the
conditions we're interested in.

The four I implemented are:

- Serial.  Yes, I confess this is more different from the others than
it has to be; the most elegant way was to open the file in "overlapped"
mode, and then issue an overlapped (similar to non-blocking) wait for
EV_RXCHAR.  It could be reworked to use a thread, like the other three,
but it would still need to use WaitCommEvent.  I did this one first.
As far as I can tell you can't wait directly on a serial handle at all,
or if you can, the MSDN documentation doesn't tell you what will
cause it to become signalled.

- Console.  You can wait on a console handle, and Mark's previous code
did so.  However, it turns out, this signals for any event in the
input queue - including key release events!  But reading from the
console will block since that only fetches keypress events.  So,
we need to explicitly look for keypress events and discard other
things.

- Pipes.  There's just no wait function.  You can wait on a connected
pipe, but it's always signalled as far as I've been able to determine
(and it's undocumented).  You can wait on a named pipe, but that
only waits for a connection to be available, not data.

- Sockets.  They're pretty easy actually; you can associate the
socket with an arbitrary event object.  But afterwards you need
to make a socket-specific call to figure out whether you got
a read or an error; otherwise GDB doesn't detect hangups.

> I'd like an explanation about this paradigm:
>
> > +      wait_events[0] = state->stop_select;
> > +      wait_events[1] = h;
> > +
> > +      event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE);
> > +
> > +      if (event_index == WAIT_OBJECT_0
> > +  || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0)
> > + {
> > +  CloseHandle (state->stop_select);
> > +  return 0;
> > + }
>
> (You have similar code in gdb_select and elsewhere.)  Why do you need
> to wait for an object again with WaitForSingleObject, after you've
> just waited for it in WaitForMultipleObjects?

WaitForMultipleObjects returns a single result.  However, both of the
input objects could have been signalled before we woke.  The case this
is trying to handle is WaitForMultipleObjects returning
WAIT_OBJECT_0 + 1, and then stop_select being signalled; if someone has
asked for the thread to exit, we should do so immediately, because
the other handles we rely on will have been closed.

(Yes, this isn't 100% perfect on race conditions.  However, it is as
close as I was able to come up with.  I suppose I could kill and
restart the threads for every select...)

> >  - Windows serial support.  This definitely deserves a NEWS entry,
> >    included.
>
> ...I don't think you included this entry.

Oops.  I added it while I was writing that bullet point.

+* Improved Windows host support
+
+GDB now builds as a cross debugger hosted on i686-mingw32, including
+native console support, and remote communications using either
+network sockets or serial ports.
+


--
Daniel Jacobowitz
CodeSourcery
Reply | Threaded
Open this post in threaded view
|

Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

Daniel Jacobowitz-2
In reply to this post by Eli Zaretskii
On Sat, Feb 04, 2006 at 02:43:33PM +0200, Eli Zaretskii wrote:

> > Date: Fri, 3 Feb 2006 17:08:11 -0500
> > From: Daniel Jacobowitz <[hidden email]>
> >
> > Oh - I missed one.  gdb_has_a_terminal() now works, so query() prints
> > questions instead of assuming yes.  That makes a big usability
> > difference.
>
> Btw, as long as you are fixing annoyances in the MinGW port, how about
> this one: whenever GDB has a large amount of data to display via
> printf_filtered and its ilk, if I type `q' in response to its question
> whether to continue or quit after displaying a screenful of text, I
> get this bogus message:
>
>   Quit (expect signal SIGINT when the program is resumed)
>
> That's a lie, of course: no SIGINT will be coming my way any time
> soon.

I'll take a look :-)

--
Daniel Jacobowitz
CodeSourcery
Reply | Threaded
Open this post in threaded view
|

Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

Eli Zaretskii
In reply to this post by Daniel Jacobowitz-2
> Date: Sat, 4 Feb 2006 10:11:24 -0500
> From: Daniel Jacobowitz <[hidden email]>
>
> The problem is that, yes, all these objects are HANDLEs, and
> WaitForMultipleObjects can wait for many kinds of HANDLEs.  But there's
> different things that "waiting for a handle" might mean, and it doesn't
> happen to pick the right one.  So what I'm doing is using other
> handles, controlled by threads or async I/O functions, to signal the
> conditions we're interested in.

Thanks for the explanations.  It sounds like there's no way to do this
significantly better than you did.  So I'm satisfied and think that
your patches can go in (modulo the on-going discussion of where to put
them).

Out of curiosity: does Cygwin's `select' use a similar object-specific
code to handle different types of handles?

> > ...I don't think you included this entry.
>
> Oops.  I added it while I was writing that bullet point.
>
> +* Improved Windows host support
> +
> +GDB now builds as a cross debugger hosted on i686-mingw32, including
> +native console support, and remote communications using either
> +network sockets or serial ports.

Thanks.  I think this is good.
Reply | Threaded
Open this post in threaded view
|

Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

Daniel Jacobowitz-2
On Sat, Feb 04, 2006 at 05:23:38PM +0200, Eli Zaretskii wrote:
> Out of curiosity: does Cygwin's `select' use a similar object-specific
> code to handle different types of handles?

Yes, it does.  Since the POSIX emulation is considerably more thorough
than the job I've done here, they have many more methods, but ever file
descriptor in Cygwin is mapped to a Windows handle and an object
providing various methods (including a thread to run during select and
a peek method, as it happens).  If anyone's wondering, I didn't borrow
the code from Cygwin, and upon a little more reflection I don't think
it would have been practical to do so without linking with all of
Cygwin anyway.

--
Daniel Jacobowitz
CodeSourcery
Reply | Threaded
Open this post in threaded view
|

Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

Ian Lance Taylor
In reply to this post by Eli Zaretskii
Eli Zaretskii <[hidden email]> writes:

> > Cc: [hidden email]
> > From: Ian Lance Taylor <[hidden email]>
> > Date: 03 Feb 2006 22:28:16 -0800
> >
> > The cleanest portable event loop which I know of is Tcl.  The Tcl code
> > has solved pretty much all the event loop issues for Unix, Windows,
> > and MacOS 9.
>
> Can you, or someone else, please show a URL where I can see that event
> loop, without having to download the whole package?

Tcl is checked into the src repository, since it's part of expect
which is part of DejaGNU.  There isn't just one place to look at the
event loop.  The Windows pipe specific code is in
src/tcl/win/tclWinPipe.c.

Ian
Reply | Threaded
Open this post in threaded view
|

Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

Eli Zaretskii
> Cc: [hidden email], [hidden email]
> From: Ian Lance Taylor <[hidden email]>
> Date: 04 Feb 2006 09:05:55 -0800
>
> Tcl is checked into the src repository, since it's part of expect
> which is part of DejaGNU.  There isn't just one place to look at the
> event loop.  The Windows pipe specific code is in
> src/tcl/win/tclWinPipe.c.

Thanks.
Reply | Threaded
Open this post in threaded view
|

Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

Ian Lance Taylor
In reply to this post by Daniel Jacobowitz-2
Daniel Jacobowitz <[hidden email]> writes:

> > Why do you have to poll?  You should be able to have a thread which
> > just sleeps on reading the pipe.  When the thread reads something, it
> > can signal the main thread, passing it the character which it read.
>
> I am glad you asked.  Oh, so glad.  I can't read from the pipe, because
> I can't pass the data to the main thread.
>
> There's a dissimilarity between the console and pipe support, and the
> serial and socket support.  I used the serial.c interface for both
> pairs, but the serial and socket devices are accessed exclusively from
> the serial interface.  On the other hand, that interface is only used
> for managing TTY state (and now for select) for the other two.  The
> pipe I was interested in supporting was on stdin, and that gets
> read from in all sorts of interesting places - like inside the
> bowels of readline.  If I read a character from the pipe, it's going
> to get lost.
>
> Pity there's no ungetc equivalent under the read() layer.

Well, OK, let's try this.  Create a new pipe.  Create yet another new
thread which reads from the original pipe and writes to the new pipe.
Freopen the new pipe onto stdin.  When the new thread writes to the
new pipe, it signals the event loop once, and then waits.  When the
event loop gets the signal, it indicates that it is OK to read from
stdin, the new pipe.  When you reenter the event loop, it signals the
new thread to tell it that it is interested in more data and more
signals.

Ian
Reply | Threaded
Open this post in threaded view
|

Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

Daniel Jacobowitz-2
On Sat, Feb 04, 2006 at 04:01:07PM -0800, Ian Lance Taylor wrote:
> Well, OK, let's try this.  Create a new pipe.  Create yet another new
> thread which reads from the original pipe and writes to the new pipe.
> Freopen the new pipe onto stdin.  When the new thread writes to the
> new pipe, it signals the event loop once, and then waits.  When the
> event loop gets the signal, it indicates that it is OK to read from
> stdin, the new pipe.  When you reenter the event loop, it signals the
> new thread to tell it that it is interested in more data and more
> signals.

I'm not convinced this would work.  Remember, not only do we read from
this pipe just about everywhere, a lot of the time we don't pass
through the event loop.  We'd have to have a thread which did blocking
reads from the original pipe, wrote that data straight through to a new
pipe, and signalled an event whenever it performed a write.  Not only
would that be tricky to get right, but we've also traded off a thread
when selecting for a thread all the time.

Do you think the 10ms poll is sufficiently nasty to justify pursuing
this?  I suppose I could be convinced to give it another try...

--
Daniel Jacobowitz
CodeSourcery
Reply | Threaded
Open this post in threaded view
|

Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

Ian Lance Taylor
Daniel Jacobowitz <[hidden email]> writes:

> On Sat, Feb 04, 2006 at 04:01:07PM -0800, Ian Lance Taylor wrote:
> > Well, OK, let's try this.  Create a new pipe.  Create yet another new
> > thread which reads from the original pipe and writes to the new pipe.
> > Freopen the new pipe onto stdin.  When the new thread writes to the
> > new pipe, it signals the event loop once, and then waits.  When the
> > event loop gets the signal, it indicates that it is OK to read from
> > stdin, the new pipe.  When you reenter the event loop, it signals the
> > new thread to tell it that it is interested in more data and more
> > signals.
>
> I'm not convinced this would work.  Remember, not only do we read from
> this pipe just about everywhere, a lot of the time we don't pass
> through the event loop.  We'd have to have a thread which did blocking
> reads from the original pipe, wrote that data straight through to a new
> pipe, and signalled an event whenever it performed a write.  Not only
> would that be tricky to get right, but we've also traded off a thread
> when selecting for a thread all the time.

I guess I had assumed that you were going to have a thread around all
the time anyhow.  Is creating a thread on Windows so cheap that one
can do it every time gdb calls select?

I also don't know whether it would work.

> Do you think the 10ms poll is sufficiently nasty to justify pursuing
> this?  I suppose I could be convinced to give it another try...

I think the 10ms poll is pretty nasty, both because polling is nasty
in general and because it introduces a delay when using gdb in a
script.  But, hey, it's only Windows, so there is certainly a limit to
how much I care.

Ian
Reply | Threaded
Open this post in threaded view
|

Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

Daniel Jacobowitz-2
On Sun, Feb 05, 2006 at 07:27:41PM -0800, Ian Lance Taylor wrote:
> I guess I had assumed that you were going to have a thread around all
> the time anyhow.  Is creating a thread on Windows so cheap that one
> can do it every time gdb calls select?

Yes, it is that cheap.  But I wasn't clear - we have the thread the
whole time, but we don't rely on it; it goes to sleep and we wake it
when selecting.  It only polls when GDB is idle in a select call for
this fd.

(Actually, I think that it continues to poll for some time afterwards
until the next byte of data does arrive on that pipe, or the pipe's
closed.  That's just because (A) it's cheap, and (B) I had no other
reason to have a cleanup hook after the end of select.)

--
Daniel Jacobowitz
CodeSourcery
Reply | Threaded
Open this post in threaded view
|

Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

Daniel Jacobowitz-2
In reply to this post by Daniel Jacobowitz-2
On Fri, Feb 03, 2006 at 05:05:29PM -0500, Daniel Jacobowitz wrote:
> Fixes, all for a mingw32-hosted GDB:

Here is a revised version, in which the Windows select magic does not
live in event-loop.c, and including the NEWS entry.  I haven't changed
the pipe polling code; thanks to Ian for his suggestions, but I'm
not sufficiently sure they're workable.

Are there any bits of this patch that strike you as too ugly or in the
wrong place?

--
Daniel Jacobowitz
CodeSourcery

2006-02-03  Daniel Jacobowitz  <[hidden email]>

        * NEWS: Mention native Windows support.
        * Makefile.in (gdb_select_h, ser_tcp_h): New.
        (ALLDEPFILES): Add ser-mingw.c.
        (event-loop.o, mingw-hdep.o, ser-base.o, ser-tcp.o): Update.
        (ser-mingw.o): New rule.
        * configure: Regenerated.
        * configure.in: Add ser-mingw.o for mingw32.
        * ser-mingw.c: New file.
        * event-loop.c: Include "gdb_select.h".
        (gdb_select): Make global.  Update comments.  Call mingw_select
        instead of inlining MinGW-specific code.
        * ser-base.c: Include "gdb_select.h".
        (ser_base_wait_for): Use gdb_select.
        * serial.c (serial_for_fd): New function.
        (serial_fdopen): Try "terminal" before "hardwire".  Initialize
        the allocated struct serial.
        (serial_wait_handle): New function.
        * serial.h (serial_for_fd, serial_wait_handle): New prototypes.
        (struct serial_ops) [USE_WIN32API]: Add wait_handle.
        * gdb_select.h: New file.
        * ser-tcp.c: Include "ser-tcp.h".  Remove unused "ser-unix.h" include.
        (net_close, net_read_prim, net_write_prim): Make global.
        (net_open): Likewise.  Pass an exception set to select.  Whitespace fix.
        (_initialize_ser_tcp) [USE_WIN32API]: Do not register TCP support here.
        * ser-tcp.h: New file.
        * inflow.c (gdb_has_a_terminal): Don't initialize stdin_serial here.
        (initialize_stdin_serial): New function.
        * terminal.h (initialize_stdin_serial): New prototype.
        * top.c (gdb_init): Call initialize_stdin_serial.
        * config/i386/xm-mingw.h (mingw_select): New prototype.
        * mingw-hdep.c (gdb_select): New function, cloned from gdb_select in
        event-loop.c.  Add exception condition support.  Use serial_for_fd
        and serial_wait_handle.

Index: src/gdb/Makefile.in
===================================================================
--- src.orig/gdb/Makefile.in 2006-02-06 15:21:55.000000000 -0500
+++ src/gdb/Makefile.in 2006-02-06 15:47:12.000000000 -0500
@@ -692,6 +692,7 @@ gdb_obstack_h = gdb_obstack.h $(obstack_
 gdb_proc_service_h = gdb_proc_service.h $(gregset_h)
 gdb_ptrace_h = gdb_ptrace.h
 gdb_regex_h = gdb_regex.h $(xregex_h)
+gdb_select_h = gdb_select.h
 gdb_stabs_h = gdb-stabs.h
 gdb_stat_h = gdb_stat.h
 gdb_string_h = gdb_string.h
@@ -764,6 +765,7 @@ scm_tags_h = scm-tags.h
 sentinel_frame_h = sentinel-frame.h
 serial_h = serial.h
 ser_base_h = ser-base.h
+ser_tcp_h = ser-tcp.h
 ser_unix_h = ser-unix.h
 shnbsd_tdep_h = shnbsd-tdep.h
 sh_tdep_h = sh-tdep.h
@@ -1440,7 +1442,7 @@ ALLDEPFILES = \
  remote-st.c remote-utils.c dcache.c \
  rs6000-nat.c rs6000-tdep.c \
  s390-tdep.c s390-nat.c \
- ser-go32.c ser-pipe.c ser-tcp.c \
+ ser-go32.c ser-pipe.c ser-tcp.c ser-mingw.c \
  sh-tdep.c sh64-tdep.c shnbsd-tdep.c shnbsd-nat.c \
  sol2-tdep.c \
  solib-irix.c solib-svr4.c solib-sunos.c \
@@ -1920,7 +1922,7 @@ eval.o: eval.c $(defs_h) $(gdb_string_h)
  $(f_lang_h) $(cp_abi_h) $(infcall_h) $(objc_lang_h) $(block_h) \
  $(parser_defs_h) $(cp_support_h)
 event-loop.o: event-loop.c $(defs_h) $(event_loop_h) $(event_top_h) \
- $(gdb_string_h) $(exceptions_h) $(gdb_assert_h)
+ $(gdb_string_h) $(exceptions_h) $(gdb_assert_h) $(gdb_select_h)
 event-top.o: event-top.c $(defs_h) $(top_h) $(inferior_h) $(target_h) \
  $(terminal_h) $(event_loop_h) $(event_top_h) $(interps_h) \
  $(exceptions_h) $(gdbcmd_h) $(readline_h) $(readline_history_h)
@@ -2280,7 +2282,8 @@ memattr.o: memattr.c $(defs_h) $(command
  $(target_h) $(value_h) $(language_h) $(gdb_string_h)
 mem-break.o: mem-break.c $(defs_h) $(symtab_h) $(breakpoint_h) $(inferior_h) \
  $(target_h)
-mingw-hdep.o: mingw-hdep.c $(defs_h) $(gdb_string_h)
+mingw-hdep.o: mingw-hdep.c $(defs_h) $(serial_h) $(gdb_assert_h) \
+ $(gdb_select_h) $(gdb_string_h)
 minsyms.o: minsyms.c $(defs_h) $(gdb_string_h) $(symtab_h) $(bfd_h) \
  $(symfile_h) $(objfiles_h) $(demangle_h) $(value_h) $(cp_abi_h)
 mips64obsd-nat.o: mips64obsd-nat.c $(defs_h) $(inferior_h) $(regcache_h) \
@@ -2512,13 +2515,15 @@ ser-e7kpc.o: ser-e7kpc.c $(defs_h) $(ser
 ser-go32.o: ser-go32.c $(defs_h) $(gdbcmd_h) $(serial_h) $(gdb_string_h)
 serial.o: serial.c $(defs_h) $(serial_h) $(gdb_string_h) $(gdbcmd_h)
 ser-base.o: ser-base.c $(defs_h) $(serial_h) $(ser_base_h) $(event_loop_h) \
- $(gdb_string_h)
+ $(gdb_select_h) $(gdb_string_h)
 ser-pipe.o: ser-pipe.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_unix_h) \
  $(gdb_vfork_h) $(gdb_string_h)
-ser-tcp.o: ser-tcp.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_unix_h) \
+ser-tcp.o: ser-tcp.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_tcp_h) \
  $(gdb_string_h)
 ser-unix.o: ser-unix.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_unix_h) \
  $(terminal_h) $(gdb_string_h)
+ser-mingw.o: ser-mingw.c $(defs_h) $(serial_h) $(ser_base_h) \
+ $(ser_tcp_h) $(gdb_assert_h) $(gdb_string_h)
 sh3-rom.o: sh3-rom.c $(defs_h) $(gdbcore_h) $(target_h) $(monitor_h) \
  $(serial_h) $(srec_h) $(arch_utils_h) $(regcache_h) $(gdb_string_h) \
  $(sh_tdep_h)
Index: src/gdb/configure
===================================================================
--- src.orig/gdb/configure 2006-02-06 15:21:55.000000000 -0500
+++ src/gdb/configure 2006-02-06 15:44:41.000000000 -0500
@@ -20093,7 +20093,7 @@ SER_HARDWIRE="ser-base.o ser-unix.o ser-
 case ${host} in
   *go32* ) SER_HARDWIRE=ser-go32.o ;;
   *djgpp* ) SER_HARDWIRE=ser-go32.o ;;
-  *mingw32*) SER_HARDWIRE="ser-base.o ser-tcp.o" ;;
+  *mingw32*) SER_HARDWIRE="ser-base.o ser-tcp.o ser-mingw.o" ;;
 esac
 
 
Index: src/gdb/configure.ac
===================================================================
--- src.orig/gdb/configure.ac 2006-02-06 15:21:55.000000000 -0500
+++ src/gdb/configure.ac 2006-02-06 15:44:41.000000000 -0500
@@ -1,5 +1,6 @@
 dnl Autoconf configure script for GDB, the GNU debugger.
-dnl Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+dnl Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+dnl 2005, 2006
 dnl Free Software Foundation, Inc.
 dnl
 dnl This file is part of GDB.
@@ -1198,7 +1199,7 @@ SER_HARDWIRE="ser-base.o ser-unix.o ser-
 case ${host} in
   *go32* ) SER_HARDWIRE=ser-go32.o ;;
   *djgpp* ) SER_HARDWIRE=ser-go32.o ;;
-  *mingw32*) SER_HARDWIRE="ser-base.o ser-tcp.o" ;;
+  *mingw32*) SER_HARDWIRE="ser-base.o ser-tcp.o ser-mingw.o" ;;
 esac
 AC_SUBST(SER_HARDWIRE)
 
Index: src/gdb/event-loop.c
===================================================================
--- src.orig/gdb/event-loop.c 2006-02-06 15:21:55.000000000 -0500
+++ src/gdb/event-loop.c 2006-02-06 15:47:01.000000000 -0500
@@ -1,5 +1,6 @@
 /* Event loop machinery for GDB, the GNU debugger.
-   Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2000, 2001, 2002, 2005, 2006
+   Free Software Foundation, Inc.
    Written by Elena Zannoni <[hidden email]> of Cygnus Solutions.
 
    This file is part of GDB.
@@ -37,6 +38,7 @@
 #include <sys/time.h>
 #include "exceptions.h"
 #include "gdb_assert.h"
+#include "gdb_select.h"
 
 typedef struct gdb_event gdb_event;
 typedef void (event_handler_func) (int);
@@ -731,92 +733,14 @@ handle_file_event (int event_file_desc)
     }
 }
 
-/* Wrapper for select.  This function is not yet exported from this
-   file because it is not sufficiently general.  For example,
-   ser-base.c uses select to check for socket activity, and this
-   function does not support sockets under Windows, so we do not want
-   to use gdb_select in ser-base.c.  */
+/* Wrapper for select.  */
 
-static int
+int
 gdb_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
     struct timeval *timeout)
 {
 #ifdef USE_WIN32API
-  HANDLE handles[MAXIMUM_WAIT_OBJECTS];
-  HANDLE h;
-  DWORD event;
-  DWORD num_handles;
-  int fd;
-  int num_ready;
-
-  num_ready = 0;
-  num_handles = 0;
-  for (fd = 0; fd < n; ++fd)
-    {
-      /* There is no support yet for WRITEFDS.  At present, this isn't
- used by GDB -- but we do not want to silently ignore WRITEFDS
- if something starts using it.  */
-      gdb_assert (!FD_ISSET (fd, writefds));
-      if (!FD_ISSET (fd, readfds)
-  && !FD_ISSET (fd, exceptfds))
- continue;
-      h = (HANDLE) _get_osfhandle (fd);
-      if (h == INVALID_HANDLE_VALUE)
- {
-  /* If the underlying handle is INVALID_HANDLE_VALUE, then
-     this descriptor is no more.  */
-  if (FD_ISSET (fd, exceptfds))
-    ++num_ready;
-  continue;
- }
-      /* The only exceptional condition we recognize is a closed file
- descriptor.  Since we have already checked for that
- condition, clear the exceptional bit for this descriptor.  */
-      FD_CLR (fd, exceptfds);
-      if (FD_ISSET (fd, readfds))
-      {
- gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS);
- handles[num_handles++] = h;
-      }
-    }
-  /* If we don't need to wait for any handles, we are done.  */
-  if (!num_handles)
-    return num_ready;
-  event = WaitForMultipleObjects (num_handles,
-  handles,
-  FALSE,
-  timeout
-  ? (timeout->tv_sec * 1000 + timeout->tv_usec)
-  : INFINITE);
-  /* EVENT can only be a value in the WAIT_ABANDONED_0 range if the
-     HANDLES included an abandoned mutex.  Since GDB doesn't use
-     mutexes, that should never occur.  */
-  gdb_assert (!(WAIT_ABANDONED_0 <= event
- && event < WAIT_ABANDONED_0 + num_handles));
-  if (event == WAIT_FAILED)
-    return -1;
-  if (event == WAIT_TIMEOUT)
-    return num_ready;
-  /* Run through the READFDS, clearing bits corresponding to descriptors
-     for which input is unavailable.  */
-  num_ready += num_handles;
-  h = handles[event - WAIT_OBJECT_0];
-  for (fd = 0; fd < n; ++fd)
-    {
-      HANDLE fd_h;
-      if (!FD_ISSET (fd, readfds))
- continue;
-      fd_h = (HANDLE) _get_osfhandle (fd);
-      /* This handle might be ready, even though it wasn't the handle
- returned by WaitForMultipleObjects.  */
-      if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0)
- {
-  FD_CLR (fd, readfds);
-  --num_ready;
- }
-    }
-
-  return num_ready;
+  return mingw_select (n, readfds, writefds, exceptfds, timeout);
 #else
   return select (n, readfds, writefds, exceptfds, timeout);
 #endif
Index: src/gdb/ser-base.c
===================================================================
--- src.orig/gdb/ser-base.c 2006-02-06 15:21:55.000000000 -0500
+++ src/gdb/ser-base.c 2006-02-06 15:44:41.000000000 -0500
@@ -1,7 +1,7 @@
 /* Generic serial interface functions.
 
    Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
-   2003, 2004, 2005 Free Software Foundation, Inc.
+   2003, 2004, 2005, 2006 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -25,6 +25,7 @@
 #include "ser-base.h"
 #include "event-loop.h"
 
+#include "gdb_select.h"
 #include "gdb_string.h"
 #include <sys/time.h>
 #ifdef USE_WIN32API
@@ -202,9 +203,9 @@ ser_base_wait_for (struct serial *scb, i
       FD_SET (scb->fd, &exceptfds);
 
       if (timeout >= 0)
- numfds = select (scb->fd + 1, &readfds, 0, &exceptfds, &tv);
+ numfds = gdb_select (scb->fd + 1, &readfds, 0, &exceptfds, &tv);
       else
- numfds = select (scb->fd + 1, &readfds, 0, &exceptfds, 0);
+ numfds = gdb_select (scb->fd + 1, &readfds, 0, &exceptfds, 0);
 
       if (numfds <= 0)
  {
Index: src/gdb/serial.c
===================================================================
--- src.orig/gdb/serial.c 2006-02-06 15:21:55.000000000 -0500
+++ src/gdb/serial.c 2006-02-06 15:44:41.000000000 -0500
@@ -1,7 +1,7 @@
 /* Generic serial interface routines
 
    Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
-   2001, 2002 Free Software Foundation, Inc.
+   2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -233,6 +233,22 @@ serial_open (const char *name)
   return scb;
 }
 
+/* Return the open serial device for FD, if found, or NULL if FD
+   is not already opened.  */
+
+struct serial *
+serial_for_fd (int fd)
+{
+  struct serial *scb;
+  struct serial_ops *ops;
+
+  for (scb = scb_base; scb; scb = scb->next)
+    if (scb->fd == fd)
+      return scb;
+
+  return NULL;
+}
+
 struct serial *
 serial_fdopen (const int fd)
 {
@@ -246,12 +262,14 @@ serial_fdopen (const int fd)
  return scb;
       }
 
-  ops = serial_interface_lookup ("hardwire");
+  ops = serial_interface_lookup ("terminal");
+  if (!ops)
+    ops = serial_interface_lookup ("hardwire");
 
   if (!ops)
     return NULL;
 
-  scb = XMALLOC (struct serial);
+  scb = XCALLOC (1, struct serial);
 
   scb->ops = ops;
 
@@ -524,6 +542,19 @@ serial_debug_p (struct serial *scb)
   return scb->debug_p || global_serial_debug_p;
 }
 
+#ifdef USE_WIN32API
+void
+serial_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except)
+{
+  if (scb->ops->wait_handle)
+    scb->ops->wait_handle (scb, read, except);
+  else
+    {
+      *read = (HANDLE) _get_osfhandle (scb->fd);
+      *except = NULL;
+    }
+}
+#endif
 
 #if 0
 /* The connect command is #if 0 because I hadn't thought of an elegant
Index: src/gdb/serial.h
===================================================================
--- src.orig/gdb/serial.h 2006-02-06 15:21:55.000000000 -0500
+++ src/gdb/serial.h 2006-02-06 15:44:41.000000000 -0500
@@ -1,5 +1,6 @@
 /* Remote serial support interface definitions for GDB, the GNU Debugger.
-   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000
+   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+   2004, 2005, 2006
    Free Software Foundation, Inc.
 
    This file is part of GDB.
@@ -22,6 +23,10 @@
 #ifndef SERIAL_H
 #define SERIAL_H
 
+#ifdef USE_WIN32API
+#include <windows.h>
+#endif
+
 struct ui_file;
 
 /* For most routines, if a failure is indicated, then errno should be
@@ -41,6 +46,10 @@ struct serial;
 
 extern struct serial *serial_open (const char *name);
 
+/* Find an already opened serial stream using a file handle.  */
+
+extern struct serial *serial_for_fd (int fd);
+
 /* Open a new serial stream using a file handle.  */
 
 extern struct serial *serial_fdopen (const int fd);
@@ -238,6 +247,13 @@ struct serial_ops
     /* Perform a low-level write operation, writing (at most) COUNT
        bytes from BUF.  */
     int (*write_prim)(struct serial *scb, const void *buf, size_t count);
+
+#ifdef USE_WIN32API
+    /* Return a handle to wait on, indicating available data from SCB
+       when signaled, in *READ.  Return a handle indicating errors
+       in *EXCEPT.  */
+    void (*wait_handle) (struct serial *scb, HANDLE *read, HANDLE *except);
+#endif /* USE_WIN32API */
   };
 
 /* Add a new serial interface to the interface list */
@@ -248,4 +264,12 @@ extern void serial_add_interface (struct
 
 extern void serial_log_command (const char *);
 
+#ifdef USE_WIN32API
+
+/* Windows-only: find or create handles that we can wait on for this
+   serial device.  */
+extern void serial_wait_handle (struct serial *, HANDLE *, HANDLE *);
+
+#endif /* USE_WIN32API */
+
 #endif /* SERIAL_H */
Index: src/gdb/gdb_select.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/gdb_select.h 2006-02-06 15:44:41.000000000 -0500
@@ -0,0 +1,37 @@
+/* Slightly more portable version of <sys/select.h>.
+
+   Copyright (C) 2006
+   Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+#if !defined(GDB_SELECT_H)
+#define GDB_SELECT_H
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#ifdef USE_WIN32API
+#include <winsock2.h>
+#endif
+
+extern int gdb_select (int n, fd_set *readfds, fd_set *writefds,
+       fd_set *exceptfds, struct timeval *timeout);
+
+#endif /* !defined(GDB_STRING_H) */
Index: src/gdb/ser-tcp.c
===================================================================
--- src.orig/gdb/ser-tcp.c 2006-02-06 15:21:55.000000000 -0500
+++ src/gdb/ser-tcp.c 2006-02-06 15:44:41.000000000 -0500
@@ -1,6 +1,6 @@
 /* Serial interface for raw TCP connections on Un*x like systems.
 
-   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2001, 2005
+   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2001, 2005, 2006
    Free Software Foundation, Inc.
 
    This file is part of GDB.
@@ -23,7 +23,7 @@
 #include "defs.h"
 #include "serial.h"
 #include "ser-base.h"
-#include "ser-unix.h"
+#include "ser-tcp.h"
 
 #include <sys/types.h>
 
@@ -56,8 +56,6 @@
 typedef int socklen_t;
 #endif
 
-static int net_open (struct serial *scb, const char *name);
-static void net_close (struct serial *scb);
 void _initialize_ser_tcp (void);
 
 /* seconds to wait for connect */
@@ -67,7 +65,7 @@ void _initialize_ser_tcp (void);
 
 /* Open a tcp socket */
 
-static int
+int
 net_open (struct serial *scb, const char *name)
 {
   char *port_str, hostname[100];
@@ -153,7 +151,7 @@ net_open (struct serial *scb, const char
     {
       /* looks like we need to wait for the connect */
       struct timeval t;
-      fd_set rset, wset;
+      fd_set rset, wset, eset;
       int polls = 0;
       FD_ZERO (&rset);
 
@@ -174,10 +172,13 @@ net_open (struct serial *scb, const char
   
   FD_SET (scb->fd, &rset);
   wset = rset;
+  eset = rset;
   t.tv_sec = 0;
   t.tv_usec = 1000000 / POLL_INTERVAL;
   
-  n = select (scb->fd + 1, &rset, &wset, NULL, &t);
+  /* Windows returns connection failure in eset rather than wset.
+     How perverse.  */
+  n = select (scb->fd + 1, &rset, &wset, &eset, &t);
   polls++;
  }
       while (n == 0 && polls <= TIMEOUT * POLL_INTERVAL);
@@ -194,7 +195,7 @@ net_open (struct serial *scb, const char
   {
     int res, err;
     socklen_t len;
-    len = sizeof(err);
+    len = sizeof (err);
     /* On Windows, the fourth parameter to getsockopt is a "char *";
        on UNIX systems it is generally "void *".  The cast to "void *"
        is OK everywhere, since in C "void *" can be implicitly
@@ -230,7 +231,7 @@ net_open (struct serial *scb, const char
   return 0;
 }
 
-static void
+void
 net_close (struct serial *scb)
 {
   if (scb->fd < 0)
@@ -240,13 +241,13 @@ net_close (struct serial *scb)
   scb->fd = -1;
 }
 
-static int
+int
 net_read_prim (struct serial *scb, size_t count)
 {
   return recv (scb->fd, scb->buf, count, 0);
 }
 
-static int
+int
 net_write_prim (struct serial *scb, const void *buf, size_t count)
 {
   return send (scb->fd, buf, count, 0);
@@ -255,13 +256,12 @@ net_write_prim (struct serial *scb, cons
 void
 _initialize_ser_tcp (void)
 {
-  struct serial_ops *ops;
 #ifdef USE_WIN32API
-  WSADATA wsa_data;
-  if (WSAStartup (MAKEWORD (1, 0), &wsa_data) != 0)
-    /* WinSock is unavailable.  */
-    return;
-#endif
+  /* Do nothing; the TCP serial operations will be initialized in
+     ser-mingw.c.  */
+  return;
+#else
+  struct serial_ops *ops;
   ops = XMALLOC (struct serial_ops);
   memset (ops, 0, sizeof (struct serial_ops));
   ops->name = "tcp";
@@ -285,4 +285,5 @@ _initialize_ser_tcp (void)
   ops->read_prim = net_read_prim;
   ops->write_prim = net_write_prim;
   serial_add_interface (ops);
+#endif /* USE_WIN32API */
 }
Index: src/gdb/ser-tcp.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/ser-tcp.h 2006-02-06 15:44:41.000000000 -0500
@@ -0,0 +1,32 @@
+/* Serial interface for raw TCP connections on Un*x like systems.
+
+   Copyright (C) 2006 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+#ifndef SER_TCP_H
+#define SER_TCP_H
+
+struct serial;
+
+extern int net_open (struct serial *scb, const char *name);
+extern void net_close (struct serial *scb);
+extern int net_read_prim (struct serial *scb, size_t count);
+extern int net_write_prim (struct serial *scb, const void *buf, size_t count);
+
+#endif
Index: src/gdb/inflow.c
===================================================================
--- src.orig/gdb/inflow.c 2006-02-06 15:21:55.000000000 -0500
+++ src/gdb/inflow.c 2006-02-06 15:44:41.000000000 -0500
@@ -1,6 +1,6 @@
 /* Low level interface to ptrace, for GDB when running under Unix.
    Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995,
-   1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+   1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
    Free Software Foundation, Inc.
 
    This file is part of GDB.
@@ -129,7 +129,6 @@ gdb_has_a_terminal (void)
 #endif
 
       gdb_has_a_terminal_flag = no;
-      stdin_serial = serial_fdopen (0);
       if (stdin_serial != NULL)
  {
   our_ttystate = serial_get_tty_state (stdin_serial);
@@ -730,6 +729,18 @@ gdb_setpgid (void)
   return retval;
 }
 
+/* Get all the current tty settings (including whether we have a
+   tty at all!).  We can't do this in _initialize_inflow because
+   serial_fdopen() won't work until the serial_ops_list is
+   initialized, but we don't want to do it lazily either, so
+   that we can guarantee stdin_serial is opened if there is
+   a terminal.  */
+void
+initialize_stdin_serial (void)
+{
+  stdin_serial = serial_fdopen (0);
+}
+
 void
 _initialize_inflow (void)
 {
Index: src/gdb/terminal.h
===================================================================
--- src.orig/gdb/terminal.h 2006-02-06 15:21:55.000000000 -0500
+++ src/gdb/terminal.h 2006-02-06 15:44:41.000000000 -0500
@@ -1,5 +1,6 @@
 /* Terminal interface definitions for GDB, the GNU Debugger.
-   Copyright (C) 1986, 1989, 1990, 1991, 1992, 1993, 1995, 1996, 1999, 2000
+   Copyright (C) 1986, 1989, 1990, 1991, 1992, 1993, 1995, 1996, 1999, 2000,
+   2006
    Free Software Foundation, Inc.
 
    This file is part of GDB.
@@ -88,4 +89,7 @@ extern int job_control;
    we lack job control.  */
 extern int gdb_setpgid (void);
 
+/* Set up a serial structure describing standard input.  In inflow.c.  */
+extern void initialize_stdin_serial (void);
+
 #endif /* !defined (TERMINAL_H) */
Index: src/gdb/top.c
===================================================================
--- src.orig/gdb/top.c 2006-02-06 15:21:55.000000000 -0500
+++ src/gdb/top.c 2006-02-06 15:44:41.000000000 -0500
@@ -1550,6 +1550,8 @@ gdb_init (char *argv0)
   init_cli_cmds();
   init_main (); /* But that omits this file!  Do it now */
 
+  initialize_stdin_serial ();
+
   async_init_signals ();
 
   /* We need a default language for parsing expressions, so simple things like
Index: src/gdb/NEWS
===================================================================
--- src.orig/gdb/NEWS 2006-02-06 15:21:55.000000000 -0500
+++ src/gdb/NEWS 2006-02-06 15:44:41.000000000 -0500
@@ -41,6 +41,12 @@ detach-fork <n> Delete a fork from the
 
 Morpho Technologies ms2 ms1-elf
 
+* Improved Windows host support
+
+GDB now builds as a cross debugger hosted on i686-mingw32, including
+native console support, and remote communications using either
+network sockets or serial ports.
+
 * REMOVED features
 
 The ARM rdi-share module.
Index: src/gdb/ser-mingw.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/ser-mingw.c 2006-02-06 15:44:41.000000000 -0500
@@ -0,0 +1,796 @@
+/* Serial interface for local (hardwired) serial ports on Windows systems
+
+   Copyright (C) 2006
+   Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+#include "defs.h"
+#include "serial.h"
+#include "ser-base.h"
+#include "ser-tcp.h"
+
+#include <windows.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "gdb_assert.h"
+#include "gdb_string.h"
+
+void _initialize_ser_windows (void);
+
+struct ser_windows_state
+{
+  int in_progress;
+  OVERLAPPED ov;
+  DWORD lastCommMask;
+  HANDLE except_event;
+};
+
+/* Open up a real live device for serial I/O.  */
+
+static int
+ser_windows_open (struct serial *scb, const char *name)
+{
+  HANDLE h;
+  struct ser_windows_state *state;
+  COMMTIMEOUTS timeouts;
+
+  /* Only allow COM ports.  */
+  if (strncmp (name, "COM", 3) != 0)
+    {
+      errno = ENOENT;
+      return -1;
+    }
+
+  h = CreateFile (name, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+  OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+  if (h == INVALID_HANDLE_VALUE)
+    {
+      errno = ENOENT;
+      return -1;
+    }
+
+  scb->fd = _open_osfhandle ((long) h, O_RDWR);
+  if (scb->fd < 0)
+    {
+      errno = ENOENT;
+      return -1;
+    }
+
+  if (!SetCommMask (h, EV_RXCHAR))
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  timeouts.ReadIntervalTimeout = MAXDWORD;
+  timeouts.ReadTotalTimeoutConstant = 0;
+  timeouts.ReadTotalTimeoutMultiplier = 0;
+  timeouts.WriteTotalTimeoutConstant = 0;
+  timeouts.WriteTotalTimeoutMultiplier = 0;
+  if (!SetCommTimeouts (h, &timeouts))
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  state = xmalloc (sizeof (struct ser_windows_state));
+  memset (state, 0, sizeof (struct ser_windows_state));
+  scb->state = state;
+
+  /* Create a manual reset event to watch the input buffer.  */
+  state->ov.hEvent = CreateEvent (0, TRUE, FALSE, 0);
+
+  /* Create a (currently unused) handle to record exceptions.  */
+  state->except_event = CreateEvent (0, TRUE, FALSE, 0);
+
+  return 0;
+}
+
+/* Wait for the output to drain away, as opposed to flushing (discarding)
+   it.  */
+
+static int
+ser_windows_drain_output (struct serial *scb)
+{
+  HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+
+  return (FlushFileBuffers (h) != 0) ? 0 : -1;
+}
+
+static int
+ser_windows_flush_output (struct serial *scb)
+{
+  HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+
+  return (PurgeComm (h, PURGE_TXCLEAR) != 0) ? 0 : -1;
+}
+
+static int
+ser_windows_flush_input (struct serial *scb)
+{
+  HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+
+  return (PurgeComm (h, PURGE_RXCLEAR) != 0) ? 0 : -1;
+}
+
+static int
+ser_windows_send_break (struct serial *scb)
+{
+  HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+
+  if (SetCommBreak (h) == 0)
+    return -1;
+
+  /* Delay for 250 milliseconds.  */
+  Sleep (250);
+
+  if (ClearCommBreak (h))
+    return -1;
+
+  return 0;
+}
+
+static void
+ser_windows_raw (struct serial *scb)
+{
+  HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+  DCB state;
+
+  if (GetCommState (h, &state) == 0)
+    return;
+
+  state.fParity = FALSE;
+  state.fOutxCtsFlow = FALSE;
+  state.fOutxDsrFlow = FALSE;
+  state.fDtrControl = DTR_CONTROL_ENABLE;
+  state.fDsrSensitivity = FALSE;
+  state.fOutX = FALSE;
+  state.fInX = FALSE;
+  state.fNull = FALSE;
+  state.fAbortOnError = FALSE;
+  state.ByteSize = 8;
+  state.Parity = NOPARITY;
+
+  scb->current_timeout = 0;
+
+  if (SetCommState (h, &state) == 0)
+    warning (_("SetCommState failed\n"));
+}
+
+static int
+ser_windows_setstopbits (struct serial *scb, int num)
+{
+  HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+  DCB state;
+
+  if (GetCommState (h, &state) == 0)
+    return -1;
+
+  switch (num)
+    {
+    case SERIAL_1_STOPBITS:
+      state.StopBits = ONESTOPBIT;
+      break;
+    case SERIAL_1_AND_A_HALF_STOPBITS:
+      state.StopBits = ONE5STOPBITS;
+      break;
+    case SERIAL_2_STOPBITS:
+      state.StopBits = TWOSTOPBITS;
+      break;
+    default:
+      return 1;
+    }
+
+  return (SetCommState (h, &state) != 0) ? 0 : -1;
+}
+
+static int
+ser_windows_setbaudrate (struct serial *scb, int rate)
+{
+  HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+  DCB state;
+
+  if (GetCommState (h, &state) == 0)
+    return -1;
+
+  state.BaudRate = rate;
+
+  return (SetCommState (h, &state) != 0) ? 0 : -1;
+}
+
+static void
+ser_windows_close (struct serial *scb)
+{
+  struct ser_windows_state *state;
+
+  /* Stop any pending selects.  */
+  CancelIo ((HANDLE) _get_osfhandle (scb->fd));
+  state = scb->state;
+  CloseHandle (state->ov.hEvent);
+  CloseHandle (state->except_event);
+
+  if (scb->fd < 0)
+    return;
+
+  close (scb->fd);
+  scb->fd = -1;
+
+  xfree (scb->state);
+}
+
+static void
+ser_windows_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except)
+{
+  struct ser_windows_state *state;
+  COMSTAT status;
+  DWORD errors;
+  HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+
+  state = scb->state;
+
+  *except = state->except_event;
+  *read = state->ov.hEvent;
+
+  if (state->in_progress)
+    return;
+
+  /* Reset the mask - we are only interested in any characters which
+     arrive after this point, not characters which might have arrived
+     and already been read.  */
+
+  /* This really, really shouldn't be necessary - just the second one.
+     But otherwise an internal flag for EV_RXCHAR does not get
+     cleared, and we get a duplicated event, if the last batch
+     of characters included at least two arriving close together.  */
+  if (!SetCommMask (h, 0))
+    warning (_("ser_windows_wait_handle: reseting mask failed"));
+
+  if (!SetCommMask (h, EV_RXCHAR))
+    warning (_("ser_windows_wait_handle: reseting mask failed (2)"));
+
+  /* There's a potential race condition here; we must check cbInQue
+     and not wait if that's nonzero.  */
+
+  ClearCommError (h, &errors, &status);
+  if (status.cbInQue > 0)
+    {
+      SetEvent (state->ov.hEvent);
+      return;
+    }
+
+  state->in_progress = 1;
+  ResetEvent (state->ov.hEvent);
+  state->lastCommMask = -2;
+  if (WaitCommEvent (h, &state->lastCommMask, &state->ov))
+    {
+      gdb_assert (state->lastCommMask & EV_RXCHAR);
+      SetEvent (state->ov.hEvent);
+    }
+  else
+    gdb_assert (GetLastError () == ERROR_IO_PENDING);
+}
+
+static int
+ser_windows_read_prim (struct serial *scb, size_t count)
+{
+  struct ser_windows_state *state;
+  OVERLAPPED ov;
+  DWORD bytes_read, bytes_read_tmp;
+  HANDLE h;
+  gdb_byte *p;
+
+  state = scb->state;
+  if (state->in_progress)
+    {
+      WaitForSingleObject (state->ov.hEvent, INFINITE);
+      state->in_progress = 0;
+      ResetEvent (state->ov.hEvent);
+    }
+
+  memset (&ov, 0, sizeof (OVERLAPPED));
+  ov.hEvent = CreateEvent (0, FALSE, FALSE, 0);
+  h = (HANDLE) _get_osfhandle (scb->fd);
+
+  if (!ReadFile (h, scb->buf, /* count */ 1, &bytes_read, &ov))
+    {
+      if (GetLastError () != ERROR_IO_PENDING
+  || !GetOverlappedResult (h, &ov, &bytes_read, TRUE))
+ bytes_read = -1;
+    }
+
+  CloseHandle (ov.hEvent);
+  return bytes_read;
+}
+
+static int
+ser_windows_write_prim (struct serial *scb, const void *buf, size_t len)
+{
+  struct ser_windows_state *state;
+  OVERLAPPED ov;
+  DWORD bytes_written;
+  HANDLE h;
+
+  memset (&ov, 0, sizeof (OVERLAPPED));
+  ov.hEvent = CreateEvent (0, FALSE, FALSE, 0);
+  h = (HANDLE) _get_osfhandle (scb->fd);
+  if (!WriteFile (h, buf, len, &bytes_written, &ov))
+    {
+      if (GetLastError () != ERROR_IO_PENDING
+  || !GetOverlappedResult (h, &ov, &bytes_written, TRUE))
+ bytes_written = -1;
+    }
+
+  CloseHandle (ov.hEvent);
+  return bytes_written;
+}
+
+struct ser_console_state
+{
+  HANDLE read_event;
+  HANDLE except_event;
+
+  HANDLE start_select;
+  HANDLE stop_select;
+};
+
+static DWORD WINAPI
+console_select_thread (void *arg)
+{
+  struct serial *scb = arg;
+  struct ser_console_state *state, state_copy;
+  int event_index, fd;
+  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);
+
+  while (1)
+    {
+      HANDLE wait_events[2];
+      INPUT_RECORD record;
+      DWORD n_records;
+
+      wait_events[0] = state->start_select;
+      wait_events[1] = state->stop_select;
+
+      if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0)
+ {
+  CloseHandle (state->stop_select);
+  return 0;
+ }
+
+    retry:
+      wait_events[0] = state->stop_select;
+      wait_events[1] = h;
+
+      event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE);
+
+      if (event_index == WAIT_OBJECT_0
+  || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0)
+ {
+  CloseHandle (state->stop_select);
+  return 0;
+ }
+
+      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;
+ }
+
+      /* 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 (record.EventType == KEY_EVENT && record.Event.KeyEvent.bKeyDown)
+ {
+  /* This is really a keypress.  */
+  SetEvent (state->read_event);
+  continue;
+ }
+
+      /* Otherwise discard it and wait again.  */
+      ReadConsoleInput (h, &record, 1, &n_records);
+      goto retry;
+    }
+}
+
+static int
+fd_is_pipe (int fd)
+{
+  if (PeekNamedPipe ((HANDLE) _get_osfhandle (fd), NULL, 0, NULL, NULL, NULL))
+    return 1;
+  else
+    return 0;
+}
+
+static DWORD WINAPI
+pipe_select_thread (void *arg)
+{
+  struct serial *scb = arg;
+  struct ser_console_state *state, state_copy;
+  int event_index, fd;
+  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);
+
+  while (1)
+    {
+      HANDLE wait_events[2];
+      DWORD n_avail;
+
+      wait_events[0] = state->start_select;
+      wait_events[1] = state->stop_select;
+
+      if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0)
+ {
+  CloseHandle (state->stop_select);
+  return 0;
+ }
+
+    retry:
+      if (!PeekNamedPipe (h, NULL, 0, NULL, &n_avail, NULL))
+ {
+  SetEvent (state->except_event);
+  continue;
+ }
+
+      if (n_avail > 0)
+ {
+  SetEvent (state->read_event);
+  continue;
+ }
+
+      if (WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0)
+ {
+  CloseHandle (state->stop_select);
+  return 0;
+ }
+
+      Sleep (10);
+      goto retry;
+    }
+}
+
+static void
+ser_console_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except)
+{
+  struct ser_console_state *state = scb->state;
+
+  if (state == NULL)
+    {
+      DWORD threadId;
+      int is_tty;
+
+      is_tty = isatty (scb->fd);
+      if (!is_tty && !fd_is_pipe (scb->fd))
+ {
+  *read = NULL;
+  *except = NULL;
+  return;
+ }
+
+      state = xmalloc (sizeof (struct ser_console_state));
+      memset (state, 0, sizeof (struct ser_console_state));
+      scb->state = state;
+
+      /* Create auto reset events to wake and terminate the select thread.  */
+      state->start_select = CreateEvent (0, FALSE, FALSE, 0);
+      state->stop_select = CreateEvent (0, FALSE, FALSE, 0);
+
+      /* Create our own events to report read and exceptions separately.
+ The exception event is currently never used.  */
+      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);
+      else
+ CreateThread (NULL, 0, pipe_select_thread, scb, 0, &threadId);
+    }
+
+  ResetEvent (state->read_event);
+  ResetEvent (state->except_event);
+
+  SetEvent (state->start_select);
+
+  *read = state->read_event;
+  *except = state->except_event;
+}
+
+static void
+ser_console_close (struct serial *scb)
+{
+  struct ser_console_state *state = scb->state;
+
+  if (scb->state)
+    {
+      SetEvent (state->stop_select);
+
+      CloseHandle (state->read_event);
+      CloseHandle (state->except_event);
+
+      xfree (scb->state);
+    }
+}
+
+struct ser_console_ttystate
+{
+  int is_a_tty;
+};
+
+static serial_ttystate
+ser_console_get_tty_state (struct serial *scb)
+{
+  if (isatty (scb->fd))
+    {
+      struct ser_console_ttystate *state;
+      state = (struct ser_console_ttystate *) xmalloc (sizeof *state);
+      state->is_a_tty = 1;
+      return state;
+    }
+  else
+    return NULL;
+}
+
+struct net_windows_state
+{
+  HANDLE read_event;
+  HANDLE except_event;
+
+  HANDLE start_select;
+  HANDLE stop_select;
+  HANDLE sock_event;
+};
+
+static DWORD WINAPI
+net_windows_select_thread (void *arg)
+{
+  struct serial *scb = arg;
+  struct net_windows_state *state, state_copy;
+  int event_index, fd;
+
+  /* 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;
+
+  while (1)
+    {
+      HANDLE wait_events[2];
+      WSANETWORKEVENTS events;
+
+      wait_events[0] = state->start_select;
+      wait_events[1] = state->stop_select;
+
+      if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0)
+ {
+  CloseHandle (state->stop_select);
+  return 0;
+ }
+
+      wait_events[0] = state->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)
+ {
+  CloseHandle (state->stop_select);
+  return 0;
+ }
+
+      if (event_index != WAIT_OBJECT_0 + 1)
+ {
+  /* 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 (fd, state->sock_event, &events);
+
+      if (events.lNetworkEvents & FD_READ)
+ SetEvent (state->read_event);
+
+      if (events.lNetworkEvents & FD_CLOSE)
+ SetEvent (state->except_event);
+    }
+}
+
+static void
+net_windows_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except)
+{
+  struct net_windows_state *state = scb->state;
+
+  ResetEvent (state->read_event);
+  ResetEvent (state->except_event);
+
+  SetEvent (state->start_select);
+
+  *read = state->read_event;
+  *except = state->except_event;
+}
+
+static int
+net_windows_open (struct serial *scb, const char *name)
+{
+  struct net_windows_state *state;
+  int ret;
+  DWORD threadId;
+
+  ret = net_open (scb, name);
+  if (ret != 0)
+    return ret;
+
+  state = xmalloc (sizeof (struct net_windows_state));
+  memset (state, 0, sizeof (struct net_windows_state));
+  scb->state = state;
+
+  /* Create auto reset events to wake and terminate the select thread.  */
+  state->start_select = CreateEvent (0, FALSE, FALSE, 0);
+  state->stop_select = CreateEvent (0, FALSE, 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.  */
+  CreateThread (NULL, 0, net_windows_select_thread, scb, 0, &threadId);
+
+  return 0;
+}
+
+
+static void
+net_windows_close (struct serial *scb)
+{
+  struct net_windows_state *state = scb->state;
+
+  SetEvent (state->stop_select);
+
+  CloseHandle (state->read_event);
+  CloseHandle (state->except_event);
+  CloseHandle (state->start_select);
+  CloseHandle (state->sock_event);
+
+  xfree (scb->state);
+
+  net_close (scb);
+}
+
+void
+_initialize_ser_windows (void)
+{
+  WSADATA wsa_data;
+  struct serial_ops *ops;
+
+  /* First register the serial port driver.  */
+
+  ops = XMALLOC (struct serial_ops);
+  memset (ops, 0, sizeof (struct serial_ops));
+  ops->name = "hardwire";
+  ops->next = 0;
+  ops->open = ser_windows_open;
+  ops->close = ser_windows_close;
+
+  ops->flush_output = ser_windows_flush_output;
+  ops->flush_input = ser_windows_flush_input;
+  ops->send_break = ser_windows_send_break;
+
+  /* These are only used for stdin; we do not need them for serial
+     ports, so supply the standard dummies.  */
+  ops->get_tty_state = ser_base_get_tty_state;
+  ops->set_tty_state = ser_base_set_tty_state;
+  ops->print_tty_state = ser_base_print_tty_state;
+  ops->noflush_set_tty_state = ser_base_noflush_set_tty_state;
+
+  ops->go_raw = ser_windows_raw;
+  ops->setbaudrate = ser_windows_setbaudrate;
+  ops->setstopbits = ser_windows_setstopbits;
+  ops->drain_output = ser_windows_drain_output;
+  ops->readchar = ser_base_readchar;
+  ops->write = ser_base_write;
+  ops->async = ser_base_async;
+  ops->read_prim = ser_windows_read_prim;
+  ops->write_prim = ser_windows_write_prim;
+  ops->wait_handle = ser_windows_wait_handle;
+
+  serial_add_interface (ops);
+
+  /* Next create the dummy serial driver used for terminals.  We only
+     provide the TTY-related methods.  */
+
+  ops = XMALLOC (struct serial_ops);
+  memset (ops, 0, sizeof (struct serial_ops));
+
+  ops->name = "terminal";
+  ops->next = 0;
+
+  ops->close = ser_console_close;
+  ops->get_tty_state = ser_console_get_tty_state;
+  ops->set_tty_state = ser_base_set_tty_state;
+  ops->print_tty_state = ser_base_print_tty_state;
+  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;
+
+  serial_add_interface (ops);
+
+  /* If WinSock works, register the TCP/UDP socket driver.  */
+
+  if (WSAStartup (MAKEWORD (1, 0), &wsa_data) != 0)
+    /* WinSock is unavailable.  */
+    return;
+
+  ops = XMALLOC (struct serial_ops);
+  memset (ops, 0, sizeof (struct serial_ops));
+  ops->name = "tcp";
+  ops->next = 0;
+  ops->open = net_windows_open;
+  ops->close = net_windows_close;
+  ops->readchar = ser_base_readchar;
+  ops->write = ser_base_write;
+  ops->flush_output = ser_base_flush_output;
+  ops->flush_input = ser_base_flush_input;
+  ops->send_break = ser_base_send_break;
+  ops->go_raw = ser_base_raw;
+  ops->get_tty_state = ser_base_get_tty_state;
+  ops->set_tty_state = ser_base_set_tty_state;
+  ops->print_tty_state = ser_base_print_tty_state;
+  ops->noflush_set_tty_state = ser_base_noflush_set_tty_state;
+  ops->setbaudrate = ser_base_setbaudrate;
+  ops->setstopbits = ser_base_setstopbits;
+  ops->drain_output = ser_base_drain_output;
+  ops->async = ser_base_async;
+  ops->read_prim = net_read_prim;
+  ops->write_prim = net_write_prim;
+  ops->wait_handle = net_windows_wait_handle;
+  serial_add_interface (ops);
+}
Index: src/gdb/config/i386/xm-mingw.h
===================================================================
--- src.orig/gdb/config/i386/xm-mingw.h 2006-02-06 15:23:13.000000000 -0500
+++ src/gdb/config/i386/xm-mingw.h 2006-02-06 15:45:37.000000000 -0500
@@ -28,3 +28,4 @@ char *mingw_strerror (int);
 # define strerror mingw_strerror
 #endif
 
+int mingw_select (int, void *, void *, void *, void *);
Index: src/gdb/mingw-hdep.c
===================================================================
--- src.orig/gdb/mingw-hdep.c 2006-02-06 15:22:52.000000000 -0500
+++ src/gdb/mingw-hdep.c 2006-02-06 15:50:58.000000000 -0500
@@ -25,7 +25,10 @@
 #define IN_MINGW_HDEP
 
 #include "defs.h"
+#include "serial.h"
 
+#include "gdb_assert.h"
+#include "gdb_select.h"
 #include "gdb_string.h"
 
 #include <windows.h>
@@ -65,3 +68,120 @@ mingw_strerror (int errnum)
 
   return buffer;
 }
+
+/* Wrapper for select.  On Windows systems, where the select interface
+   only works for sockets, this uses the GDB serial abstraction to
+   handle sockets, consoles, pipes, and serial ports.
+
+   The arguments to this function are the same as the traditional
+   arguments to select; the void * usage is necessary to avoid
+   including <windows.h> in order to prototype this function.  */
+
+int
+mingw_select (int n, void *readfds_, void *writefds_, void *exceptfds_,
+      void *timeout_)
+{
+  static HANDLE never_handle;
+  HANDLE handles[MAXIMUM_WAIT_OBJECTS];
+  HANDLE h;
+  DWORD event;
+  DWORD num_handles;
+  int fd;
+  int num_ready;
+  int indx;
+  struct timeval *timeout = timeout_;
+  fd_set *readfds = readfds_, *writefds = writefds_, *exceptfds = exceptfds_;
+
+  num_ready = 0;
+  num_handles = 0;
+  for (fd = 0; fd < n; ++fd)
+    {
+      HANDLE read = NULL, except = NULL;
+      struct serial *scb;
+
+      /* There is no support yet for WRITEFDS.  At present, this isn't
+ used by GDB -- but we do not want to silently ignore WRITEFDS
+ if something starts using it.  */
+      gdb_assert (!writefds || !FD_ISSET (fd, writefds));
+
+      if (!FD_ISSET (fd, readfds)
+  && !FD_ISSET (fd, exceptfds))
+ continue;
+      h = (HANDLE) _get_osfhandle (fd);
+
+      scb = serial_for_fd (fd);
+      if (scb)
+ serial_wait_handle (scb, &read, &except);
+
+      if (read == NULL)
+ read = h;
+      if (except == NULL)
+ {
+  if (!never_handle)
+    never_handle = CreateEvent (0, FALSE, FALSE, 0);
+
+  except = never_handle;
+ }
+
+      if (FD_ISSET (fd, readfds))
+ {
+  gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS);
+  handles[num_handles++] = read;
+ }
+
+      if (FD_ISSET (fd, exceptfds))
+ {
+  gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS);
+  handles[num_handles++] = except;
+ }
+    }
+  /* If we don't need to wait for any handles, we are done.  */
+  if (!num_handles)
+    return 0;
+  event = WaitForMultipleObjects (num_handles,
+  handles,
+  FALSE,
+  timeout
+  ? (timeout->tv_sec * 1000 + timeout->tv_usec)
+  : INFINITE);
+  /* EVENT can only be a value in the WAIT_ABANDONED_0 range if the
+     HANDLES included an abandoned mutex.  Since GDB doesn't use
+     mutexes, that should never occur.  */
+  gdb_assert (!(WAIT_ABANDONED_0 <= event
+ && event < WAIT_ABANDONED_0 + num_handles));
+  if (event == WAIT_FAILED)
+    return -1;
+  if (event == WAIT_TIMEOUT)
+    return 0;
+  /* Run through the READFDS, clearing bits corresponding to descriptors
+     for which input is unavailable.  */
+  h = handles[event - WAIT_OBJECT_0];
+  for (fd = 0, indx = 0; fd < n; ++fd)
+    {
+      HANDLE fd_h;
+
+      if (FD_ISSET (fd, readfds))
+ {
+  fd_h = handles[indx++];
+  /* This handle might be ready, even though it wasn't the handle
+     returned by WaitForMultipleObjects.  */
+  if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0)
+    FD_CLR (fd, readfds);
+  else
+    num_ready++;
+ }
+
+      if (FD_ISSET (fd, exceptfds))
+ {
+  fd_h = handles[indx++];
+  /* This handle might be ready, even though it wasn't the handle
+     returned by WaitForMultipleObjects.  */
+  if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0)
+    FD_CLR (fd, exceptfds);
+  else
+    num_ready++;
+ }
+    }
+
+  return num_ready;
+}
Reply | Threaded
Open this post in threaded view
|

Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

Mark Kettenis
> Date: Mon, 6 Feb 2006 16:02:38 -0500
> From: Daniel Jacobowitz <[hidden email]>
>
> On Fri, Feb 03, 2006 at 05:05:29PM -0500, Daniel Jacobowitz wrote:
> > Fixes, all for a mingw32-hosted GDB:
>
> Here is a revised version, in which the Windows select magic does not
> live in event-loop.c, and including the NEWS entry.  I haven't changed
> the pipe polling code; thanks to Ian for his suggestions, but I'm
> not sufficiently sure they're workable.
>
> Are there any bits of this patch that strike you as too ugly or in the
> wrong place?

Hmm, can't mingw_select() just be called select()?  That'd remove much
of the ugliness.

Mark
Reply | Threaded
Open this post in threaded view
|

Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports

Daniel Jacobowitz-2
On Tue, Feb 07, 2006 at 12:01:43AM +0100, Mark Kettenis wrote:

> > Date: Mon, 6 Feb 2006 16:02:38 -0500
> > From: Daniel Jacobowitz <[hidden email]>
> >
> > On Fri, Feb 03, 2006 at 05:05:29PM -0500, Daniel Jacobowitz wrote:
> > > Fixes, all for a mingw32-hosted GDB:
> >
> > Here is a revised version, in which the Windows select magic does not
> > live in event-loop.c, and including the NEWS entry.  I haven't changed
> > the pipe polling code; thanks to Ian for his suggestions, but I'm
> > not sufficiently sure they're workable.
> >
> > Are there any bits of this patch that strike you as too ugly or in the
> > wrong place?
>
> Hmm, can't mingw_select() just be called select()?  That'd remove much
> of the ugliness.

No, it can't.  Two reasons.  One is the same reason I mentioned for
strerror: with __attribute__((dllimport)) in the headers, GCC will
generate a load from the import table at call sites, and overriding the
function won't work.  MinGW doesn't use this today, if compiling with
GCC, but from the bits in the header files, it looks like they either
did in the past or want to in the future.  And I think they do use the
equivalent when compiling with MSVC.  No, I haven't tried building
GDB with MSVC.

Another reason is that I don't want to intercept all calls to select.
First of all, because select() is a useful function on Windows (works
on sockets only, but does work there) and it would be a valid way
to implement mingw_select for sockets - I nearly did that.  And second
because we call select, the socket version, explicitly from ser-tcp.c
before the connection and associated serial device are completely set
up.

If I could get around #1, I would look into handling #2, but it seems
likely to be more confusing than helpful, to me.

It wouldn't get rid of too much of the ugliness either, I'm afraid :-(
For instance you'd still need gdb_select.h; if you want fd_set on
Windows, you need <windows.h> or <winsock2.h>.  No <sys/select.h> to be
found.

--
Daniel Jacobowitz
CodeSourcery
12