Introduce gdbsupport/cxx-thread.h and use it

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>
This commit is contained in:
Tom Tromey
2025-10-01 10:27:15 -06:00
parent 29c3f00511
commit 2caf7b1689
10 changed files with 297 additions and 252 deletions

View File

@@ -22,11 +22,9 @@
#include "cli/cli-cmds.h" #include "cli/cli-cmds.h"
#include "run-on-main-thread.h" #include "run-on-main-thread.h"
#include "top.h" #include "top.h"
#include "gdbsupport/cxx-thread.h"
#include "gdbsupport/selftest.h" #include "gdbsupport/selftest.h"
#include "gdbsupport/unordered_map.h" #include "gdbsupport/unordered_map.h"
#if CXX_STD_THREAD
#include <mutex>
#endif
/* Map format strings to counters. */ /* Map format strings to counters. */
@@ -38,9 +36,7 @@ static gdb::unordered_map<const char *, int> counters;
int stop_whining = 0; int stop_whining = 0;
#if CXX_STD_THREAD static gdb::mutex complaint_mutex;
static std::mutex complaint_mutex;
#endif /* CXX_STD_THREAD */
/* See complaints.h. */ /* See complaints.h. */
@@ -50,9 +46,7 @@ complaint_internal (const char *fmt, ...)
va_list args; va_list args;
{ {
#if CXX_STD_THREAD gdb::lock_guard<gdb::mutex> guard (complaint_mutex);
std::lock_guard<std::mutex> guard (complaint_mutex);
#endif
if (++counters[fmt] > stop_whining) if (++counters[fmt] > stop_whining)
return; return;
} }
@@ -126,9 +120,7 @@ re_emit_complaints (const complaint_collection &complaints)
void void
complaint_interceptor::warn (const char *fmt, va_list args) complaint_interceptor::warn (const char *fmt, va_list args)
{ {
#if CXX_STD_THREAD gdb::lock_guard<gdb::mutex> guard (complaint_mutex);
std::lock_guard<std::mutex> guard (complaint_mutex);
#endif
g_complaint_interceptor->m_complaints.insert (string_vprintf (fmt, args)); g_complaint_interceptor->m_complaints.insert (string_vprintf (fmt, args));
} }

View File

