public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Pedro Alves <pedro@palves.net>
To: gdb-patches@sourceware.org
Subject: [PATCH 32/34] Windows gdb: Add non-stop support
Date: Wed,  8 May 2024 00:42:31 +0100	[thread overview]
Message-ID: <20240507234233.371123-33-pedro@palves.net> (raw)
In-Reply-To: <20240507234233.371123-1-pedro@palves.net>

This patch adds non-stop support to the native Windows target.

This is made possible by the ContinueDebugEvent DBG_REPLY_LATER flag:

https://learn.microsoft.com/en-us/windows/win32/api/debugapi/nf-debugapi-continuedebugevent

  Supported in Windows 10, version 1507 or above, this flag causes
  dwThreadId to replay the existing breaking event after the target
  continues. By calling the SuspendThread API against dwThreadId, a
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  debugger can resume other threads in the process and later return to
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  the breaking.
  ^^^^^^^^^^^^

The patch adds a new comment section in gdb/windows-nat.c providing an
overall picture of how all-stop / non-stop work.

Without DBG_REPLY_LATER, if we SuspendThread the thread, and then
immediately ContinueDebugThread(DBG_CONTINUE) before getting back to
the prompt, we could still have non-stop mode working, however, then
users wouldn't have a chance to decide whether to pass the signal to
the inferior the next time they resume the program, as that is done by
passing DBG_EXCEPTION_NOT_HANDLED to ContinueDebugEvent, and that has
already been called.

The patch adds that DBG_REPLY_LATER handling, and also adds support
for target_stop, so the core can pause threads at its discretion.
This pausing does not use the same mechanisms used in
windows_nat_target::interrupt, as those inject a new thread in the
inferior.  Instead, for each thread the core wants paused, it uses
SuspendThread, and enqueues a pending GDB_SIGNAL_0 stop on the thread.

Since DBG_REPLY_LATER only exists on Windows 10 and later, we only
enable non-stop mode on Windows 10 and later.

Since having the target backend work in non-stop mode adds features
compared to old all-stop mode (signal/exception passing/suppression is
truly per-thread), this patch also switches the backend to do
all-stop-on-top-of-non-stop, by having
windows_nat_target::always_non_stop_p return true if non-stop mode is
possible.  To be clear, this just changes how the backend works in
coordination with infrun.  The user-visible mode default mode is still
all-stop.  The difference is that infrun is responsible for stopping
all threads when needed, instead of the backend (actually the kernel)
always doing that before reporting an event to infrun.

There is no displaced stepping support, but that's "just" a missed
optimization to be done later.

Cygwin signals handling was a major headache, but I managed to get it
working.  See the "Cygwin signals" description section I added at the
top of windows-nat.c.

Change-Id: Id71aef461c43c244120635b5bedc638fe77c31fb
---
 gdb/nat/windows-nat.c  |  17 +-
 gdb/nat/windows-nat.h  |  56 ++-
 gdb/windows-nat.c      | 753 +++++++++++++++++++++++++++++++++++------
 gdbserver/win32-low.cc |  10 +-
 gdbserver/win32-low.h  |   4 +-
 5 files changed, 725 insertions(+), 115 deletions(-)

diff --git a/gdb/nat/windows-nat.c b/gdb/nat/windows-nat.c
index 99bdadd80e6..11251be07be 100644
--- a/gdb/nat/windows-nat.c
+++ b/gdb/nat/windows-nat.c
@@ -727,17 +727,20 @@ get_last_debug_event_ptid ()
 /* See nat/windows-nat.h.  */
 
 BOOL
-continue_last_debug_event (DWORD continue_status, bool debug_events)
+continue_last_debug_event (DWORD cont_status, bool debug_events)
 {
-  DEBUG_EVENTS ("ContinueDebugEvent (cpid=%d, ctid=0x%x, %s)",
-		(unsigned) last_wait_event.dwProcessId,
-		(unsigned) last_wait_event.dwThreadId,
-		continue_status == DBG_CONTINUE ?
-		"DBG_CONTINUE" : "DBG_EXCEPTION_NOT_HANDLED");
+  DEBUG_EVENTS
+    ("ContinueDebugEvent (cpid=%d, ctid=0x%x, %s)",
+     (unsigned) last_wait_event.dwProcessId,
+     (unsigned) last_wait_event.dwThreadId,
+     cont_status == DBG_CONTINUE ? "DBG_CONTINUE" :
+     cont_status == DBG_EXCEPTION_NOT_HANDLED ? "DBG_EXCEPTION_NOT_HANDLED" :
+     cont_status == DBG_REPLY_LATER ? "DBG_REPLY_LATER" :
+     "DBG_???");
 
   return ContinueDebugEvent (last_wait_event.dwProcessId,
 			     last_wait_event.dwThreadId,
-			     continue_status);
+			     cont_status);
 }
 
 /* See nat/windows-nat.h.  */
diff --git a/gdb/nat/windows-nat.h b/gdb/nat/windows-nat.h
index 256355f6ad0..c6d0c4e98dd 100644
--- a/gdb/nat/windows-nat.h
+++ b/gdb/nat/windows-nat.h
@@ -81,12 +81,55 @@ struct windows_thread_info
   /* Thread Information Block address.  */
   CORE_ADDR thread_local_base;
 
+#ifdef __CYGWIN__
+  /* These two fields are used to handle Cygwin signals.  When a
+     thread is signaled, the "sig" thread inside the Cygwin runtime
+     reports the fact to us via a special OutputDebugString message.
+     In order to make stepping into a signal handler work, we can only
+     resume the "sig" thread when we also resume the target signaled
+     thread.  When we intercept a Cygwin signal, we set up a cross
+     link between the two threads using the two fields below, so we
+     can always identify one from the other.  See the "Cygwin signals"
+     description in gdb/windows-nat.c for more.  */
+
+  /* If this thread received a signal, then 'cygwin_sig_thread' points
+     to the "sig" thread within the Cygwin runtime.  */
+  windows_thread_info *cygwin_sig_thread = nullptr;
+
+  /* If this thread is the Cygwin runtime's "sig" thread, then
+     'signaled_thread' points at the thread that received a
+     signal.  */
+  windows_thread_info *signaled_thread = nullptr;
+#endif
+
+  /* If the thread had its event postponed with DBG_REPLY_LATER, when
+     we later ResumeThread this thread, WaitForDebugEvent will
+     re-report the postponed event.  This field holds the continue
+     status value to be automatically passed to ContinueDebugEvent
+     when we encounter this re-reported event.  0 if the thread has
+     not had its event postponed with DBG_REPLY_LATER. */
+  DWORD auto_cont = 0;
+
   /* This keeps track of whether SuspendThread was called on this
      thread.  -1 means there was a failure or that the thread was
      explicitly not suspended, 1 means it was called, and 0 means it
      was not.  */
   int suspended = 0;
 
