Skip to content

Commit 55b4c65

Browse files
author
MarcoFalke
committed
Merge bitcoin#16127: More thread safety annotation coverage
5478d6c logging: thread safety annotations (Anthony Towns) e685ca1 util/system.cpp: add thread safety annotations for dir_locks (Anthony Towns) a788789 test/checkqueue_tests: thread safety annotations (Anthony Towns) 479c584 rpc/blockchain.cpp: thread safety annotations for latestblock (Anthony Towns) 8b5af3d net: fMsgProcWake use LOCK instead of lock_guard (Anthony Towns) de7c5f4 wallet/wallet.h: Remove mutexScanning which was only protecting a single atomic bool (Anthony Towns) c3cf2f5 rpc/blockchain.cpp: Remove g_utxosetscan mutex that is only protecting a single atomic variable (Anthony Towns) Pull request description: In a few cases we need to use `std::mutex` rather than the sync.h primitives. But `std::lock_guard<std::mutex>` doesn't include the clang thread safety annotations unless you also use clang's C library, which means you can't indicate when variables should be guarded by `std::mutex` mutexes. This adds an annotated version of `std::lock_guard<std::mutex>` to threadsafety.h to fix that, and modifies places where `std::mutex` is used to take advantage of the annotations. It's based on top of bitcoin#16112, and turns the thread safety comments included there into annotations. It also changes the RAII classes in wallet/wallet.h and rpc/blockchain.cpp to just use the atomic<bool> flag for synchronisation rather than having a mutex that doesn't actually guard anything as well. ACKs for top commit: MarcoFalke: ACK 5478d6c 🗾 hebasto: re-ACK 5478d6c, only renamed s/`MutexGuard`/`LockGuard`/, and dropped the commit "test/util_threadnames_tests: add thread safety annotations" since the [previous](bitcoin#16127 (review)) review. ryanofsky: Code review ACK 5478d6c. Thanks for taking suggestions! Only changes since last review are dropping thread rename test commit d53072e and renaming mutex guard to lock guard Tree-SHA512: 7b00d31f6f2b5a222ec69431eb810a74abf0542db3a65d1bbad54e354c40df2857ec89c00b4a5e466c81ba223267ca95f3f98d5fbc1a1d052a2c3a7d2209790a
2 parents 9ccaee1 + 5478d6c commit 55b4c65

File tree

9 files changed

+58
-47
lines changed

9 files changed

+58
-47
lines changed

src/logging.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ static int FileWriteStr(const std::string &str, FILE *fp)
4141

