Add deadlock detection with timeouts (#364)

Co-authored-by: Levi Neely <lkn@darkstar.example.net>
This commit is contained in:
Levi Neely 2026-03-08 11:55:25 +01:00 committed by GitHub
parent 5769bb4882
commit 645596ac15
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 101 additions and 11 deletions

View File

@ -31,6 +31,8 @@
#include "plocks.h" #include "plocks.h"
#include "plibs.h" #include "plibs.h"
#include <errno.h>
#include <time.h>
#define PSYNC_RW_OPT_PREFER_READ 1U #define PSYNC_RW_OPT_PREFER_READ 1U
#define PSYNC_RW_OPT_RESERVED 2U #define PSYNC_RW_OPT_RESERVED 2U
@ -143,9 +145,17 @@ static psync_rwlock_lockcnt_t psync_rwlock_create_cnt(uint_halfptr_t rd,
} }
void plocks_rdlock(psync_rwlock_t *rw) { void plocks_rdlock(psync_rwlock_t *rw) {
struct timespec ts;
int ret;
if (psync_rwlock_check_rdrecursive_in(rw)) if (psync_rwlock_check_rdrecursive_in(rw))
return; return;
pthread_mutex_lock(&rw->mutex); clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 30;
ret = pthread_mutex_timedlock(&rw->mutex, &ts);
if (ret == ETIMEDOUT) {
pdbg_logf(D_CRITICAL, "pthread_mutex_timedlock timeout on rdlock");
abort();
}
while (rw->wcount || (rw->wwait && !(rw->opts & PSYNC_RW_OPT_RESERVED))) { while (rw->wcount || (rw->wwait && !(rw->opts & PSYNC_RW_OPT_RESERVED))) {
rw->rwait++; rw->rwait++;
pthread_cond_wait(&rw->rcond, &rw->mutex); pthread_cond_wait(&rw->rcond, &rw->mutex);
@ -157,9 +167,17 @@ void plocks_rdlock(psync_rwlock_t *rw) {
} }
int plocks_tryrdlock(psync_rwlock_t *rw) { int plocks_tryrdlock(psync_rwlock_t *rw) {
struct timespec ts;
int ret;
if (psync_rwlock_check_rdrecursive_in(rw)) if (psync_rwlock_check_rdrecursive_in(rw))
return 0; return 0;
pthread_mutex_lock(&rw->mutex); clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 30;
ret = pthread_mutex_timedlock(&rw->mutex, &ts);
if (ret == ETIMEDOUT) {
pdbg_logf(D_CRITICAL, "pthread_mutex_timedlock timeout on tryrdlock");
abort();
}
if (rw->wcount || (rw->wwait && !(rw->opts & PSYNC_RW_OPT_RESERVED))) { if (rw->wcount || (rw->wwait && !(rw->opts & PSYNC_RW_OPT_RESERVED))) {
pthread_mutex_unlock(&rw->mutex); pthread_mutex_unlock(&rw->mutex);
return -1; return -1;
@ -172,9 +190,17 @@ int plocks_tryrdlock(psync_rwlock_t *rw) {
int plocks_timedrdlock(psync_rwlock_t *rw, int plocks_timedrdlock(psync_rwlock_t *rw,
const struct timespec *abstime) { const struct timespec *abstime) {
struct timespec ts;
int ret;
if (psync_rwlock_check_rdrecursive_in(rw)) if (psync_rwlock_check_rdrecursive_in(rw))
return 0; return 0;
pthread_mutex_lock(&rw->mutex); clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 30;
ret = pthread_mutex_timedlock(&rw->mutex, &ts);
if (ret == ETIMEDOUT) {
pdbg_logf(D_CRITICAL, "pthread_mutex_timedlock timeout on timedrdlock");
abort();
}
while (rw->wcount || (rw->wwait && !(rw->opts & PSYNC_RW_OPT_RESERVED))) { while (rw->wcount || (rw->wwait && !(rw->opts & PSYNC_RW_OPT_RESERVED))) {
rw->rwait++; rw->rwait++;
if (unlikely(pthread_cond_timedwait(&rw->rcond, &rw->mutex, abstime))) { if (unlikely(pthread_cond_timedwait(&rw->rcond, &rw->mutex, abstime))) {
@ -191,9 +217,17 @@ int plocks_timedrdlock(psync_rwlock_t *rw,
} }
void plocks_rdlock_starvewr(psync_rwlock_t *rw) { void plocks_rdlock_starvewr(psync_rwlock_t *rw) {
struct timespec ts;
int ret;
if (psync_rwlock_check_rdrecursive_in(rw)) if (psync_rwlock_check_rdrecursive_in(rw))
return; return;
pthread_mutex_lock(&rw->mutex); clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 30;
ret = pthread_mutex_timedlock(&rw->mutex, &ts);
if (ret == ETIMEDOUT) {
pdbg_logf(D_CRITICAL, "pthread_mutex_timedlock timeout on rdlock_starvewr");
abort();
}
while (rw->wcount) { while (rw->wcount) {
rw->rwait++; rw->rwait++;
rw->opts |= PSYNC_RW_OPT_PREFER_READ; rw->opts |= PSYNC_RW_OPT_PREFER_READ;
@ -207,9 +241,17 @@ void plocks_rdlock_starvewr(psync_rwlock_t *rw) {
} }
void plocks_wrlock(psync_rwlock_t *rw) { void plocks_wrlock(psync_rwlock_t *rw) {
struct timespec ts;
int ret;
if (psync_rwlock_check_wrrecursive_in(rw)) if (psync_rwlock_check_wrrecursive_in(rw))
return; return;
pthread_mutex_lock(&rw->mutex); clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 30;
ret = pthread_mutex_timedlock(&rw->mutex, &ts);
if (ret == ETIMEDOUT) {
pdbg_logf(D_CRITICAL, "pthread_mutex_timedlock timeout on wrlock");
abort();
}
while (rw->rcount || rw->wcount || (rw->opts & PSYNC_RW_OPT_RESERVED)) { while (rw->rcount || rw->wcount || (rw->opts & PSYNC_RW_OPT_RESERVED)) {
rw->wwait++; rw->wwait++;
pthread_cond_wait(&rw->wcond, &rw->mutex); pthread_cond_wait(&rw->wcond, &rw->mutex);
@ -221,9 +263,17 @@ void plocks_wrlock(psync_rwlock_t *rw) {
} }
int plocks_trywrlock(psync_rwlock_t *rw) { int plocks_trywrlock(psync_rwlock_t *rw) {
struct timespec ts;
int ret;
if (psync_rwlock_check_wrrecursive_in(rw)) if (psync_rwlock_check_wrrecursive_in(rw))
return 0; return 0;
pthread_mutex_lock(&rw->mutex); clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 30;
ret = pthread_mutex_timedlock(&rw->mutex, &ts);
if (ret == ETIMEDOUT) {
pdbg_logf(D_CRITICAL, "pthread_mutex_timedlock timeout on trywrlock");
abort();
}
if (rw->rcount || rw->wcount || (rw->opts & PSYNC_RW_OPT_RESERVED)) { if (rw->rcount || rw->wcount || (rw->opts & PSYNC_RW_OPT_RESERVED)) {
pthread_mutex_unlock(&rw->mutex); pthread_mutex_unlock(&rw->mutex);
return -1; return -1;
@ -236,9 +286,17 @@ int plocks_trywrlock(psync_rwlock_t *rw) {
int plocks_timedwrlock(psync_rwlock_t *rw, int plocks_timedwrlock(psync_rwlock_t *rw,
const struct timespec *abstime) { const struct timespec *abstime) {
struct timespec ts;
int ret;
if (psync_rwlock_check_wrrecursive_in(rw)) if (psync_rwlock_check_wrrecursive_in(rw))
return 0; return 0;
pthread_mutex_lock(&rw->mutex); clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 30;
ret = pthread_mutex_timedlock(&rw->mutex, &ts);
if (ret == ETIMEDOUT) {
pdbg_logf(D_CRITICAL, "pthread_mutex_timedlock timeout on timedwrlock");
abort();
}
while (rw->rcount || rw->wcount || (rw->opts & PSYNC_RW_OPT_RESERVED)) { while (rw->rcount || rw->wcount || (rw->opts & PSYNC_RW_OPT_RESERVED)) {
rw->wwait++; rw->wwait++;
if (unlikely(pthread_cond_timedwait(&rw->wcond, &rw->mutex, abstime))) { if (unlikely(pthread_cond_timedwait(&rw->wcond, &rw->mutex, abstime))) {
@ -256,6 +314,8 @@ int plocks_timedwrlock(psync_rwlock_t *rw,
} }
void plocks_rslock(psync_rwlock_t *rw) { void plocks_rslock(psync_rwlock_t *rw) {
struct timespec ts;
int ret;
psync_rwlock_lockcnt_t cnt; psync_rwlock_lockcnt_t cnt;
cnt = psync_rwlock_get_count(rw); cnt = psync_rwlock_get_count(rw);
pdbg_assert(cnt.cnt[0] == 0); pdbg_assert(cnt.cnt[0] == 0);
@ -267,7 +327,13 @@ void plocks_rslock(psync_rwlock_t *rw) {
psync_rwlock_set_count(rw, cnt); psync_rwlock_set_count(rw, cnt);
return; return;
} }
pthread_mutex_lock(&rw->mutex); clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 30;
ret = pthread_mutex_timedlock(&rw->mutex, &ts);
if (ret == ETIMEDOUT) {
pdbg_logf(D_CRITICAL, "pthread_mutex_timedlock timeout on rslock");
abort();
}
while (rw->wcount || (rw->opts & PSYNC_RW_OPT_RESERVED)) { while (rw->wcount || (rw->opts & PSYNC_RW_OPT_RESERVED)) {
rw->wwait++; rw->wwait++;
pthread_cond_wait(&rw->wcond, &rw->mutex); pthread_cond_wait(&rw->wcond, &rw->mutex);
@ -284,12 +350,20 @@ void plocks_rslock(psync_rwlock_t *rw) {
} }
int plocks_towrlock(psync_rwlock_t *rw) { int plocks_towrlock(psync_rwlock_t *rw) {
struct timespec ts;
int ret;
psync_rwlock_lockcnt_t cnt; psync_rwlock_lockcnt_t cnt;
cnt = psync_rwlock_get_count(rw); cnt = psync_rwlock_get_count(rw);
if (cnt.cnt[1] && cnt.cnt[1] != PSYNC_WR_RESERVED) if (cnt.cnt[1] && cnt.cnt[1] != PSYNC_WR_RESERVED)
return 0; return 0;
pdbg_assert(cnt.cnt[0]); pdbg_assert(cnt.cnt[0]);
pthread_mutex_lock(&rw->mutex); clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 30;
ret = pthread_mutex_timedlock(&rw->mutex, &ts);
if (ret == ETIMEDOUT) {
pdbg_logf(D_CRITICAL, "pthread_mutex_timedlock timeout on towrlock");
abort();
}
pdbg_assert(rw->rcount); pdbg_assert(rw->rcount);
pdbg_assert(!rw->wcount); pdbg_assert(!rw->wcount);
if (rw->opts & PSYNC_RW_OPT_RESERVED) { if (rw->opts & PSYNC_RW_OPT_RESERVED) {
@ -318,9 +392,17 @@ int plocks_towrlock(psync_rwlock_t *rw) {
} }
void plocks_unlock(psync_rwlock_t *rw) { void plocks_unlock(psync_rwlock_t *rw) {
struct timespec ts;
int ret;
if (psync_rwlock_check_recursive_out(rw)) if (psync_rwlock_check_recursive_out(rw))
return; return;
pthread_mutex_lock(&rw->mutex); clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 30;
ret = pthread_mutex_timedlock(&rw->mutex, &ts);
if (ret == ETIMEDOUT) {
pdbg_logf(D_CRITICAL, "pthread_mutex_timedlock timeout on unlock");
abort();
}
pdbg_assert(!(rw->rcount && rw->wcount)); pdbg_assert(!(rw->rcount && rw->wcount));
if (rw->rcount) { if (rw->rcount) {
if ((rw->opts & PSYNC_RW_OPT_RESERVED) && if ((rw->opts & PSYNC_RW_OPT_RESERVED) &&
@ -360,8 +442,16 @@ void plocks_unlock(psync_rwlock_t *rw) {
} }
unsigned plocks_num_waiters(psync_rwlock_t *rw) { unsigned plocks_num_waiters(psync_rwlock_t *rw) {
struct timespec ts;
int ret_lock;
unsigned ret; unsigned ret;
pthread_mutex_lock(&rw->mutex); clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 30;
ret_lock = pthread_mutex_timedlock(&rw->mutex, &ts);
if (ret_lock == ETIMEDOUT) {
pdbg_logf(D_CRITICAL, "pthread_mutex_timedlock timeout on num_waiters");
abort();
}
ret = rw->rwait + rw->wwait; ret = rw->rwait + rw->wwait;
pthread_mutex_unlock(&rw->mutex); pthread_mutex_unlock(&rw->mutex);
return ret; return ret;