+  /* This flag indicates whether we are explicitly stopping this
+     thread in response to a target_stop request.  This allows
+     distinguishing between threads that are explicitly stopped by the
+     debugger and threads that are stopped due to other reasons.
+
+     Typically, when we want to stop a thread, we suspend it, enqueue
+     a pending GDB_SIGNAL_0 stop status on the thread, and then set
+     this flag to true.  However, if the thread has had its event
+     previously postponed with DBG_REPLY_LATER, it means that it
+     already has an event to report.  In such case, we simply set the
+     'stopping' flag without suspending the thread or enqueueing a
+     pending stop.  See stop_one_thread.  */
+  bool stopping = false;
+
 /* Info about a potential pending stop.
 
    Sometimes, Windows will report a stop on a thread that has been
@@ -167,15 +210,15 @@ struct windows_process_info
   virtual windows_thread_info *find_thread (ptid_t ptid) = 0;
 
   /* Handle OUTPUT_DEBUG_STRING_EVENT from child process.  Updates
-     OURSTATUS and returns the thread id if this represents a thread
-     change (this is specific to Cygwin), otherwise 0.
+     OURSTATUS and returns true if this represents a Cygwin signal,
+     otherwise false.
 
      Cygwin prepends its messages with a "cygwin:".  Interpret this as
      a Cygwin signal.  Otherwise just print the string as a warning.
 
      This function must be supplied by the embedding application.  */
-  virtual DWORD handle_output_debug_string (const DEBUG_EVENT &current_event,
-					    struct target_waitstatus *ourstatus) = 0;
+  virtual bool handle_output_debug_string (const DEBUG_EVENT &current_event,
+					   struct target_waitstatus *ourstatus) = 0;
 
   /* Handle a DLL load event.
 
@@ -415,6 +458,11 @@ extern DeleteProcThreadAttributeList_ftype *DeleteProcThreadAttributeList;
 
 extern bool disable_randomization_available ();
 
+/* This is available starting with Windows 10.  */
+#ifndef DBG_REPLY_LATER
+# define DBG_REPLY_LATER 0x40010001L
+#endif
+
 /* Return true if it's possible to use DBG_REPLY_LATER with
    ContinueDebugEvent on this host.  */
 extern bool dbg_reply_later_available ();
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 73a6237dfa7..a33ef868566 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -74,6 +74,162 @@
 #include "ser-event.h"
 #include "inf-loop.h"
 
+/* This comment documents high-level logic of this file.
+
+all-stop
+========
+
+In all-stop mode, there is only ever one Windows debug event in
+flight. When we receive an event from WaitForDebugEvent, the kernel
+has already implicitly suspended all the threads of the process.  We
+report the breaking event to the core.  When the core decides to
+resume the inferior, it calls windows_nat_target:resume, which
+triggers a ContinueDebugEvent call.  This call makes all unsuspended
+threads schedulable again, and we go back to waiting for the next
+event in WaitForDebugEvent.
+
+non-stop
+========
+
+For non-stop mode, we utilize the DBG_REPLY_LATER flag in the
+ContinueDebugEvent function.  According to Microsoft:
+
+ "This flag causes dwThreadId to replay the existing breaking event
+ after the target continues.  By calling the SuspendThread API against
+ dwThreadId, a debugger can resume other threads in the process and
+ later return to the breaking."
+
+To enable non-stop mode, windows_nat_target::wait suspends the thread,
+calls 'ContinueForDebugEvent(..., DBG_REPLY_LATER)', and sets the
+process_thread thread to wait for the next event using
+WaitForDebugEvent, all before returning the original breaking event to
+the core.
+
+When the user/core finally decides to resume the inferior thread that
+reported the event, we unsuspend it using ResumeThread.  Unlike in
+all-stop mode, we don't call ContinueDebugEvent then, as it has
+already been called when the event was first encountered.  By making
+the inferior thread schedulable again, WaitForDebugEvent re-reports
+the same event (due to the earlier DBG_REPLY_LATER).  In
+windows_nat_target::wait, we detect this delayed re-report and call
+ContinueDebugEvent on the thread, instructing the process_thread
+thread to continue waiting for the next event.
+
+During the initial thread resumption in windows_nat_target::resume, we
+recorded the dwContinueStatus argument to be passed to the last
+ContinueDebugEvent.  See windows_thread_info::auto_cont for details.
+
+Note that with this setup, in non-stop mode, every stopped thread has
+its own independent last-reported Windows debug event.  Therefore, we
+can decide on a per-thread basis whether to pass the thread's
+exception (DBG_EXCEPTION_NOT_HANDLED / DBG_CONTINUE) to the inferior.
+This per-thread decision is not possible in all-stop mode, where we
+only call ContinueDebugEvent for the thread that last reported a stop,
+at windows_nat_target::resume time.
+
+Cygwin signals
+==============
+
+The Cygwin runtime always spawns a "sig" thread, which is responsible
+for receiving signal delivery requests, and hijacking the signaled
+thread's execution to make it run the signal handler.  This is all
+explained here:
+
+  https://sourceware.org/cgit/newlib-cygwin/tree/winsup/cygwin/DevDocs/how-signals-work.txt
+
+There's a custom debug api protocol between GDB and Cygwin to be able
+to intercept Cygwin signals before they're seen by the signaled
+thread, just like the debugger intercepts signals with ptrace on
+Linux.  This Cygwin debugger protocol isn't well documented, though.
+Here's what happens: when the special "sig" thread in the Cygwin
+runtime is about to deliver a signal to the target thread, it calls
+OutputDebugString with a special message:
+
+  https://sourceware.org/cgit/newlib-cygwin/tree/winsup/cygwin/exceptions.cc?id=4becae7bd833e183c789821a477f25898ed0db1f#n1866
+
+OutputDebugString is a function that is part of the Windows debug API.
+It generates an OUTPUT_DEBUG_STRING_EVENT event out of
+WaitForDebugEvent in the debugger, which freezes the inferior, like
+any other event.
+
+GDB recognizes the special Cygwin signal marker string, and is able to
+report the intercepted Cygwin signal to the user.
+
+With the windows-nat backend in all-stop mode, if the user decides to
+single-step the signaled thread, GDB will set the trace flag in the
+signaled thread to force it to single-step, and then re-resume the
+program with ContinueDebugEvent.  This resumes both the signaled
+thread, and the special "sig" thread.  The special "sig" thread
+decides to make the signaled thread run the signal handler, so it
+suspends it with SuspendThread, does a read-modify-write operation
+with GetThreadContext/SetThreadContext, and then re-resumes it with
+ResumeThread.  This is all done here:
+
+   https://sourceware.org/cgit/newlib-cygwin/tree/winsup/cygwin/exceptions.cc?id=4becae7bd833e183c789821a477f25898ed0db1f#n1011
+
+That resulting register context will still have its trace flag set, so
+the signaled thread ends up single-stepping the signal handler and
+reporting the trace stop to GDB, which reports the stop where the
+thread is now stopped, inside the signal handler.
+
+That is the intended behavior; stepping into a signal handler is a
+feature that works on other ports as well, including x86 GNU/Linux,
+for example.  This is exercised by the gdb.base/sigstep.exp testcase.
+
+Now, making that work with the backend in non-stop mode (the default
+on Windows 10 and above) is tricker.  In that case, when GDB sees the
+magic OUTPUT_DEBUG_STRING_EVENT event mentioned above, reported for
+the "sig" thread, GDB reports the signal stop for the target signaled
+thread to the user (leaving that thread stopped), but, unlike with an
+all-stop backend, in non-stop, only the evented/signaled thread should
+be stopped, so the backend would normally want to re-resume the Cygwin
+runtime's "sig" thread after handling the OUTPUT_DEBUG_STRING_EVENT
+event, like it does with any other event out of WaitForDebugEvent that
+is not reported to the core.  If it did that (resume the "sig" thread)
+however, at that point, the signaled thread would be stopped,
+suspended with SuspendThread by GDB (while the user is inspecting it),
+but, unlike in all-stop, the "sig" thread would be set running free.
+The "sig" thread would reach the code that wants to redirect the
+signaled thread's execution to the signal handler (by hacking the
+registers context, as described above), but unlike in the all-stop
+case, the "sig" thread would notice that the signaled thread is
+suspended, and so would decide to defer the signal handler until a
+later time.  It's the same code as described above for the all-stop
+case, except it would take the "then" branch:
+
+   https://sourceware.org/cgit/newlib-cygwin/tree/winsup/cygwin/exceptions.cc?id=4becae7bd833e183c789821a477f25898ed0db1f#n1019
+
+   // Just set pending if thread is already suspended
+   if (res)
+     {
+       tls->unlock ();
+       ResumeThread (hth);
+       goto out;
+     }
+
+The result would be that when the GDB user later finally decides to
+step the signaled thread, the signaled thread would just single step
+the mainline code, instead of stepping into the signal handler.
+
+To avoid this difference of behavior in non-stop mode compared to
+all-stop mode, we use a trick -- whenever we see that magic
+OUTPUT_DEBUG_STRING_EVENT event reported for the "sig" thread, we
+report a stop for the target signaled thread, _and_ leave the "sig"
+thread suspended as well, for as long as the target signaled thread is
+suspended.  I.e., we don't let the "sig" thread run before the user
+decides what to do with the signaled thread's signal.  Only when the
+user re-resumes the signaled thread, will we resume the "sig" thread
+as well.  The trick is that all this is done here in the Windows
+backend, while providing the illusion to the core of GDB (and the
+user) that the "sig" thread is "running", for as long as the core
+wants the "sig" thread to be running.
+
+This isn't ideal, since this means that with user-visible non-stop,
+the inferior will only be able to process and report one signal at a
+time (as the "sig" thread is responsible for that), but that seems
+like an acceptible compromise, better than not being able to have the
+target work in non-stop by default on Cygwin.  */
+
 using namespace windows_nat;
 
 /* Maintain a linked list of "so" information.  */
@@ -100,6 +256,11 @@ enum windows_continue_flag
        call to continue the inferior -- we are either mourning it or
        detaching.  */
     WCONT_LAST_CALL = 2,
+
+    /* By default, windows_continue only calls ContinueDebugEvent in
+       all-stop mode.  This flag indicates that windows_continue
+       should call ContinueDebugEvent even in non-stop mode.  */
+    WCONT_CONTINUE_DEBUG_EVENT = 4,
   };
 
 DEF_ENUM_FLAGS_TYPE (windows_continue_flag, windows_continue_flags);
@@ -107,8 +268,8 @@ DEF_ENUM_FLAGS_TYPE (windows_continue_flag, windows_continue_flags);
 struct windows_per_inferior : public windows_process_info
 {
   windows_thread_info *find_thread (ptid_t ptid) override;
-  DWORD handle_output_debug_string (const DEBUG_EVENT &current_event,
-				    struct target_waitstatus *ourstatus) override;
+  bool handle_output_debug_string (const DEBUG_EVENT &current_event,
+				   struct target_waitstatus *ourstatus) override;
   void handle_load_dll (const char *dll_name, LPVOID base) override;
   void handle_unload_dll (const DEBUG_EVENT &current_event) override;
   bool handle_access_violation (const EXCEPTION_RECORD *rec) override;
@@ -269,7 +430,11 @@ struct windows_nat_target final : public x86_nat_target<inf_child_target>
   void attach (const char *, int) override;
 
   bool attach_no_wait () override
-  { return true; }
+  {
+    /* In non-stop, after attach, we leave all threads running, like
+       other targets.  */
+    return !target_is_non_stop_p ();
+  }
 
   void detach (inferior *, int) override;
 
@@ -312,8 +477,11 @@ struct windows_nat_target final : public x86_nat_target<inf_child_target>
   std::string pid_to_str (ptid_t) override;
 
   void interrupt () override;
+  void stop (ptid_t) override;
   void pass_ctrlc () override;
 
+  void thread_events (int enable) override;
+
   const char *pid_to_exec_file (int pid) override;
 
   ptid_t get_ada_task_ptid (long lwp, ULONGEST thread) override;
@@ -343,6 +511,9 @@ struct windows_nat_target final : public x86_nat_target<inf_child_target>
     return m_is_async;
   }
 
+  bool supports_non_stop () override;
+  bool always_non_stop_p () override;
+
   void async (bool enable) override;
 
   int async_wait_fd () override
@@ -357,6 +528,8 @@ struct windows_nat_target final : public x86_nat_target<inf_child_target>
   void delete_thread (ptid_t ptid, DWORD exit_code, bool main_thread_p);
   DWORD fake_create_process (const DEBUG_EVENT &current_event);
 
+  void stop_one_thread (windows_thread_info *th);
+
   BOOL windows_continue (DWORD continue_status, int id,
 			 windows_continue_flags cont_flags = 0);
 
@@ -420,6 +593,9 @@ struct windows_nat_target final : public x86_nat_target<inf_child_target>
      already returned an event, and we need to ContinueDebugEvent
      again to restart the inferior.  */
   bool m_continued = false;
+
+  /* Whether target_thread_events is in effect.  */
+  int m_report_thread_events = 0;
 };
 
 static void
@@ -629,6 +805,13 @@ windows_nat_target::add_thread (ptid_t ptid, HANDLE h, void *tlb,
      registers.  */
   th->debug_registers_changed = true;
 
+  /* Even if we're stopping the thread for some reason internal to
+     this module, from the perspective of infrun and the
+     user/frontend, this new thread is running until it next reports a
+     stop.  */
+  set_running (this, ptid, true);
+  set_executing (this, ptid, true);
+
   return th;
 }
 
@@ -1048,12 +1231,17 @@ signal_event_command (const char *args, int from_tty)
 
 /* See nat/windows-nat.h.  */
 
-DWORD
+bool
 windows_per_inferior::handle_output_debug_string
   (const DEBUG_EVENT &current_event,
    struct target_waitstatus *ourstatus)
 {
-  DWORD thread_id = 0;
+  windows_thread_info *event_thr
+    = windows_process.find_thread (ptid_t (current_event.dwProcessId,
+					   current_event.dwThreadId));
+  if (event_thr->auto_cont)
+    internal_error ("OutputDebugString thread 0x%x has auto-cont set",
+		    event_thr->tid);
 
   gdb::unique_xmalloc_ptr<char> s
     = (target_read_string
@@ -1090,15 +1278,37 @@ windows_per_inferior::handle_output_debug_string
       int sig = strtol (s.get () + sizeof (_CYGWIN_SIGNAL_STRING) - 1, &p, 0);
       gdb_signal gotasig = gdb_signal_from_host (sig);
       LPCVOID x = 0;
+      DWORD thread_id = 0;
 
-      if (gotasig)
+      if (gotasig != GDB_SIGNAL_0)
 	{
-	  ourstatus->set_stopped (gotasig);
 	  thread_id = strtoul (p, &p, 0);
-	  if (thread_id == 0)
-	    thread_id = current_event.dwThreadId;
-	  else
-	    x = (LPCVOID) (uintptr_t) strtoull (p, NULL, 0);
+	  if (thread_id != 0)
+	    {
+	      x = (LPCVOID) (uintptr_t) strtoull (p, NULL, 0);
+
+	      ptid_t ptid (current_event.dwProcessId, thread_id, 0);
+	      windows_thread_info *th = find_thread (ptid);
+
+	      /* Suspend the signaled thread, and leave the signal as
+		 a pending event.  It will be picked up by
+		 windows_nat_target::wait.  */
+	      th->suspend ();
+	      th->stopping = true;
+	      th->last_event = {};
+	      th->pending_status.set_stopped (gotasig);
+
+	      /* Link the "sig" thread and the signaled threads, so we
+		 can keep the "sig" thread suspended until we resume
+		 the signaled thread.  See "Cygwin signals" at the
+		 top.  */
+	      event_thr->signaled_thread = th;
+	      th->cygwin_sig_thread = event_thr;
+
+	      /* Leave the "sig" thread suspended.  */
+	      event_thr->suspend ();
+	      return true;
+	    }
 	}
 
       DEBUG_EVENTS ("gdb: cygwin signal %d, thread 0x%x, CONTEXT @ %p",
@@ -1106,7 +1316,7 @@ windows_per_inferior::handle_output_debug_string
     }
 #endif
 
-  return thread_id;
+  return false;
 }
 
 static int
@@ -1395,6 +1605,7 @@ windows_per_inferior::continue_one_thread (windows_thread_info *th,
     }
 
   th->resume ();
+  th->stopping = false;
   th->last_sig = GDB_SIGNAL_0;
 }
 
