Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Pedro Alves <pedro@palves.net>
To: gdb-patches@sourceware.org
Subject: [PATCH v2 37/47] Windows GDB: make windows_thread_info be private thread_info data
Date: Mon, 19 May 2025 14:22:58 +0100	[thread overview]
Message-ID: <20250519132308.3553663-38-pedro@palves.net> (raw)
In-Reply-To: <20250519132308.3553663-1-pedro@palves.net>

With Windows non-stop support, we'll add support for
target_thread_events to the Windows target.

When that is supported, and the core wants to be notified of thread
exit events, the target backend does not delete the thread for which
the event is being reported.  Instead, infrun takes care of that.

That causes one problem on Windows, which is that Windows maintains
its own separate Windows threads list, in parallel with the struct
thread_info thread list maintained by the core.  In the
target_thread_events events scenario, when infrun deletes the thread,
the corresponding object in the Windows backend thread list is left
dangling, causing problems.

Fix this by eliminating the parallel thread list from the Windows
backend, instead making the windows_thread_info data by registered as
the private data associated with thread_info, like other targets do.

It also adds a all_windows_threads walker function, and associated
range and iterator classes, so that most of the Windows target code
can iterate over Windows threads without having to worry about
fetching the Windows thread data out of thread_info's private data.

Change-Id: I5058969b46e0dd238c89b6c28403c1848f083683
---
 gdb/windows-nat.c | 170 ++++++++++++++++++++++++++++++++--------------
 1 file changed, 118 insertions(+), 52 deletions(-)

diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index d336889a251..492d2e3f55b 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -107,6 +107,26 @@ enum windows_continue_flag
 
 DEF_ENUM_FLAGS_TYPE (windows_continue_flag, windows_continue_flags);
 
+/* We want to register windows_thread_info as struct thread_info
+   private data.  thread_info::priv must point to a class that
+   inherits from private_thread_info.  But we can't make
+   windows_thread_info inherit private_thread_info, because
+   windows_thread_info is shared with GDBserver.  So we make a new
+   class that inherits from both private_thread_info,
+   windows_thread_info, and register that one as thread_info::private.
+   This multiple inheritance is benign, because private_thread_info is
+   a java-style interface class with no data.  */
+struct windows_private_thread_info : private_thread_info, windows_thread_info
+{
+  windows_private_thread_info (windows_process_info *proc,
+			       DWORD tid, HANDLE h, CORE_ADDR tlb)
+    : windows_thread_info (proc, tid, h, tlb)
+  {}
+
+  ~windows_private_thread_info () override
+  {}
+};
+
 struct windows_per_inferior : public windows_process_info
 {
   windows_thread_info *find_thread (ptid_t ptid) override;
@@ -124,8 +144,6 @@ struct windows_per_inferior : public windows_process_info
 
   int windows_initialization_done = 0;
 
-  std::vector<std::unique_ptr<windows_thread_info>> thread_list;
-
   /* Counts of things.  */
   int saw_create = 0;
   int open_process_used = 0;
@@ -415,6 +433,82 @@ struct windows_nat_target final : public x86_nat_target<inf_child_target>
   bool m_continued = false;
 };
 
