// T(A) gets W(L)
// T(B) tries W(L), gets DB_LOCK_NOTGRANTED
// T(A) releases locks
// T(B) gets W(L)
// T(B) releases locks

#include "test.h"

static int write_lock(toku_lock_tree *lt, TXNID txnid, char *k) {
    DBT key; dbt_init(&key, k, strlen(k));
    toku_lock_request lr;
    toku_lock_request_init(&lr, txnid, &key, &key, LOCK_REQUEST_WRITE);
    int r;
    if (0) {
        r = toku_lt_acquire_lock_request_with_timeout(lt, &lr, NULL);
    } else {
        r = toku_lock_request_start(&lr, lt, true);
        if (r == DB_LOCK_NOTGRANTED)
            r = toku_lock_request_wait_with_default_timeout(&lr, lt);
    }
    toku_lock_request_destroy(&lr);
    return r;
}

struct writer_arg {
    TXNID id;
    toku_lock_tree *lt;
    char *name;
};

static void *writer_thread(void *arg) {
    struct writer_arg *writer_arg = (struct writer_arg *) arg;
    printf("%lu wait\n", writer_arg->id);
    int r = write_lock(writer_arg->lt, writer_arg->id, writer_arg->name); assert(r == 0);
    printf("%lu locked\n", writer_arg->id);
    sleep(1);
    toku_lt_unlock_txn(writer_arg->lt, writer_arg->id);
    printf("%lu unlocked\n", writer_arg->id);
    return arg;
}

int main(int argc, const char *argv[]) {
    int r;

    uint32_t max_locks = 1;
    uint64_t max_lock_memory = 4096;
    int max_threads = 1;

    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
            verbose++;
            continue;
        }
        if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
            if (verbose > 0) verbose--;
            continue;
        }
        if (strcmp(argv[i], "--max_locks") == 0 && i+1 < argc) {
            max_locks = atoi(argv[++i]);
            continue;
        }
        if (strcmp(argv[i], "--max_lock_memory") == 0 && i+1 < argc) {
            max_lock_memory = atoi(argv[++i]);
            continue;
        }        
        if (strcmp(argv[i], "--max_threads") == 0 && i+1 < argc) {
            max_threads = atoi(argv[++i]);
            continue;
        }        
        assert(0);
    }

    // setup
    toku_ltm *ltm = NULL;
    r = toku_ltm_create(&ltm, max_locks, max_lock_memory, dbpanic);
    assert(r == 0 && ltm);
    toku_ltm_set_lock_wait_time(ltm, UINT64_MAX);

    toku_lock_tree *lt = NULL;
    r = toku_ltm_get_lt(ltm, &lt, (DICTIONARY_ID){1}, NULL, dbcmp);
    assert(r == 0 && lt);

    const TXNID txn_a = 1;

    r = write_lock(lt, txn_a, "L"); assert(r == 0);
    printf("main locked\n");

    toku_pthread_t tids[max_threads];
    for (int i = 0 ; i < max_threads; i++) {
        struct writer_arg *writer_arg = (struct writer_arg *) toku_malloc(sizeof (struct writer_arg));
        *writer_arg = (struct writer_arg) { i+10, lt, "L"};
        r = toku_pthread_create(&tids[i], NULL, writer_thread, writer_arg); assert(r == 0);
    }
    sleep(10);
    r = toku_lt_unlock_txn(lt, txn_a);  assert(r == 0);
    printf("main unlocked\n");

    for (int i = 0; i < max_threads; i++) {
        void *retarg;
        r = toku_pthread_join(tids[i], &retarg); assert(r == 0);
        toku_free(retarg);
    }

    // shutdown 
    toku_lt_remove_db_ref(lt);
    r = toku_ltm_close(ltm); assert(r == 0);

    return 0;
}