@@ -1406,31 +1617,77 @@ BOOL
 windows_nat_target::windows_continue (DWORD continue_status, int id,
 				      windows_continue_flags cont_flags)
 {
-  for (auto &th : windows_process.thread_list)
-    {
-      if ((id == -1 || id == (int) th->tid)
-	  && !th->suspended
-	  && th->pending_status.kind () != TARGET_WAITKIND_IGNORE)
-	{
-	  DEBUG_EVENTS ("got matching pending stop event "
-			"for 0x%x, not resuming",
-			th->tid);
-
-	  /* There's no need to really continue, because there's already
-	     another event pending.  However, we do need to inform the
-	     event loop of this.  */
-	  serial_event_set (m_wait_event);
-	  return TRUE;
-	}
-    }
+  if ((cont_flags & (WCONT_LAST_CALL | WCONT_KILLED)) == 0)
+    for (auto &th : windows_process.thread_list)
+      {
+	if ((id == -1 || id == (int) th->tid)
+	    && th->pending_status.kind () != TARGET_WAITKIND_IGNORE)
+	  {
+	    DEBUG_EVENTS ("got matching pending stop event "
+			  "for 0x%x, not resuming",
+			  th->tid);
+
+	    /* There's no need to really continue, because there's already
+	       another event pending.  However, we do need to inform the
+	       event loop of this.  */
+	    serial_event_set (m_wait_event);
+	    return TRUE;
+	  }
+      }
 
+  /* Resume any suspended thread whose ID matches "ID".  Skip the
+     Cygwin "sig" thread in the main iteration, though.  That one is
+     only resumed when the target signaled thread is resumed.  See
+     "Cygwin signals" in the intro section.  */
   for (auto &th : windows_process.thread_list)
-    if (id == -1 || id == (int) th->tid)
-      windows_process.continue_one_thread (th.get (), cont_flags);
+    if (th->suspended
+#ifdef __CYGWIN__
+	&& th->signaled_thread == nullptr
+#endif
+	&& (id == -1 || id == (int) th->tid))
+      {
+	windows_process.continue_one_thread (th.get (), cont_flags);
 
-  continue_last_debug_event_main_thread
-    (_("Failed to resume program execution"), continue_status,
-     cont_flags & WCONT_LAST_CALL);
+#ifdef __CYGWIN__
+	/* See if we're resuming a thread that caught a Cygwin signal.
+	   If so, also resume the Cygwin runtime's "sig" thread.  */
+	if (th->cygwin_sig_thread != nullptr)
+	  {
+	    DEBUG_EVENTS ("\"sig\" thread %d (0x%x) blocked by "
+			  "just-resumed thread %d (0x%x)",
+			  th->cygwin_sig_thread->tid,
+			  th->cygwin_sig_thread->tid,
+			  th->tid, th->tid);
+
+	    inferior *inf = find_inferior_pid (this,
+					       windows_process.process_id);
+	    thread_info *sig_thr
+	      = inf->find_thread (ptid_t (windows_process.process_id,
+					  th->cygwin_sig_thread->tid));
+	    if (sig_thr->executing ())
+	      {
+		DEBUG_EVENTS ("\"sig\" thread %d (0x%x) meant to be executing, "
+			      "continuing it now",
+			      th->cygwin_sig_thread->tid,
+			      th->cygwin_sig_thread->tid);
+		windows_process.continue_one_thread (th->cygwin_sig_thread,
+						     cont_flags);
+	      }
+	    /* Break the chain.  */
+	    th->cygwin_sig_thread->signaled_thread = nullptr;
+	    th->cygwin_sig_thread = nullptr;
+	  }
+#endif
+      }
+
+  if (!target_is_non_stop_p ()
+      || (cont_flags & WCONT_CONTINUE_DEBUG_EVENT) != 0)
+    {
+      DEBUG_EVENTS ("windows_continue -> continue_last_debug_event");
+      continue_last_debug_event_main_thread
+	(_("Failed to resume program execution"), continue_status,
+	 cont_flags & WCONT_LAST_CALL);
+    }
 
   return TRUE;
 }
@@ -1481,13 +1738,27 @@ windows_nat_target::resume (ptid_t ptid, int step, enum gdb_signal sig)
 
   if (sig != GDB_SIGNAL_0)
     {
+      /* Allow continuing with the same signal that interrupted us.
+	 Otherwise complain.  */
+
       /* Note it is OK to call get_last_debug_event_ptid() from the
-	 main thread here, because we know the process_thread thread
-	 isn't waiting for an event at this point, so there's no data
-	 race.  */
-      if (inferior_ptid != get_last_debug_event_ptid ())
+	 main thread here in all-stop, because we know the
+	 process_thread thread is not waiting for an event at this
+	 point, so there is no data race.  We cannot call it in
+	 non-stop mode, as the process_thread thread _is_ waiting for
+	 events right now in that case.  However, the restriction does
+	 not exist in non-stop mode, so we don't even call it in that
+	 mode.  */
+      if (!target_is_non_stop_p ()
+	  && inferior_ptid != get_last_debug_event_ptid ())
 	{
-	  /* ContinueDebugEvent will be for a different thread.  */
+	  /* In all-stop, ContinueDebugEvent will be for a different
+	     thread.  For non-stop, we've called ContinueDebugEvent
+	     with DBG_REPLY_LATER for this thread, so we just set the
+	     intended continue status in 'auto_cont', which is later
+	     passed to ContinueDebugEvent in windows_nat_target::wait
+	     after we resume the thread and we get the replied-later
+	     (repeated) event out of WaitForDebugEvent.  */
 	  DEBUG_EXCEPT ("Cannot continue with signal %d here.  "
 			"Not last-event thread", sig);
 	}
@@ -1523,33 +1794,40 @@ windows_nat_target::resume (ptid_t ptid, int step, enum gdb_signal sig)
 		    th->last_sig);
     }
 
