Split synchronization mechanisms from util.{h,cpp}
This commit is contained in:
parent
5456ef3092
commit
7f3ccb59da
14 changed files with 353 additions and 330 deletions
|
@ -109,6 +109,7 @@ HEADERS += src/qt/bitcoingui.h \
|
||||||
src/bignum.h \
|
src/bignum.h \
|
||||||
src/checkpoints.h \
|
src/checkpoints.h \
|
||||||
src/compat.h \
|
src/compat.h \
|
||||||
|
src/sync.h \
|
||||||
src/util.h \
|
src/util.h \
|
||||||
src/uint256.h \
|
src/uint256.h \
|
||||||
src/serialize.h \
|
src/serialize.h \
|
||||||
|
@ -172,6 +173,7 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \
|
||||||
src/qt/editaddressdialog.cpp \
|
src/qt/editaddressdialog.cpp \
|
||||||
src/qt/bitcoinaddressvalidator.cpp \
|
src/qt/bitcoinaddressvalidator.cpp \
|
||||||
src/version.cpp \
|
src/version.cpp \
|
||||||
|
src/sync.cpp \
|
||||||
src/util.cpp \
|
src/util.cpp \
|
||||||
src/netbase.cpp \
|
src/netbase.cpp \
|
||||||
src/key.cpp \
|
src/key.cpp \
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "netbase.h"
|
#include "netbase.h"
|
||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "sync.h"
|
||||||
|
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#define BITCOIN_KEYSTORE_H
|
#define BITCOIN_KEYSTORE_H
|
||||||
|
|
||||||
#include "crypter.h"
|
#include "crypter.h"
|
||||||
#include "util.h"
|
#include "sync.h"
|
||||||
#include "base58.h"
|
#include "base58.h"
|
||||||
|
|
||||||
class CScript;
|
class CScript;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#define BITCOIN_MAIN_H
|
#define BITCOIN_MAIN_H
|
||||||
|
|
||||||
#include "bignum.h"
|
#include "bignum.h"
|
||||||
|
#include "sync.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
#include "script.h"
|
#include "script.h"
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ OBJS= \
|
||||||
obj/bitcoinrpc.o \
|
obj/bitcoinrpc.o \
|
||||||
obj/rpcdump.o \
|
obj/rpcdump.o \
|
||||||
obj/script.o \
|
obj/script.o \
|
||||||
|
obj/sync.o \
|
||||||
obj/util.o \
|
obj/util.o \
|
||||||
obj/wallet.o \
|
obj/wallet.o \
|
||||||
obj/walletdb.o \
|
obj/walletdb.o \
|
||||||
|
|
|
@ -58,6 +58,7 @@ OBJS= \
|
||||||
obj/bitcoinrpc.o \
|
obj/bitcoinrpc.o \
|
||||||
obj/rpcdump.o \
|
obj/rpcdump.o \
|
||||||
obj/script.o \
|
obj/script.o \
|
||||||
|
obj/sync.o \
|
||||||
obj/util.o \
|
obj/util.o \
|
||||||
obj/wallet.o \
|
obj/wallet.o \
|
||||||
obj/walletdb.o \
|
obj/walletdb.o \
|
||||||
|
|
|
@ -85,6 +85,7 @@ OBJS= \
|
||||||
obj/bitcoinrpc.o \
|
obj/bitcoinrpc.o \
|
||||||
obj/rpcdump.o \
|
obj/rpcdump.o \
|
||||||
obj/script.o \
|
obj/script.o \
|
||||||
|
obj/sync.o \
|
||||||
obj/util.o \
|
obj/util.o \
|
||||||
obj/wallet.o \
|
obj/wallet.o \
|
||||||
obj/walletdb.o \
|
obj/walletdb.o \
|
||||||
|
|
|
@ -102,6 +102,7 @@ OBJS= \
|
||||||
obj/bitcoinrpc.o \
|
obj/bitcoinrpc.o \
|
||||||
obj/rpcdump.o \
|
obj/rpcdump.o \
|
||||||
obj/script.o \
|
obj/script.o \
|
||||||
|
obj/sync.o \
|
||||||
obj/util.o \
|
obj/util.o \
|
||||||
obj/wallet.o \
|
obj/wallet.o \
|
||||||
obj/walletdb.o \
|
obj/walletdb.o \
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "wallet.h"
|
#include "wallet.h"
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
#include "messagepage.h"
|
#include "messagepage.h"
|
||||||
#include "ui_messagepage.h"
|
#include "ui_messagepage.h"
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||||
|
|
||||||
#include "ui_interface.h"
|
#include "ui_interface.h"
|
||||||
#include "util.h"
|
|
||||||
#include "qtipcserver.h"
|
#include "qtipcserver.h"
|
||||||
|
|
||||||
using namespace boost::interprocess;
|
using namespace boost::interprocess;
|
||||||
|
|
119
src/sync.cpp
Normal file
119
src/sync.cpp
Normal file
|
@ -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 */
|
216
src/sync.h
Normal file
216
src/sync.h
Normal file
|
@ -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
|
||||||
|
|
132
src/util.cpp
132
src/util.cpp
|
@ -4,6 +4,7 @@
|
||||||
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
|
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "sync.h"
|
||||||
#include "strlcpy.h"
|
#include "strlcpy.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "ui_interface.h"
|
#include "ui_interface.h"
|
||||||
|
@ -23,8 +24,6 @@ namespace boost {
|
||||||
#include <boost/program_options/parsers.hpp>
|
#include <boost/program_options/parsers.hpp>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/filesystem/fstream.hpp>
|
#include <boost/filesystem/fstream.hpp>
|
||||||
#include <boost/interprocess/sync/interprocess_mutex.hpp>
|
|
||||||
#include <boost/interprocess/sync/interprocess_recursive_mutex.hpp>
|
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
#include <openssl/crypto.h>
|
#include <openssl/crypto.h>
|
||||||
#include <openssl/rand.h>
|
#include <openssl/rand.h>
|
||||||
|
@ -71,13 +70,14 @@ bool fLogTimestamps = false;
|
||||||
CMedianFilter<int64> vTimeOffsets(200,0);
|
CMedianFilter<int64> vTimeOffsets(200,0);
|
||||||
|
|
||||||
// Init openssl library multithreading support
|
// Init openssl library multithreading support
|
||||||
static boost::interprocess::interprocess_mutex** ppmutexOpenSSL;
|
static CCriticalSection** ppmutexOpenSSL;
|
||||||
void locking_callback(int mode, int i, const char* file, int line)
|
void locking_callback(int mode, int i, const char* file, int line)
|
||||||
{
|
{
|
||||||
if (mode & CRYPTO_LOCK)
|
if (mode & CRYPTO_LOCK) {
|
||||||
ppmutexOpenSSL[i]->lock();
|
ENTER_CRITICAL_SECTION(*ppmutexOpenSSL[i]);
|
||||||
else
|
} else {
|
||||||
ppmutexOpenSSL[i]->unlock();
|
LEAVE_CRITICAL_SECTION(*ppmutexOpenSSL[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init
|
// Init
|
||||||
|
@ -87,9 +87,9 @@ public:
|
||||||
CInit()
|
CInit()
|
||||||
{
|
{
|
||||||
// Init openssl library multithreading support
|
// Init openssl library multithreading support
|
||||||
ppmutexOpenSSL = (boost::interprocess::interprocess_mutex**)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(boost::interprocess::interprocess_mutex*));
|
ppmutexOpenSSL = (CCriticalSection**)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(CCriticalSection*));
|
||||||
for (int i = 0; i < CRYPTO_num_locks(); i++)
|
for (int i = 0; i < CRYPTO_num_locks(); i++)
|
||||||
ppmutexOpenSSL[i] = new boost::interprocess::interprocess_mutex();
|
ppmutexOpenSSL[i] = new CCriticalSection();
|
||||||
CRYPTO_set_locking_callback(locking_callback);
|
CRYPTO_set_locking_callback(locking_callback);
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
|
@ -1221,117 +1221,3 @@ bool GetStartOnSystemStartup() { return false; }
|
||||||
bool SetStartOnSystemStartup(bool fAutoStart) { return false; }
|
bool SetStartOnSystemStartup(bool fAutoStart) { return false; }
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#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 */
|
|
||||||
|
|
204
src/util.h
204
src/util.h
|
@ -21,10 +21,6 @@ typedef int pid_t; /* define for windows compatiblity */
|
||||||
#include <boost/thread.hpp>
|
#include <boost/thread.hpp>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
#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>
|
|
||||||
#include <boost/date_time/gregorian/gregorian_types.hpp>
|
#include <boost/date_time/gregorian/gregorian_types.hpp>
|
||||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||||
|
|
||||||
|
@ -188,206 +184,6 @@ void AddTimeData(const CNetAddr& ip, int64 nTime);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** 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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline std::string i64tostr(int64 n)
|
inline std::string i64tostr(int64 n)
|
||||||
{
|
{
|
||||||
return strprintf("%"PRI64d, n);
|
return strprintf("%"PRI64d, n);
|
||||||
|
|
Loading…
Reference in a new issue