Add deadlock detection with timeouts (#364)
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
This commit is contained in:
parent
5769bb4882
commit
645596ac15
112
pclsync/plocks.c
112
pclsync/plocks.c
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue