mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-11-16 04:24:43 +00:00
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:
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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 @@ 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. */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 @@ 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);
|
||||
}
|
||||
|
||||
@@ -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 ();
|
||||
});
|
||||
}
|
||||
|
||||
243
gdbsupport/cxx-thread.h
Normal file
243
gdbsupport/cxx-thread.h
Normal 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 */
|
||||
@@ -24,100 +24,13 @@
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <chrono>
|
||||
#if CXX_STD_THREAD
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <future>
|
||||
#endif
|
||||
#include <optional>
|
||||
|
||||
#include "gdbsupport/cxx-thread.h"
|
||||
|
||||
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.
|
||||
|
||||
There is a single global thread pool, see g_thread_pool. Tasks can
|
||||
|
||||
Reference in New Issue
Block a user