#ifndef lock_h
#define lock_h
#include <stdint.h>
#include <string.h>
#include "mydef.h"
#include "now.h"
#define debug_lock 1
typedef struct {
intptr_t lck;
uintptr_t tid;
uintptr_t nr;
#if debug_lock
const char* file;
uintptr_t line;
#endif
} lock_t;
#define lock_initial {0}
#define lock_initial_locked {1, 0, 1}
static __attribute__((unused)) lock_t lock_val = lock_initial;
#if (debug_lock == 2)
#define locktrace_begin() uintptr_t tms = now();
#define lock_backtrace(lkp)
do {
uintptr_t current = now();
if (tms == 0) {
tms = current;
}
if (current > tms + 3000) {
tms = current - 2000;
logmsg("locktrace: %d %s:%d
", (int) lkp->lck, lkp->file, (int) lkp->line);
}
} while (0)
#else
#define locktrace_begin() (void) 0
#define lock_backtrace(x) (void) 0
#endif
#if debug_lock
#define log_lock(ptr, l, f) do {ptr->line = l; ptr->file = f;} while (0)
#define log_unlock(ptr) do {ptr->line = -1; ptr->file = "";} while (0)
#else
#define log_lock(ptr, l, f) (void) (0)
#define log_unlock(ptr) (void) (0)
#endif
#ifdef __linux__
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#define my_lock(lkp, re)
do {
lock_t* ptr = lkp;
if (!__sync_bool_compare_and_swap(&ptr->lck, 0, 1)) {
if (ptr->lck == 2) {
syscall(__NR_futex, &ptr->lck, FUTEX_WAIT, 2, NULL, NULL, 0);
}
locktrace_begin();
while (0 != __sync_lock_test_and_set(&ptr->lck, 2)) {
syscall(__NR_futex, &ptr->lck, FUTEX_WAIT, 2, NULL, NULL, 0);
lock_backtrace(ptr);
}
}
my_assert2(ptr->lck != 0, "lck = %d", ptr->lck);
my_assert2(ptr->nr == 0, "lck = %d, nr = %d, %d@%s", ptr->lck, ptr->nr, ptr->line, ptr->file);
log_lock(ptr, __LINE__, __FILE__);
my_assert(ptr->tid == 0);
if (re) {
ptr->tid = systid();
}
++ptr->nr;
} while (0)
#define unlock(lkp)
do {
lock_t* ptr = lkp;
my_assert2(ptr->lck != 0, "lck = %d, nr = %d", ptr->lck, ptr->nr);
--ptr->nr;
wmb();
if (ptr->nr > 0) {
my_assert2(ptr->tid != 0, "tid != 0, ptr->nr = %d, lck = %d, %d@%s", ptr->nr, ptr->lck, ptr->line, ptr->file);
} else {
ptr->tid = 0;
/* wmb(); */
log_unlock(ptr);
if (2 == __sync_lock_test_and_set(&ptr->lck, 0)) {
while (-1 == syscall(__NR_futex, &ptr->lck, FUTEX_WAKE, 1, NULL, NULL, 0));
}
}
} while (0)
#else
#define my_lock(lkp, re)
do {
lock_t* ptr = lkp;
locktrace_begin();
while (!__sync_bool_compare_and_swap((void **) &ptr->lck, (void *) 0, (void *) 1)) {
sched_yield();
lock_backtrace(ptr);
}
log_lock(ptr, __LINE__, __FILE__);
my_assert(ptr->tid == 0);
if (re) {
ptr->tid = systid();
}
++ptr->nr;
} while (0)
#define unlock(lkp)
do {
lock_t* ptr = lkp;
my_assert(ptr->lck != 0);
--ptr->nr;
wmb();
if (ptr->nr > 0) {
my_assert(ptr->tid != 0);
} else {
ptr->tid = 0;
/* wmb(); */
log_unlock(ptr);
ptr->lck = 0;
}
} while (0)
#endif
#define lock(lkp) my_lock(lkp, 0)
#define relock(lkp)
do {
lock_t* ptr = lkp;
/* this rmb() is here to assure to see ptr->tid = 0 in unlock */
/* if thread exit after unlock(), then another thread is spwaned with same tid */
/* on another cpu core and then call lock_recursive. */
/* all the above happens so quickly that the other cpu core does not see ptr->tid = 0 */
/* it is so impossible to happen that I comment the "correct" implemention. */
/* rmb(); */
if (ptr->tid == systid()) {
/* if true, it's same thread, event in another cpu core, no mb() is needed. */
++ptr->nr;
} else {
my_lock(lkp, 1);
}
} while (0)
static __attribute__((unused)) inline intptr_t my_try_lock(lock_t* lkp, uintptr_t re, uintptr_t line, const char* file)
{
if (!__sync_bool_compare_and_swap((void **) &((lkp)->lck), (void *) 0, (void *) 1)) {
return -1;
}
log_lock(lkp, line, file);
my_assert(lkp->tid == 0);
if (re) {
lkp->tid = systid();
}
++lkp->nr;
return 0;
}
#define try_lock(lkp) my_try_lock(lkp, 0, __LINE__, __FILE__)
#define retry_lock(lkp) my_try_lock(lkp, 1, __LINE__, __FILE__)
typedef struct {
intptr_t nr;
} rwlock_t;
#define read_write_max 8000
#define rw_lock_initial {read_write_max}
static __attribute__((unused)) rwlock_t rw_lock_val = rw_lock_initial;
#define read_write_lock(lckp, val)
do {
rwlock_t* lck = lckp;
do {
intptr_t n = __sync_sub_and_fetch(&lck->nr, val);
if (n >= 0) {
break;
}
__sync_add_and_fetch(&lck->nr, val);
sched_yield();
} while (1);
} while (0)
#define read_write_unlock(lckp, val)
do {
rwlock_t* lck = lckp;
__sync_add_and_fetch(&lck->nr, val);
} while (0)
#define read_lock(lckp) read_write_lock(lckp, 1)
#define write_lock(lckp) read_write_lock(lckp, read_write_max)
#define read_unlock(lckp) read_write_unlock(lckp, 1)
#define write_unlock(lckp) read_write_unlock(lckp, read_write_max)
#endif