]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Introduce gdbsupport/cxx-thread.h and use it
authorTom Tromey <tromey@adacore.com>
Wed, 1 Oct 2025 16:27:15 +0000 (10:27 -0600)
committerTom Tromey <tromey@adacore.com>
Thu, 2 Oct 2025 17:55:14 +0000 (11:55 -0600)
This introduces a new file, gdbsupport/cxx-thread.h, which provides
stubs for the C++ threading functionality on systems that don't
support it.

On fully-working ports, this header just supplies a number of aliases
in the gdb namespace.  So, for instance, gdb::mutex is just an alias
for std::mutex.

For non-working ports, compatibility stubs are provided for the subset
of threading functionality that's used in gdb.  These generally do
nothing and assume single-threaded operation.

The idea behind this is to reduce the number of checks of
CXX_STD_THREAD, making the code cleaner.

Not all spots using CXX_STD_THREAD could readily be converted.
In particular:

* Unit tests
* --config output
* Code manipulating threads themselves
* The extension interrupting handling code

These all seem fine to me.

Note there's also a check in py-dap.c.  This one is perhaps slightly
subtle: DAP starts threads on the Python side, but it relies on gdb
itself being thread-savvy, for instance in gdb.post_event.

Approved-By: Simon Marchi <simon.marchi@efficios.com>
gdb/complaints.c
gdb/dwarf2/cooked-index-worker.c
gdb/dwarf2/cooked-index-worker.h
gdb/dwarf2/read.c
gdb/dwarf2/read.h
gdb/gdb_bfd.c
gdb/minsyms.c
gdb/run-on-main-thread.c
gdbsupport/cxx-thread.h [new file with mode: 0644]
gdbsupport/thread-pool.h

index 3c7f9bc7c89604de766672ef0f4c78005ed7d83b..03d27d615a58e6e5e7d89cda951bbbb2ae9c53a2 100644 (file)
 #include "cli/cli-cmds.h"
 #include "run-on-main-thread.h"
 #include "top.h"
+#include "gdbsupport/cxx-thread.h"
 #include "gdbsupport/selftest.h"
 #include "gdbsupport/unordered_map.h"
-#if CXX_STD_THREAD
-#include <mutex>
-#endif
 
 /* Map format strings to counters.  */
 
@@ -38,9 +36,7 @@ static gdb::unordered_map<const char *, int> counters;
 
 int stop_whining = 0;
 
-#if CXX_STD_THREAD
-static std::mutex complaint_mutex;
-#endif /* CXX_STD_THREAD */
+static gdb::mutex complaint_mutex;
 
 /* See complaints.h.  */
 
@@ -50,9 +46,7 @@ complaint_internal (const char *fmt, ...)
   va_list args;
 
   {
-#if CXX_STD_THREAD
-    std::lock_guard<std::mutex> guard (complaint_mutex);
-#endif
+    gdb::lock_guard<gdb::mutex> guard (complaint_mutex);
     if (++counters[fmt] > stop_whining)
       return;
   }
@@ -126,9 +120,7 @@ re_emit_complaints (const complaint_collection &complaints)
 void
 complaint_interceptor::warn (const char *fmt, va_list args)
 {
-#if CXX_STD_THREAD
-  std::lock_guard<std::mutex> guard (complaint_mutex);
-#endif
+  gdb::lock_guard<gdb::mutex> guard (complaint_mutex);
   g_complaint_interceptor->m_complaints.insert (string_vprintf (fmt, args));
 }
 