+  /* If DBG_REPLY_LATER was used on the thread, we override the
+     continue status that will be passed to ContinueDebugEvent later
+     with the continue status we've just determined fulfils the
+     caller's resumption request.  Note that DBG_REPLY_LATER is only
+     used in non-stop mode, and in that mode, windows_continue (called
+     below) does not call ContinueDebugEvent.  */
+  if (th->auto_cont != 0)
+    th->auto_cont = continue_status;
+
+  /* Single step by setting t bit (trap flag).  The trap flag is
+     automatically reset as soon as the single-step exception arrives,
+     however, it's possible to suspend/stop a thread before it
+     executes any instruction, leaving the trace flag set.  If we
+     subsequently decide to continue such a thread instead of stepping
+     it, and we didn't clear the trap flag, the thread would step, and
+     we'd end up reporting a SIGTRAP to the core which the core
+     couldn't explain (because the thread wasn't supposed to be
+     stepping), and end up reporting a spurious SIGTRAP to the
+     user.  */
+  regcache *regcache = get_thread_regcache (inferior_thread ());
+  fetch_registers (regcache, gdbarch_ps_regnum (regcache->arch ()));
+
+  DWORD *eflags;
 #ifdef __x86_64__
   if (windows_process.wow64_process)
-    {
-      if (step)
-	{
-	  /* Single step by setting t bit.  */
-	  regcache *regcache = get_thread_regcache (inferior_thread ());
-	  struct gdbarch *gdbarch = regcache->arch ();
-	  fetch_registers (regcache, gdbarch_ps_regnum (gdbarch));
-	  th->wow64_context.EFlags |= FLAG_TRACE_BIT;
-	}
-    }
+    eflags = &th->wow64_context.EFlags;
   else
 #endif
-    {
-      if (step)
-	{
-	  /* Single step by setting t bit.  */
-	  regcache *regcache = get_thread_regcache (inferior_thread ());
-	  struct gdbarch *gdbarch = regcache->arch ();
-	  fetch_registers (regcache, gdbarch_ps_regnum (gdbarch));
-	  th->context.EFlags |= FLAG_TRACE_BIT;
-	}
-    }
+    eflags = &th->context.EFlags;
 
-  /* Allow continuing with the same signal that interrupted us.
-     Otherwise complain.  */
+  if (step)
+    *eflags |= FLAG_TRACE_BIT;
+  else
+    *eflags &= ~FLAG_TRACE_BIT;
 
   if (resume_all)
     windows_continue (continue_status, -1);
@@ -1598,12 +1876,88 @@ windows_nat_target::interrupt ()
 	     "Press Ctrl-c in the program console."));
 }
 
+/* Stop thread TH.  This leaves a GDB_SIGNAL_0 pending in the thread,
+   which is later consumed by windows_nat_target::wait.  */
+
+void
+windows_nat_target::stop_one_thread (windows_thread_info *th)
+{
+  ptid_t thr_ptid (windows_process.process_id, th->tid);
+
+  if (th->suspended == -1)
+    {
+      /* Already known to be stopped; and suspension failed, most
+	 probably because the thread is exiting.  Do nothing, and let
+	 the thread exit event be reported.  */
+      DEBUG_EVENTS ("already suspended %s: suspended=%d, stopping=%d",
+		    thr_ptid.to_string ().c_str (),
+		    th->suspended, th->stopping);
+    }
+#ifdef __CYGWIN__
+  else if (th->suspended
+	   && th->signaled_thread != nullptr
+	   && th->pending_status.kind () == TARGET_WAITKIND_IGNORE)
+    {
+      DEBUG_EVENTS ("explict stop for \"sig\" thread %s held for signal",
+		    thr_ptid.to_string ().c_str ());
+
+      th->stopping = true;
+      th->pending_status.set_stopped (GDB_SIGNAL_0);
+      th->last_event = {};
+      serial_event_set (m_wait_event);
+    }
+#endif
+  else if (th->suspended)
+    {
+      /* Already known to be stopped; do nothing.  */
+
+      DEBUG_EVENTS ("already suspended %s: suspended=%d, stopping=%d",
+		    thr_ptid.to_string ().c_str (),
+		    th->suspended, th->stopping);
+
+      th->stopping = true;
+    }
+  else
+    {
+      DEBUG_EVENTS ("stop request for %s", thr_ptid.to_string ().c_str ());
+
+      th->suspend ();
+      gdb_assert (th->suspended);
+
+      th->stopping = true;
+      th->pending_status.set_stopped (GDB_SIGNAL_0);
+      th->last_event = {};
+      serial_event_set (m_wait_event);
+    }
+}
+
+/* Implementation of target_ops::stop.  */
+
+void
+windows_nat_target::stop (ptid_t ptid)
+{
+  for (auto &th : windows_process.thread_list)
+    {
+      ptid_t thr_ptid (windows_process.process_id, th->tid);
+      if (thr_ptid.matches (ptid))
+	stop_one_thread (th.get ());
+    }
+}
+
 void
 windows_nat_target::pass_ctrlc ()
 {
   interrupt ();
 }
 
+/* Implementation of the target_ops::thread_events method.  */
+
+void
+windows_nat_target::thread_events (int enable)
+{
+  m_report_thread_events = enable;
+}
+
 /* Get the next event from the child.  Returns the thread ptid.  */
 
 ptid_t
@@ -1619,7 +1973,7 @@ windows_nat_target::get_windows_debug_event
      for details on why this is needed.  */
   for (auto &th : windows_process.thread_list)
     {
-      if (!th->suspended
+      if ((!th->suspended || th->stopping)
 	  && th->pending_status.kind () != TARGET_WAITKIND_IGNORE)
 	{
 	  DEBUG_EVENTS ("reporting pending event for 0x%x", th->tid);
@@ -1630,7 +1984,6 @@ windows_nat_target::get_windows_debug_event
 	  *current_event = th->last_event;
 
 	  ptid_t ptid (windows_process.process_id, thread_id);
-	  windows_process.invalidate_context (th.get ());
 	  return ptid;
 	}
     }
@@ -1648,6 +2001,50 @@ windows_nat_target::get_windows_debug_event
   event_code = current_event->dwDebugEventCode;
   ourstatus->set_spurious ();
 
+  ptid_t result_ptid (current_event->dwProcessId,
+		      current_event->dwThreadId, 0);
+  windows_thread_info *result_th = windows_process.find_thread (result_ptid);
+
+  /* If we previously used DBG_REPLY_LATER on this thread, and we're
+     seeing an event for it, it means we've already processed the
+     event, and then subsequently resumed the thread [1], intending to
+     pass AUTO_CONT to ContinueDebugEvent.  Do that now, before the
+     switch table below, which may have side effects that don't make
+     sense for a delayed event.
+
+     [1] - with the caveat that sometimes Windows reports an event for
+     a suspended thread.  Also handled below.  */
+  if (result_th != nullptr && result_th->auto_cont != 0)
+    {
+      DEBUG_EVENTS ("auto-cont thread 0x%x", result_th->tid);
+
+      gdb_assert (dbg_reply_later_available ());
+
+      if (result_th->suspended)
+	{
+	  /* Pending stop.  See the comment by the definition of
+	     "pending_status" for details on why this is needed.  */
+	  DEBUG_EVENTS ("unexpected auto-cont stop in suspended thread 0x%x",
+			result_th->tid);
+
+	  /* Put the event back in the kernel queue.  We haven't yet
+	     decided which reply to use.  */
+	  continue_status = DBG_REPLY_LATER;
+	}
+      else
+	{
+	  continue_status = result_th->auto_cont;
+	  result_th->auto_cont = 0;
+	}
+
+      /* Go back to waiting for the next event.  */
+      continue_last_debug_event_main_thread
+	(_("Failed to auto-continue thread"), continue_status);
+
+      ourstatus->set_ignore ();
+      return null_ptid;
+    }
+
   switch (event_code)
     {
     case CREATE_THREAD_DEBUG_EVENT:
@@ -1680,21 +2077,35 @@ windows_nat_target::get_windows_debug_event
 	      current_event->u.CreateThread.lpThreadLocalBase,
 	      false /* main_thread_p */));
 
