Gregory Maxwell
13 years ago
16 changed files with 388 additions and 281 deletions
@ -0,0 +1,119 @@ |
|||||||
|
// Copyright (c) 2011-2012 The Bitcoin developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "sync.h" |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef DEBUG_LOCKORDER |
||||||
|
//
|
||||||
|
// Early deadlock detection.
|
||||||
|
// Problem being solved:
|
||||||
|
// Thread 1 locks A, then B, then C
|
||||||
|
// Thread 2 locks D, then C, then A
|
||||||
|
// --> may result in deadlock between the two threads, depending on when they run.
|
||||||
|
// Solution implemented here:
|
||||||
|
// Keep track of pairs of locks: (A before B), (A before C), etc.
|
||||||
|
// Complain if any thread trys to lock in a different order.
|
||||||
|
//
|
||||||
|
|
||||||
|
struct CLockLocation |
||||||
|
{ |
||||||
|
CLockLocation(const char* pszName, const char* pszFile, int nLine) |
||||||
|
{ |
||||||
|
mutexName = pszName; |
||||||
|
sourceFile = pszFile; |
||||||
|
sourceLine = nLine; |
||||||
|
} |
||||||
|
|
||||||
|
std::string ToString() const |
||||||
|
{ |
||||||
|
return mutexName+" "+sourceFile+":"+itostr(sourceLine); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
std::string mutexName; |
||||||
|
std::string sourceFile; |
||||||
|
int sourceLine; |
||||||
|
}; |
||||||
|
|
||||||
|
typedef std::vector< std::pair<void*, CLockLocation> > LockStack; |
||||||
|
|
||||||
|
static boost::interprocess::interprocess_mutex dd_mutex; |
||||||
|
static std::map<std::pair<void*, void*>, LockStack> lockorders; |
||||||
|
static boost::thread_specific_ptr<LockStack> lockstack; |
||||||
|
|
||||||
|
|
||||||
|
static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, const LockStack& s1, const LockStack& s2) |
||||||
|
{ |
||||||
|
printf("POTENTIAL DEADLOCK DETECTED\n"); |
||||||
|
printf("Previous lock order was:\n"); |
||||||
|
BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, s2) |
||||||
|
{ |
||||||
|
if (i.first == mismatch.first) printf(" (1)"); |
||||||
|
if (i.first == mismatch.second) printf(" (2)"); |
||||||
|
printf(" %s\n", i.second.ToString().c_str()); |
||||||
|
} |
||||||
|
printf("Current lock order is:\n"); |
||||||
|
BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, s1) |
||||||
|
{ |
||||||
|
if (i.first == mismatch.first) printf(" (1)"); |
||||||
|
if (i.first == mismatch.second) printf(" (2)"); |
||||||
|
printf(" %s\n", i.second.ToString().c_str()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) |
||||||
|
{ |
||||||
|
bool fOrderOK = true; |
||||||
|
if (lockstack.get() == NULL) |
||||||
|
lockstack.reset(new LockStack); |
||||||
|
|
||||||
|
if (fDebug) printf("Locking: %s\n", locklocation.ToString().c_str()); |
||||||
|
dd_mutex.lock(); |
||||||
|
|
||||||
|
(*lockstack).push_back(std::make_pair(c, locklocation)); |
||||||
|
|
||||||
|
if (!fTry) BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, (*lockstack)) |
||||||
|
{ |
||||||
|
if (i.first == c) break; |
||||||
|
|
||||||
|
std::pair<void*, void*> p1 = std::make_pair(i.first, c); |
||||||
|
if (lockorders.count(p1)) |
||||||
|
continue; |
||||||
|
lockorders[p1] = (*lockstack); |
||||||
|
|
||||||
|
std::pair<void*, void*> p2 = std::make_pair(c, i.first); |
||||||
|
if (lockorders.count(p2)) |
||||||
|
{ |
||||||
|
potential_deadlock_detected(p1, lockorders[p2], lockorders[p1]); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
dd_mutex.unlock(); |
||||||
|
} |
||||||
|
|
||||||
|
static void pop_lock() |
||||||
|
{ |
||||||
|
if (fDebug) |
||||||
|
{ |
||||||
|
const CLockLocation& locklocation = (*lockstack).rbegin()->second; |
||||||
|
printf("Unlocked: %s\n", locklocation.ToString().c_str()); |
||||||
|
} |
||||||
|
dd_mutex.lock(); |
||||||
|
(*lockstack).pop_back(); |
||||||
|
dd_mutex.unlock(); |
||||||
|
} |
||||||
|
|
||||||
|
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry) |
||||||
|
{ |
||||||
|
push_lock(cs, CLockLocation(pszName, pszFile, nLine), fTry); |
||||||
|
} |
||||||
|
|
||||||
|
void LeaveCritical() |
||||||
|
{ |
||||||
|
pop_lock(); |
||||||
|
} |
||||||
|
|
||||||
|
#endif /* DEBUG_LOCKORDER */ |
@ -0,0 +1,216 @@ |
|||||||
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||||
|
// Copyright (c) 2009-2012 The Bitcoin developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
#ifndef BITCOIN_SYNC_H |
||||||
|
#define BITCOIN_SYNC_H |
||||||
|
|
||||||
|
#include <boost/interprocess/sync/interprocess_recursive_mutex.hpp> |
||||||
|
#include <boost/interprocess/sync/scoped_lock.hpp> |
||||||
|
#include <boost/interprocess/sync/interprocess_semaphore.hpp> |
||||||
|
#include <boost/interprocess/sync/lock_options.hpp> |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Wrapped boost mutex: supports recursive locking, but no waiting */ |
||||||
|
typedef boost::interprocess::interprocess_recursive_mutex CCriticalSection; |
||||||
|
|
||||||
|
/** Wrapped boost mutex: supports waiting but not recursive locking */ |
||||||
|
typedef boost::interprocess::interprocess_mutex CWaitableCriticalSection; |
||||||
|
|
||||||
|
#ifdef DEBUG_LOCKORDER |
||||||
|
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false); |
||||||
|
void LeaveCritical(); |
||||||
|
#else |
||||||
|
void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} |
||||||
|
void static inline LeaveCritical() {} |
||||||
|
#endif |
||||||
|
|
||||||
|
/** Wrapper around boost::interprocess::scoped_lock */ |
||||||
|
template<typename Mutex> |
||||||
|
class CMutexLock |
||||||
|
{ |
||||||
|
private: |
||||||
|
boost::interprocess::scoped_lock<Mutex> lock; |
||||||
|
public: |
||||||
|
|
||||||
|
void Enter(const char* pszName, const char* pszFile, int nLine) |
||||||
|
{ |
||||||
|
if (!lock.owns()) |
||||||
|
{ |
||||||
|
EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex())); |
||||||
|
#ifdef DEBUG_LOCKCONTENTION |
||||||
|
if (!lock.try_lock()) |
||||||
|
{ |
||||||
|
printf("LOCKCONTENTION: %s\n", pszName); |
||||||
|
printf("Locker: %s:%d\n", pszFile, nLine); |
||||||
|
#endif |
||||||
|
lock.lock(); |
||||||
|
#ifdef DEBUG_LOCKCONTENTION |
||||||
|
} |
||||||
|
#endif |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Leave() |
||||||
|
{ |
||||||
|
if (lock.owns()) |
||||||
|
{ |
||||||
|
lock.unlock(); |
||||||
|
LeaveCritical(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool TryEnter(const char* pszName, const char* pszFile, int nLine) |
||||||
|
{ |
||||||
|
if (!lock.owns()) |
||||||
|
{ |
||||||
|
EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex()), true); |
||||||
|
lock.try_lock(); |
||||||
|
if (!lock.owns()) |
||||||
|
LeaveCritical(); |
||||||
|
} |
||||||
|
return lock.owns(); |
||||||
|
} |
||||||
|
|
||||||
|
CMutexLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) : lock(mutexIn, boost::interprocess::defer_lock) |
||||||
|
{ |
||||||
|
if (fTry) |
||||||
|
TryEnter(pszName, pszFile, nLine); |
||||||
|
else |
||||||
|
Enter(pszName, pszFile, nLine); |
||||||
|
} |
||||||
|
|
||||||
|
~CMutexLock() |
||||||
|
{ |
||||||
|
if (lock.owns()) |
||||||
|
LeaveCritical(); |
||||||
|
} |
||||||
|
|
||||||
|
operator bool() |
||||||
|
{ |
||||||
|
return lock.owns(); |
||||||
|
} |
||||||
|
|
||||||
|
boost::interprocess::scoped_lock<Mutex> &GetLock() |
||||||
|
{ |
||||||
|
return lock; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
typedef CMutexLock<CCriticalSection> CCriticalBlock; |
||||||
|
|
||||||
|
#define LOCK(cs) CCriticalBlock criticalblock(cs, #cs, __FILE__, __LINE__) |
||||||
|
#define LOCK2(cs1,cs2) CCriticalBlock criticalblock1(cs1, #cs1, __FILE__, __LINE__),criticalblock2(cs2, #cs2, __FILE__, __LINE__) |
||||||
|
#define TRY_LOCK(cs,name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__, true) |
||||||
|
|
||||||
|
#define ENTER_CRITICAL_SECTION(cs) \ |
||||||
|
{ \ |
||||||
|
EnterCritical(#cs, __FILE__, __LINE__, (void*)(&cs)); \ |
||||||
|
(cs).lock(); \ |
||||||
|
} |
||||||
|
|
||||||
|
#define LEAVE_CRITICAL_SECTION(cs) \ |
||||||
|
{ \ |
||||||
|
(cs).unlock(); \ |
||||||
|
LeaveCritical(); \ |
||||||
|
} |
||||||
|
|
||||||
|
#ifdef MAC_OSX |
||||||
|
// boost::interprocess::interprocess_semaphore seems to spinlock on OSX; prefer polling instead
|
||||||
|
class CSemaphore |
||||||
|
{ |
||||||
|
private: |
||||||
|
CCriticalSection cs; |
||||||
|
int val; |
||||||
|
|
||||||
|
public: |
||||||
|
CSemaphore(int init) : val(init) {} |
||||||
|
|
||||||
|
void wait() { |
||||||
|
do { |
||||||
|
{ |
||||||
|
LOCK(cs); |
||||||
|
if (val>0) { |
||||||
|
val--; |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
Sleep(100); |
||||||
|
} while(1); |
||||||
|
} |
||||||
|
|
||||||
|
bool try_wait() { |
||||||
|
LOCK(cs); |
||||||
|
if (val>0) { |
||||||
|
val--; |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
void post() { |
||||||
|
LOCK(cs); |
||||||
|
val++; |
||||||
|
} |
||||||
|
}; |
||||||
|
#else |
||||||
|
typedef boost::interprocess::interprocess_semaphore CSemaphore; |
||||||
|
#endif |
||||||
|
|
||||||
|
/** RAII-style semaphore lock */ |
||||||
|
class CSemaphoreGrant |
||||||
|
{ |
||||||
|
private: |
||||||
|
CSemaphore *sem; |
||||||
|
bool fHaveGrant; |
||||||
|
|
||||||
|
public: |
||||||
|
void Acquire() { |
||||||
|
if (fHaveGrant) |
||||||
|
return; |
||||||
|
sem->wait(); |
||||||
|
fHaveGrant = true; |
||||||
|
} |
||||||
|
|
||||||
|
void Release() { |
||||||
|
if (!fHaveGrant) |
||||||
|
return; |
||||||
|
sem->post(); |
||||||
|
fHaveGrant = false; |
||||||
|
} |
||||||
|
|
||||||
|
bool TryAcquire() { |
||||||
|
if (!fHaveGrant && sem->try_wait()) |
||||||
|
fHaveGrant = true; |
||||||
|
return fHaveGrant; |
||||||
|
} |
||||||
|
|
||||||
|
void MoveTo(CSemaphoreGrant &grant) { |
||||||
|
grant.Release(); |
||||||
|
grant.sem = sem; |
||||||
|
grant.fHaveGrant = fHaveGrant; |
||||||
|
sem = NULL; |
||||||
|
fHaveGrant = false; |
||||||
|
} |
||||||
|
|
||||||
|
CSemaphoreGrant() : sem(NULL), fHaveGrant(false) {} |
||||||
|
|
||||||
|
CSemaphoreGrant(CSemaphore &sema, bool fTry = false) : sem(&sema), fHaveGrant(false) { |
||||||
|
if (fTry) |
||||||
|
TryAcquire(); |
||||||
|
else |
||||||
|
Acquire(); |
||||||
|
} |
||||||
|
|
||||||
|
~CSemaphoreGrant() { |
||||||
|
Release(); |
||||||
|
} |
||||||
|
|
||||||
|
operator bool() { |
||||||
|
return fHaveGrant; |
||||||
|
} |
||||||
|
}; |
||||||
|
#endif |
||||||
|
|
Loading…
Reference in new issue