@@ -132,9 +132,8 @@ bool
cooked_index_worker::wait (cooked_state desired_state, bool allow_quit) cooked_index_worker::wait (cooked_state desired_state, bool allow_quit)
{ {
bool done; 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 /* This may be called from a non-main thread -- this functionality
is needed for the index cache -- but in this case we require 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) if (allow_quit)
{ {
std::chrono::milliseconds duration { 15 }; 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; QUIT;
} }
else else
@@ -154,11 +153,6 @@ cooked_index_worker::wait (cooked_state desired_state, bool allow_quit)
} }
done = m_state == cooked_state::CACHE_DONE; 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 /* Only the main thread is allowed to report complaints and the
like. */ like. */
@@ -213,15 +207,10 @@ cooked_index_worker::set (cooked_state desired_state)
{ {
gdb_assert (desired_state != cooked_state::INITIAL); gdb_assert (desired_state != cooked_state::INITIAL);
#if CXX_STD_THREAD gdb::lock_guard<gdb::mutex> guard (m_mutex);
std::lock_guard<std::mutex> guard (m_mutex);
gdb_assert (desired_state > m_state); gdb_assert (desired_state > m_state);
m_state = desired_state; m_state = desired_state;
m_cond.notify_one (); 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. */ /* See cooked-index-worker.h. */

View File

@@ -27,11 +27,7 @@
#include "dwarf2/read.h" #include "dwarf2/read.h"
#include "maint.h" #include "maint.h"
#include "run-on-main-thread.h" #include "run-on-main-thread.h"
#include "gdbsupport/cxx-thread.h"
#if CXX_STD_THREAD
#include <mutex>
#include <condition_variable>
#endif /* CXX_STD_THREAD */
using cutu_reader_up = std::unique_ptr<cutu_reader>; using cutu_reader_up = std::unique_ptr<cutu_reader>;
@@ -300,11 +296,9 @@ protected:
/* Result of each worker task. */ /* Result of each worker task. */
std::vector<cooked_index_worker_result> m_results; std::vector<cooked_index_worker_result> m_results;
#if CXX_STD_THREAD
/* Mutex to synchronize access to M_RESULTS when workers append their /* Mutex to synchronize access to M_RESULTS when workers append their
result. */ result. */
std::mutex m_results_mutex; gdb::mutex m_results_mutex;
#endif /* CXX_STD_THREAD */
/* Any warnings emitted. For the time being at least, this only /* Any warnings emitted. For the time being at least, this only
needed in do_reading, not in every worker. Note that needed in do_reading, not in every worker. Note that
@@ -317,13 +311,12 @@ protected:
parent relationships. */ parent relationships. */
parent_map_map m_all_parents_map; parent_map_map m_all_parents_map;
#if CXX_STD_THREAD
/* Current state of this object. */ /* Current state of this object. */
cooked_state m_state = cooked_state::INITIAL; cooked_state m_state = cooked_state::INITIAL;
/* Mutex and condition variable used to synchronize. */ /* Mutex and condition variable used to synchronize. */
std::mutex m_mutex; gdb::mutex m_mutex;
std::condition_variable m_cond; gdb::condition_variable m_cond;
#endif /* CXX_STD_THREAD */
/* This flag indicates whether any complaints or exceptions that /* This flag indicates whether any complaints or exceptions that
arose during scanning have been reported by 'wait'. This may arose during scanning have been reported by 'wait'. This may
only be modified on the main thread. */ only be modified on the main thread. */

View File

@@ -604,10 +604,8 @@ struct dwp_file
dwo_unit_set loaded_cus; dwo_unit_set loaded_cus;
dwo_unit_set loaded_tus; dwo_unit_set loaded_tus;
#if CXX_STD_THREAD
/* Mutex to synchronize access to LOADED_CUS and LOADED_TUS. */ /* Mutex to synchronize access to LOADED_CUS and LOADED_TUS. */
std::mutex loaded_cutus_lock; gdb::mutex loaded_cutus_lock;
#endif
/* Table to map ELF section numbers to their sections. /* Table to map ELF section numbers to their sections.
This is only needed for the DWP V1 file format. */ This is only needed for the DWP V1 file format. */
@@ -3326,9 +3324,7 @@ private:
m_thread_storage.done_reading (m_complaint_handler.release ()); m_thread_storage.done_reading (m_complaint_handler.release ());
/* Append the results of this worker to the parent instance. */ /* Append the results of this worker to the parent instance. */
#if CXX_STD_THREAD gdb::lock_guard<gdb::mutex> lock (m_parent->m_results_mutex);
std::lock_guard<std::mutex> lock (m_parent->m_results_mutex);
#endif
m_parent->m_results.emplace_back (std::move (m_thread_storage)); 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, lookup_dwo_file (dwarf2_per_bfd *per_bfd, const char *dwo_name,
const char *comp_dir) const char *comp_dir)
{ {
#if CXX_STD_THREAD gdb::lock_guard<gdb::mutex> guard (per_bfd->dwo_files_lock);
std::lock_guard<std::mutex> guard (per_bfd->dwo_files_lock);
#endif
auto it = per_bfd->dwo_files.find (dwo_file_search {dwo_name, comp_dir}); auto it = per_bfd->dwo_files.find (dwo_file_search {dwo_name, comp_dir});
return it != per_bfd->dwo_files.end () ? it->get() : nullptr; 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 * static dwo_file *
add_dwo_file (dwarf2_per_bfd *per_bfd, dwo_file_up dwo_file) add_dwo_file (dwarf2_per_bfd *per_bfd, dwo_file_up dwo_file)
{ {
#if CXX_STD_THREAD gdb::lock_guard<gdb::mutex> lock (per_bfd->dwo_files_lock);
std::lock_guard<std::mutex> lock (per_bfd->dwo_files_lock);
#endif
return per_bfd->dwo_files.emplace (std::move (dwo_file)).first->get (); 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; = is_debug_types ? dwp_file->loaded_tus : dwp_file->loaded_cus;
{ {
#if CXX_STD_THREAD gdb::lock_guard<gdb::mutex> guard (dwp_file->loaded_cutus_lock);
std::lock_guard<std::mutex> guard (dwp_file->loaded_cutus_lock);
#endif
if (auto it = dwo_unit_set.find (signature); if (auto it = dwo_unit_set.find (signature);
it != dwo_unit_set.end ()) it != dwo_unit_set.end ())
return it->get (); 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 /* If another thread raced with this one, opening the exact same
DWO unit, then we'll keep that other thread's copy. */ DWO unit, then we'll keep that other thread's copy. */
#if CXX_STD_THREAD gdb::lock_guard<gdb::mutex> guard (dwp_file->loaded_cutus_lock);
std::lock_guard<std::mutex> guard (dwp_file->loaded_cutus_lock);
#endif
auto it = dwo_unit_set.emplace (std::move (dwo_unit)).first; auto it = dwo_unit_set.emplace (std::move (dwo_unit)).first;
return it->get (); 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; return NULL;
{ {
#if CXX_STD_THREAD
/* The operations below are not thread-safe, use a lock to synchronize /* The operations below are not thread-safe, use a lock to synchronize
concurrent accesses. */ concurrent accesses. */
static std::mutex mutex; static gdb::mutex mutex;
std::lock_guard<std::mutex> lock (mutex); gdb::lock_guard<gdb::mutex> lock (mutex);
#endif
if (!bfd_check_format (sym_bfd.get (), bfd_object)) if (!bfd_check_format (sym_bfd.get (), bfd_object))
return NULL; return NULL;

View File

@@ -20,9 +20,6 @@
#ifndef GDB_DWARF2_READ_H #ifndef GDB_DWARF2_READ_H
#define GDB_DWARF2_READ_H #define GDB_DWARF2_READ_H
#if CXX_STD_THREAD
#include <mutex>
#endif
#include <queue> #include <queue>
#include "dwarf2/abbrev.h" #include "dwarf2/abbrev.h"
#include "dwarf2/unit-head.h" #include "dwarf2/unit-head.h"
@@ -32,6 +29,7 @@
#include "dwarf2/section.h" #include "dwarf2/section.h"
#include "dwarf2/cu.h" #include "dwarf2/cu.h"
#include "dwarf2/dwz.h" #include "dwarf2/dwz.h"
#include "gdbsupport/cxx-thread.h"
#include "gdbsupport/gdb_obstack.h" #include "gdbsupport/gdb_obstack.h"
#include "gdbsupport/function-view.h" #include "gdbsupport/function-view.h"
#include "gdbsupport/packed.h" #include "gdbsupport/packed.h"
@@ -618,10 +616,8 @@ public:
/* Set of dwo_file objects. */ /* Set of dwo_file objects. */
dwo_file_up_set dwo_files; dwo_file_up_set dwo_files;
#if CXX_STD_THREAD
/* Mutex to synchronize access to DWO_FILES. */ /* Mutex to synchronize access to DWO_FILES. */
std::mutex dwo_files_lock; gdb::mutex dwo_files_lock;
#endif
/* The DWP file if there is one, or NULL. */ /* The DWP file if there is one, or NULL. */
dwp_file_up dwp_file; dwp_file_up dwp_file;

View File

@@ -33,18 +33,15 @@
#include "gdbsupport/fileio.h" #include "gdbsupport/fileio.h"
#include "inferior.h" #include "inferior.h"
#include "cli/cli-style.h" #include "cli/cli-style.h"
#include "gdbsupport/cxx-thread.h"
#include "gdbsupport/unordered_map.h" #include "gdbsupport/unordered_map.h"
#include "gdbsupport/unordered_set.h" #include "gdbsupport/unordered_set.h"
#if CXX_STD_THREAD
#include <mutex>
/* Lock held when doing BFD operations. A recursive mutex is used /* Lock held when doing BFD operations. A recursive mutex is used
because we use this mutex internally and also for BFD, just to make 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 life a bit simpler, and we may sometimes hold it while calling into
BFD. */ BFD. */
static std::recursive_mutex gdb_bfd_mutex; static gdb::recursive_mutex gdb_bfd_mutex;
/* BFD locking function. */ /* BFD locking function. */
@@ -64,8 +61,6 @@ gdb_bfd_unlock (void *ignore)
return true; return true;
} }
#endif /* CXX_STD_THREAD */
/* An object of this type is stored in the section's user data when /* An object of this type is stored in the section's user data when
mapping a section. */ mapping a section. */
@@ -153,7 +148,6 @@ struct gdb_bfd_data
/* The registry. */ /* The registry. */
registry<bfd> registry_fields; registry<bfd> registry_fields;
#if CXX_STD_THREAD
/* Most of the locking needed for multi-threaded operation is /* Most of the locking needed for multi-threaded operation is
handled by BFD itself. However, the current BFD model is that handled by BFD itself. However, the current BFD model is that
locking is only needed for global operations -- but it turned out 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 This lock is the fix: wrappers for important BFD functions will
acquire this lock before performing operations that might modify acquire this lock before performing operations that might modify
the state of this BFD. */ the state of this BFD. */
std::mutex per_bfd_mutex; gdb::mutex per_bfd_mutex;
#endif
}; };
registry<bfd> * registry<bfd> *
@@ -548,9 +541,7 @@ gdb_bfd_open (const char *name, const char *target, int fd,
name += strlen (TARGET_SYSROOT_PREFIX); name += strlen (TARGET_SYSROOT_PREFIX);
} }
#if CXX_STD_THREAD gdb::lock_guard<gdb::recursive_mutex> guard (gdb_bfd_mutex);
std::lock_guard<std::recursive_mutex> guard (gdb_bfd_mutex);
#endif
if (fd == -1) if (fd == -1)
{ {
@@ -677,9 +668,7 @@ gdb_bfd_ref (struct bfd *abfd)
if (abfd == NULL) if (abfd == NULL)
return; return;
#if CXX_STD_THREAD gdb::lock_guard<gdb::recursive_mutex> guard (gdb_bfd_mutex);
std::lock_guard<std::recursive_mutex> guard (gdb_bfd_mutex);
#endif
gdata = (struct gdb_bfd_data *) bfd_usrdata (abfd); gdata = (struct gdb_bfd_data *) bfd_usrdata (abfd);
@@ -709,9 +698,7 @@ gdb_bfd_unref (struct bfd *abfd)
if (abfd == NULL) if (abfd == NULL)
return; return;
#if CXX_STD_THREAD gdb::lock_guard<gdb::recursive_mutex> guard (gdb_bfd_mutex);
std::lock_guard<std::recursive_mutex> guard (gdb_bfd_mutex);
#endif
gdata = (struct gdb_bfd_data *) bfd_usrdata (abfd); gdata = (struct gdb_bfd_data *) bfd_usrdata (abfd);
gdb_assert (gdata->refc >= 1); gdb_assert (gdata->refc >= 1);
@@ -779,10 +766,8 @@ gdb_bfd_map_section (asection *sectp, bfd_size_type *size)
abfd = sectp->owner; abfd = sectp->owner;
#if CXX_STD_THREAD
gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd); gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd);
std::lock_guard<std::mutex> guard (gdata->per_bfd_mutex); gdb::lock_guard<gdb::mutex> guard (gdata->per_bfd_mutex);
#endif
descriptor = get_section_descriptor (sectp); descriptor = get_section_descriptor (sectp);
@@ -1115,10 +1100,8 @@ bool
gdb_bfd_get_full_section_contents (bfd *abfd, asection *section, gdb_bfd_get_full_section_contents (bfd *abfd, asection *section,
gdb::byte_vector *contents) gdb::byte_vector *contents)
{ {
#if CXX_STD_THREAD
gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd); gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd);
std::lock_guard<std::mutex> guard (gdata->per_bfd_mutex); gdb::lock_guard<gdb::mutex> guard (gdata->per_bfd_mutex);
#endif
bfd_size_type section_size = bfd_section_size (section); bfd_size_type section_size = bfd_section_size (section);
@@ -1133,10 +1116,8 @@ gdb_bfd_get_full_section_contents (bfd *abfd, asection *section,
int int
gdb_bfd_stat (bfd *abfd, struct stat *sbuf) gdb_bfd_stat (bfd *abfd, struct stat *sbuf)
{ {
#if CXX_STD_THREAD
gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd); gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd);
std::lock_guard<std::mutex> guard (gdata->per_bfd_mutex); gdb::lock_guard<gdb::mutex> guard (gdata->per_bfd_mutex);
#endif
return bfd_stat (abfd, sbuf); return bfd_stat (abfd, sbuf);
} }
@@ -1146,10 +1127,8 @@ gdb_bfd_stat (bfd *abfd, struct stat *sbuf)
long long
gdb_bfd_get_mtime (bfd *abfd) gdb_bfd_get_mtime (bfd *abfd)
{ {
#if CXX_STD_THREAD
gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd); gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd);
std::lock_guard<std::mutex> guard (gdata->per_bfd_mutex); gdb::lock_guard<gdb::mutex> guard (gdata->per_bfd_mutex);
#endif
return bfd_get_mtime (abfd); return bfd_get_mtime (abfd);
} }
@@ -1290,9 +1269,7 @@ get_bfd_inferior_data (struct inferior *inf)
static unsigned long static unsigned long
increment_bfd_error_count (const std::string &str) increment_bfd_error_count (const std::string &str)
{ {
#if CXX_STD_THREAD gdb::lock_guard<gdb::recursive_mutex> guard (gdb_bfd_mutex);
std::lock_guard<std::recursive_mutex> guard (gdb_bfd_mutex);
#endif
struct bfd_inferior_data *bid = get_bfd_inferior_data (current_inferior ()); struct bfd_inferior_data *bid = get_bfd_inferior_data (current_inferior ());
auto &map = bid->bfd_error_string_counts; auto &map = bid->bfd_error_string_counts;
@@ -1337,9 +1314,7 @@ gdb_bfd_init ()
{ {
if (bfd_init () == BFD_INIT_MAGIC) if (bfd_init () == BFD_INIT_MAGIC)
{ {
#if CXX_STD_THREAD
if (bfd_thread_init (gdb_bfd_lock, gdb_bfd_unlock, nullptr)) if (bfd_thread_init (gdb_bfd_lock, gdb_bfd_unlock, nullptr))
#endif
return; return;
} }

View File

@@ -51,13 +51,10 @@
#include "cli/cli-utils.h" #include "cli/cli-utils.h"
#include "gdbsupport/symbol.h" #include "gdbsupport/symbol.h"
#include <algorithm> #include <algorithm>
#include "gdbsupport/cxx-thread.h"
#include "gdbsupport/parallel-for.h" #include "gdbsupport/parallel-for.h"
#include "inferior.h" #include "inferior.h"
#if CXX_STD_THREAD
#include <mutex>
#endif
/* Return true if MINSYM is a cold clone symbol. /* Return true if MINSYM is a cold clone symbol.
Recognize f.i. these symbols (mangled/demangled): Recognize f.i. these symbols (mangled/demangled):
- _ZL3foov.cold - _ZL3foov.cold
@@ -1398,18 +1395,13 @@ public:
minimal_symbol_install_worker minimal_symbol_install_worker
(minimal_symbol *msymbols, (minimal_symbol *msymbols,
gdb::array_view<computed_hash_values> hash_values, gdb::array_view<computed_hash_values> hash_values,
objfile_per_bfd_storage *per_bfd objfile_per_bfd_storage *per_bfd,
#if CXX_STD_THREAD gdb::mutex &demangled_mutex)
, std::mutex &demangled_mutex
#endif
)
: m_time_it ("minsym install worker"), : m_time_it ("minsym install worker"),
m_msymbols (msymbols), m_msymbols (msymbols),
m_hash_values (hash_values), m_hash_values (hash_values),
m_per_bfd (per_bfd) m_per_bfd (per_bfd),
#if CXX_STD_THREAD m_demangled_mutex (demangled_mutex)
, m_demangled_mutex (demangled_mutex)
#endif
{} {}
void operator() (iterator_range<minimal_symbol *> msym_range) noexcept 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 /* To limit how long we hold the lock, we only acquire it here
and not while we demangle the names above. */ and not while we demangle the names above. */
#if CXX_STD_THREAD gdb::lock_guard<gdb::mutex> guard (m_demangled_mutex);
std::lock_guard<std::mutex> guard (m_demangled_mutex);
#endif
for (minimal_symbol &msym : msym_range) for (minimal_symbol &msym : msym_range)
{ {
size_t idx = &msym - m_msymbols; size_t idx = &msym - m_msymbols;
@@ -1467,9 +1457,7 @@ private:
minimal_symbol *m_msymbols; minimal_symbol *m_msymbols;
gdb::array_view<computed_hash_values> m_hash_values; gdb::array_view<computed_hash_values> m_hash_values;
objfile_per_bfd_storage *m_per_bfd; objfile_per_bfd_storage *m_per_bfd;
#if CXX_STD_THREAD gdb::mutex &m_demangled_mutex;
std::mutex &m_demangled_mutex;
#endif
}; };
/* Add the minimal symbols in the existing bunches to the objfile's official /* 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->minimal_symbol_count = mcount;
m_objfile->per_bfd->msymbols = std::move (msym_holder); m_objfile->per_bfd->msymbols = std::move (msym_holder);
#if CXX_STD_THREAD
/* Mutex that is used when modifying or accessing the demangled /* Mutex that is used when modifying or accessing the demangled
hash table. */ hash table. */
std::mutex demangled_mutex; gdb::mutex demangled_mutex;
#endif
std::vector<computed_hash_values> hash_values (mcount); 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> gdb::parallel_for_each<1000, minimal_symbol *, minimal_symbol_install_worker>
(&msymbols[0], &msymbols[mcount], msymbols, (&msymbols[0], &msymbols[mcount], msymbols,
gdb::array_view<computed_hash_values> (hash_values), gdb::array_view<computed_hash_values> (hash_values),
m_objfile->per_bfd m_objfile->per_bfd,
#if CXX_STD_THREAD demangled_mutex);
, demangled_mutex
#endif
);
build_minimal_symbol_hash_tables (m_objfile, hash_values); build_minimal_symbol_hash_tables (m_objfile, hash_values);
} }

View File

@@ -18,11 +18,8 @@
#include "run-on-main-thread.h" #include "run-on-main-thread.h"
#include "ser-event.h" #include "ser-event.h"
#if CXX_STD_THREAD
#include <thread>
#include <mutex>
#endif
#include "gdbsupport/cleanups.h" #include "gdbsupport/cleanups.h"
#include "gdbsupport/cxx-thread.h"
#include "gdbsupport/event-loop.h" #include "gdbsupport/event-loop.h"
/* The serial event used when posting runnables. */ /* The serial event used when posting runnables. */
@@ -33,17 +30,13 @@ static struct serial_event *runnable_event;
static std::vector<std::function<void ()>> runnables; static std::vector<std::function<void ()>> runnables;
#if CXX_STD_THREAD
/* Mutex to hold when handling RUNNABLE_EVENT or RUNNABLES. */ /* 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. */ /* The main thread's thread id. */
static std::thread::id main_thread_id; static gdb::thread::id main_thread_id;
#endif
/* Run all the queued runnables. */ /* 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 /* Hold the lock while changing the globals, but not while running
the runnables. */ the runnables. */
{ {
#if CXX_STD_THREAD gdb::lock_guard<gdb::mutex> lock (runnable_mutex);
std::lock_guard<std::mutex> lock (runnable_mutex);
#endif
/* Clear the event fd. Do this before flushing the events list, /* Clear the event fd. Do this before flushing the events list,
so that any new event post afterwards is sure to re-awaken the 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 void
run_on_main_thread (std::function<void ()> &&func) run_on_main_thread (std::function<void ()> &&func)
{ {
#if CXX_STD_THREAD gdb::lock_guard<gdb::mutex> lock (runnable_mutex);
std::lock_guard<std::mutex> lock (runnable_mutex);
#endif
runnables.emplace_back (std::move (func)); runnables.emplace_back (std::move (func));
serial_event_set (runnable_event); serial_event_set (runnable_event);
} }
#if CXX_STD_THREAD
static bool main_thread_id_initialized = false; static bool main_thread_id_initialized = false;
#endif
/* See run-on-main-thread.h. */ /* See run-on-main-thread.h. */
bool bool
is_main_thread () is_main_thread ()
{ {
#if CXX_STD_THREAD
/* Initialize main_thread_id on first use of is_main_thread. */ /* Initialize main_thread_id on first use of is_main_thread. */
if (!main_thread_id_initialized) if (!main_thread_id_initialized)
{ {
main_thread_id_initialized = true; 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; return gdb::this_thread::get_id () == main_thread_id;
#else
return true;
#endif
} }
INIT_GDB_FILE (run_on_main_thread) INIT_GDB_FILE (run_on_main_thread)
{ {
#if CXX_STD_THREAD
/* The variable main_thread_id should be initialized when entering main, or /* The variable main_thread_id should be initialized when entering main, or
at an earlier use, so it should already be initialized here. */ at an earlier use, so it should already be initialized here. */
gdb_assert (main_thread_id_initialized); gdb_assert (main_thread_id_initialized);
/* Assume that we execute this in the main thread. */ /* Assume that we execute this in the main thread. */
gdb_assert (is_main_thread ()); gdb_assert (is_main_thread ());
#endif
runnable_event = make_serial_event (); runnable_event = make_serial_event ();
add_file_handler (serial_event_fd (runnable_event), run_events, nullptr, add_file_handler (serial_event_fd (runnable_event), run_events, nullptr,
"run-on-main-thread"); "run-on-main-thread");
@@ -150,9 +132,7 @@ INIT_GDB_FILE (run_on_main_thread)
languages are shut down. */ languages are shut down. */
add_final_cleanup ([] () add_final_cleanup ([] ()
{ {
#if CXX_STD_THREAD gdb::lock_guard<gdb::mutex> lock (runnable_mutex);
std::lock_guard<std::mutex> lock (runnable_mutex);
#endif
runnables.clear (); runnables.clear ();
}); });
} }

243
gdbsupport/cxx-thread.h Normal file
View File

@@ -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 */

View File

@@ -24,100 +24,13 @@
#include <vector> #include <vector>
#include <functional> #include <functional>
#include <chrono> #include <chrono>
#if CXX_STD_THREAD
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#endif
#include <optional> #include <optional>
#include "gdbsupport/cxx-thread.h"
namespace gdb 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;
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 () { }
};
#endif /* CXX_STD_THREAD */
/* A thread pool. /* A thread pool.
There is a single global thread pool, see g_thread_pool. Tasks can There is a single global thread pool, see g_thread_pool. Tasks can