index 09d80eff629c4d168e3108e5ee5d9bd082be6543..abaf41fb00c315e2e3261d0293e25faed4a147c9 100644 (file)
@@ -132,9 +132,8 @@ bool
 cooked_index_worker::wait (cooked_state desired_state, bool allow_quit)
 {
   bool done;
-#if CXX_STD_THREAD
   {
-    std::unique_lock<std::mutex> lock (m_mutex);
+    gdb::unique_lock<gdb::mutex> lock (m_mutex);
 
     /* This may be called from a non-main thread -- this functionality
        is needed for the index cache -- but in this case we require
@@ -146,7 +145,7 @@ cooked_index_worker::wait (cooked_state desired_state, bool allow_quit)
        if (allow_quit)
          {
            std::chrono::milliseconds duration { 15 };
-           if (m_cond.wait_for (lock, duration) == std::cv_status::timeout)
+           if (m_cond.wait_for (lock, duration) == gdb::cv_status::timeout)
              QUIT;
          }
        else
@@ -154,11 +153,6 @@ cooked_index_worker::wait (cooked_state desired_state, bool allow_quit)
       }
     done = m_state == cooked_state::CACHE_DONE;
   }
-#else
-  /* Without threads, all the work is done immediately on the main
-     thread, and there is never anything to wait for.  */
-  done = desired_state == cooked_state::CACHE_DONE;
-#endif /* CXX_STD_THREAD */
 
   /* Only the main thread is allowed to report complaints and the
      like.  */
@@ -213,15 +207,10 @@ cooked_index_worker::set (cooked_state desired_state)
 {
   gdb_assert (desired_state != cooked_state::INITIAL);
 
-#if CXX_STD_THREAD
-  std::lock_guard<std::mutex> guard (m_mutex);
+  gdb::lock_guard<gdb::mutex> guard (m_mutex);
   gdb_assert (desired_state > m_state);
   m_state = desired_state;
   m_cond.notify_one ();
-#else
-  /* Without threads, all the work is done immediately on the main
-     thread, and there is never anything to do.  */
-#endif /* CXX_STD_THREAD */
 }
 
 /* See cooked-index-worker.h.  */
index dfdc82f52744ec36e626ab883d31a5d44071b063..4213b4e12b7214520f3856494777bee8aa9e2e01 100644 (file)
 #include "dwarf2/read.h"
 #include "maint.h"
 #include "run-on-main-thread.h"
-
-#if CXX_STD_THREAD
-#include <mutex>
-#include <condition_variable>
-#endif /* CXX_STD_THREAD */
+#include "gdbsupport/cxx-thread.h"
 
 using cutu_reader_up = std::unique_ptr<cutu_reader>;
 
@@ -300,11 +296,9 @@ protected:
   /* Result of each worker task.  */
   std::vector<cooked_index_worker_result> m_results;
 
-#if CXX_STD_THREAD
   /* Mutex to synchronize access to M_RESULTS when workers append their
      result.  */
-  std::mutex m_results_mutex;
-#endif /* CXX_STD_THREAD */
+  gdb::mutex m_results_mutex;
 
   /* Any warnings emitted.  For the time being at least, this only
      needed in do_reading, not in every worker.  Note that
@@ -317,13 +311,12 @@ protected:
      parent relationships.  */
   parent_map_map m_all_parents_map;
 
-#if CXX_STD_THREAD
   /* Current state of this object.  */
   cooked_state m_state = cooked_state::INITIAL;
   /* Mutex and condition variable used to synchronize.  */
-  std::mutex m_mutex;
-  std::condition_variable m_cond;
-#endif /* CXX_STD_THREAD */
+  gdb::mutex m_mutex;
+  gdb::condition_variable m_cond;
+
   /* This flag indicates whether any complaints or exceptions that
      arose during scanning have been reported by 'wait'.  This may
      only be modified on the main thread.  */
index db535bcf66963d406e0fc964126dee756fee34f5..955893c5f0c148231758d7146b3175d35f85c6e7 100644 (file)
@@ -604,10 +604,8 @@ struct dwp_file
   dwo_unit_set loaded_cus;
   dwo_unit_set loaded_tus;
 
-#if CXX_STD_THREAD
   /* Mutex to synchronize access to LOADED_CUS and LOADED_TUS.  */
-  std::mutex loaded_cutus_lock;
-#endif
+  gdb::mutex loaded_cutus_lock;
 
   /* Table to map ELF section numbers to their sections.
      This is only needed for the DWP V1 file format.  */
@@ -3326,9 +3324,7 @@ private:
       m_thread_storage.done_reading (m_complaint_handler.release ());
 
       /* Append the results of this worker to the parent instance.  */
-#if CXX_STD_THREAD
-      std::lock_guard<std::mutex> lock (m_parent->m_results_mutex);
-#endif
+      gdb::lock_guard<gdb::mutex> lock (m_parent->m_results_mutex);
       m_parent->m_results.emplace_back (std::move (m_thread_storage));
     }
 
@@ -6317,12 +6313,8 @@ static dwo_file *
 lookup_dwo_file (dwarf2_per_bfd *per_bfd, const char *dwo_name,
                 const char *comp_dir)
 {
-#if CXX_STD_THREAD
-  std::lock_guard<std::mutex> guard (per_bfd->dwo_files_lock);
-#endif
-
+  gdb::lock_guard<gdb::mutex> guard (per_bfd->dwo_files_lock);
   auto it = per_bfd->dwo_files.find (dwo_file_search {dwo_name, comp_dir});
-
   return it != per_bfd->dwo_files.end () ? it->get() : nullptr;
 }
 
@@ -6337,10 +6329,7 @@ lookup_dwo_file (dwarf2_per_bfd *per_bfd, const char *dwo_name,
 static dwo_file *
 add_dwo_file (dwarf2_per_bfd *per_bfd, dwo_file_up dwo_file)
 {
-#if CXX_STD_THREAD
-  std::lock_guard<std::mutex> lock (per_bfd->dwo_files_lock);
-#endif
-
+  gdb::lock_guard<gdb::mutex> lock (per_bfd->dwo_files_lock);
   return per_bfd->dwo_files.emplace (std::move (dwo_file)).first->get ();
 }
 
@@ -7472,10 +7461,7 @@ lookup_dwo_unit_in_dwp (dwarf2_per_bfd *per_bfd,
     = is_debug_types ? dwp_file->loaded_tus : dwp_file->loaded_cus;
 
   {
-#if CXX_STD_THREAD
-    std::lock_guard<std::mutex> guard (dwp_file->loaded_cutus_lock);
-#endif
-
+    gdb::lock_guard<gdb::mutex> guard (dwp_file->loaded_cutus_lock);
     if (auto it = dwo_unit_set.find (signature);
        it != dwo_unit_set.end ())
       return it->get ();
@@ -7510,10 +7496,7 @@ lookup_dwo_unit_in_dwp (dwarf2_per_bfd *per_bfd,
 
          /* If another thread raced with this one, opening the exact same
             DWO unit, then we'll keep that other thread's copy.  */
-#if CXX_STD_THREAD
-         std::lock_guard<std::mutex> guard (dwp_file->loaded_cutus_lock);
-#endif
-
+         gdb::lock_guard<gdb::mutex> guard (dwp_file->loaded_cutus_lock);
          auto it = dwo_unit_set.emplace (std::move (dwo_unit)).first;
          return it->get ();
        }
@@ -7593,12 +7576,10 @@ try_open_dwop_file (dwarf2_per_bfd *per_bfd, const char *file_name, int is_dwp,
     return NULL;
 
   {
-#if CXX_STD_THREAD
     /* The operations below are not thread-safe, use a lock to synchronize
        concurrent accesses.  */
-    static std::mutex mutex;
-    std::lock_guard<std::mutex> lock (mutex);
-#endif
+    static gdb::mutex mutex;
+    gdb::lock_guard<gdb::mutex> lock (mutex);
 
     if (!bfd_check_format (sym_bfd.get (), bfd_object))
       return NULL;
index 2f9ad05b792bfeeb059229274fd43d2dc6ebc3d3..f0d46f6acc6cd95381c53c7b21876df9b2087661 100644 (file)
@@ -20,9 +20,6 @@
 #ifndef GDB_DWARF2_READ_H
 #define GDB_DWARF2_READ_H
 
-#if CXX_STD_THREAD
-#include <mutex>
-#endif
 #include <queue>
 #include "dwarf2/abbrev.h"
 #include "dwarf2/unit-head.h"
@@ -32,6 +29,7 @@
 #include "dwarf2/section.h"
 #include "dwarf2/cu.h"
 #include "dwarf2/dwz.h"
+#include "gdbsupport/cxx-thread.h"
 #include "gdbsupport/gdb_obstack.h"
 #include "gdbsupport/function-view.h"
 #include "gdbsupport/packed.h"
@@ -618,10 +616,8 @@ public:
   /* Set of dwo_file objects.  */
   dwo_file_up_set dwo_files;
 
-#if CXX_STD_THREAD
   /* Mutex to synchronize access to DWO_FILES.  */
-  std::mutex dwo_files_lock;
-#endif
+  gdb::mutex dwo_files_lock;
 
   /* The DWP file if there is one, or NULL.  */
   dwp_file_up dwp_file;
index 2e477eba6a9b8f31c558aeb6d37cdeeed369a6e6..4c641fe4c7b88094003b47ca77040b533f039453 100644 (file)
 #include "gdbsupport/fileio.h"
 #include "inferior.h"
 #include "cli/cli-style.h"
+#include "gdbsupport/cxx-thread.h"
 #include "gdbsupport/unordered_map.h"
 #include "gdbsupport/unordered_set.h"
 
-#if CXX_STD_THREAD
-
-#include <mutex>
-
 /* Lock held when doing BFD operations.  A recursive mutex is used
    because we use this mutex internally and also for BFD, just to make
    life a bit simpler, and we may sometimes hold it while calling into
    BFD.  */
-static std::recursive_mutex gdb_bfd_mutex;
+static gdb::recursive_mutex gdb_bfd_mutex;
 
 /* BFD locking function.  */
 
@@ -64,8 +61,6 @@ gdb_bfd_unlock (void *ignore)
   return true;
 }
 
-#endif /* CXX_STD_THREAD */
-
 /* An object of this type is stored in the section's user data when
    mapping a section.  */
 
@@ -153,7 +148,6 @@ struct gdb_bfd_data
   /* The registry.  */
   registry<bfd> registry_fields;
 
-#if CXX_STD_THREAD
   /* Most of the locking needed for multi-threaded operation is
      handled by BFD itself.  However, the current BFD model is that
      locking is only needed for global operations -- but it turned out
@@ -163,8 +157,7 @@ struct gdb_bfd_data
      This lock is the fix: wrappers for important BFD functions will
      acquire this lock before performing operations that might modify
      the state of this BFD.  */
-  std::mutex per_bfd_mutex;
-#endif
+  gdb::mutex per_bfd_mutex;
 };
 
 registry<bfd> *
@@ -548,9 +541,7 @@ gdb_bfd_open (const char *name, const char *target, int fd,
       name += strlen (TARGET_SYSROOT_PREFIX);
     }
 
-#if CXX_STD_THREAD
-  std::lock_guard<std::recursive_mutex> guard (gdb_bfd_mutex);
-#endif
+  gdb::lock_guard<gdb::recursive_mutex> guard (gdb_bfd_mutex);
 
   if (fd == -1)
     {
@@ -677,9 +668,7 @@ gdb_bfd_ref (struct bfd *abfd)
   if (abfd == NULL)
     return;
 
-#if CXX_STD_THREAD
-  std::lock_guard<std::recursive_mutex> guard (gdb_bfd_mutex);
-#endif
+  gdb::lock_guard<gdb::recursive_mutex> guard (gdb_bfd_mutex);
 
   gdata = (struct gdb_bfd_data *) bfd_usrdata (abfd);
 
@@ -709,9 +698,7 @@ gdb_bfd_unref (struct bfd *abfd)
   if (abfd == NULL)
     return;
 
-#if CXX_STD_THREAD
-  std::lock_guard<std::recursive_mutex> guard (gdb_bfd_mutex);
-#endif
+  gdb::lock_guard<gdb::recursive_mutex> guard (gdb_bfd_mutex);
 
   gdata = (struct gdb_bfd_data *) bfd_usrdata (abfd);
   gdb_assert (gdata->refc >= 1);
@@ -779,10 +766,8 @@ gdb_bfd_map_section (asection *sectp, bfd_size_type *size)
 
   abfd = sectp->owner;
 
-#if CXX_STD_THREAD
   gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd);
-  std::lock_guard<std::mutex> guard (gdata->per_bfd_mutex);
-#endif
+  gdb::lock_guard<gdb::mutex> guard (gdata->per_bfd_mutex);
 
   descriptor = get_section_descriptor (sectp);
 
@@ -1115,10 +1100,8 @@ bool
 gdb_bfd_get_full_section_contents (bfd *abfd, asection *section,
                                   gdb::byte_vector *contents)
 {
-#if CXX_STD_THREAD
   gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd);
-  std::lock_guard<std::mutex> guard (gdata->per_bfd_mutex);
-#endif
+  gdb::lock_guard<gdb::mutex> guard (gdata->per_bfd_mutex);
 
   bfd_size_type section_size = bfd_section_size (section);
 
@@ -1133,10 +1116,8 @@ gdb_bfd_get_full_section_contents (bfd *abfd, asection *section,
 int
 gdb_bfd_stat (bfd *abfd, struct stat *sbuf)
 {
-#if CXX_STD_THREAD
   gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd);
-  std::lock_guard<std::mutex> guard (gdata->per_bfd_mutex);
-#endif
+  gdb::lock_guard<gdb::mutex> guard (gdata->per_bfd_mutex);
 
   return bfd_stat (abfd, sbuf);
 }
@@ -1146,10 +1127,8 @@ gdb_bfd_stat (bfd *abfd, struct stat *sbuf)
 long
 gdb_bfd_get_mtime (bfd *abfd)
 {
-#if CXX_STD_THREAD
   gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd);
-  std::lock_guard<std::mutex> guard (gdata->per_bfd_mutex);
-#endif
+  gdb::lock_guard<gdb::mutex> guard (gdata->per_bfd_mutex);
 
   return bfd_get_mtime (abfd);
 }
@@ -1290,9 +1269,7 @@ get_bfd_inferior_data (struct inferior *inf)
 static unsigned long
 increment_bfd_error_count (const std::string &str)
 {
-#if CXX_STD_THREAD
-  std::lock_guard<std::recursive_mutex> guard (gdb_bfd_mutex);
-#endif
+  gdb::lock_guard<gdb::recursive_mutex> guard (gdb_bfd_mutex);
   struct bfd_inferior_data *bid = get_bfd_inferior_data (current_inferior ());
 
   auto &map = bid->bfd_error_string_counts;
@@ -1337,9 +1314,7 @@ gdb_bfd_init ()
 {
   if (bfd_init () == BFD_INIT_MAGIC)
     {
-#if CXX_STD_THREAD
       if (bfd_thread_init (gdb_bfd_lock, gdb_bfd_unlock, nullptr))
-#endif
        return;
     }
 
index d3a8d670d1b5b8daa3266b69a371ba197ec2aba8..c23d170663744a03bda2a7ccec7e3a0763aace60 100644 (file)
 #include "cli/cli-utils.h"
 #include "gdbsupport/symbol.h"
 #include <algorithm>
+#include "gdbsupport/cxx-thread.h"
 #include "gdbsupport/parallel-for.h"
 #include "inferior.h"
 
-#if CXX_STD_THREAD
-#include <mutex>
-#endif
-
 /* Return true if MINSYM is a cold clone symbol.
    Recognize f.i. these symbols (mangled/demangled):
    - _ZL3foov.cold
@@ -1398,18 +1395,13 @@ public:
   minimal_symbol_install_worker
     (minimal_symbol *msymbols,
      gdb::array_view<computed_hash_values> hash_values,
-     objfile_per_bfd_storage *per_bfd
-#if CXX_STD_THREAD
-     , std::mutex &demangled_mutex
-#endif
-    )
+     objfile_per_bfd_storage *per_bfd,
+     gdb::mutex &demangled_mutex)
     : m_time_it ("minsym install worker"),
       m_msymbols (msymbols),
       m_hash_values (hash_values),
-      m_per_bfd (per_bfd)
-#if CXX_STD_THREAD
-      , m_demangled_mutex (demangled_mutex)
-#endif
+      m_per_bfd (per_bfd),
+      m_demangled_mutex (demangled_mutex)
   {}
 
   void operator() (iterator_range<minimal_symbol *> msym_range) noexcept
@@ -1447,9 +1439,7 @@ public:
     {
       /* To limit how long we hold the lock, we only acquire it here
         and not while we demangle the names above.  */
-#if CXX_STD_THREAD
-      std::lock_guard<std::mutex> guard (m_demangled_mutex);
-#endif
+      gdb::lock_guard<gdb::mutex> guard (m_demangled_mutex);
       for (minimal_symbol &msym : msym_range)
        {
          size_t idx = &msym - m_msymbols;
@@ -1467,9 +1457,7 @@ private:
   minimal_symbol *m_msymbols;
   gdb::array_view<computed_hash_values> m_hash_values;
   objfile_per_bfd_storage *m_per_bfd;
-#if CXX_STD_THREAD
-  std::mutex &m_demangled_mutex;
-#endif
+  gdb::mutex &m_demangled_mutex;
 };
 
 /* Add the minimal symbols in the existing bunches to the objfile's official
@@ -1549,11 +1537,9 @@ minimal_symbol_reader::install ()
       m_objfile->per_bfd->minimal_symbol_count = mcount;
       m_objfile->per_bfd->msymbols = std::move (msym_holder);
 
-#if CXX_STD_THREAD
       /* Mutex that is used when modifying or accessing the demangled
         hash table.  */
-      std::mutex demangled_mutex;
-#endif
+      gdb::mutex demangled_mutex;
 
       std::vector<computed_hash_values> hash_values (mcount);
 
@@ -1562,11 +1548,8 @@ minimal_symbol_reader::install ()
       gdb::parallel_for_each<1000, minimal_symbol *, minimal_symbol_install_worker>
        (&msymbols[0], &msymbols[mcount], msymbols,
         gdb::array_view<computed_hash_values> (hash_values),
-        m_objfile->per_bfd
-#if CXX_STD_THREAD
-        , demangled_mutex
-#endif
-       );
+        m_objfile->per_bfd,
+        demangled_mutex);
 
       build_minimal_symbol_hash_tables (m_objfile, hash_values);
     }
index 0cfe4716c24739f4de3daacff555e546ea2e5925..d68811bebddcd8522c812930e8eec2efc3c462fe 100644 (file)
 
 #include "run-on-main-thread.h"
 #include "ser-event.h"
-#if CXX_STD_THREAD
-#include <thread>
-#include <mutex>
-#endif
 #include "gdbsupport/cleanups.h"
+#include "gdbsupport/cxx-thread.h"
 #include "gdbsupport/event-loop.h"
 
 /* The serial event used when posting runnables.  */
@@ -33,17 +30,13 @@ static struct serial_event *runnable_event;
 
 static std::vector<std::function<void ()>> runnables;
 
-#if CXX_STD_THREAD
-
 /* Mutex to hold when handling RUNNABLE_EVENT or RUNNABLES.  */
 
-static std::mutex runnable_mutex;
+static gdb::mutex runnable_mutex;
 
 /* The main thread's thread id.  */
 
-static std::thread::id main_thread_id;
-
-#endif
+static gdb::thread::id main_thread_id;
 
 /* Run all the queued runnables.  */
 
@@ -55,9 +48,7 @@ run_events (int error, gdb_client_data client_data)
   /* Hold the lock while changing the globals, but not while running
      the runnables.  */
   {
-#if CXX_STD_THREAD
-    std::lock_guard<std::mutex> lock (runnable_mutex);
-#endif
+    gdb::lock_guard<gdb::mutex> lock (runnable_mutex);
 
     /* Clear the event fd.  Do this before flushing the events list,
        so that any new event post afterwards is sure to re-awaken the
@@ -100,47 +91,38 @@ run_events (int error, gdb_client_data client_data)
 void
 run_on_main_thread (std::function<void ()> &&func)
 {
-#if CXX_STD_THREAD
-  std::lock_guard<std::mutex> lock (runnable_mutex);
-#endif
+  gdb::lock_guard<gdb::mutex> lock (runnable_mutex);
   runnables.emplace_back (std::move (func));
   serial_event_set (runnable_event);
 }
 
-#if CXX_STD_THREAD
 static bool main_thread_id_initialized = false;
-#endif
 
 /* See run-on-main-thread.h.  */
 
 bool
 is_main_thread ()
 {
-#if CXX_STD_THREAD
   /* Initialize main_thread_id on first use of is_main_thread.  */
   if (!main_thread_id_initialized)
     {
       main_thread_id_initialized = true;
 
-      main_thread_id = std::this_thread::get_id ();
+      main_thread_id = gdb::this_thread::get_id ();
     }
 
-  return std::this_thread::get_id () == main_thread_id;
-#else
-  return true;
-#endif
+  return gdb::this_thread::get_id () == main_thread_id;
 }
 
 INIT_GDB_FILE (run_on_main_thread)
 {
-#if CXX_STD_THREAD
   /* The variable main_thread_id should be initialized when entering main, or
      at an earlier use, so it should already be initialized here.  */
   gdb_assert (main_thread_id_initialized);
 
   /* Assume that we execute this in the main thread.  */
   gdb_assert (is_main_thread ());
-#endif
+
   runnable_event = make_serial_event ();
   add_file_handler (serial_event_fd (runnable_event), run_events, nullptr,
                    "run-on-main-thread");
@@ -150,9 +132,7 @@ INIT_GDB_FILE (run_on_main_thread)
      languages are shut down.  */
   add_final_cleanup ([] ()
     {
-#if CXX_STD_THREAD
-      std::lock_guard<std::mutex> lock (runnable_mutex);
-#endif
+      gdb::lock_guard<gdb::mutex> lock (runnable_mutex);
       runnables.clear ();
     });
 }
diff --git a/gdbsupport/cxx-thread.h b/gdbsupport/cxx-thread.h
new file mode 100644 (file)
index 0000000..e4061eb
--- /dev/null
@@ -0,0 +1,243 @@
+/* Wrappers for C++ threading
+
+   Copyright (C) 2025 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 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef GDBSUPPORT_CXX_THREAD_H
+#define GDBSUPPORT_CXX_THREAD_H
+
+/* This header implements shims for the parts of the C++ threading
+   library that are needed by gdb.
+
+   The reason this exists is that some versions of libstdc++ do not
+   supply a working C++ thread implementation.  In particular this was
+   true for several versions of the Windows compiler.  See
+   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93687.
+
+   For systems where this works, this header just supplies aliases of
+   the standard functionality, in the "gdb" namespace.  For example,
+   "gdb::mutex" is an alias for "std::mutex".
+
+   For non-working ports, shims are provided.  These are just the
+   subset needed by gdb, and they generally do nothing, or as little
+   as possible.  In particular they all simply assume single-threaded
+   operation.  */
+
+#if CXX_STD_THREAD
+
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <future>
+
+namespace gdb
+{
+
+using condition_variable = std::condition_variable;
+using cv_status = std::cv_status;
+using future_status = std::future_status;
+using mutex = std::mutex;
+using recursive_mutex = std::recursive_mutex;
+using thread = std::thread;
+
+namespace this_thread = std::this_thread;
+
+template<typename T>
+using lock_guard = std::lock_guard<T>;
+
+template<typename T>
+using unique_lock = std::unique_lock<T>;
+
+template<typename T>
+using future = std::future<T>;
+
+} /* namespace gdb*/
+
+#else
+
+#include <chrono>
+
+namespace gdb
+{
+
+/* A do-nothing replacement for std::mutex.  */
+struct mutex
+{
+  mutex () = default;
+
+  DISABLE_COPY_AND_ASSIGN (mutex);
+
+  void lock ()
+  {
+  }
+
+  void unlock ()
+  {
+  }
+};
+
+/* A do-nothing replacement for std::recursive_mutex.  */
+struct recursive_mutex
+{
+  recursive_mutex () = default;
+
+  DISABLE_COPY_AND_ASSIGN (recursive_mutex);
+
+  void lock ()
+  {
+  }
+
+  void unlock ()
+  {
+  }
+};
+
+/* A do-nothing replacement for std::lock_guard.  */
+template<typename T>
+struct lock_guard
+{
+  explicit lock_guard (T &m)
+  {
+  }
+
+  DISABLE_COPY_AND_ASSIGN (lock_guard);
+};
+
+/* A do-nothing replacement for std::unique_lock.  */
+template<typename T>
+struct unique_lock
+{
+  explicit unique_lock (T &m)
+  {
+  }
+
+  DISABLE_COPY_AND_ASSIGN (unique_lock);
+};
+
+/* A compatibility enum for std::cv_status.  */
+enum class cv_status
+{
+  no_timeout,
+  timeout,
+};
+
+/* A do-nothing replacement for std::condition_variable.  */
+struct condition_variable
+{
+  condition_variable () = default;
+
+  DISABLE_COPY_AND_ASSIGN (condition_variable);
+
+  void notify_one () noexcept
+  {
+  }
+
+  void wait (unique_lock<mutex> &lock)
+  {
+  }
+
+  template<class Rep, class Period>
+  cv_status wait_for (unique_lock<mutex> &lock,
+                     const std::chrono::duration<Rep, Period> &rel_time)
+  {
+    return cv_status::no_timeout;
+  }
+};
+
+/* A compatibility enum for std::future_status.  This is just the
+   subset needed by gdb.  */
+enum class future_status
+{
+  ready,
+  timeout,
+};
+
+/* A compatibility implementation of std::future.  */
+template<typename T>
+class future
+{
+public:
+
+  explicit future (T value)
+    : m_value (std::move (value))
+  {
+  }
+
+  future () = default;
+  future (future &&other) = default;
+  future (const future &other) = delete;
+  future &operator= (future &&other) = default;
+  future &operator= (const future &other) = delete;
+
+  void wait () const { }
+
+  template<class Rep, class Period>
+  future_status wait_for (const std::chrono::duration<Rep,Period> &duration)
+    const
+  {
+    return future_status::ready;
+  }
+
+  T get () { return std::move (m_value); }
+
+private:
+
+  T m_value;
+};
+
+/* A specialization for void.  */
+
+template<>
+class future<void>
+{
+public:
+  void wait () const { }
+
+  template<class Rep, class Period>
+  future_status wait_for (const std::chrono::duration<Rep,Period> &duration)
+    const
+  {
+    return future_status::ready;
+  }
+
+  void get () { }
+};
+
+/* Rather than try to write a gdb::thread class, we just use a
+   namespace since only the 'id' type is needed.  Code manipulating
+   actual std::thread objects has to be wrapped in a check anyway.  */
+namespace thread
+{
+/* Replacement for std::thread::id.  */
+using id = int;
+}
+
+/* Replacement for std::this_thread.  */
+namespace this_thread
+{
+static inline thread::id
+get_id ()
+{
+  return 0;
+}
+}
+
+} /* namespace gdb */
+
+#endif /* CXX_STD_THREAD */
+
+#endif /* GDBSUPPORT_CXX_THREAD_H */
index f3ac94bf5882cb4233a5d4857449e41f48b9125f..b5b2934b8c3f8b56fbbe766b556a0ba808186a6c 100644 (file)
 #include <vector>
 #include <functional>
 #include <chrono>
-#if CXX_STD_THREAD
-#include <thread>
-#include <mutex>
-#include <condition_variable>
-#include <future>
-#endif
 #include <optional>
 
-namespace gdb
-{
-
-#if CXX_STD_THREAD
-
-/* Simply use the standard future.  */
-template<typename T>
-using future = std::future<T>;
-
-/* ... and the standard future_status.  */
-using future_status = std::future_status;
-
-#else /* CXX_STD_THREAD */
-
-/* A compatibility enum for std::future_status.  This is just the
-   subset needed by gdb.  */
-enum class future_status
-{
-  ready,
-  timeout,
-};
-
-/* A compatibility wrapper for std::future.  Once <thread> and
-   <future> are available in all GCC builds -- should that ever happen
-   -- this can be removed.  GCC does not implement threading for
-   MinGW, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93687.
-
-   Meanwhile, in this mode, there are no threads.  Tasks submitted to
-   the thread pool are invoked immediately and their result is stored
-   here.  The base template here simply wraps a T and provides some
-   std::future compatibility methods.  The provided methods are chosen
-   based on what GDB needs presently.  */
-
-template<typename T>
-class future
-{
-public:
-
-  explicit future (T value)
-    : m_value (std::move (value))
-  {
-  }
-
-  future () = default;
-  future (future &&other) = default;
-  future (const future &other) = delete;
-  future &operator= (future &&other) = default;
-  future &operator= (const future &other) = delete;
+#include "gdbsupport/cxx-thread.h"
 
-  void wait () const { }
-
-  template<class Rep, class Period>
-  future_status wait_for (const std::chrono::duration<Rep,Period> &duration)
-    const
-  {
-    return future_status::ready;
-  }
-
-  T get () { return std::move (m_value); }
-
-private:
-
-  T m_value;
-};
-
-/* A specialization for void.  */
-
-template<>
-class future<void>
+namespace gdb
 {
-public:
-  void wait () const { }
-
-  template<class Rep, class Period>
-  future_status wait_for (const std::chrono::duration<Rep,Period> &duration)
-    const
-  {
-    return future_status::ready;
-  }
-
-  void get () { }
-};
-
-#endif /* CXX_STD_THREAD */
-
 
 /* A thread pool.