+/* Get the windows_thread_info object associated with THR.  */
+
+static windows_thread_info *
+as_windows_thread_info (thread_info *thr)
+{
+  /* Cast to windows_private_thread_info, which inherits from
+     private_thread_info, and is implicitly convertible to
+     windows_thread_info, the return type.  */
+  return static_cast<windows_private_thread_info *> (thr->priv.get ());
+}
+
+/* Creates an iterator that works like all_matching_threads_iterator,
+   but that returns windows_thread_info pointers instead of
+   thread_info.  This could be replaced with a std::range::transform
+   when we require C++20.  */
+class all_windows_threads_iterator
+{
+public:
+  typedef all_windows_threads_iterator self_type;
+  typedef windows_thread_info *value_type;
+  typedef windows_thread_info *&reference;
+  typedef windows_thread_info **pointer;
+  typedef std::forward_iterator_tag iterator_category;
+  typedef int difference_type;
+
+  explicit all_windows_threads_iterator (all_non_exited_threads_iterator base_iter)
+    : m_base_iter (base_iter)
+  {}
+
+  windows_thread_info *operator* () const { return as_windows_thread_info (*m_base_iter); }
+
+  all_windows_threads_iterator &operator++ ()
+  {
+    ++m_base_iter;
+    return *this;
+  }
+
+  bool operator== (const all_windows_threads_iterator &other) const
+  { return m_base_iter == other.m_base_iter; }
+
+  bool operator!= (const all_windows_threads_iterator &other) const
+  { return !(*this == other); }
+
+private:
+  all_non_exited_threads_iterator m_base_iter;
+};
+
+/* The range for all_windows_threads, below.  */
+
+class all_windows_threads_range : public all_non_exited_threads_range
+{
+public:
+  all_windows_threads_range (all_non_exited_threads_range base_range)
+    : m_base_range (base_range)
+  {}
+
+  all_windows_threads_iterator begin () const
+  { return all_windows_threads_iterator (m_base_range.begin ()); }
+  all_windows_threads_iterator end () const
+  { return all_windows_threads_iterator (m_base_range.end ()); }
+
+private:
+  all_non_exited_threads_range m_base_range;
+};
+
+/* Return a range that can be used to walk over all non-exited Windows
+   threads of all inferiors, with range-for.  */
+
+inline all_windows_threads_range
+all_windows_threads ()
+{
+  auto *win_tgt = static_cast<windows_nat_target *> (get_native_target ());
+  return (all_windows_threads_range
+	  (all_non_exited_threads_range (win_tgt, minus_one_ptid)));
+}
+
 static void
 check (BOOL ok, const char *file, int line)
 {
@@ -562,10 +656,11 @@ windows_nat_target::continue_last_debug_event_main_thread
 windows_thread_info *
 windows_per_inferior::find_thread (ptid_t ptid)
 {
-  for (auto &th : thread_list)
-    if (th->tid == ptid.lwp ())
-      return th.get ();
-  return nullptr;
+  auto *win_tgt = static_cast<windows_nat_target *> (get_native_target ());
+  thread_info *thr = win_tgt->find_thread (ptid);
+  if (thr == nullptr)
+    return nullptr;
+  return as_windows_thread_info (thr);
 }
 
 /* Invalidate TH's context.  */
@@ -593,12 +688,11 @@ windows_thread_info *
 windows_nat_target::add_thread (ptid_t ptid, HANDLE h, void *tlb,
 				bool main_thread_p)
 {
-  windows_thread_info *th;
-
   gdb_assert (ptid.lwp () != 0);
 
-  if ((th = windows_process.find_thread (ptid)))
-    return th;
+  windows_thread_info *existing = windows_process.find_thread (ptid);
+  if (existing != nullptr)
+    return existing;
 
   CORE_ADDR base = (CORE_ADDR) (uintptr_t) tlb;
 #ifdef __x86_64__
@@ -607,18 +701,18 @@ windows_nat_target::add_thread (ptid_t ptid, HANDLE h, void *tlb,
   if (windows_process.wow64_process)
     base += 0x2000;
 #endif
-  th = new windows_thread_info (&windows_process, ptid.lwp (), h, base);
-  windows_process.thread_list.emplace_back (th);
+  windows_private_thread_info *th
+    = new windows_private_thread_info (&windows_process, ptid.lwp (), h, base);
 
   /* Add this new thread to the list of threads.
 
      To be consistent with what's done on other platforms, we add
      the main thread silently (in reality, this thread is really
      more of a process to the user than a thread).  */
-  if (main_thread_p)
-    add_thread_silent (this, ptid);
-  else
-    ::add_thread (this, ptid);
+  thread_info *gth = (main_thread_p
+		      ? ::add_thread_silent (this, ptid)
+		      : ::add_thread (this, ptid));
+  gth->priv.reset (th);
 
   /* It's simplest to always set this and update the debug
      registers.  */
@@ -627,15 +721,6 @@ windows_nat_target::add_thread (ptid_t ptid, HANDLE h, void *tlb,
   return th;
 }
 
-/* Clear out any old thread list and reinitialize it to a
-   pristine state.  */
-static void
-windows_init_thread_list (void)
-{
-  DEBUG_EVENTS ("called");
-  windows_process.thread_list.clear ();
-}
-
 /* Delete a thread from the list of threads.
 
    PTID is the ptid of the thread to be deleted.
@@ -647,12 +732,6 @@ void
 windows_nat_target::delete_thread (ptid_t ptid, DWORD exit_code,
 				   bool main_thread_p)
 {
-  DWORD id;
-
-  gdb_assert (ptid.lwp () != 0);
-
-  id = ptid.lwp ();
-
   /* Note that no notification was printed when the main thread was
      created, and thus, unless in verbose mode, we should be symmetrical,
      and avoid an exit notification for the main thread here as well.  */
@@ -660,16 +739,6 @@ windows_nat_target::delete_thread (ptid_t ptid, DWORD exit_code,
   bool silent = (main_thread_p && !info_verbose);
   thread_info *to_del = this->find_thread (ptid);
   delete_thread_with_exit_code (to_del, exit_code, silent);
-
-  auto iter = std::find_if (windows_process.thread_list.begin (),
-			    windows_process.thread_list.end (),
-			    [=] (std::unique_ptr<windows_thread_info> &th)
-			    {
-			      return th->tid == id;
-			    });
-
-  if (iter != windows_process.thread_list.end ())
-    windows_process.thread_list.erase (iter);
 }
 
 /* Fetches register number R from the given windows_thread_info,
@@ -1318,7 +1387,7 @@ BOOL
 windows_nat_target::windows_continue (DWORD continue_status, int id,
 				      windows_continue_flags cont_flags)
 {
-  for (auto &th : windows_process.thread_list)
+  for (auto *th : all_windows_threads ())
     {
       if ((id == -1 || id == (int) th->tid)
 	  && !th->suspended
@@ -1336,9 +1405,9 @@ windows_nat_target::windows_continue (DWORD continue_status, int id,
 	}
     }
 
-  for (auto &th : windows_process.thread_list)
+  for (auto *th : all_windows_threads ())
     if (id == -1 || id == (int) th->tid)
-      windows_process.continue_one_thread (th.get (), cont_flags);
+      windows_process.continue_one_thread (th, cont_flags);
 
   continue_last_debug_event_main_thread
     (_("Failed to resume program execution"), continue_status,
@@ -1516,7 +1585,7 @@ windows_nat_target::get_windows_debug_event
   /* If there is a relevant pending stop, report it now.  See the
      comment by the definition of "windows_thread_info::pending_status"
      for details on why this is needed.  */
-  for (auto &th : windows_process.thread_list)
+  for (auto *th : all_windows_threads ())
     {
       if (!th->suspended
 	  && th->pending_status.kind () != TARGET_WAITKIND_IGNORE)
@@ -1529,7 +1598,7 @@ 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 ());
+	  windows_process.invalidate_context (th);
 	  return ptid;
 	}
     }
@@ -1833,7 +1902,7 @@ windows_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
 
 	      /* All-stop, suspend all threads until they are
 		 explicitly resumed.  */
-	      for (auto &thr : windows_process.thread_list)
+	      for (auto *thr : all_windows_threads ())
 		thr->suspend ();
 	    }
 
