X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gdb%2Fser-mingw.c;h=99612dfc6f8565fdc1b6d9dea17fae0ee0f5bab2;hb=efdb2a86e46122879fab082916287486d5208b41;hp=7a6f232379ec78a6894c405a7892a2230a0295b8;hpb=0ea3f30e219bd42259f09f68bcd605bf4ed4a1ea;p=binutils-gdb.git diff --git a/gdb/ser-mingw.c b/gdb/ser-mingw.c index 7a6f232379e..99612dfc6f8 100644 --- a/gdb/ser-mingw.c +++ b/gdb/ser-mingw.c @@ -1,13 +1,12 @@ /* Serial interface for local (hardwired) serial ports on Windows systems - Copyright (C) 2006 - Free Software Foundation, Inc. + Copyright (C) 2006, 2007, 2008 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 + the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -16,9 +15,7 @@ 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. */ + along with this program. If not, see . */ #include "defs.h" #include "serial.h" @@ -26,6 +23,7 @@ #include "ser-tcp.h" #include +#include #include #include @@ -34,6 +32,8 @@ #include "gdb_assert.h" #include "gdb_string.h" +#include "command.h" + void _initialize_ser_windows (void); struct ser_windows_state @@ -53,13 +53,6 @@ ser_windows_open (struct serial *scb, const char *name) 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) @@ -343,30 +336,172 @@ ser_windows_write_prim (struct serial *scb, const void *buf, size_t len) return bytes_written; } +/* On Windows, gdb_select is implemented using WaitForMulpleObjects. + A "select thread" is created for each file descriptor. These + threads looks for activity on the corresponding descriptor, using + whatever techniques are appropriate for the descriptor type. When + that activity occurs, the thread signals an appropriate event, + which wakes up WaitForMultipleObjects. + + Each select thread is in one of two states: stopped or started. + Select threads begin in the stopped state. When gdb_select is + called, threads corresponding to the descriptors of interest are + started by calling a wait_handle function. Each thread that + notices activity signals the appropriate event and then reenters + the stopped state. Before gdb_select returns it calls the + wait_handle_done functions, which return the threads to the stopped + state. */ + +enum select_thread_state { + STS_STARTED, + STS_STOPPED +}; + struct ser_console_state { + /* Signaled by the select thread to indicate that data is available + on the file descriptor. */ HANDLE read_event; + /* Signaled by the select thread to indicate that an exception has + occurred on the file descriptor. */ HANDLE except_event; - + /* Signaled by the select thread to indicate that it has entered the + started state. HAVE_STARTED and HAVE_STOPPED are never signaled + simultaneously. */ + HANDLE have_started; + /* Signaled by the select thread to indicate that it has stopped, + either because data is available (and READ_EVENT is signaled), + because an exception has occurred (and EXCEPT_EVENT is signaled), + or because STOP_SELECT was signaled. */ + HANDLE have_stopped; + + /* Signaled by the main program to tell the select thread to enter + the started state. */ HANDLE start_select; + /* Signaled by the main program to tell the select thread to enter + the stopped state. */ HANDLE stop_select; + /* Signaled by the main program to tell the select thread to + exit. */ + HANDLE exit_select; + + /* The handle for the select thread. */ + HANDLE thread; + /* The state of the select thread. This field is only accessed in + the main program, never by the select thread itself. */ + enum select_thread_state thread_state; }; +/* Called by a select thread to enter the stopped state. This + function does not return until the thread has re-entered the + started state. */ +static void +select_thread_wait (struct ser_console_state *state) +{ + HANDLE wait_events[2]; + + /* There are two things that can wake us up: a request that we enter + the started state, or that we exit this thread. */ + wait_events[0] = state->start_select; + wait_events[1] = state->exit_select; + if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) + != WAIT_OBJECT_0) + /* Either the EXIT_SELECT event was signaled (requesting that the + thread exit) or an error has occurred. In either case, we exit + the thread. */ + ExitThread (0); + + /* We are now in the started state. */ + SetEvent (state->have_started); +} + +typedef DWORD WINAPI (*thread_fn_type)(void *); + +/* Create a new select thread for SCB executing THREAD_FN. The STATE + will be filled in by this function before return. */ +void +create_select_thread (thread_fn_type thread_fn, + struct serial *scb, + struct ser_console_state *state) +{ + DWORD threadId; + + /* Create all of the events. These are all auto-reset events. */ + state->read_event = CreateEvent (NULL, FALSE, FALSE, NULL); + state->except_event = CreateEvent (NULL, FALSE, FALSE, NULL); + state->have_started = CreateEvent (NULL, FALSE, FALSE, NULL); + state->have_stopped = CreateEvent (NULL, FALSE, FALSE, NULL); + state->start_select = CreateEvent (NULL, FALSE, FALSE, NULL); + state->stop_select = CreateEvent (NULL, FALSE, FALSE, NULL); + state->exit_select = CreateEvent (NULL, FALSE, FALSE, NULL); + + state->thread = CreateThread (NULL, 0, thread_fn, scb, 0, &threadId); + /* The thread begins in the stopped state. */ + state->thread_state = STS_STOPPED; +} + +/* Destroy the select thread indicated by STATE. */ +static void +destroy_select_thread (struct ser_console_state *state) +{ + /* Ask the thread to exit. */ + SetEvent (state->exit_select); + /* Wait until it does. */ + WaitForSingleObject (state->thread, INFINITE); + + /* Destroy the events. */ + CloseHandle (state->read_event); + CloseHandle (state->except_event); + CloseHandle (state->have_started); + CloseHandle (state->have_stopped); + CloseHandle (state->start_select); + CloseHandle (state->stop_select); + CloseHandle (state->exit_select); +} + +/* Called by gdb_select to start the select thread indicated by STATE. + This function does not return until the thread has started. */ +static void +start_select_thread (struct ser_console_state *state) +{ + /* Ask the thread to start. */ + SetEvent (state->start_select); + /* Wait until it does. */ + WaitForSingleObject (state->have_started, INFINITE); + /* The thread is now started. */ + state->thread_state = STS_STARTED; +} + +/* Called by gdb_select to stop the select thread indicated by STATE. + This function does not return until the thread has stopped. */ +static void +stop_select_thread (struct ser_console_state *state) +{ + /* If the thread is already in the stopped state, we have nothing to + do. Some of the wait_handle functions avoid calling + start_select_thread if they notice activity on the relevant file + descriptors. The wait_handle_done functions still call + stop_select_thread -- but it is already stopped. */ + if (state->thread_state != STS_STARTED) + return; + /* Ask the thread to stop. */ + SetEvent (state->stop_select); + /* Wait until it does. */ + WaitForSingleObject (state->have_stopped, INFINITE); + /* The thread is now stopped. */ + state->thread_state = STS_STOPPED; +} + static DWORD WINAPI console_select_thread (void *arg) { struct serial *scb = arg; - struct ser_console_state *state, state_copy; - int event_index, fd; + struct ser_console_state *state; + int event_index; HANDLE h; - /* Copy useful information out of the control block, to make sure - that we do not race with freeing it. */ - state_copy = *(struct ser_console_state *) scb->state; - state = &state_copy; - fd = scb->fd; - - h = (HANDLE) _get_osfhandle (fd); + state = scb->state; + h = (HANDLE) _get_osfhandle (scb->fd); while (1) { @@ -374,55 +509,69 @@ console_select_thread (void *arg) INPUT_RECORD record; DWORD n_records; - wait_events[0] = state->start_select; - wait_events[1] = state->stop_select; + select_thread_wait (state); - if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0) + while (1) { - CloseHandle (state->stop_select); - return 0; + 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) + break; + + if (event_index != WAIT_OBJECT_0 + 1) + { + /* Wait must have failed; assume an error has occured, e.g. + the handle has been closed. */ + SetEvent (state->except_event); + break; + } + + /* We've got a pending event on the console. See if it's + of interest. */ + if (!PeekConsoleInput (h, &record, 1, &n_records) || n_records != 1) + { + /* Something went wrong. Maybe the console is gone. */ + SetEvent (state->except_event); + break; + } + + if (record.EventType == KEY_EVENT && record.Event.KeyEvent.bKeyDown) + { + WORD keycode = record.Event.KeyEvent.wVirtualKeyCode; + + /* Ignore events containing only control keys. We must + recognize "enhanced" keys which we are interested in + reading via getch, if they do not map to ASCII. But we + do not want to report input available for e.g. the + control key alone. */ + + if (record.Event.KeyEvent.uChar.AsciiChar != 0 + || keycode == VK_PRIOR + || keycode == VK_NEXT + || keycode == VK_END + || keycode == VK_HOME + || keycode == VK_LEFT + || keycode == VK_UP + || keycode == VK_RIGHT + || keycode == VK_DOWN + || keycode == VK_INSERT + || keycode == VK_DELETE) + { + /* This is really a keypress. */ + SetEvent (state->read_event); + break; + } + } + + /* Otherwise discard it and wait again. */ + ReadConsoleInput (h, &record, 1, &n_records); } - 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; + SetEvent(state->have_stopped); } } @@ -435,57 +584,78 @@ fd_is_pipe (int fd) return 0; } +static int +fd_is_file (int fd) +{ + if (GetFileType ((HANDLE) _get_osfhandle (fd)) == FILE_TYPE_DISK) + 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; + struct ser_console_state *state; + int event_index; HANDLE h; - /* Copy useful information out of the control block, to make sure - that we do not race with freeing it. */ - state_copy = *(struct ser_console_state *) scb->state; - state = &state_copy; - fd = scb->fd; - - h = (HANDLE) _get_osfhandle (fd); + state = scb->state; + h = (HANDLE) _get_osfhandle (scb->fd); while (1) { - HANDLE wait_events[2]; DWORD n_avail; - wait_events[0] = state->start_select; - wait_events[1] = state->stop_select; + select_thread_wait (state); - if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0) + /* Wait for something to happen on the pipe. */ + while (1) { - CloseHandle (state->stop_select); - return 0; + if (!PeekNamedPipe (h, NULL, 0, NULL, &n_avail, NULL)) + { + SetEvent (state->except_event); + break; + } + + if (n_avail > 0) + { + SetEvent (state->read_event); + break; + } + + /* Delay 10ms before checking again, but allow the stop + event to wake us. */ + if (WaitForSingleObject (state->stop_select, 10) == WAIT_OBJECT_0) + break; } - retry: - if (!PeekNamedPipe (h, NULL, 0, NULL, &n_avail, NULL)) - { - SetEvent (state->except_event); - continue; - } + SetEvent (state->have_stopped); + } +} - if (n_avail > 0) - { - SetEvent (state->read_event); - continue; - } +static DWORD WINAPI +file_select_thread (void *arg) +{ + struct serial *scb = arg; + struct ser_console_state *state; + int event_index; + HANDLE h; - if (WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0) - { - CloseHandle (state->stop_select); - return 0; - } + state = scb->state; + h = (HANDLE) _get_osfhandle (scb->fd); - Sleep (10); - goto retry; + while (1) + { + select_thread_wait (state); + + if (SetFilePointer (h, 0, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER) + SetEvent (state->except_event); + else + SetEvent (state->read_event); + + SetEvent (state->have_stopped); } } @@ -496,11 +666,11 @@ ser_console_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) if (state == NULL) { - DWORD threadId; + thread_fn_type thread_fn; int is_tty; is_tty = isatty (scb->fd); - if (!is_tty && !fd_is_pipe (scb->fd)) + if (!is_tty && !fd_is_file (scb->fd) && !fd_is_pipe (scb->fd)) { *read = NULL; *except = NULL; @@ -511,29 +681,48 @@ ser_console_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) memset (state, 0, sizeof (struct ser_console_state)); scb->state = state; - /* Create auto reset events to wake and terminate the select thread. */ - 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); + thread_fn = console_select_thread; + else if (fd_is_pipe (scb->fd)) + thread_fn = pipe_select_thread; else - CreateThread (NULL, 0, pipe_select_thread, scb, 0, &threadId); + thread_fn = file_select_thread; + + create_select_thread (thread_fn, scb, state); } + *read = state->read_event; + *except = state->except_event; + + /* Start from a blank state. */ ResetEvent (state->read_event); ResetEvent (state->except_event); + ResetEvent (state->stop_select); + + /* First check for a key already in the buffer. If there is one, + we don't need a thread. This also catches the second key of + multi-character returns from getch, for instance for arrow + keys. The second half is in a C library internal buffer, + and PeekConsoleInput will not find it. */ + if (_kbhit ()) + { + SetEvent (state->read_event); + return; + } - SetEvent (state->start_select); + /* Otherwise, start the select thread. */ + start_select_thread (state); +} - *read = state->read_event; - *except = state->except_event; +static void +ser_console_done_wait_handle (struct serial *scb) +{ + struct ser_console_state *state = scb->state; + + if (state == NULL) + return; + + stop_select_thread (state); } static void @@ -543,11 +732,7 @@ ser_console_close (struct serial *scb) if (scb->state) { - SetEvent (state->stop_select); - - CloseHandle (state->read_event); - CloseHandle (state->except_event); - + destroy_select_thread (state); xfree (scb->state); } } @@ -571,13 +756,241 @@ ser_console_get_tty_state (struct serial *scb) return NULL; } -struct net_windows_state +struct pipe_state { - HANDLE read_event; - HANDLE except_event; + /* Since we use the pipe_select_thread for our select emulation, + we need to place the state structure it requires at the front + of our state. */ + struct ser_console_state wait; - HANDLE start_select; - HANDLE stop_select; + /* The pex obj for our (one-stage) pipeline. */ + struct pex_obj *pex; + + /* Streams for the pipeline's input and output. */ + FILE *input, *output; +}; + +static struct pipe_state * +make_pipe_state (void) +{ + struct pipe_state *ps = XMALLOC (struct pipe_state); + + memset (ps, 0, sizeof (*ps)); + ps->wait.read_event = INVALID_HANDLE_VALUE; + ps->wait.except_event = INVALID_HANDLE_VALUE; + ps->wait.start_select = INVALID_HANDLE_VALUE; + ps->wait.stop_select = INVALID_HANDLE_VALUE; + + return ps; +} + +static void +free_pipe_state (struct pipe_state *ps) +{ + int saved_errno = errno; + + if (ps->wait.read_event != INVALID_HANDLE_VALUE) + destroy_select_thread (&ps->wait); + + /* Close the pipe to the child. We must close the pipe before + calling pex_free because pex_free will wait for the child to exit + and the child will not exit until the pipe is closed. */ + if (ps->input) + fclose (ps->input); + if (ps->pex) + pex_free (ps->pex); + /* pex_free closes ps->output. */ + + xfree (ps); + + errno = saved_errno; +} + +static void +cleanup_pipe_state (void *untyped) +{ + struct pipe_state *ps = untyped; + + free_pipe_state (ps); +} + +static int +pipe_windows_open (struct serial *scb, const char *name) +{ + struct pipe_state *ps; + FILE *pex_stderr; + char **argv; + struct cleanup *back_to; + + if (name == NULL) + error_no_arg (_("child command")); + + argv = gdb_buildargv (name); + back_to = make_cleanup_freeargv (argv); + + if (! argv[0] || argv[0][0] == '\0') + error ("missing child command"); + + ps = make_pipe_state (); + make_cleanup (cleanup_pipe_state, ps); + + ps->pex = pex_init (PEX_USE_PIPES, "target remote pipe", NULL); + if (! ps->pex) + goto fail; + ps->input = pex_input_pipe (ps->pex, 1); + if (! ps->input) + goto fail; + + { + int err; + const char *err_msg + = pex_run (ps->pex, PEX_SEARCH | PEX_BINARY_INPUT | PEX_BINARY_OUTPUT + | PEX_STDERR_TO_PIPE, + argv[0], argv, NULL, NULL, + &err); + + if (err_msg) + { + /* Our caller expects us to return -1, but all they'll do with + it generally is print the message based on errno. We have + all the same information here, plus err_msg provided by + pex_run, so we just raise the error here. */ + if (err) + error ("error starting child process '%s': %s: %s", + name, err_msg, safe_strerror (err)); + else + error ("error starting child process '%s': %s", + name, err_msg); + } + } + + ps->output = pex_read_output (ps->pex, 1); + if (! ps->output) + goto fail; + scb->fd = fileno (ps->output); + + pex_stderr = pex_read_err (ps->pex, 1); + if (! pex_stderr) + goto fail; + scb->error_fd = fileno (pex_stderr); + + scb->state = (void *) ps; + + discard_cleanups (back_to); + return 0; + + fail: + do_cleanups (back_to); + return -1; +} + + +static void +pipe_windows_close (struct serial *scb) +{ + struct pipe_state *ps = scb->state; + + /* In theory, we should try to kill the subprocess here, but the pex + interface doesn't give us enough information to do that. Usually + closing the input pipe will get the message across. */ + + free_pipe_state (ps); +} + + +static int +pipe_windows_read (struct serial *scb, size_t count) +{ + HANDLE pipeline_out = (HANDLE) _get_osfhandle (scb->fd); + DWORD available; + DWORD bytes_read; + + if (pipeline_out == INVALID_HANDLE_VALUE) + return -1; + + if (! PeekNamedPipe (pipeline_out, NULL, 0, NULL, &available, NULL)) + return -1; + + if (count > available) + count = available; + + if (! ReadFile (pipeline_out, scb->buf, count, &bytes_read, NULL)) + return -1; + + return bytes_read; +} + + +static int +pipe_windows_write (struct serial *scb, const void *buf, size_t count) +{ + struct pipe_state *ps = scb->state; + HANDLE pipeline_in; + DWORD written; + + int pipeline_in_fd = fileno (ps->input); + if (pipeline_in_fd < 0) + return -1; + + pipeline_in = (HANDLE) _get_osfhandle (pipeline_in_fd); + if (pipeline_in == INVALID_HANDLE_VALUE) + return -1; + + if (! WriteFile (pipeline_in, buf, count, &written, NULL)) + return -1; + + return written; +} + + +static void +pipe_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) +{ + struct pipe_state *ps = scb->state; + + /* Have we allocated our events yet? */ + if (ps->wait.read_event == INVALID_HANDLE_VALUE) + /* Start the thread. */ + create_select_thread (pipe_select_thread, scb, &ps->wait); + + *read = ps->wait.read_event; + *except = ps->wait.except_event; + + /* Start from a blank state. */ + ResetEvent (ps->wait.read_event); + ResetEvent (ps->wait.except_event); + ResetEvent (ps->wait.stop_select); + + start_select_thread (&ps->wait); +} + +static void +pipe_done_wait_handle (struct serial *scb) +{ + struct pipe_state *ps = scb->state; + + /* Have we allocated our events yet? */ + if (ps->wait.read_event == INVALID_HANDLE_VALUE) + return; + + stop_select_thread (&ps->wait); +} + +static int +pipe_avail (struct serial *scb, int fd) +{ + HANDLE h = (HANDLE) _get_osfhandle (fd); + DWORD numBytes; + BOOL r = PeekNamedPipe (h, NULL, 0, NULL, &numBytes, NULL); + if (r == FALSE) + numBytes = 0; + return numBytes; +} + +struct net_windows_state +{ + struct ser_console_state base; + HANDLE sock_event; }; @@ -585,73 +998,125 @@ static DWORD WINAPI net_windows_select_thread (void *arg) { struct serial *scb = arg; - struct net_windows_state *state, state_copy; - int event_index, fd; + struct net_windows_state *state; + int event_index; - /* Copy useful information out of the control block, to make sure - that we do not race with freeing it. */ - state_copy = *(struct net_windows_state *) scb->state; - state = &state_copy; - fd = scb->fd; + state = scb->state; while (1) { HANDLE wait_events[2]; WSANETWORKEVENTS events; - 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; - } + select_thread_wait (&state->base); - wait_events[0] = state->stop_select; + wait_events[0] = state->base.stop_select; wait_events[1] = state->sock_event; event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE); if (event_index == WAIT_OBJECT_0 - || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0) + || WaitForSingleObject (state->base.stop_select, 0) == WAIT_OBJECT_0) + /* We have been requested to stop. */ + ; + else if (event_index != WAIT_OBJECT_0 + 1) + /* Some error has occured. Assume that this is an error + condition. */ + SetEvent (state->base.except_event); + else { - CloseHandle (state->stop_select); - return 0; + /* Enumerate the internal network events, and reset the + object that signalled us to catch the next event. */ + WSAEnumNetworkEvents (scb->fd, state->sock_event, &events); + + gdb_assert (events.lNetworkEvents & (FD_READ | FD_CLOSE)); + + if (events.lNetworkEvents & FD_READ) + SetEvent (state->base.read_event); + + if (events.lNetworkEvents & FD_CLOSE) + SetEvent (state->base.except_event); } - if (event_index != WAIT_OBJECT_0 + 1) - { - /* Some error has occured. Assume that this is an error - condition. */ - SetEvent (state->except_event); - continue; - } + SetEvent (state->base.have_stopped); + } +} + +static void +net_windows_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) +{ + struct net_windows_state *state = scb->state; + + /* Start from a clean slate. */ + ResetEvent (state->base.read_event); + ResetEvent (state->base.except_event); + ResetEvent (state->base.stop_select); + + *read = state->base.read_event; + *except = state->base.except_event; + + /* Check any pending events. This both avoids starting the thread + unnecessarily, and handles stray FD_READ events (see below). */ + if (WaitForSingleObject (state->sock_event, 0) == WAIT_OBJECT_0) + { + WSANETWORKEVENTS events; + int any = 0; /* Enumerate the internal network events, and reset the object that signalled us to catch the next event. */ - WSAEnumNetworkEvents (fd, state->sock_event, &events); - + WSAEnumNetworkEvents (scb->fd, state->sock_event, &events); + + /* You'd think that FD_READ or FD_CLOSE would be set here. But, + sometimes, neither is. I suspect that the FD_READ is set and + the corresponding event signalled while recv is running, and + the FD_READ is then lowered when recv consumes all the data, + but there's no way to un-signal the event. This isn't a + problem for the call in net_select_thread, since any new + events after this point will not have been drained by recv. + It just means that we can't have the obvious assert here. */ + + /* If there is a read event, it might be still valid, or it might + not be - it may have been signalled before we last called + recv. Double-check that there is data. */ if (events.lNetworkEvents & FD_READ) - SetEvent (state->read_event); + { + unsigned long available; + + if (ioctlsocket (scb->fd, FIONREAD, &available) == 0 + && available > 0) + { + SetEvent (state->base.read_event); + any = 1; + } + else + /* Oops, no data. This call to recv will cause future + data to retrigger the event, e.g. while we are + in net_select_thread. */ + recv (scb->fd, NULL, 0, 0); + } + /* If there's a close event, then record it - it is obviously + still valid, and it will not be resignalled. */ if (events.lNetworkEvents & FD_CLOSE) - SetEvent (state->except_event); + { + SetEvent (state->base.except_event); + any = 1; + } + + /* If we set either handle, there's no need to wake the thread. */ + if (any) + return; } + + start_select_thread (&state->base); } static void -net_windows_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) +net_windows_done_wait_handle (struct serial *scb) { 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; + stop_select_thread (&state->base); } static int @@ -669,20 +1134,12 @@ net_windows_open (struct serial *scb, const char *name) memset (state, 0, sizeof (struct net_windows_state)); scb->state = state; - /* Create auto reset events to wake and terminate the select thread. */ - 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); + /* Start the thread. */ + create_select_thread (net_windows_select_thread, scb, &state->base); return 0; } @@ -693,11 +1150,7 @@ 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); + destroy_select_thread (&state->base); CloseHandle (state->sock_event); xfree (scb->state); @@ -760,6 +1213,37 @@ _initialize_ser_windows (void) ops->noflush_set_tty_state = ser_base_noflush_set_tty_state; ops->drain_output = ser_base_drain_output; ops->wait_handle = ser_console_wait_handle; + ops->done_wait_handle = ser_console_done_wait_handle; + + serial_add_interface (ops); + + /* The pipe interface. */ + + ops = XMALLOC (struct serial_ops); + memset (ops, 0, sizeof (struct serial_ops)); + ops->name = "pipe"; + ops->next = 0; + ops->open = pipe_windows_open; + ops->close = pipe_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 = pipe_windows_read; + ops->write_prim = pipe_windows_write; + ops->wait_handle = pipe_wait_handle; + ops->done_wait_handle = pipe_done_wait_handle; + ops->avail = pipe_avail; serial_add_interface (ops); @@ -779,7 +1263,7 @@ _initialize_ser_windows (void) 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->send_break = ser_tcp_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; @@ -792,5 +1276,6 @@ _initialize_ser_windows (void) ops->read_prim = net_read_prim; ops->write_prim = net_write_prim; ops->wait_handle = net_windows_wait_handle; + ops->done_wait_handle = net_windows_done_wait_handle; serial_add_interface (ops); }