Skip to content

Commit b9c4260

Browse files
committed
sync.h: add REVERSE_LOCK
1 parent 306f71b commit b9c4260

File tree

3 files changed

+94
-9
lines changed

3 files changed

+94
-9
lines changed

src/sync.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#include <util/strencodings.h>
1414
#include <util/threadnames.h>
1515

16-
16+
#include <system_error>
1717
#include <map>
1818
#include <set>
1919

@@ -60,6 +60,11 @@ struct CLockLocation {
6060
mutexName, sourceFile, itostr(sourceLine), (fTry ? " (TRY)" : ""), m_thread_name);
6161
}
6262

63+
std::string Name() const
64+
{
65+
return mutexName;
66+
}
67+
6368
private:
6469
bool fTry;
6570
std::string mutexName;
@@ -155,6 +160,18 @@ void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs
155160
push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry, util::ThreadGetInternalName()));
156161
}
157162

163+
void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line)
164+
{
165+
if (!g_lockstack.empty()) {
166+
const auto& lastlock = g_lockstack.back();
167+
if (lastlock.first == cs) {
168+
lockname = lastlock.second.Name();
169+
return;
170+
}
171+
}
172+
throw std::system_error(EPERM, std::generic_category(), strprintf("%s:%s %s was not most recent critical section locked", file, line, guardname));
173+
}
174+
158175
void LeaveCritical()
159176
{
160177
pop_lock();

src/sync.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ LEAVE_CRITICAL_SECTION(mutex); // no RAII
5050
#ifdef DEBUG_LOCKORDER
5151
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false);
5252
void LeaveCritical();
53+
void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line);
5354
std::string LocksHeld();
5455
void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) ASSERT_EXCLUSIVE_LOCK(cs);
5556
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
@@ -64,6 +65,7 @@ extern bool g_debug_lockorder_abort;
6465
#else
6566
void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {}
6667
void static inline LeaveCritical() {}
68+
void static inline CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {}
6769
void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) ASSERT_EXCLUSIVE_LOCK(cs) {}
6870
void static inline AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
6971
void static inline DeleteLock(void* cs) {}
@@ -171,8 +173,45 @@ class SCOPED_LOCKABLE UniqueLock : public Base
171173
{
172174
return Base::owns_lock();
173175
}
176+
177+
protected:
178+
// needed for reverse_lock
179+
UniqueLock() { }
180+
181+
public:
182+
/**
183+
* An RAII-style reverse lock. Unlocks on construction and locks on destruction.
184+
*/
185+
class reverse_lock {
186+
public:
187+
explicit reverse_lock(UniqueLock& _lock, const char* _guardname, const char* _file, int _line) : lock(_lock), file(_file), line(_line) {
188+
CheckLastCritical((void*)lock.mutex(), lockname, _guardname, _file, _line);
189+
lock.unlock();
190+
LeaveCritical();
191+
lock.swap(templock);
192+
}
193+
194+
~reverse_lock() {
195+
templock.swap(lock);
196+
EnterCritical(lockname.c_str(), file.c_str(), line, (void*)lock.mutex());
197+
lock.lock();
198+
}
199+
200+
private:
201+
reverse_lock(reverse_lock const&);
202+
reverse_lock& operator=(reverse_lock const&);
203+
204+
UniqueLock& lock;
205+
UniqueLock templock;
206+
std::string lockname;
207+
const std::string file;
208+
const int line;
209+
};
210+
friend class reverse_lock;
174211
};
175212

213+
#define REVERSE_LOCK(g) decltype(g)::reverse_lock PASTE2(revlock, __COUNTER__)(g, #g, __FILE__, __LINE__)
214+
176215
template<typename MutexArg>
177216
using DebugLock = UniqueLock<typename std::remove_reference<typename std::remove_pointer<MutexArg>::type>::type>;
178217

src/test/reverselock_tests.cpp

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Distributed under the MIT software license, see the accompanying
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

5-
#include <reverselock.h>
5+
#include <sync.h>
66
#include <test/util/setup_common.h>
77

88
#include <boost/test/unit_test.hpp>
@@ -11,21 +11,50 @@ BOOST_FIXTURE_TEST_SUITE(reverselock_tests, BasicTestingSetup)
1111

1212
BOOST_AUTO_TEST_CASE(reverselock_basics)
1313
{
14-
boost::mutex mutex;
15-
boost::unique_lock<boost::mutex> lock(mutex);
14+
Mutex mutex;
15+
WAIT_LOCK(mutex, lock);
1616

1717
BOOST_CHECK(lock.owns_lock());
1818
{
19-
reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock);
19+
REVERSE_LOCK(lock);
2020
BOOST_CHECK(!lock.owns_lock());
2121
}
2222
BOOST_CHECK(lock.owns_lock());
2323
}
2424

25+
BOOST_AUTO_TEST_CASE(reverselock_multiple)
26+
{
27+
Mutex mutex2;
28+
Mutex mutex;
29+
WAIT_LOCK(mutex2, lock2);
30+
WAIT_LOCK(mutex, lock);
31+
32+
// Make sure undoing two locks succeeds
33+
{
34+
REVERSE_LOCK(lock);
35+
BOOST_CHECK(!lock.owns_lock());
36+
REVERSE_LOCK(lock2);
37+
BOOST_CHECK(!lock2.owns_lock());
38+
}
39+
BOOST_CHECK(lock.owns_lock());
40+
BOOST_CHECK(lock2.owns_lock());
41+
}
42+
2543
BOOST_AUTO_TEST_CASE(reverselock_errors)
2644
{
27-
boost::mutex mutex;
28-
boost::unique_lock<boost::mutex> lock(mutex);
45+
Mutex mutex2;
46+
Mutex mutex;
47+
WAIT_LOCK(mutex2, lock2);
48+
WAIT_LOCK(mutex, lock);
49+
50+
#ifdef DEBUG_LOCKORDER
51+
// Make sure trying to reverse lock a previous lock fails
52+
try {
53+
REVERSE_LOCK(lock2);
54+
BOOST_CHECK(false); // REVERSE_LOCK(lock2) succeeded
55+
} catch(...) { }
56+
BOOST_CHECK(lock2.owns_lock());
57+
#endif
2958

3059
// Make sure trying to reverse lock an unlocked lock fails
3160
lock.unlock();
@@ -34,7 +63,7 @@ BOOST_AUTO_TEST_CASE(reverselock_errors)
3463

3564
bool failed = false;
3665
try {
37-
reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock);
66+
REVERSE_LOCK(lock);
3867
} catch(...) {
3968
failed = true;
4069
}
@@ -49,7 +78,7 @@ BOOST_AUTO_TEST_CASE(reverselock_errors)
4978
lock.lock();
5079
BOOST_CHECK(lock.owns_lock());
5180
{
52-
reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock);
81+
REVERSE_LOCK(lock);
5382
BOOST_CHECK(!lock.owns_lock());
5483
}
5584

0 commit comments

Comments
 (0)