@ -13,10 +13,14 @@
# include <util/strencodings.h>
# include <util/threadnames.h>
# include <boost/thread/mutex.hpp>
# include <map>
# include <mutex>
# include <set>
# include <system_error>
# include <thread>
# include <type_traits>
# include <unordered_map>
# include <utility>
# include <vector>
@ -135,16 +139,50 @@ static void potential_deadlock_detected(const LockPair& mismatch, const LockStac
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 )
static void double_lock_detected( const void * mutex , LockStack & lock_stack )
{
LogPrintf ( " DOUBLE LOCK DETECTED \n " ) ;
LogPrintf ( " Lock order: \n " ) ;
for ( const LockStackItem & i : lock_stack ) {
if ( i . first = = mutex ) {
LogPrintf ( " (*) " ) ; /* Continued */
}
LogPrintf ( " %s \n " , i . second . ToString ( ) ) ;
}
if ( g_debug_lockorder_abort ) {
tfm : : format ( std : : cerr , " Assertion failed: detected double lock at %s:%i, details in debug log. \n " , __FILE__ , __LINE__ ) ;
abort ( ) ;
}
throw std : : logic_error ( " double lock detected " ) ;
}
template < typename MutexType >
static void push_lock ( MutexType * c , const CLockLocation & locklocation )
{
constexpr bool is_recursive_mutex =
std : : is_base_of < RecursiveMutex , MutexType > : : value | |
std : : is_base_of < std : : recursive_mutex , MutexType > : : value ;
LockData & lockdata = GetLockData ( ) ;
std : : lock_guard < std : : mutex > 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 ;
for ( size_t j = 0 ; j < lock_stack . size ( ) - 1 ; + + j ) {
const LockStackItem & i = lock_stack [ j ] ;
if ( i . first = = c ) {
if ( is_recursive_mutex ) {
break ;
}
// It is not a recursive mutex and it appears in the stack two times:
// at position `j` and at the end (which we added just before this loop).
// Can't allow locking the same (non-recursive) mutex two times from the
// same thread as that results in an undefined behavior.
auto lock_stack_copy = lock_stack ;
lock_stack . pop_back ( ) ;
double_lock_detected ( c , lock_stack_copy ) ;
// double_lock_detected() does not return.
}
const LockPair p1 = std : : make_pair ( i . first , c ) ;
if ( lockdata . lockorders . count ( p1 ) )
@ -175,10 +213,16 @@ static void pop_lock()
}
}
void EnterCritical ( const char * pszName , const char * pszFile , int nLine , void * cs , bool fTry )
template < typename MutexType >
void EnterCritical ( const char * pszName , const char * pszFile , int nLine , MutexType * cs , bool fTry )
{
push_lock ( cs , CLockLocation ( pszName , pszFile , nLine , fTry , util : : ThreadGetInternalName ( ) ) ) ;
}
template void EnterCritical ( const char * , const char * , int , Mutex * , bool ) ;
template void EnterCritical ( const char * , const char * , int , RecursiveMutex * , bool ) ;
template void EnterCritical ( const char * , const char * , int , std : : mutex * , bool ) ;
template void EnterCritical ( const char * , const char * , int , std : : recursive_mutex * , bool ) ;
template void EnterCritical ( const char * , const char * , int , boost : : mutex * , bool ) ;
void CheckLastCritical ( void * cs , std : : string & lockname , const char * guardname , const char * file , int line )
{