-	/* This updates debug registers if necessary.  */
-	windows_process.continue_one_thread (th, 0);
+	/* Update the debug registers if we're not reporting the stop.
+	   If we are (reporting the stop), the debug registers will be
+	   updated when the thread is eventually re-resumed.  */
+	if (m_report_thread_events)
+	  ourstatus->set_thread_created ();
+	else
+	  windows_process.continue_one_thread (th, 0);
       }
       break;
 
     case EXIT_THREAD_DEBUG_EVENT:
-      DEBUG_EVENTS ("kernel event for pid=%u tid=0x%x code=%s",
-		    (unsigned) current_event->dwProcessId,
-		    (unsigned) current_event->dwThreadId,
-		    "EXIT_THREAD_DEBUG_EVENT");
-      delete_thread (ptid_t (current_event->dwProcessId,
-			     current_event->dwThreadId, 0),
-		     current_event->u.ExitThread.dwExitCode,
-		     false /* main_thread_p */);
-      thread_id = 0;
+      {
+	DEBUG_EVENTS ("kernel event for pid=%u tid=0x%x code=%s",
+		      (unsigned) current_event->dwProcessId,
+		      (unsigned) current_event->dwThreadId,
+		      "EXIT_THREAD_DEBUG_EVENT");
+	ptid_t thr_ptid (current_event->dwProcessId,
+			 current_event->dwThreadId, 0);
+	if (m_report_thread_events)
+	  {
+	    ourstatus->set_thread_exited
+	      (current_event->u.ExitThread.dwExitCode);
+	    return thr_ptid;
+	  }
+	delete_thread (thr_ptid,
+		       current_event->u.ExitThread.dwExitCode,
+		       false /* main_thread_p */);
+	thread_id = 0;
+      }
       break;
 
     case CREATE_PROCESS_DEBUG_EVENT:
@@ -1820,8 +2231,24 @@ windows_nat_target::get_windows_debug_event
 		    "OUTPUT_DEBUG_STRING_EVENT");
       if (windows_process.saw_create != 1)
 	break;
-      thread_id = windows_process.handle_output_debug_string (*current_event,
-							      ourstatus);
+      if (windows_process.handle_output_debug_string (*current_event,
+						      ourstatus))
+	{
+	  /* We caught a Cygwin signal for a thread.  That thread now
+	     has a pending event, and the "sig" thread is
+	     suspended.  */
+	  serial_event_set (m_wait_event);
+
+	  /* In all-stop, return now to avoid reaching
+	     ContinueDebugEvent further below.  In all-stop, it's
+	     always windows_nat_target::resume that does the
+	     ContinueDebugEvent call.  */
+	  if (!target_is_non_stop_p ())
+	    {
+	      ourstatus->set_ignore ();
+	      return null_ptid;
+	    }
+	}
       break;
 
     default:
@@ -1856,22 +2283,35 @@ windows_nat_target::get_windows_debug_event
 		    "unexpected stop in suspended thread 0x%x",
 		    thread_id);
 
-      if (current_event->dwDebugEventCode == EXCEPTION_DEBUG_EVENT
-	  && ((current_event->u.Exception.ExceptionRecord.ExceptionCode
-	       == EXCEPTION_BREAKPOINT)
-	      || (current_event->u.Exception.ExceptionRecord.ExceptionCode
-		  == STATUS_WX86_BREAKPOINT))
-	  && windows_process.windows_initialization_done)
+      if (dbg_reply_later_available ())
 	{
-	  th->stopped_at_software_breakpoint = true;
-	  th->pc_adjusted = false;
+	  /* Thankfully, the Windows kernel doesn't immediately
+	     re-report the unexpected event for a suspended thread
+	     when we defer it with DBG_REPLY_LATER, otherwise this
+	     would get us stuck in an infinite loop re-processing the
+	     same unexpected event over and over.  */
+	  continue_status = DBG_REPLY_LATER;
 	}
+      else
+	{
+	  if (current_event->dwDebugEventCode == EXCEPTION_DEBUG_EVENT
+	      && ((current_event->u.Exception.ExceptionRecord.ExceptionCode
+		   == EXCEPTION_BREAKPOINT)
+		  || (current_event->u.Exception.ExceptionRecord.ExceptionCode
+		      == STATUS_WX86_BREAKPOINT))
+	      && windows_process.windows_initialization_done)
+	    {
+	      th->stopped_at_software_breakpoint = true;
+	      th->pc_adjusted = false;
+	    }
 
-      th->pending_status = *ourstatus;
-      ourstatus->set_ignore ();
+	  th->pending_status = *ourstatus;
+	  th->last_event = {};
+	}
 
       continue_last_debug_event_main_thread
 	(_("Failed to resume program execution"), continue_status);
+      ourstatus->set_ignore ();
       return null_ptid;
     }
 
@@ -1902,6 +2342,14 @@ windows_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
 
       ptid_t result = get_windows_debug_event (pid, ourstatus, options,
 					       &current_event);
+      /* True if this is a pending event that we injected ourselves,
+	 instead of a real event out of WaitForDebugEvent.  */
+      bool fake = current_event.dwDebugEventCode == 0;
+
+      DEBUG_EVENTS ("get_windows_debug_event returned [%s : %s, fake=%d]",
+		    result.to_string ().c_str (),
+		    ourstatus->to_string ().c_str(),
+		    fake);
 
       if ((options & TARGET_WNOHANG) != 0
 	  && ourstatus->kind () == TARGET_WAITKIND_IGNORE)
@@ -1932,10 +2380,46 @@ windows_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
 		  th->pc_adjusted = false;
 		}
 
+	      /* If non-stop, suspend the event thread, and continue
+		 it with DBG_REPLY_LATER, so the other threads go back
+		 to running as soon as possible.  Don't do this if
+		 stopping the thread, as in that case the thread was
+		 already suspended, and also there's no real Windows
+		 debug event to continue in that case.  */
+	      if (windows_process.windows_initialization_done
+		  && target_is_non_stop_p ()
+		  && !fake)
+		{
+		  if (ourstatus->kind () == TARGET_WAITKIND_THREAD_EXITED)
+		    {
+		      /* If we failed to suspend the thread in
+			 windows_nat_target::stop, then 'suspended'
+			 will be -1 here.  */
+		      gdb_assert (th->suspended < 1);
+		      delete_thread (result,
+				     ourstatus->exit_status (),
+				     false /* main_thread_p */);
+		      continue_last_debug_event_main_thread
+			(_("Init: Failed to DBG_CONTINUE after thread exit"),
+			 DBG_CONTINUE);
+		    }
+		  else
+		    {
+		      th->suspend ();
+		      th->auto_cont = DBG_CONTINUE;
+		      continue_last_debug_event_main_thread
+			(_("Init: Failed to defer event with DBG_REPLY_LATER"),
+			 DBG_REPLY_LATER);
+		    }
+		}
+
 	      /* All-stop, suspend all threads until they are
 		 explicitly resumed.  */
