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