+/* 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. */
+static 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;
+}
+