-	      for (auto &thr : windows_process.thread_list)
-		thr->suspend ();
+	      if (!target_is_non_stop_p ())
+		for (auto &thr : windows_process.thread_list)
+		  thr->suspend ();
+
+	      th->stopping = false;
 	    }
 
 	  /* If something came out, assume there may be more.  This is
@@ -2017,7 +2501,7 @@ windows_nat_target::do_initial_windows_stuff (DWORD pid, bool attaching)
       /* Don't use windows_nat_target::resume here because that
 	 assumes that inferior_ptid points at a valid thread, and we
 	 haven't switched to any thread yet.  */
-      windows_continue (DBG_CONTINUE, -1);
+      windows_continue (DBG_CONTINUE, -1, WCONT_CONTINUE_DEBUG_EVENT);
     }
 
   switch_to_thread (this->find_thread (last_ptid));
@@ -2162,7 +2646,29 @@ windows_nat_target::attach (const char *args, int from_tty)
 #endif
 
   do_initial_windows_stuff (pid, 1);
-  target_terminal::ours ();
+
+  if (target_is_non_stop_p ())
+    {
+      /* Leave all threads running.  */
+
+      continue_last_debug_event_main_thread
+	(_("Failed to DBG_CONTINUE after attach"),
+	 DBG_CONTINUE);
+
+      /* The thread that reports the initial breakpoint, and thus ends
+	 up as selected thread here, was injected by Windows into the
+	 program for the attach, and it exits as soon as we resume it.
+	 Switch to the first thread in the inferior, otherwise the
+	 user will be left with an exited thread selected.  */
+      switch_to_thread (first_thread_of_inferior (current_inferior ()));
+    }
+  else
+    {
+      set_running (this, minus_one_ptid, false);
+      set_executing (this, minus_one_ptid, false);
+
+      target_terminal::ours ();
+    }
 }
 
 void
@@ -2280,10 +2786,11 @@ windows_nat_target::detach (inferior *inf, int from_tty)
   bool process_alive = true;
 
   /* The process_thread helper thread will be blocked in
-     WaitForDebugEvent waiting for events if we've resumed the target
-     before we get here, e.g., with "attach&" or "c&".  We need to
-     unblock it so that we can have it call DebugActiveProcessStop
-     below, in the do_synchronously block.  */
+     WaitForDebugEvent waiting for events if we're in non-stop mode,
+     or if in all-stop and we've resumed the target before we get
+     here, e.g., with "attach&" or "c&".  We need to unblock it so
+     that we can have it call DebugActiveProcessStop below, in the
+     do_synchronously block.  */
   if (m_continued)
     break_out_process_thread (process_alive);
 
