diff --git a/src/script/scriptcache.h b/src/script/scriptcache.h index b4928424c..adeaf9a47 100644 --- a/src/script/scriptcache.h +++ b/src/script/scriptcache.h @@ -1,58 +1,66 @@ // Copyright (c) 2017 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_SCRIPT_SCRIPTCACHE_H #define BITCOIN_SCRIPT_SCRIPTCACHE_H #include #include +#include + +// Actually declared in validation.cpp; can't include because of circular +// dependency. +extern RecursiveMutex cs_main; + class CTransaction; /** * The script cache is a map using a key/value element, that caches the * success of executing a specific transaction's input scripts under a * specific set of flags, along with any associated information learned * during execution. * * The key is slightly shorter than a power-of-two size to make room for * the value. */ class ScriptCacheKey { std::array data; public: ScriptCacheKey() = default; ScriptCacheKey(const ScriptCacheKey &rhs) = default; ScriptCacheKey(const CTransaction &tx, uint32_t flags); bool operator==(const ScriptCacheKey &rhs) const { return rhs.data == data; } friend class ScriptCacheHasher; }; // DoS prevention: limit cache size to 32MB (over 1000000 entries on 64-bit // systems). Due to how we count cache size, actual memory usage is slightly // more (~32.25 MB) static const unsigned int DEFAULT_MAX_SCRIPT_CACHE_SIZE = 32; // Maximum sig cache size allowed static const int64_t MAX_MAX_SCRIPT_CACHE_SIZE = 16384; /** Initializes the script-execution cache */ void InitScriptExecutionCache(); /** * Check if a given key is in the cache, and if so, return its values. * (if not found, nSigChecks may or may not be set to an arbitrary value) */ -bool IsKeyInScriptCache(ScriptCacheKey key, bool erase, int &nSigChecksOut); +bool IsKeyInScriptCache(ScriptCacheKey key, bool erase, int &nSigChecksOut) + EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** * Add an entry in the cache. */ -void AddKeyInScriptCache(ScriptCacheKey key, int nSigChecks); +void AddKeyInScriptCache(ScriptCacheKey key, int nSigChecks) + EXCLUSIVE_LOCKS_REQUIRED(cs_main); #endif // BITCOIN_SCRIPT_SCRIPTCACHE_H diff --git a/src/sync.cpp b/src/sync.cpp index 92e5a6aef..3474f9f9a 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -1,288 +1,293 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEBUG_LOCKCONTENTION void PrintLockContention(const char *pszName, const char *pszFile, int nLine) { LogPrintf("LOCKCONTENTION: %s\n", pszName); LogPrintf("Locker: %s:%d\n", pszFile, nLine); } #endif /* DEBUG_LOCKCONTENTION */ #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 tries to lock in a different order. // struct CLockLocation { CLockLocation(const char *pszName, const char *pszFile, int nLine, bool fTryIn, const std::string &thread_name) : fTry(fTryIn), mutexName(pszName), sourceFile(pszFile), m_thread_name(thread_name), sourceLine(nLine) {} std::string ToString() const { return strprintf("'%s' in %s:%s%s (in thread '%s')", mutexName, sourceFile, sourceLine, (fTry ? " (TRY)" : ""), m_thread_name); } std::string Name() const { return mutexName; } private: bool fTry; std::string mutexName; std::string sourceFile; const std::string &m_thread_name; int sourceLine; }; using LockStackItem = std::pair; using LockStack = std::vector; using LockStacks = std::unordered_map; using LockPair = std::pair; using LockOrders = std::map; using InvLockOrders = std::set; struct LockData { LockStacks m_lock_stacks; LockOrders lockorders; InvLockOrders invlockorders; std::mutex dd_mutex; }; LockData &GetLockData() { // This approach guarantees that the object is not destroyed until after its // last use. The operating system automatically reclaims all the memory in a // program's heap when that program exits. // Since the ~LockData() destructor is never called, the LockData class and // all its subclasses must have implicitly-defined destructors. static LockData &lock_data = *new LockData(); return lock_data; } static void potential_deadlock_detected(const LockPair &mismatch, const LockStack &s1, const LockStack &s2) { LogPrintf("POTENTIAL DEADLOCK DETECTED\n"); LogPrintf("Previous lock order was:\n"); for (const LockStackItem &i : s1) { if (i.first == mismatch.first) { LogPrintfToBeContinued(" (1)"); } if (i.first == mismatch.second) { LogPrintfToBeContinued(" (2)"); } LogPrintf(" %s\n", i.second.ToString()); } std::string mutex_a, mutex_b; LogPrintf("Current lock order is:\n"); for (const LockStackItem &i : s2) { if (i.first == mismatch.first) { LogPrintfToBeContinued(" (1)"); mutex_a = i.second.Name(); } if (i.first == mismatch.second) { LogPrintfToBeContinued(" (2)"); mutex_b = i.second.Name(); } LogPrintf(" %s\n", i.second.ToString()); } if (g_debug_lockorder_abort) { tfm::format( std::cerr, "Assertion failed: detected inconsistent lock order for %s, " "details in debug log.\n", s2.back().second.ToString()); abort(); } throw std::logic_error( strprintf("potential deadlock detected: %s -> %s -> %s", mutex_b, mutex_a, mutex_b)); } static void push_lock(void *c, const CLockLocation &locklocation) { LockData &lockdata = GetLockData(); std::lock_guard lock(lockdata.dd_mutex); LockStack &lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()]; lock_stack.emplace_back(c, locklocation); for (const LockStackItem &i : lock_stack) { if (i.first == c) { break; } const LockPair p1 = std::make_pair(i.first, c); if (lockdata.lockorders.count(p1)) { continue; } const LockPair p2 = std::make_pair(c, i.first); if (lockdata.lockorders.count(p2)) { auto lock_stack_copy = lock_stack; lock_stack.pop_back(); potential_deadlock_detected(p1, lockdata.lockorders[p2], lock_stack_copy); // potential_deadlock_detected() does not return. } lockdata.lockorders.emplace(p1, lock_stack); lockdata.invlockorders.insert(p2); } } static void pop_lock() { LockData &lockdata = GetLockData(); std::lock_guard lock(lockdata.dd_mutex); LockStack &lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()]; lock_stack.pop_back(); if (lock_stack.empty()) { lockdata.m_lock_stacks.erase(std::this_thread::get_id()); } } void EnterCritical(const char *pszName, const char *pszFile, int nLine, void *cs, bool fTry) { push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry, util::ThreadGetInternalName())); } void CheckLastCritical(void *cs, std::string &lockname, const char *guardname, const char *file, int line) { { LockData &lockdata = GetLockData(); std::lock_guard lock(lockdata.dd_mutex); const LockStack &lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()]; if (!lock_stack.empty()) { const auto &lastlock = lock_stack.back(); if (lastlock.first == cs) { lockname = lastlock.second.Name(); return; } } } throw std::system_error( EPERM, std::generic_category(), strprintf("%s:%s %s was not most recent critical section locked", file, line, guardname)); } void LeaveCritical() { pop_lock(); } std::string LocksHeld() { LockData &lockdata = GetLockData(); std::lock_guard lock(lockdata.dd_mutex); const LockStack &lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()]; std::string result; for (const LockStackItem &i : lock_stack) { result += i.second.ToString() + std::string("\n"); } return result; } static bool LockHeld(void *mutex) { LockData &lockdata = GetLockData(); std::lock_guard lock(lockdata.dd_mutex); const LockStack &lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()]; for (const LockStackItem &i : lock_stack) { if (i.first == mutex) { return true; } } return false; } template void AssertLockHeldInternal(const char *pszName, const char *pszFile, int nLine, MutexType *cs) { if (LockHeld(cs)) { return; } tfm::format(std::cerr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld()); abort(); } template void AssertLockHeldInternal(const char *, const char *, int, Mutex *); template void AssertLockHeldInternal(const char *, const char *, int, RecursiveMutex *); +template void AssertLockNotHeldInternal(const char *pszName, const char *pszFile, - int nLine, void *cs) { + int nLine, MutexType *cs) { if (!LockHeld(cs)) { return; } tfm::format(std::cerr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld()); abort(); } +template void AssertLockNotHeldInternal(const char *, const char *, int, + Mutex *); +template void AssertLockNotHeldInternal(const char *, const char *, int, + RecursiveMutex *); void DeleteLock(void *cs) { LockData &lockdata = GetLockData(); std::lock_guard lock(lockdata.dd_mutex); const LockPair item = std::make_pair(cs, nullptr); LockOrders::iterator it = lockdata.lockorders.lower_bound(item); while (it != lockdata.lockorders.end() && it->first.first == cs) { const LockPair invitem = std::make_pair(it->first.second, it->first.first); lockdata.invlockorders.erase(invitem); lockdata.lockorders.erase(it++); } InvLockOrders::iterator invit = lockdata.invlockorders.lower_bound(item); while (invit != lockdata.invlockorders.end() && invit->first == cs) { const LockPair invinvitem = std::make_pair(invit->second, invit->first); lockdata.lockorders.erase(invinvitem); lockdata.invlockorders.erase(invit++); } } bool LockStackEmpty() { LockData &lockdata = GetLockData(); std::lock_guard lock(lockdata.dd_mutex); const auto it = lockdata.m_lock_stacks.find(std::this_thread::get_id()); if (it == lockdata.m_lock_stacks.end()) { return true; } return it->second.empty(); } bool g_debug_lockorder_abort = true; #endif /* DEBUG_LOCKORDER */ diff --git a/src/sync.h b/src/sync.h index 62d8a491d..c801cc93d 100644 --- a/src/sync.h +++ b/src/sync.h @@ -1,373 +1,378 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_SYNC_H #define BITCOIN_SYNC_H #include #include #include #include #include #include ///////////////////////////////////////////////// // // // THE SIMPLE DEFINITION, EXCLUDING DEBUG CODE // // // ///////////////////////////////////////////////// /* RecursiveMutex mutex; std::recursive_mutex mutex; LOCK(mutex); std::unique_lock criticalblock(mutex); LOCK2(mutex1, mutex2); std::unique_lock criticalblock1(mutex1); std::unique_lock criticalblock2(mutex2); TRY_LOCK(mutex, name); std::unique_lock name(mutex, std::try_to_lock_t); ENTER_CRITICAL_SECTION(mutex); // no RAII mutex.lock(); LEAVE_CRITICAL_SECTION(mutex); // no RAII mutex.unlock(); */ /////////////////////////////// // // // THE ACTUAL IMPLEMENTATION // // // /////////////////////////////// #ifdef DEBUG_LOCKORDER void EnterCritical(const char *pszName, const char *pszFile, int nLine, void *cs, bool fTry = false); void LeaveCritical(); void CheckLastCritical(void *cs, std::string &lockname, const char *guardname, const char *file, int line); std::string LocksHeld(); template void AssertLockHeldInternal(const char *pszName, const char *pszFile, int nLine, - MutexType *cs); + MutexType *cs) EXCLUSIVE_LOCKS_REQUIRED(cs); +template void AssertLockNotHeldInternal(const char *pszName, const char *pszFile, - int nLine, void *cs); + int nLine, MutexType *cs) + EXCLUSIVE_LOCKS_REQUIRED(!cs); void DeleteLock(void *cs); bool LockStackEmpty(); /** * Call abort() if a potential lock order deadlock bug is detected, instead of * just logging information and throwing a logic_error. Defaults to true, and * set to false in DEBUG_LOCKORDER unit tests. */ extern bool g_debug_lockorder_abort; #else inline void EnterCritical(const char *pszName, const char *pszFile, int nLine, void *cs, bool fTry = false) {} inline void LeaveCritical() {} inline void CheckLastCritical(void *cs, std::string &lockname, const char *guardname, const char *file, int line) {} template inline void AssertLockHeldInternal(const char *pszName, const char *pszFile, - int nLine, MutexType *cs) {} -inline void AssertLockNotHeldInternal(const char *pszName, const char *pszFile, - int nLine, void *cs) {} + int nLine, MutexType *cs) + EXCLUSIVE_LOCKS_REQUIRED(cs) {} +template +void AssertLockNotHeldInternal(const char *pszName, const char *pszFile, + int nLine, MutexType *cs) + EXCLUSIVE_LOCKS_REQUIRED(!cs) {} inline void DeleteLock(void *cs) {} inline bool LockStackEmpty() { return true; } #endif #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) #define AssertLockNotHeld(cs) \ AssertLockNotHeldInternal(#cs, __FILE__, __LINE__, &cs) /** * Template mixin that adds -Wthread-safety locking annotations and lock order * checking to a subset of the mutex API. */ template class LOCKABLE AnnotatedMixin : public PARENT { public: ~AnnotatedMixin() { DeleteLock((void *)this); } void lock() EXCLUSIVE_LOCK_FUNCTION() { PARENT::lock(); } void unlock() UNLOCK_FUNCTION() { PARENT::unlock(); } bool try_lock() EXCLUSIVE_TRYLOCK_FUNCTION(true) { return PARENT::try_lock(); } using UniqueLock = std::unique_lock; #ifdef __clang__ //! For negative capabilities in the Clang Thread Safety Analysis. //! A negative requirement uses the EXCLUSIVE_LOCKS_REQUIRED attribute, in //! conjunction with the ! operator, to indicate that a mutex should not be //! held. const AnnotatedMixin &operator!() const { return *this; } #endif // __clang__ }; /** * Wrapped mutex: supports recursive locking, but no waiting * TODO: We should move away from using the recursive lock by default. */ using RecursiveMutex = AnnotatedMixin; /** Wrapped mutex: supports waiting but not recursive locking */ typedef AnnotatedMixin Mutex; #ifdef DEBUG_LOCKCONTENTION void PrintLockContention(const char *pszName, const char *pszFile, int nLine); #endif /** Wrapper around std::unique_lock style lock for Mutex. */ template class SCOPED_LOCKABLE UniqueLock : public Base { private: void Enter(const char *pszName, const char *pszFile, int nLine) { EnterCritical(pszName, pszFile, nLine, (void *)(Base::mutex())); #ifdef DEBUG_LOCKCONTENTION if (!Base::try_lock()) { PrintLockContention(pszName, pszFile, nLine); #endif Base::lock(); #ifdef DEBUG_LOCKCONTENTION } #endif } bool TryEnter(const char *pszName, const char *pszFile, int nLine) { EnterCritical(pszName, pszFile, nLine, (void *)(Base::mutex()), true); Base::try_lock(); if (!Base::owns_lock()) { LeaveCritical(); } return Base::owns_lock(); } public: UniqueLock(Mutex &mutexIn, const char *pszName, const char *pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock) { if (fTry) { TryEnter(pszName, pszFile, nLine); } else { Enter(pszName, pszFile, nLine); } } UniqueLock(Mutex *pmutexIn, const char *pszName, const char *pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn) { if (!pmutexIn) { return; } *static_cast(this) = Base(*pmutexIn, std::defer_lock); if (fTry) { TryEnter(pszName, pszFile, nLine); } else { Enter(pszName, pszFile, nLine); } } ~UniqueLock() UNLOCK_FUNCTION() { if (Base::owns_lock()) { LeaveCritical(); } } operator bool() { return Base::owns_lock(); } protected: // needed for reverse_lock UniqueLock() {} public: /** * An RAII-style reverse lock. Unlocks on construction and locks on * destruction. */ class reverse_lock { public: explicit reverse_lock(UniqueLock &_lock, const char *_guardname, const char *_file, int _line) : lock(_lock), file(_file), line(_line) { CheckLastCritical((void *)lock.mutex(), lockname, _guardname, _file, _line); lock.unlock(); LeaveCritical(); lock.swap(templock); } ~reverse_lock() { templock.swap(lock); EnterCritical(lockname.c_str(), file.c_str(), line, (void *)lock.mutex()); lock.lock(); } private: reverse_lock(reverse_lock const &); reverse_lock &operator=(reverse_lock const &); UniqueLock &lock; UniqueLock templock; std::string lockname; const std::string file; const int line; }; friend class reverse_lock; }; #define REVERSE_LOCK(g) \ typename std::decay::type::reverse_lock PASTE2( \ revlock, __COUNTER__)(g, #g, __FILE__, __LINE__) template using DebugLock = UniqueLock::type>::type>; #define LOCK(cs) \ DebugLock PASTE2(criticalblock, \ __COUNTER__)(cs, #cs, __FILE__, __LINE__) #define LOCK2(cs1, cs2) \ DebugLock criticalblock1(cs1, #cs1, __FILE__, __LINE__); \ DebugLock criticalblock2(cs2, #cs2, __FILE__, __LINE__); #define TRY_LOCK(cs, name) \ DebugLock name(cs, #cs, __FILE__, __LINE__, true) #define WAIT_LOCK(cs, name) \ DebugLock name(cs, #cs, __FILE__, __LINE__) #define ENTER_CRITICAL_SECTION(cs) \ { \ EnterCritical(#cs, __FILE__, __LINE__, (void *)(&cs)); \ (cs).lock(); \ } #define LEAVE_CRITICAL_SECTION(cs) \ { \ (cs).unlock(); \ LeaveCritical(); \ } //! Run code while locking a mutex. //! //! Examples: //! //! WITH_LOCK(cs, shared_val = shared_val + 1); //! //! int val = WITH_LOCK(cs, return shared_val); //! #define WITH_LOCK(cs, code) \ [&] { \ LOCK(cs); \ code; \ }() class CSemaphore { private: std::condition_variable condition; std::mutex mutex; int value; public: explicit CSemaphore(int init) : value(init) {} void wait() { std::unique_lock lock(mutex); condition.wait(lock, [&]() { return value >= 1; }); value--; } bool try_wait() { std::lock_guard lock(mutex); if (value < 1) { return false; } value--; return true; } void post() { { std::lock_guard lock(mutex); value++; } condition.notify_one(); } }; /** 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; fHaveGrant = false; } CSemaphoreGrant() : sem(nullptr), fHaveGrant(false) {} explicit CSemaphoreGrant(CSemaphore &sema, bool fTry = false) : sem(&sema), fHaveGrant(false) { if (fTry) { TryAcquire(); } else { Acquire(); } } ~CSemaphoreGrant() { Release(); } operator bool() const { return fHaveGrant; } }; // Utility class for indicating to compiler thread analysis that a mutex is // locked (when it couldn't be determined otherwise). struct SCOPED_LOCKABLE LockAssertion { template explicit LockAssertion(Mutex &mutex) EXCLUSIVE_LOCK_FUNCTION(mutex) { #ifdef DEBUG_LOCKORDER AssertLockHeld(mutex); #endif } ~LockAssertion() UNLOCK_FUNCTION() {} }; #endif // BITCOIN_SYNC_H diff --git a/src/validation.h b/src/validation.h index 849bb6bbe..0df7839fa 100644 --- a/src/validation.h +++ b/src/validation.h @@ -1,1317 +1,1318 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2019 The Bitcoin Core developers // Copyright (c) 2017-2020 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_VALIDATION_H #define BITCOIN_VALIDATION_H #if defined(HAVE_CONFIG_H) #include #endif #include #include #include #include #include #include #include #include #include // For CMessageHeader::MessageMagic #include