#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\n", (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

 

posted on 2016-10-19 00:38  zylthinking  阅读(251)  评论(0编辑  收藏  举报