* [PATCH 3/3] Introduce gdbsupport/cxx-thread.h and use it
2025-10-01 18:57 [PATCH 0/3] Add compatibility definitions for C++ thread support Tom Tromey
2025-10-01 18:57 ` [PATCH 1/3] Remove an unused include Tom Tromey
2025-10-01 18:57 ` [PATCH 2/3] Don't rely on deduction in run-on-main-thread.c Tom Tromey
@ 2025-10-01 18:57 ` Tom Tromey
2025-10-01 20:16 ` Simon Marchi
2 siblings, 1 reply; 9+ messages in thread
From: Tom Tromey @ 2025-10-01 18:57 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
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.
---
gdb/complaints.c | 16 +--
gdb/dwarf2/cooked-index-worker.c | 17 +--
gdb/dwarf2/cooked-index-worker.h | 17 +--
gdb/dwarf2/read.c | 35 ++----
gdb/dwarf2/read.h | 8 +-
gdb/gdb_bfd.c | 47 ++------
gdb/minsyms.c | 37 ++----
gdb/run-on-main-thread.c | 38 ++----
gdbsupport/cxx-thread.h | 243 +++++++++++++++++++++++++++++++++++++++
gdbsupport/thread-pool.h | 91 +--------------
10 files changed, 297 insertions(+), 252 deletions(-)
diff --git a/gdb/complaints.c b/gdb/complaints.c
index 3c7f9bc7c89604de766672ef0f4c78005ed7d83b..03d27d615a58e6e5e7d89cda951bbbb2ae9c53a2 100644
--- a/gdb/complaints.c
+++ b/gdb/complaints.c
@@ -22,11 +22,9 @@
#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));
}
diff --git a/gdb/dwarf2/cooked-index-worker.c b/gdb/dwarf2/cooked-index-worker.c
index 09d80eff629c4d168e3108e5ee5d9bd082be6543..abaf41fb00c315e2e3261d0293e25faed4a147c9 100644
--- a/gdb/dwarf2/cooked-index-worker.c
+++ b/gdb/dwarf2/cooked-index-worker.c
@@ -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. */
diff --git a/gdb/dwarf2/cooked-index-worker.h b/gdb/dwarf2/cooked-index-worker.h
index dfdc82f52744ec36e626ab883d31a5d44071b063..4213b4e12b7214520f3856494777bee8aa9e2e01 100644
--- a/gdb/dwarf2/cooked-index-worker.h
+++ b/gdb/dwarf2/cooked-index-worker.h
@@ -27,11 +27,7 @@
#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 @@ class cooked_index_worker
/* 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 @@ class cooked_index_worker
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. */
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 05f8452895a0d7e2f7e53a3c45c5a0317d27c995..6618b2242ff8e6106e81a3d72cac504725be561d 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -605,10 +605,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. */
@@ -3327,9 +3325,7 @@ class cooked_index_worker_debug_info : public cooked_index_worker
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));
}
@@ -6318,12 +6314,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;
}
@@ -6338,10 +6330,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 ();
}
@@ -7473,10 +7462,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 ();
@@ -7511,10 +7497,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 ();
}
@@ -7594,12 +7577,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;
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index 2f9ad05b792bfeeb059229274fd43d2dc6ebc3d3..f0d46f6acc6cd95381c53c7b21876df9b2087661 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -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 @@ struct dwarf2_per_bfd
/* 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;
diff --git a/gdb/gdb_bfd.c b/gdb/gdb_bfd.c
index 2e477eba6a9b8f31c558aeb6d37cdeeed369a6e6..4c641fe4c7b88094003b47ca77040b533f039453 100644
--- a/gdb/gdb_bfd.c
+++ b/gdb/gdb_bfd.c
@@ -33,18 +33,15 @@
#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;
}
diff --git a/gdb/minsyms.c b/gdb/minsyms.c
index d3a8d670d1b5b8daa3266b69a371ba197ec2aba8..c23d170663744a03bda2a7ccec7e3a0763aace60 100644
--- a/gdb/minsyms.c
+++ b/gdb/minsyms.c
@@ -51,13 +51,10 @@
#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 @@ class minimal_symbol_install_worker
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 @@ class minimal_symbol_install_worker
{
/* 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 @@ class minimal_symbol_install_worker
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);
}
diff --git a/gdb/run-on-main-thread.c b/gdb/run-on-main-thread.c
index 0cfe4716c24739f4de3daacff555e546ea2e5925..d68811bebddcd8522c812930e8eec2efc3c462fe 100644
--- a/gdb/run-on-main-thread.c
+++ b/gdb/run-on-main-thread.c
@@ -18,11 +18,8 @@
#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
index 0000000000000000000000000000000000000000..e4061ebef9dfbc4287c258ac4c4754a3a41cf410
--- /dev/null
+++ b/gdbsupport/cxx-thread.h
@@ -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 */
diff --git a/gdbsupport/thread-pool.h b/gdbsupport/thread-pool.h
index f3ac94bf5882cb4233a5d4857449e41f48b9125f..b5b2934b8c3f8b56fbbe766b556a0ba808186a6c 100644
--- a/gdbsupport/thread-pool.h
+++ b/gdbsupport/thread-pool.h
@@ -24,99 +24,12 @@
#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.
--
2.51.0
^ permalink raw reply [flat|nested] 9+ messages in thread