@@ -2003,7 +2072,6 @@ windows_nat_target::attach (const char *args, int from_tty)
     warning ("Failed to get SE_DEBUG_NAME privilege\n"
 	     "This can cause attach to fail on Windows NT/2K/XP");
 
-  windows_init_thread_list ();
   windows_process.saw_create = 0;
 
   std::optional<unsigned> err;
@@ -2756,7 +2824,6 @@ windows_nat_target::create_inferior (const char *exec_file,
 	}
     }
 
-  windows_init_thread_list ();
   do_synchronously ([&] ()
     {
       BOOL ok = create_process (nullptr, args, flags, w32_env,
@@ -2885,7 +2952,6 @@ windows_nat_target::create_inferior (const char *exec_file,
   /* Final nil string to terminate new env.  */
   *temp = 0;
 
-  windows_init_thread_list ();
   do_synchronously ([&] ()
     {
       BOOL ok = create_process (nullptr, /* image */
@@ -3254,7 +3320,7 @@ windows_set_dr (int i, CORE_ADDR addr)
   if (i < 0 || i > 3)
     internal_error (_("Invalid register %d in windows_set_dr.\n"), i);
 
-  for (auto &th : windows_process.thread_list)
+  for (auto *th : all_windows_threads ())
     th->debug_registers_changed = true;
 }
 
@@ -3264,7 +3330,7 @@ windows_set_dr (int i, CORE_ADDR addr)
 static void
 windows_set_dr7 (unsigned long val)
 {
-  for (auto &th : windows_process.thread_list)
+  for (auto *th : all_windows_threads ())
     th->debug_registers_changed = true;
 }
 
-- 
2.49.0


  parent reply	other threads:[~2025-05-19 13:37 UTC|newest]

Thread overview: 97+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-05-19 13:22 [PATCH v2 00/47] Windows non-stop mode Pedro Alves
2025-05-19 13:22 ` [PATCH v2 01/47] Make default_gdb_exit resilient to failed closes Pedro Alves
2025-05-19 13:56   ` Andrew Burgess
2025-06-06 13:56     ` Pedro Alves
2025-05-19 13:22 ` [PATCH v2 02/47] Add test for continuing with some threads running Pedro Alves
2025-05-21 19:36   ` Kevin Buettner
2026-04-02 13:07     ` Pedro Alves
2025-05-19 13:22 ` [PATCH v2 03/47] infrun: Remove unnecessary currently_stepping call Pedro Alves
2025-05-21 19:44   ` Kevin Buettner
2026-04-02 13:17     ` Pedro Alves
2025-05-19 13:22 ` [PATCH v2 04/47] infrun: Split currently_stepping, fix sw watchpoints issue Pedro Alves
2026-04-02 13:33   ` Pedro Alves
2025-05-19 13:22 ` [PATCH v2 05/47] thread_info::executing+resumed -> thread_info::internal_state Pedro Alves
2026-04-06 18:01   ` Pedro Alves
2025-05-19 13:22 ` [PATCH v2 06/47] Windows gdb: Dead code in windows_nat_target::do_initial_windows_stuff Pedro Alves
2025-05-19 13:22 ` [PATCH v2 07/47] Windows gdb: Eliminate global current_process.dr[8] global Pedro Alves
2025-05-28 19:09   ` Tom Tromey
2026-04-06 19:44   ` Pedro Alves
2025-05-19 13:22 ` [PATCH v2 08/47] Windows gdb+gdbserver: New find_thread, replaces thread_rec(DONT_INVALIDATE_CONTEXT) Pedro Alves
2025-05-19 13:22 ` [PATCH v2 09/47] Windows gdb: handle_output_debug_string return type Pedro Alves
2025-05-19 13:22 ` [PATCH v2 10/47] Windows gdb: Eliminate reload_context Pedro Alves
2025-05-19 13:22 ` [PATCH v2 11/47] Windows gdb+gdbserver: Eliminate thread_rec(INVALIDATE_CONTEXT) calls Pedro Alves
2025-05-19 13:22 ` [PATCH v2 12/47] Windows gdb+gdbserver: Eliminate DONT_SUSPEND Pedro Alves
2025-05-19 13:22 ` [PATCH v2 13/47] Windows gdb+gdbserver: Eliminate windows_process_info::thread_rec Pedro Alves
2025-05-19 13:22 ` [PATCH v2 14/47] Windows gdb: Simplify windows_nat_target::wait Pedro Alves
2025-05-28 19:16   ` Tom Tromey
2025-05-19 13:22 ` [PATCH v2 15/47] Windows gdb+gdbserver: Move suspending thread to when returning event Pedro Alves
2025-05-28 19:17   ` Tom Tromey
2025-05-19 13:22 ` [PATCH v2 16/47] Windows gdb: Introduce continue_last_debug_event_main_thread Pedro Alves
2025-05-28 19:18   ` Tom Tromey
2025-05-19 13:22 ` [PATCH v2 17/47] Windows gdb: Introduce windows_continue_flags Pedro Alves
2025-05-19 13:22 ` [PATCH v2 18/47] Windows gdb: Factor code out of windows_nat_target::windows_continue Pedro Alves
2025-05-19 13:22 ` [PATCH v2 19/47] Windows gdb: Pending stop and current_event Pedro Alves
2025-05-19 13:22 ` [PATCH v2 20/47] Windows gdb+gdbserver: Elim desired_stop_thread_id / rework pending_stops Pedro Alves
2025-05-30 20:41   ` Tom Tromey
2025-05-19 13:22 ` [PATCH v2 21/47] Windows gdb+gdbserver: Introduce get_last_debug_event_ptid Pedro Alves
2025-05-28 19:21   ` Tom Tromey
2025-05-19 13:22 ` [PATCH v2 22/47] Windows gdb: Can't pass signal to thread other than last stopped thread Pedro Alves
2025-05-28 19:22   ` Tom Tromey
2025-05-19 13:22 ` [PATCH v2 23/47] Windows gdbserver: Fix scheduler-locking Pedro Alves
2025-05-30 20:37   ` Tom Tromey
2025-05-19 13:22 ` [PATCH v2 24/47] Windows gdb: Enable "set scheduler-locking on" Pedro Alves
2025-05-19 13:22 ` [PATCH v2 25/47] Windows gdbserver: Eliminate soft-interrupt mechanism Pedro Alves
2025-05-19 13:22 ` [PATCH v2 26/47] Windows gdb+gdbserver: Make current_event per-thread state Pedro Alves
2025-05-28 19:30   ` Tom Tromey
2025-05-19 13:22 ` [PATCH v2 27/47] Windows gdb+gdbserver: Make last_sig " Pedro Alves
2025-05-28 19:31   ` Tom Tromey
2025-05-19 13:22 ` [PATCH v2 28/47] Windows gdb+gdbserver: Make siginfo_er " Pedro Alves
2025-05-28 19:33   ` Tom Tromey
2025-05-19 13:22 ` [PATCH v2 29/47] Add backpointer from windows_thread_info to windows_process_info Pedro Alves
2025-05-19 13:22 ` [PATCH v2 30/47] Windows gdb+gdbserver: Share $_siginfo reading code Pedro Alves
2025-05-19 13:22 ` [PATCH v2 31/47] Windows gdb+gdbserver: Eliminate struct pending_stop Pedro Alves
2025-05-28 19:36   ` Tom Tromey
2025-05-19 13:22 ` [PATCH v2 32/47] Windows gdb: Change serial_event management Pedro Alves
2025-05-28 19:37   ` Tom Tromey
2025-05-19 13:22 ` [PATCH v2 33/47] Windows gdb: cygwin_set_dr => windows_set_dr, etc Pedro Alves
2025-05-19 13:22 ` [PATCH v2 34/47] Windows gdb: Avoid writing debug registers if watchpoint hit pending Pedro Alves
2025-05-30 20:43   ` Tom Tromey
2025-05-19 13:22 ` [PATCH v2 35/47] Windows gdb+gdbserver: Check whether DBG_REPLY_LATER is available Pedro Alves
2025-05-19 13:22 ` [PATCH v2 36/47] linux-nat: Factor out get_detach_signal code to common code Pedro Alves
2025-05-28 19:44   ` Tom Tromey
2025-05-19 13:22 ` Pedro Alves [this message]
2025-05-28 19:52   ` [PATCH v2 37/47] Windows GDB: make windows_thread_info be private thread_info data Tom Tromey
2025-05-19 13:22 ` [PATCH v2 38/47] Introduce windows_nat::event_code_to_string Pedro Alves
2025-05-28 19:53   ` Tom Tromey
2025-05-19 13:23 ` [PATCH v2 39/47] Windows gdb: Add non-stop support Pedro Alves
2025-06-05 16:21   ` Tom Tromey
2025-05-19 13:23 ` [PATCH v2 40/47] Windows gdb: Eliminate invalidate_context Pedro Alves
2025-05-28 19:54   ` Tom Tromey
2025-05-19 13:23 ` [PATCH v2 41/47] Windows gdb: Watchpoints while running (internal vs external stops) Pedro Alves
2025-05-30 20:50   ` Tom Tromey
2025-05-19 13:23 ` [PATCH v2 42/47] gdb_test_multiple: Anchor prompt match if -lbl Pedro Alves
2025-05-21 15:19   ` Tom de Vries
2025-05-27 22:41     ` Pedro Alves
2025-05-27 23:20       ` Pedro Alves
2025-05-28 11:59         ` [PATCH v2] of " Pedro Alves
2025-06-05 16:37           ` Pedro Alves
2025-06-05 17:20             ` [PATCH v3] " Pedro Alves
2025-06-06  9:58               ` Tom de Vries
2025-06-06 13:53                 ` Pedro Alves
2025-05-19 13:23 ` [PATCH v2 43/47] Windows gdb: extra thread info => show exiting Pedro Alves
2025-05-28 19:58   ` Tom Tromey
2025-05-19 13:23 ` [PATCH v2 44/47] Add gdb.threads/leader-exit-schedlock.exp Pedro Alves
2025-05-29 16:09   ` Tom Tromey
2025-05-19 13:23 ` [PATCH v2 45/47] infrun: with AS+NS, prefer process exit over thread exit Pedro Alves
2025-05-19 13:23 ` [PATCH v2 46/47] Windows gdb: Always non-stop (default to "maint set target-non-stop on") Pedro Alves
2025-05-29 16:02   ` Tom Tromey
2025-05-19 13:23 ` [PATCH v2 47/47] Mention Windows scheduler-locking and non-stop support in NEWS Pedro Alves
2025-05-19 14:07   ` Eli Zaretskii
2025-06-05 17:57 ` [PATCH v2 00/47] Windows non-stop mode Tom Tromey
2025-06-11 22:06   ` [PATCH] Improve attach on Windows (was: Re: [PATCH v2 00/47] Windows non-stop mode) Pedro Alves
2026-04-02 12:21     ` [PATCH] Improve attach on Windows Pedro Alves
2026-04-02 18:52       ` Tom Tromey
2025-06-11 23:51   ` [PATCH v2 00/47] Windows non-stop mode Pedro Alves
2025-06-12 19:23     ` Tom Tromey
2025-06-13 10:34       ` Pedro Alves
2025-06-13 14:23         ` 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=20250519132308.3553663-38-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