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 "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));
}

View File

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

View File

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

View File

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

View File

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

View 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;
}

View File

@@ -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);
}

View File

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