4242
bool BCLog::Logger::StartLogging()
4343
{
44-
std::lock_guard<std::mutex> scoped_lock(m_cs);
44+
LockGuard scoped_lock(m_cs);
4545

4646
assert(m_buffering);
4747
assert(m_fileout == nullptr);
@@ -80,7 +80,7 @@ bool BCLog::Logger::StartLogging()
8080

8181
void BCLog::Logger::DisconnectTestLogger()
8282
{
83-
std::lock_guard<std::mutex> scoped_lock(m_cs);
83+
LockGuard scoped_lock(m_cs);
8484
m_buffering = true;
8585
if (m_fileout != nullptr) fclose(m_fileout);
8686
m_fileout = nullptr;
@@ -246,7 +246,7 @@ namespace BCLog {
246246

247247
void BCLog::Logger::LogPrintStr(const std::string& str)
248248
{
249-
std::lock_guard<std::mutex> scoped_lock(m_cs);
249+
LockGuard scoped_lock(m_cs);
250250
std::string str_prefixed = LogEscapeMessage(str);
251251

252252
if (m_log_threadnames && m_started_new_line) {

src/logging.h

+9-7
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <fs.h>
1010
#include <tinyformat.h>
11+
#include <threadsafety.h>
1112
#include <util/string.h>
1213

1314
#include <atomic>
@@ -62,9 +63,10 @@ namespace BCLog {
6263
{
6364
private:
6465
mutable std::mutex m_cs; // Can not use Mutex from sync.h because in debug mode it would cause a deadlock when a potential deadlock was detected
65-
FILE* m_fileout = nullptr; // GUARDED_BY(m_cs)
66-
std::list<std::string> m_msgs_before_open; // GUARDED_BY(m_cs)
67-
bool m_buffering{true}; //!< Buffer messages before logging can be started. GUARDED_BY(m_cs)
66+
67+
FILE* m_fileout GUARDED_BY(m_cs) = nullptr;
68+
std::list<std::string> m_msgs_before_open GUARDED_BY(m_cs);
69+
bool m_buffering GUARDED_BY(m_cs) = true; //!< Buffer messages before logging can be started.
6870

6971
/**
7072
* m_started_new_line is a state variable that will suppress printing of
@@ -79,7 +81,7 @@ namespace BCLog {
7981
std::string LogTimestampStr(const std::string& str);
8082

8183
/** Slots that connect to the print signal */
82-
std::list<std::function<void(const std::string&)>> m_print_callbacks /* GUARDED_BY(m_cs) */ {};
84+
std::list<std::function<void(const std::string&)>> m_print_callbacks GUARDED_BY(m_cs) {};
8385

8486
public:
8587
bool m_print_to_console = false;
@@ -98,22 +100,22 @@ namespace BCLog {
98100
/** Returns whether logs will be written to any output */
99101
bool Enabled() const
100102
{
101-
std::lock_guard<std::mutex> scoped_lock(m_cs);
103+
LockGuard scoped_lock(m_cs);
102104
return m_buffering || m_print_to_console || m_print_to_file || !m_print_callbacks.empty();
103105
}
104106

105107
/** Connect a slot to the print signal and return the connection */
106108
std::list<std::function<void(const std::string&)>>::iterator PushBackCallback(std::function<void(const std::string&)> fun)
107109
{
108-
std::lock_guard<std::mutex> scoped_lock(m_cs);
110+
LockGuard scoped_lock(m_cs);
109111
m_print_callbacks.push_back(std::move(fun));
110112
return --m_print_callbacks.end();
111113
}
112114

113115
/** Delete a connection */
114116
void DeleteCallback(std::list<std::function<void(const std::string&)>>::iterator it)
115117
{
116-
std::lock_guard<std::mutex> scoped_lock(m_cs);
118+
LockGuard scoped_lock(m_cs);
117119
m_print_callbacks.erase(it);
118120
}
119121

src/net.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -1455,7 +1455,7 @@ void CConnman::ThreadSocketHandler()
14551455
void CConnman::WakeMessageHandler()
14561456
{
14571457
{
1458-
std::lock_guard<std::mutex> lock(mutexMsgProc);
1458+
LOCK(mutexMsgProc);
14591459
fMsgProcWake = true;
14601460
}
14611461
condMsgProc.notify_one();
@@ -2058,7 +2058,7 @@ void CConnman::ThreadMessageHandler()
20582058

20592059
WAIT_LOCK(mutexMsgProc, lock);
20602060
if (!fMoreWork) {
2061-
condMsgProc.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100), [this] { return fMsgProcWake; });
2061+
condMsgProc.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100), [this]() EXCLUSIVE_LOCKS_REQUIRED(mutexMsgProc) { return fMsgProcWake; });
20622062
}
20632063
fMsgProcWake = false;
20642064
}
@@ -2366,7 +2366,7 @@ static CNetCleanup instance_of_cnetcleanup;
23662366
void CConnman::Interrupt()
23672367
{
23682368
{
2369-
std::lock_guard<std::mutex> lock(mutexMsgProc);
2369+
LOCK(mutexMsgProc);
23702370
flagInterruptMsgProc = true;
23712371
}
23722372
condMsgProc.notify_all();

src/net.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ class CConnman
454454
const uint64_t nSeed0, nSeed1;
455455

456456
/** flag for waking the message processor. */
457-
bool fMsgProcWake;
457+
bool fMsgProcWake GUARDED_BY(mutexMsgProc);
458458

459459
std::condition_variable condMsgProc;
460460
Mutex mutexMsgProc;

src/rpc/blockchain.cpp

+9-13
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ struct CUpdatedBlock
5252

5353
static Mutex cs_blockchange;
5454
static std::condition_variable cond_blockchange;
55-
static CUpdatedBlock latestblock;
55+
static CUpdatedBlock latestblock GUARDED_BY(cs_blockchange);
5656

5757
NodeContext& EnsureNodeContext(const util::Ref& context)
5858
{
@@ -223,7 +223,7 @@ static UniValue getbestblockhash(const JSONRPCRequest& request)
223223
void RPCNotifyBlockChange(const CBlockIndex* pindex)
224224
{
225225
if(pindex) {
226-
std::lock_guard<std::mutex> lock(cs_blockchange);
226+
LOCK(cs_blockchange);
227227
latestblock.hash = pindex->GetBlockHash();
228228
latestblock.height = pindex->nHeight;
229229
}
@@ -258,9 +258,9 @@ static UniValue waitfornewblock(const JSONRPCRequest& request)
258258
WAIT_LOCK(cs_blockchange, lock);
259259
block = latestblock;
260260
if(timeout)
261-
cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&block]{return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
261+
cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&block]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
262262
else
263-
cond_blockchange.wait(lock, [&block]{return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
263+
cond_blockchange.wait(lock, [&block]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
264264
block = latestblock;
265265
}
266266
UniValue ret(UniValue::VOBJ);
@@ -300,9 +300,9 @@ static UniValue waitforblock(const JSONRPCRequest& request)
300300
{
301301
WAIT_LOCK(cs_blockchange, lock);
302302
if(timeout)
303-
cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&hash]{return latestblock.hash == hash || !IsRPCRunning();});
303+
cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&hash]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.hash == hash || !IsRPCRunning();});
304304
else
305-
cond_blockchange.wait(lock, [&hash]{return latestblock.hash == hash || !IsRPCRunning(); });
305+
cond_blockchange.wait(lock, [&hash]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.hash == hash || !IsRPCRunning(); });
306306
block = latestblock;
307307
}
308308

@@ -344,9 +344,9 @@ static UniValue waitforblockheight(const JSONRPCRequest& request)
344344
{
345345
WAIT_LOCK(cs_blockchange, lock);
346346
if(timeout)
347-
cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&height]{return latestblock.height >= height || !IsRPCRunning();});
347+
cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&height]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height >= height || !IsRPCRunning();});
348348
else
349-
cond_blockchange.wait(lock, [&height]{return latestblock.height >= height || !IsRPCRunning(); });
349+
cond_blockchange.wait(lock, [&height]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height >= height || !IsRPCRunning(); });
350350
block = latestblock;
351351
}
352352
UniValue ret(UniValue::VOBJ);
@@ -1995,7 +1995,6 @@ bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>&
19951995
}
19961996

