mirror of https://github.com/lukechilds/komodo.git
14 changed files with 353 additions and 330 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