Implement deadlock detection
Former-commit-id: fa797408d9c5d5f12053641144fe1a8b24f66185
This commit is contained in:
parent
10561c2e57
commit
b84d9671ec
@ -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);
|
||||||
|
@ -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++
|
||||||
|
Loading…
x
Reference in New Issue
Block a user