19971997
/** RAII object to prevent concurrency issue when scanning the txout set */
1998-
static std::mutex g_utxosetscan;
19991998
static std::atomic<int> g_scan_progress;
20001999
static std::atomic<bool> g_scan_in_progress;
20012000
static std::atomic<bool> g_should_abort_scan;
@@ -2008,18 +2007,15 @@ class CoinsViewScanReserver
20082007

20092008
bool reserve() {
20102009
CHECK_NONFATAL(!m_could_reserve);
2011-
std::lock_guard<std::mutex> lock(g_utxosetscan);
2012-
if (g_scan_in_progress) {
2010+
if (g_scan_in_progress.exchange(true)) {
20132011
return false;
20142012
}
2015-
g_scan_in_progress = true;
20162013
m_could_reserve = true;
20172014
return true;
20182015
}
20192016

20202017
~CoinsViewScanReserver() {
20212018
if (m_could_reserve) {
2022-
std::lock_guard<std::mutex> lock(g_utxosetscan);
20232019
g_scan_in_progress = false;
20242020
}
20252021
}

src/test/checkqueue_tests.cpp

+14-9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

55
#include <checkqueue.h>
6+
#include <sync.h>
67
#include <test/util/setup_common.h>
78
#include <util/memory.h>
89
#include <util/system.h>
@@ -57,14 +58,14 @@ struct FailingCheck {
5758
};
5859

5960
struct UniqueCheck {
60-
static std::mutex m;
61-
static std::unordered_multiset<size_t> results;
61+
static Mutex m;
62+
static std::unordered_multiset<size_t> results GUARDED_BY(m);
6263
size_t check_id;
6364
UniqueCheck(size_t check_id_in) : check_id(check_id_in){};
6465
UniqueCheck() : check_id(0){};
6566
bool operator()()
6667
{
67-
std::lock_guard<std::mutex> l(m);
68+
LOCK(m);
6869
results.insert(check_id);
6970
return true;
7071
}
@@ -127,7 +128,7 @@ struct FrozenCleanupCheck {
127128
std::mutex FrozenCleanupCheck::m{};
128129
std::atomic<uint64_t> FrozenCleanupCheck::nFrozen{0};
129130
std::condition_variable FrozenCleanupCheck::cv{};
130-
std::mutex UniqueCheck::m;
131+
Mutex UniqueCheck::m;
131132
std::unordered_multiset<size_t> UniqueCheck::results;
132133
std::atomic<size_t> FakeCheckCheckCompletion::n_calls{0};
133134
std::atomic<size_t> MemoryCheck::fake_allocated_memory{0};
@@ -290,11 +291,15 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck)
290291
control.Add(vChecks);
291292
}
292293
}
293-
bool r = true;
294-
BOOST_REQUIRE_EQUAL(UniqueCheck::results.size(), COUNT);
295-
for (size_t i = 0; i < COUNT; ++i)
296-
r = r && UniqueCheck::results.count(i) == 1;
297-
BOOST_REQUIRE(r);
294+
{
295+
LOCK(UniqueCheck::m);
296+
bool r = true;
297+
BOOST_REQUIRE_EQUAL(UniqueCheck::results.size(), COUNT);
298+
for (size_t i = 0; i < COUNT; ++i) {
299+
r = r && UniqueCheck::results.count(i) == 1;
300+
}
301+
BOOST_REQUIRE(r);
302+
}
298303
tg.interrupt_all();
299304
tg.join_all();
300305
}