@@ -3035,13 +3542,33 @@ windows_nat_target::create_inferior (const char *exec_file,
 
   do_initial_windows_stuff (pi.dwProcessId, 0);
 
-  /* windows_continue (DBG_CONTINUE, -1); */
+  /* There is one thread in the process now, and inferior_ptid points
+     to it.  Present it as stopped to the core.  */
+  windows_thread_info *th = windows_process.find_thread (inferior_ptid);
+
+  th->suspend ();
+  set_running (this, inferior_ptid, false);
+  set_executing (this, inferior_ptid, false);
+
+  if (target_is_non_stop_p ())
+    {
+      /* In non-stop mode, we always immediately use DBG_REPLY_LATER
+	 on threads as soon as they report an event.  However, during
+	 the initial startup, windows_nat_target::wait does not do
+	 this, so we need to handle it here for the initial
+	 thread.  */
+      th->auto_cont = DBG_CONTINUE;
+      continue_last_debug_event_main_thread
+	(_("Failed to defer event with DBG_REPLY_LATER"),
+	 DBG_REPLY_LATER);
+    }
 }
 
 void
 windows_nat_target::mourn_inferior ()
 {
-  windows_continue (DBG_CONTINUE, -1, WCONT_LAST_CALL);
+  windows_continue (DBG_CONTINUE, -1,
+		    WCONT_LAST_CALL | WCONT_CONTINUE_DEBUG_EVENT);
   x86_cleanup_dregs();
   if (windows_process.open_process_used)
     {
@@ -3096,14 +3623,28 @@ windows_nat_target::kill ()
 {
   CHECK (TerminateProcess (windows_process.handle, 0));
 
+  /* In non-stop mode, windows_continue does not call
+     ContinueDebugEvent by default.  This behavior is appropriate for
+     the first call to windows_continue because any thread that is
+     stopped has already been ContinueDebugEvent'ed with
+     DBG_REPLY_LATER.  However, after the first
+     wait_for_debug_event_main_thread call in the loop, this will no
+     longer be true.
+
+     In all-stop mode, the WCONT_CONTINUE_DEBUG_EVENT flag has no
+     effect, so writing the code in this way ensures that the code is
+     the same for both modes.  */
+  windows_continue_flags flags = WCONT_KILLED;
+
   for (;;)
     {
-      if (!windows_continue (DBG_CONTINUE, -1, WCONT_KILLED))
+      if (!windows_continue (DBG_CONTINUE, -1, flags))
 	break;
       DEBUG_EVENT current_event;
       wait_for_debug_event_main_thread (&current_event);
       if (current_event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
 	break;
+      flags |= WCONT_CONTINUE_DEBUG_EVENT;
     }
 
   target_mourn_inferior (inferior_ptid);	/* Or just windows_mourn_inferior?  */
@@ -3237,6 +3778,24 @@ windows_nat_target::thread_name (struct thread_info *thr)
   return th->thread_name ();
 }
 
+/* Implementation of the target_ops::supports_non_stop method.  */
+
+bool
+windows_nat_target::supports_non_stop ()
+{
+  /* Non-stop support requires DBG_REPLY_LATER, which only exists on
+     Windows 10 and later.  */
+  return dbg_reply_later_available ();
+}
+
+/* Implementation of the target_ops::always_non_stop_p method.  */
+
+bool
+windows_nat_target::always_non_stop_p ()
+{
+  /* If we can do non-stop, prefer it.  */
+  return supports_non_stop ();
+}
 
 void _initialize_windows_nat ();
 void
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
index 65b01dc97ac..b15d8d0c16d 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -625,7 +625,7 @@ win32_process_target::attach (unsigned long pid)
 
 /* See nat/windows-nat.h.  */
 
-DWORD
+bool
 gdbserver_windows_process::handle_output_debug_string
   (const DEBUG_EVENT &current_event,
    struct target_waitstatus *ourstatus)
@@ -636,7 +636,7 @@ gdbserver_windows_process::handle_output_debug_string
   DWORD nbytes = current_event.u.DebugString.nDebugStringLength;
 
   if (nbytes == 0)
-    return 0;
+    return false;
 
   if (nbytes > READ_BUFFER_LEN)
     nbytes = READ_BUFFER_LEN;
@@ -655,7 +655,7 @@ gdbserver_windows_process::handle_output_debug_string
   else
     {
       if (read_inferior_memory (addr, (unsigned char *) s, nbytes) != 0)
-	return 0;
+	return false;
     }
 
   if (!startswith (s, "cYg"))
@@ -663,14 +663,14 @@ gdbserver_windows_process::handle_output_debug_string
       if (!server_waiting)
 	{
 	  OUTMSG2(("%s", s));
-	  return 0;
+	  return false;
 	}
 
       monitor_output (s);
     }
 #undef READ_BUFFER_LEN
 
-  return 0;
+  return false;
 }
 
 static void
diff --git a/gdbserver/win32-low.h b/gdbserver/win32-low.h
index ea2a9b4c5b6..2c1b3b9335c 100644
--- a/gdbserver/win32-low.h
+++ b/gdbserver/win32-low.h
@@ -175,8 +175,8 @@ class win32_process_target : public process_stratum_target
 struct gdbserver_windows_process : public windows_nat::windows_process_info
 {
   windows_nat::windows_thread_info *find_thread (ptid_t ptid) override;
-  DWORD handle_output_debug_string (const DEBUG_EVENT &current_event,
-				    struct target_waitstatus *ourstatus) override;
+  bool handle_output_debug_string (const DEBUG_EVENT &current_event,
+				   struct target_waitstatus *ourstatus) override;
   void handle_load_dll (const char *dll_name, LPVOID base) override;
   void handle_unload_dll (const DEBUG_EVENT &current_event) override;
   bool handle_access_violation (const EXCEPTION_RECORD *rec) override;
-- 
2.43.2


  parent reply	other threads:[~2024-05-07 23:44 UTC|newest]

Thread overview: 64+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-05-07 23:41 [PATCH 00/34] Windows non-stop mode Pedro Alves
2024-05-07 23:42 ` [PATCH 01/34] Windows gdb: Dead code in windows_nat_target::do_initial_windows_stuff Pedro Alves
2024-05-08 14:39   ` Tom Tromey
2024-05-07 23:42 ` [PATCH 02/34] Windows gdb: Eliminate global current_process.dr[8] global Pedro Alves
2024-05-08 15:02   ` Tom Tromey
2024-05-07 23:42 ` [PATCH 03/34] Windows gdb+gdbserver: New find_thread, replaces thread_rec(DONT_INVALIDATE_CONTEXT) Pedro Alves
2024-05-08 15:03   ` Tom Tromey
2024-05-07 23:42 ` [PATCH 04/34] Windows gdb: handle_output_debug_string return type Pedro Alves
2024-05-08 14:43   ` Tom Tromey
2024-05-07 23:42 ` [PATCH 05/34] Windows gdb: Eliminate reload_context Pedro Alves
2024-05-08 14:45   ` Tom Tromey
2024-05-07 23:42 ` [PATCH 06/34] Windows gdb+gdbserver: Eliminate thread_rec(INVALIDATE_CONTEXT) calls Pedro Alves
2024-05-08 15:08   ` Tom Tromey
2024-05-07 23:42 ` [PATCH 07/34] Windows gdb+gdbserver: Eliminate DONT_SUSPEND Pedro Alves
2024-05-08 15:12   ` Tom Tromey
2024-05-07 23:42 ` [PATCH 08/34] Windows gdb+gdbserver: Eliminate windows_process_info::thread_rec Pedro Alves
2024-05-08 15:12   ` Tom Tromey
2024-05-07 23:42 ` [PATCH 09/34] Windows gdb: Simplify windows_nat_target::wait Pedro Alves
2024-05-07 23:42 ` [PATCH 10/34] Windows gdb+gdbserver: Move suspending thread to when returning event Pedro Alves
2024-05-07 23:42 ` [PATCH 11/34] Windows gdb: Introduce continue_last_debug_event_main_thread Pedro Alves
2024-05-08 14:53   ` Tom Tromey
2024-05-07 23:42 ` [PATCH 12/34] Windows gdb: Introduce windows_continue_flags Pedro Alves
2024-05-08 15:16   ` Tom Tromey
2024-05-07 23:42 ` [PATCH 13/34] Windows gdb: Factor code out of windows_nat_target::windows_continue Pedro Alves
2024-05-08 15:18   ` Tom Tromey
2024-05-07 23:42 ` [PATCH 14/34] Windows gdb: Pending stop and current_event Pedro Alves
2024-05-08 15:18   ` Tom Tromey
2024-05-07 23:42 ` [PATCH 15/34] Windows gdb+gdbserver: Elim desired_stop_thread_id / rework pending_stops Pedro Alves
2024-05-07 23:42 ` [PATCH 16/34] Windows gdb+gdbserver: Introduce get_last_debug_event_ptid Pedro Alves
2024-05-07 23:42 ` [PATCH 17/34] Windows gdb: Can't pass signal to thread other than last stopped thread Pedro Alves
2024-05-07 23:42 ` [PATCH 18/34] Windows gdbserver: Fix scheduler-locking Pedro Alves
2024-05-07 23:42 ` [PATCH 19/34] Windows gdb: Enable "set scheduler-locking on" Pedro Alves
2024-05-08 15:25   ` Tom Tromey
2024-05-07 23:42 ` [PATCH 20/34] Windows gdbserver: Eliminate soft-interrupt mechanism Pedro Alves
2024-05-08 15:26   ` Tom Tromey
2024-05-07 23:42 ` [PATCH 21/34] Windows gdb+gdbserver: Make current_event per-thread state Pedro Alves
2024-05-07 23:42 ` [PATCH 22/34] Windows gdb+gdbserver: Make last_sig " Pedro Alves
2024-05-07 23:42 ` [PATCH 23/34] Windows gdb+gdbserver: Make siginfo_er " Pedro Alves
2024-05-07 23:42 ` [PATCH 24/34] Add backpointer from windows_thread_info to windows_process_info Pedro Alves
2024-05-08 15:28   ` Tom Tromey
2024-05-07 23:42 ` [PATCH 25/34] Windows gdb+gdbserver: Share $_siginfo reading code Pedro Alves
2024-05-08 15:29   ` Tom Tromey
2024-05-07 23:42 ` [PATCH 26/34] Windows gdb+gdbserver: Eliminate struct pending_stop Pedro Alves
2024-05-07 23:42 ` [PATCH 27/34] Windows gdb: Change serial_event management Pedro Alves
2024-05-07 23:42 ` [PATCH 28/34] Windows gdb: cygwin_set_dr => windows_set_dr, etc Pedro Alves
2024-05-08 14:46   ` Tom Tromey
2024-05-07 23:42 ` [PATCH 29/34] windows_per_inferior::continue_one_thread, unify WoW64/non-WoW64 paths Pedro Alves
2024-05-07 23:42 ` [PATCH 30/34] windows-nat.c: Avoid writing debug registers if watchpoint hit pending Pedro Alves
2024-05-07 23:42 ` [PATCH 31/34] Windows gdb+gdbserver: Check whether DBG_REPLY_LATER is available Pedro Alves
2024-05-08 12:45   ` Eli Zaretskii
2024-05-08 21:33     ` [PATCH 31/34 v1.2] " Pedro Alves
2024-05-09 10:07       ` Hannes Domani
     [not found]         ` <86zfsz5kly.fsf@gnu.org>
2024-05-09 11:11           ` Pedro Alves
2024-05-09 11:47             ` [PATCH 31/34 v1.3] " Pedro Alves
2024-05-09 12:28               ` Eli Zaretskii
2024-05-09 14:17               ` Tom Tromey
     [not found]             ` <86r0eb5g2n.fsf@gnu.org>
2024-05-09 13:27               ` [PATCH 31/34 v1.2] " Pedro Alves
2024-05-07 23:42 ` Pedro Alves [this message]
2024-05-07 23:42 ` [PATCH 33/34] Windows gdb: Watchpoints while running (internal vs external stops) Pedro Alves
2024-05-07 23:42 ` [PATCH 34/34] Mention Windows non-stop support in NEWS Pedro Alves
2024-05-08 15:40 ` [PATCH 00/34] Windows non-stop mode Tom Tromey
2024-05-15 17:35   ` Tom Tromey
2024-05-15 20:39     ` Pedro Alves
2024-05-16 15:53       ` Tom Tromey

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240507234233.371123-33-pedro@palves.net \
    --to=pedro@palves.net \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).