Implement deadlock detection

Former-commit-id: fa797408d9c5d5f12053641144fe1a8b24f66185
This commit is contained in:
John Sully 2019-10-22 00:43:32 -04:00
parent 10561c2e57
commit b84d9671ec
2 changed files with 97 additions and 13 deletions

View File

@ -36,6 +36,7 @@
#include <assert.h> #include <assert.h>
#include <pthread.h> #include <pthread.h>
#include <limits.h> #include <limits.h>
#include <map>
#ifdef __linux__ #ifdef __linux__
#include <linux/futex.h> #include <linux/futex.h>
#endif #endif
@ -125,6 +126,60 @@
#endif #endif
#pragma weak _serverPanic
extern "C" void _serverPanic(const char * /*file*/, int /*line*/, const char * /*msg*/, ...)
{
*((char*)-1) = 'x';
}
class DeadlockDetector
{
std::map<pid_t, fastlock *> m_mapwait;
fastlock m_lock;
public:
void registerwait(fastlock *lock, pid_t thispid)
{
if (lock == &m_lock)
return;
fastlock_lock(&m_lock);
m_mapwait.insert(std::make_pair(thispid, lock));
// Detect cycles
pid_t pidCheck = thispid;
for (;;)
{
auto itr = m_mapwait.find(pidCheck);
if (itr == m_mapwait.end())
break;
pidCheck = itr->second->m_pidOwner;
if (pidCheck == thispid)
_serverPanic(__FILE__, __LINE__, "Deadlock detected");
}
fastlock_unlock(&m_lock);
}
void clearwait(fastlock *lock, pid_t thispid)
{
if (lock == &m_lock)
return;
fastlock_lock(&m_lock);
m_mapwait.erase(thispid);
fastlock_unlock(&m_lock);
}
};
DeadlockDetector g_dlock;
extern "C" void registerwait(fastlock *lock, pid_t thispid)
{
g_dlock.registerwait(lock, thispid);
}
extern "C" void clearwait(fastlock *lock, pid_t thispid)
{
g_dlock.clearwait(lock, thispid);
}
static_assert(sizeof(pid_t) <= sizeof(fastlock::m_pidOwner), "fastlock::m_pidOwner not large enough"); static_assert(sizeof(pid_t) <= sizeof(fastlock::m_pidOwner), "fastlock::m_pidOwner not large enough");
uint64_t g_longwaits = 0; uint64_t g_longwaits = 0;
@ -184,12 +239,18 @@ extern "C" void fastlock_lock(struct fastlock *lock)
return; return;
} }
int tid = gettid();
unsigned myticket = __atomic_fetch_add(&lock->m_ticket.m_avail, 1, __ATOMIC_RELEASE); unsigned myticket = __atomic_fetch_add(&lock->m_ticket.m_avail, 1, __ATOMIC_RELEASE);
#ifdef __linux__ #ifdef __linux__
unsigned mask = (1U << (myticket % 32)); unsigned mask = (1U << (myticket % 32));
#endif #endif
int cloops = 0; int cloops = 0;
ticket ticketT; ticket ticketT;
__atomic_load(&lock->m_ticket.u, &ticketT.u, __ATOMIC_ACQUIRE);
if ((ticketT.u & 0xffff) != myticket)
{
registerwait(lock, tid);
for (;;) for (;;)
{ {
__atomic_load(&lock->m_ticket.u, &ticketT.u, __ATOMIC_ACQUIRE); __atomic_load(&lock->m_ticket.u, &ticketT.u, __ATOMIC_ACQUIRE);
@ -209,9 +270,10 @@ extern "C" void fastlock_lock(struct fastlock *lock)
__atomic_fetch_add(&g_longwaits, 1, __ATOMIC_RELAXED); __atomic_fetch_add(&g_longwaits, 1, __ATOMIC_RELAXED);
} }
} }
clearwait(lock, tid);
}
lock->m_depth = 1; lock->m_depth = 1;
int tid = gettid();
__atomic_store(&lock->m_pidOwner, &tid, __ATOMIC_RELEASE); __atomic_store(&lock->m_pidOwner, &tid, __ATOMIC_RELEASE);
ANNOTATE_RWLOCK_ACQUIRED(lock, true); ANNOTATE_RWLOCK_ACQUIRED(lock, true);
std::atomic_thread_fence(std::memory_order_acquire); std::atomic_thread_fence(std::memory_order_acquire);

View File

@ -3,6 +3,8 @@ section .text
extern gettid extern gettid
extern sched_yield extern sched_yield
extern g_longwaits extern g_longwaits
extern registerwait
extern clearwait
; This is the first use of assembly in this codebase, a valid question is WHY? ; This is the first use of assembly in this codebase, a valid question is WHY?
; The spinlock we implement here is performance critical, and simply put GCC ; The spinlock we implement here is performance critical, and simply put GCC
@ -31,11 +33,24 @@ fastlock_lock:
inc eax ; we want to add one inc eax ; we want to add one
lock xadd [rdi+2], ax ; do the xadd, ax contains the value before the addition lock xadd [rdi+2], ax ; do the xadd, ax contains the value before the addition
; ax now contains the ticket ; ax now contains the ticket
mov edx, [rdi]
cmp dx, ax ; is our ticket up?
je .LLocked ; no need to loop
; Lock is contended, so inform the deadlock detector
push rax
push rdi
push rsi
call registerwait
pop rsi
pop rdi
pop rax
; OK Start the wait loop
xor ecx, ecx
ALIGN 16 ALIGN 16
.LLoop: .LLoop:
mov edx, [rdi] mov edx, [rdi]
cmp dx, ax ; is our ticket up? cmp dx, ax ; is our ticket up?
je .LLocked ; leave the loop je .LExitLoop ; leave the loop
pause pause
add ecx, 1000h ; Have we been waiting a long time? (oflow if we have) add ecx, 1000h ; Have we been waiting a long time? (oflow if we have)
; 1000h is set so we overflow on the 1024*1024'th iteration (like the C code) ; 1000h is set so we overflow on the 1024*1024'th iteration (like the C code)
@ -69,6 +84,13 @@ ALIGN 16
xor ecx, ecx ; Reset our loop counter xor ecx, ecx ; Reset our loop counter
jmp .LLoop ; Get back in the game jmp .LLoop ; Get back in the game
ALIGN 16 ALIGN 16
.LExitLoop:
push rsi
push rdi
call clearwait
pop rdi
pop rsi
ALIGN 16
.LLocked: .LLocked:
mov [rdi+4], esi ; lock->m_pidOwner = gettid() mov [rdi+4], esi ; lock->m_pidOwner = gettid()
inc dword [rdi+8] ; lock->m_depth++ inc dword [rdi+8] ; lock->m_depth++