src/threadsafety.h

+11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#ifndef BITCOIN_THREADSAFETY_H
77
#define BITCOIN_THREADSAFETY_H
88

9+
#include <mutex>
10+
911
#ifdef __clang__
1012
// TL;DR Add GUARDED_BY(mutex) to member variables. The others are
1113
// rarely necessary. Ex: int nFoo GUARDED_BY(cs_foo);
@@ -54,4 +56,13 @@
5456
#define ASSERT_EXCLUSIVE_LOCK(...)
5557
#endif // __GNUC__
5658

59+
// LockGuard provides an annotated version of lock_guard for us
60+
// should only be used when sync.h Mutex/LOCK/etc aren't usable
61+
class SCOPED_LOCKABLE LockGuard : public std::lock_guard<std::mutex>
62+
{
63+
public:
64+
explicit LockGuard(std::mutex& cs) EXCLUSIVE_LOCK_FUNCTION(cs) : std::lock_guard<std::mutex>(cs) { }
65+
~LockGuard() UNLOCK_FUNCTION() {};
66+
};
67+
5768
#endif // BITCOIN_THREADSAFETY_H

src/util/system.cpp

+7-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// Distributed under the MIT software license, see the accompanying
44
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
55

6+
#include <sync.h>
67
#include <util/system.h>
78

89
#include <chainparamsbase.h>
@@ -75,18 +76,18 @@ const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
7576

7677
ArgsManager gArgs;
7778

79+
/** Mutex to protect dir_locks. */
80+
static Mutex cs_dir_locks;
7881
/** A map that contains all the currently held directory locks. After
7982
* successful locking, these will be held here until the global destructor
8083
* cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks
8184
* is called.
8285
*/
83-
static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks;
84-
/** Mutex to protect dir_locks. */
85-
static std::mutex cs_dir_locks;
86+
static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks GUARDED_BY(cs_dir_locks);
8687

8788
bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only)
8889
{
89-
std::lock_guard<std::mutex> ulock(cs_dir_locks);
90+
LOCK(cs_dir_locks);
9091
fs::path pathLockFile = directory / lockfile_name;
9192

9293
// If a lock for this directory already exists in the map, don't try to re-lock it
@@ -110,13 +111,13 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b
110111

111112
void UnlockDirectory(const fs::path& directory, const std::string& lockfile_name)
112113
{
113-
std::lock_guard<std::mutex> lock(cs_dir_locks);
114+
LOCK(cs_dir_locks);
114115
dir_locks.erase((directory / lockfile_name).string());
115116
}
116117

117118
void ReleaseDirectoryLocks()
118119
{
119-
std::lock_guard<std::mutex> ulock(cs_dir_locks);
120+
LOCK(cs_dir_locks);
120121
dir_locks.clear();
121122
}
122123

src/wallet/wallet.h

+1-5
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,6 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
631631
std::atomic<bool> fScanningWallet{false}; // controlled by WalletRescanReserver
632632
std::atomic<int64_t> m_scanning_start{0};
633633
std::atomic<double> m_scanning_progress{0};
634-
std::mutex mutexScanning;
635634
friend class WalletRescanReserver;
636635

637636
//! the current wallet version: clients below this version are not able to load the wallet
@@ -1287,13 +1286,11 @@ class WalletRescanReserver
12871286
bool reserve()
12881287
{
12891288
assert(!m_could_reserve);
1290-
std::lock_guard<std::mutex> lock(m_wallet.mutexScanning);
1291-
if (m_wallet.fScanningWallet) {
1289+
if (m_wallet.fScanningWallet.exchange(true)) {
12921290
return false;
12931291
}
12941292
m_wallet.m_scanning_start = GetTimeMillis();
12951293
m_wallet.m_scanning_progress = 0;
1296-
m_wallet.fScanningWallet = true;
12971294
m_could_reserve = true;
12981295
return true;
12991296
}
@@ -1305,7 +1302,6 @@ class WalletRescanReserver
13051302

13061303
~WalletRescanReserver()
13071304
{
1308-
std::lock_guard<std::mutex> lock(m_wallet.mutexScanning);
13091305
if (m_could_reserve) {
13101306
m_wallet.fScanningWallet = false;
13111307
}

0 commit comments

Comments
 (0)