mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-04-06 13:47:56 +02:00
Merge bitcoin/bitcoin#34809: threadsafety: Add STDLOCK() macro for StdMutex
8d2f06853async: Use StdMutex for thread safety annotations (Anthony Towns)cbc231ed8escripted-diff: logging: Switch from StdLockGuard to STDLOCK (Anthony Towns)f808786f48logging: Add missing thread safety annotations (Anthony Towns)e196cf26e0util/stdmutex.h: Add STDLOCK() and improve annotation checking for StdMutex (Anthony Towns) Pull request description: Using `STDLOCK(mutex)` instead of `StdLockGuard guard(mutex)` allows clang to propagate missing lock annotations backwards (for global locks or locks in the same class, anyway), and also avoids declaring a dummy name. Use this in logging.h, and also use it in sync.cpp, adding annotations around the internal structure. ACKs for top commit: theuni: ACK8d2f06853asedited: ACK8d2f06853aTree-SHA512: ee23f6a7bcc62cc6d9ea88afa863a9018e53a0932272bb14241441fb69066c6633c4e19aadd3dd7599848d4742099d63756be4eff1969775293b728f3dd187aa
This commit is contained in:
@@ -53,7 +53,7 @@ static int FileWriteStr(std::string_view str, FILE *fp)
|
||||
|
||||
bool BCLog::Logger::StartLogging()
|
||||
{
|
||||
StdLockGuard scoped_lock(m_cs);
|
||||
STDLOCK(m_cs);
|
||||
|
||||
assert(m_buffering);
|
||||
assert(m_fileout == nullptr);
|
||||
@@ -97,7 +97,7 @@ bool BCLog::Logger::StartLogging()
|
||||
|
||||
void BCLog::Logger::DisconnectTestLogger()
|
||||
{
|
||||
StdLockGuard scoped_lock(m_cs);
|
||||
STDLOCK(m_cs);
|
||||
m_buffering = true;
|
||||
if (m_fileout != nullptr) fclose(m_fileout);
|
||||
m_fileout = nullptr;
|
||||
@@ -111,7 +111,7 @@ void BCLog::Logger::DisconnectTestLogger()
|
||||
void BCLog::Logger::DisableLogging()
|
||||
{
|
||||
{
|
||||
StdLockGuard scoped_lock(m_cs);
|
||||
STDLOCK(m_cs);
|
||||
assert(m_buffering);
|
||||
assert(m_print_callbacks.empty());
|
||||
}
|
||||
@@ -159,7 +159,7 @@ bool BCLog::Logger::WillLogCategoryLevel(BCLog::LogFlags category, BCLog::Level
|
||||
|
||||
if (!WillLogCategory(category)) return false;
|
||||
|
||||
StdLockGuard scoped_lock(m_cs);
|
||||
STDLOCK(m_cs);
|
||||
const auto it{m_category_log_levels.find(category)};
|
||||
return level >= (it == m_category_log_levels.end() ? LogLevel() : it->second);
|
||||
}
|
||||
@@ -392,7 +392,7 @@ BCLog::LogRateLimiter::Status BCLog::LogRateLimiter::Consume(
|
||||
const SourceLocation& source_loc,
|
||||
const std::string& str)
|
||||
{
|
||||
StdLockGuard scoped_lock(m_mutex);
|
||||
STDLOCK(m_mutex);
|
||||
auto& stats{m_source_locations.try_emplace(source_loc, m_max_bytes).first->second};
|
||||
Status status{stats.m_dropped_bytes > 0 ? Status::STILL_SUPPRESSED : Status::UNSUPPRESSED};
|
||||
|
||||
@@ -423,7 +423,7 @@ void BCLog::Logger::FormatLogStrInPlace(std::string& str, BCLog::LogFlags catego
|
||||
|
||||
void BCLog::Logger::LogPrintStr(std::string_view str, SourceLocation&& source_loc, BCLog::LogFlags category, BCLog::Level level, bool should_ratelimit)
|
||||
{
|
||||
StdLockGuard scoped_lock(m_cs);
|
||||
STDLOCK(m_cs);
|
||||
return LogPrintStr_(str, std::move(source_loc), category, level, should_ratelimit);
|
||||
}
|
||||
|
||||
@@ -556,7 +556,7 @@ void BCLog::LogRateLimiter::Reset()
|
||||
{
|
||||
decltype(m_source_locations) source_locations;
|
||||
{
|
||||
StdLockGuard scoped_lock(m_mutex);
|
||||
STDLOCK(m_mutex);
|
||||
source_locations.swap(m_source_locations);
|
||||
m_suppression_active = false;
|
||||
}
|
||||
@@ -598,7 +598,7 @@ bool BCLog::Logger::SetCategoryLogLevel(std::string_view category_str, std::stri
|
||||
const auto level = GetLogLevel(level_str);
|
||||
if (!level.has_value() || level.value() > MAX_USER_SETABLE_SEVERITY_LEVEL) return false;
|
||||
|
||||
StdLockGuard scoped_lock(m_cs);
|
||||
STDLOCK(m_cs);
|
||||
m_category_log_levels[flag] = level.value();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -192,14 +192,14 @@ namespace BCLog {
|
||||
/** Returns whether logs will be written to any output */
|
||||
bool Enabled() const EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
|
||||
{
|
||||
StdLockGuard scoped_lock(m_cs);
|
||||
STDLOCK(m_cs);
|
||||
return m_buffering || m_print_to_console || m_print_to_file || !m_print_callbacks.empty();
|
||||
}
|
||||
|
||||
/** Connect a slot to the print signal and return the connection */
|
||||
std::list<std::function<void(const std::string&)>>::iterator PushBackCallback(std::function<void(const std::string&)> fun) EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
|
||||
{
|
||||
StdLockGuard scoped_lock(m_cs);
|
||||
STDLOCK(m_cs);
|
||||
m_print_callbacks.push_back(std::move(fun));
|
||||
return --m_print_callbacks.end();
|
||||
}
|
||||
@@ -207,13 +207,13 @@ namespace BCLog {
|
||||
/** Delete a connection */
|
||||
void DeleteCallback(std::list<std::function<void(const std::string&)>>::iterator it) EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
|
||||
{
|
||||
StdLockGuard scoped_lock(m_cs);
|
||||
STDLOCK(m_cs);
|
||||
m_print_callbacks.erase(it);
|
||||
}
|
||||
|
||||
size_t NumConnections()
|
||||
size_t NumConnections() EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
|
||||
{
|
||||
StdLockGuard scoped_lock(m_cs);
|
||||
STDLOCK(m_cs);
|
||||
return m_print_callbacks.size();
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ namespace BCLog {
|
||||
|
||||
void SetRateLimiting(std::shared_ptr<LogRateLimiter> limiter) EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
|
||||
{
|
||||
StdLockGuard scoped_lock(m_cs);
|
||||
STDLOCK(m_cs);
|
||||
m_limiter = std::move(limiter);
|
||||
}
|
||||
|
||||
@@ -240,17 +240,17 @@ namespace BCLog {
|
||||
|
||||
std::unordered_map<LogFlags, Level> CategoryLevels() const EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
|
||||
{
|
||||
StdLockGuard scoped_lock(m_cs);
|
||||
STDLOCK(m_cs);
|
||||
return m_category_log_levels;
|
||||
}
|
||||
void SetCategoryLogLevel(const std::unordered_map<LogFlags, Level>& levels) EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
|
||||
{
|
||||
StdLockGuard scoped_lock(m_cs);
|
||||
STDLOCK(m_cs);
|
||||
m_category_log_levels = levels;
|
||||
}
|
||||
void AddCategoryLogLevel(LogFlags category, Level level)
|
||||
void AddCategoryLogLevel(LogFlags category, Level level) EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
|
||||
{
|
||||
StdLockGuard scoped_lock(m_cs);
|
||||
STDLOCK(m_cs);
|
||||
m_category_log_levels[category] = level;
|
||||
}
|
||||
bool SetCategoryLogLevel(std::string_view category_str, std::string_view level_str) EXCLUSIVE_LOCKS_REQUIRED(!m_cs);
|
||||
|
||||
23
src/sync.cpp
23
src/sync.cpp
@@ -7,6 +7,7 @@
|
||||
#include <logging/timer.h>
|
||||
#include <tinyformat.h>
|
||||
#include <util/log.h>
|
||||
#include <util/stdmutex.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/threadnames.h>
|
||||
|
||||
@@ -87,10 +88,10 @@ using LockOrders = std::map<LockPair, LockStack>;
|
||||
using InvLockOrders = std::set<LockPair>;
|
||||
|
||||
struct LockData {
|
||||
LockStacks m_lock_stacks;
|
||||
LockOrders lockorders;
|
||||
InvLockOrders invlockorders;
|
||||
std::mutex dd_mutex;
|
||||
LockStacks m_lock_stacks GUARDED_BY(dd_mutex);
|
||||
LockOrders lockorders GUARDED_BY(dd_mutex);
|
||||
InvLockOrders invlockorders GUARDED_BY(dd_mutex);
|
||||
StdMutex dd_mutex;
|
||||
};
|
||||
|
||||
LockData& GetLockData() {
|
||||
@@ -166,7 +167,7 @@ static void push_lock(MutexType* c, const CLockLocation& locklocation)
|
||||
std::is_base_of_v<std::recursive_mutex, MutexType>;
|
||||
|
||||
LockData& lockdata = GetLockData();
|
||||
std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
|
||||
STDLOCK(lockdata.dd_mutex);
|
||||
|
||||
LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
|
||||
lock_stack.emplace_back(c, locklocation);
|
||||
@@ -206,7 +207,7 @@ static void push_lock(MutexType* c, const CLockLocation& locklocation)
|
||||
static void pop_lock()
|
||||
{
|
||||
LockData& lockdata = GetLockData();
|
||||
std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
|
||||
STDLOCK(lockdata.dd_mutex);
|
||||
|
||||
LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
|
||||
lock_stack.pop_back();
|
||||
@@ -226,7 +227,7 @@ template void EnterCritical(const char*, const char*, int, std::recursive_mutex*
|
||||
void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line)
|
||||
{
|
||||
LockData& lockdata = GetLockData();
|
||||
std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
|
||||
STDLOCK(lockdata.dd_mutex);
|
||||
|
||||
const LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
|
||||
if (!lock_stack.empty()) {
|
||||
@@ -257,7 +258,7 @@ void LeaveCritical()
|
||||
static std::string LocksHeld()
|
||||
{
|
||||
LockData& lockdata = GetLockData();
|
||||
std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
|
||||
STDLOCK(lockdata.dd_mutex);
|
||||
|
||||
const LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
|
||||
std::string result;
|
||||
@@ -269,7 +270,7 @@ static std::string LocksHeld()
|
||||
static bool LockHeld(void* mutex)
|
||||
{
|
||||
LockData& lockdata = GetLockData();
|
||||
std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
|
||||
STDLOCK(lockdata.dd_mutex);
|
||||
|
||||
const LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
|
||||
for (const LockStackItem& i : lock_stack) {
|
||||
@@ -302,7 +303,7 @@ template void AssertLockNotHeldInternal(const char*, const char*, int, Recursive
|
||||
void DeleteLock(void* cs)
|
||||
{
|
||||
LockData& lockdata = GetLockData();
|
||||
std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
|
||||
STDLOCK(lockdata.dd_mutex);
|
||||
const LockPair item = std::make_pair(cs, nullptr);
|
||||
LockOrders::iterator it = lockdata.lockorders.lower_bound(item);
|
||||
while (it != lockdata.lockorders.end() && it->first.first == cs) {
|
||||
@@ -321,7 +322,7 @@ void DeleteLock(void* cs)
|
||||
bool LockStackEmpty()
|
||||
{
|
||||
LockData& lockdata = GetLockData();
|
||||
std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
|
||||
STDLOCK(lockdata.dd_mutex);
|
||||
const auto it = lockdata.m_lock_stacks.find(std::this_thread::get_id());
|
||||
if (it == lockdata.m_lock_stacks.end()) {
|
||||
return true;
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
// Thread Safety Analysis and provides appropriate annotation macros.
|
||||
#include <threadsafety.h> // IWYU pragma: export
|
||||
|
||||
#include <util/macros.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
// StdMutex provides an annotated version of std::mutex for us,
|
||||
@@ -23,15 +25,21 @@ public:
|
||||
//! with the ! operator, to indicate that a mutex should not be held.
|
||||
const StdMutex& operator!() const { return *this; }
|
||||
#endif // __clang__
|
||||
|
||||
// StdMutex::Guard provides an annotated version of std::lock_guard for us.
|
||||
class SCOPED_LOCKABLE Guard : public std::lock_guard<StdMutex>
|
||||
{
|
||||
public:
|
||||
explicit Guard(StdMutex& cs) EXCLUSIVE_LOCK_FUNCTION(cs) : std::lock_guard<StdMutex>(cs) {}
|
||||
~Guard() UNLOCK_FUNCTION() = default;
|
||||
};
|
||||
|
||||
static inline StdMutex& CheckNotHeld(StdMutex& cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) LOCK_RETURNED(cs) { return cs; }
|
||||
};
|
||||
|
||||
// StdLockGuard provides an annotated version of std::lock_guard for us,
|
||||
// and should only be used when sync.h Mutex/LOCK/etc are not usable.
|
||||
class SCOPED_LOCKABLE StdLockGuard : public std::lock_guard<StdMutex>
|
||||
{
|
||||
public:
|
||||
explicit StdLockGuard(StdMutex& cs) EXCLUSIVE_LOCK_FUNCTION(cs) : std::lock_guard<StdMutex>(cs) {}
|
||||
~StdLockGuard() UNLOCK_FUNCTION() = default;
|
||||
};
|
||||
// Provide STDLOCK(..) wrapper around StdMutex::Guard that checks the lock is not already held
|
||||
#define STDLOCK(cs) StdMutex::Guard UNIQUE_NAME(criticalblock){StdMutex::CheckNotHeld(cs)}
|
||||
|
||||
using StdLockGuard = StdMutex::Guard; // TODO: remove, provided for backwards compat only
|
||||
|
||||
#endif // BITCOIN_UTIL_STDMUTEX_H
|
||||
|
||||
Reference in New Issue
Block a user