Commit 6873c00c authored by Rich Prohaska's avatar Rich Prohaska Committed by Yoni Fogel

#3452 new row locking APIs refs[t:3452]

git-svn-id: file:///svn/toku/tokudb@35392 c7de825b-a66e-492c-adef-691d508d4ae1
parent 40088c8b
......@@ -210,7 +210,7 @@ typedef struct __toku_engine_status {
uint64_t mem_requested; /* number of bytes requested via malloc/realloc */
uint64_t mem_used; /* number of bytes used (obtained from malloc_usable_size()) */
uint64_t mem_freed; /* number of bytes freed */
uint64_t max_mem_in_use; /* estimatd max value of (used - freed) */
uint64_t max_mem_in_use; /* estimated max value of (used - freed) */
} ENGINE_STATUS;
typedef enum {
DB_BTREE=1,
......@@ -345,7 +345,9 @@ struct __toku_db_env {
int (*set_lk_max_memory) (DB_ENV *env, uint64_t max);
int (*get_lk_max_memory) (DB_ENV *env, uint64_t *max);
void (*set_update) (DB_ENV *env, int (*update_function)(DB *, const DBT *key, const DBT *old_val, const DBT *extra, void (*set_val)(const DBT *new_val, void *set_extra), void *set_extra));
void* __toku_dummy0[13];
int (*set_lock_timeout) (DB_ENV *env, uint64_t lock_wait_time_usec);
int (*get_lock_timeout) (DB_ENV *env, uint64_t *lock_wait_time_usec);
void* __toku_dummy0[11];
char __toku_dummy1[64];
void *api1_internal; /* 32-bit offset=212 size=4, 64=bit offset=360 size=8 */
void* __toku_dummy2[7];
......
......@@ -210,7 +210,7 @@ typedef struct __toku_engine_status {
uint64_t mem_requested; /* number of bytes requested via malloc/realloc */
uint64_t mem_used; /* number of bytes used (obtained from malloc_usable_size()) */
uint64_t mem_freed; /* number of bytes freed */
uint64_t max_mem_in_use; /* estimatd max value of (used - freed) */
uint64_t max_mem_in_use; /* estimated max value of (used - freed) */
} ENGINE_STATUS;
typedef enum {
DB_BTREE=1,
......@@ -347,7 +347,9 @@ struct __toku_db_env {
int (*set_lk_max_memory) (DB_ENV *env, uint64_t max);
int (*get_lk_max_memory) (DB_ENV *env, uint64_t *max);
void (*set_update) (DB_ENV *env, int (*update_function)(DB *, const DBT *key, const DBT *old_val, const DBT *extra, void (*set_val)(const DBT *new_val, void *set_extra), void *set_extra));
void* __toku_dummy0[13];
int (*set_lock_timeout) (DB_ENV *env, uint64_t lock_wait_time_usec);
int (*get_lock_timeout) (DB_ENV *env, uint64_t *lock_wait_time_usec);
void* __toku_dummy0[11];
char __toku_dummy1[96];
void *api1_internal; /* 32-bit offset=244 size=4, 64=bit offset=392 size=8 */
void* __toku_dummy2[7];
......
......@@ -210,7 +210,7 @@ typedef struct __toku_engine_status {
uint64_t mem_requested; /* number of bytes requested via malloc/realloc */
uint64_t mem_used; /* number of bytes used (obtained from malloc_usable_size()) */
uint64_t mem_freed; /* number of bytes freed */
uint64_t max_mem_in_use; /* estimatd max value of (used - freed) */
uint64_t max_mem_in_use; /* estimated max value of (used - freed) */
} ENGINE_STATUS;
typedef enum {
DB_BTREE=1,
......@@ -347,7 +347,9 @@ struct __toku_db_env {
int (*set_lk_max_memory) (DB_ENV *env, uint64_t max);
int (*get_lk_max_memory) (DB_ENV *env, uint64_t *max);
void (*set_update) (DB_ENV *env, int (*update_function)(DB *, const DBT *key, const DBT *old_val, const DBT *extra, void (*set_val)(const DBT *new_val, void *set_extra), void *set_extra));
void* __toku_dummy0[28];
int (*set_lock_timeout) (DB_ENV *env, uint64_t lock_wait_time_usec);
int (*get_lock_timeout) (DB_ENV *env, uint64_t *lock_wait_time_usec);
void* __toku_dummy0[26];
char __toku_dummy1[128];
void *api1_internal; /* 32-bit offset=336 size=4, 64=bit offset=544 size=8 */
void* __toku_dummy2[7];
......
......@@ -210,7 +210,7 @@ typedef struct __toku_engine_status {
uint64_t mem_requested; /* number of bytes requested via malloc/realloc */
uint64_t mem_used; /* number of bytes used (obtained from malloc_usable_size()) */
uint64_t mem_freed; /* number of bytes freed */
uint64_t max_mem_in_use; /* estimatd max value of (used - freed) */
uint64_t max_mem_in_use; /* estimated max value of (used - freed) */
} ENGINE_STATUS;
typedef enum {
DB_BTREE=1,
......@@ -347,7 +347,9 @@ struct __toku_db_env {
int (*set_lk_max_memory) (DB_ENV *env, uint64_t max);
int (*get_lk_max_memory) (DB_ENV *env, uint64_t *max);
void (*set_update) (DB_ENV *env, int (*update_function)(DB *, const DBT *key, const DBT *old_val, const DBT *extra, void (*set_val)(const DBT *new_val, void *set_extra), void *set_extra));
void* __toku_dummy0[28];
int (*set_lock_timeout) (DB_ENV *env, uint64_t lock_wait_time_usec);
int (*get_lock_timeout) (DB_ENV *env, uint64_t *lock_wait_time_usec);
void* __toku_dummy0[26];
char __toku_dummy1[128];
void *api1_internal; /* 32-bit offset=336 size=4, 64=bit offset=544 size=8 */
void* __toku_dummy2[8];
......
......@@ -210,7 +210,7 @@ typedef struct __toku_engine_status {
uint64_t mem_requested; /* number of bytes requested via malloc/realloc */
uint64_t mem_used; /* number of bytes used (obtained from malloc_usable_size()) */
uint64_t mem_freed; /* number of bytes freed */
uint64_t max_mem_in_use; /* estimatd max value of (used - freed) */
uint64_t max_mem_in_use; /* estimated max value of (used - freed) */
} ENGINE_STATUS;
typedef enum {
DB_BTREE=1,
......@@ -348,7 +348,9 @@ struct __toku_db_env {
int (*set_lk_max_memory) (DB_ENV *env, uint64_t max);
int (*get_lk_max_memory) (DB_ENV *env, uint64_t *max);
void (*set_update) (DB_ENV *env, int (*update_function)(DB *, const DBT *key, const DBT *old_val, const DBT *extra, void (*set_val)(const DBT *new_val, void *set_extra), void *set_extra));
void* __toku_dummy0[29];
int (*set_lock_timeout) (DB_ENV *env, uint64_t lock_wait_time_usec);
int (*get_lock_timeout) (DB_ENV *env, uint64_t *lock_wait_time_usec);
void* __toku_dummy0[27];
char __toku_dummy1[144];
void *api1_internal; /* 32-bit offset=356 size=4, 64=bit offset=568 size=8 */
void* __toku_dummy2[8];
......
......@@ -652,6 +652,8 @@ int main (int argc __attribute__((__unused__)), char *const argv[] __attribute__
"int (*set_lk_max_memory) (DB_ENV *env, uint64_t max)",
"int (*get_lk_max_memory) (DB_ENV *env, uint64_t *max)",
"void (*set_update) (DB_ENV *env, int (*update_function)(DB *, const DBT *key, const DBT *old_val, const DBT *extra, void (*set_val)(const DBT *new_val, void *set_extra), void *set_extra))",
"int (*set_lock_timeout) (DB_ENV *env, uint64_t lock_wait_time_usec)",
"int (*get_lock_timeout) (DB_ENV *env, uint64_t *lock_wait_time_usec)",
NULL};
print_struct("db_env", 1, db_env_fields32, db_env_fields64, sizeof(db_env_fields32)/sizeof(db_env_fields32[0]), extra);
}
......
......@@ -210,7 +210,7 @@ typedef struct __toku_engine_status {
uint64_t mem_requested; /* number of bytes requested via malloc/realloc */
uint64_t mem_used; /* number of bytes used (obtained from malloc_usable_size()) */
uint64_t mem_freed; /* number of bytes freed */
uint64_t max_mem_in_use; /* estimatd max value of (used - freed) */
uint64_t max_mem_in_use; /* estimated max value of (used - freed) */
} ENGINE_STATUS;
typedef enum {
DB_BTREE=1,
......@@ -348,6 +348,8 @@ struct __toku_db_env {
int (*set_lk_max_memory) (DB_ENV *env, uint64_t max);
int (*get_lk_max_memory) (DB_ENV *env, uint64_t *max);
void (*set_update) (DB_ENV *env, int (*update_function)(DB *, const DBT *key, const DBT *old_val, const DBT *extra, void (*set_val)(const DBT *new_val, void *set_extra), void *set_extra));
int (*set_lock_timeout) (DB_ENV *env, uint64_t lock_wait_time_usec);
int (*get_lock_timeout) (DB_ENV *env, uint64_t *lock_wait_time_usec);
void *api1_internal;
int (*close) (DB_ENV *, u_int32_t);
int (*dbremove) (DB_ENV *, DB_TXN *, const char *, const char *, u_int32_t);
......
......@@ -210,7 +210,7 @@ typedef struct __toku_engine_status {
uint64_t mem_requested; /* number of bytes requested via malloc/realloc */
uint64_t mem_used; /* number of bytes used (obtained from malloc_usable_size()) */
uint64_t mem_freed; /* number of bytes freed */
uint64_t max_mem_in_use; /* estimatd max value of (used - freed) */
uint64_t max_mem_in_use; /* estimated max value of (used - freed) */
} ENGINE_STATUS;
typedef enum {
DB_BTREE=1,
......@@ -348,6 +348,8 @@ struct __toku_db_env {
int (*set_lk_max_memory) (DB_ENV *env, uint64_t max);
int (*get_lk_max_memory) (DB_ENV *env, uint64_t *max);
void (*set_update) (DB_ENV *env, int (*update_function)(DB *, const DBT *key, const DBT *old_val, const DBT *extra, void (*set_val)(const DBT *new_val, void *set_extra), void *set_extra));
int (*set_lock_timeout) (DB_ENV *env, uint64_t lock_wait_time_usec);
int (*get_lock_timeout) (DB_ENV *env, uint64_t *lock_wait_time_usec);
void *api1_internal;
int (*close) (DB_ENV *, u_int32_t);
int (*dbremove) (DB_ENV *, DB_TXN *, const char *, const char *, u_int32_t);
......
......@@ -1307,7 +1307,6 @@ static void flush_and_maybe_remove (CACHETABLE ct, PAIR p) {
}
}
static void do_partial_eviction(CACHETABLE ct, PAIR p) {
// This really should be something else, but need to set it to something
// other than CTPAIR_IDLE so that other threads know to not hold
......
......@@ -80,13 +80,13 @@ toku_ydb_lock(void) {
if (new_num_waiters > status.max_waiters) status.max_waiters = new_num_waiters;
status.total_time_since_start = now - ydb_big_lock.starttime;
invariant((status.ydb_lock_ctr & 0x01) == 1);
// invariant((status.ydb_lock_ctr & 0x01) == 1);
}
static void
ydb_unlock_internal(unsigned long useconds) {
status.ydb_lock_ctr++;
invariant((status.ydb_lock_ctr & 0x01) == 0);
// invariant((status.ydb_lock_ctr & 0x01) == 0);
tokutime_t now = get_tokutime();
tokutime_t time_held = now - ydb_big_lock.acquired_time;
......@@ -113,3 +113,8 @@ void
toku_ydb_unlock_and_yield(unsigned long useconds) {
ydb_unlock_internal(useconds);
}
toku_pthread_mutex_t *
toku_ydb_mutex(void) {
return &ydb_big_lock.lock;
}
......@@ -20,6 +20,8 @@ OBJS_RAW = \
idlth \
lth \
rth \
txnid_set \
wfg \
#end
LIBRARIES=$(LOCKTREE_LINEAR) $(LOCKTREE_TLOG) $(LOCKTREE) # $(LOCKTREE_LOG)
......
This diff is collapsed.
......@@ -17,7 +17,7 @@
each other, due to some system error like failed malloc,
we defer to the db panic handler. Pass in another parameter to do this.
*/
#include <stdbool.h>
#include <db.h>
#include <brttypes.h>
#include <rangetree.h>
......@@ -25,7 +25,7 @@
#include <rth.h>
#include <idlth.h>
#include <omt.h>
#include "toku_pthread.h"
#include "toku_assert.h"
#if defined(__cplusplus)
......@@ -117,6 +117,8 @@ struct __toku_lock_tree {
/** DICTIONARY_ID associated with the lock tree */
DICTIONARY_ID dict_id;
OMT dbs; //The extant dbs using this lock tree.
OMT lock_requests;
};
......@@ -162,6 +164,10 @@ struct __toku_ltm {
void (*free) (void*);
/** The user realloc function */
void* (*realloc)(void*, size_t);
toku_pthread_mutex_t lock;
toku_pthread_mutex_t *use_lock;
struct timeval lock_wait_time;
};
extern const DBT* const toku_lt_infinity; /**< Special value denoting
......@@ -472,6 +478,149 @@ toku_range_tree* toku_lt_ifexist_selfwrite(toku_lock_tree* tree, TXNID txn);
void toku_lt_verify(toku_lock_tree *tree, DB *db);
typedef enum {
LOCK_REQUEST_INIT = 0,
LOCK_REQUEST_PENDING = 1,
LOCK_REQUEST_COMPLETE = 2,
} toku_lock_request_state;
// TODO: use DB_LOCK_READ/WRITE instead?
typedef enum {
LOCK_REQUEST_UNKNOWN = 0,
LOCK_REQUEST_READ = 1,
LOCK_REQUEST_WRITE = 2,
} toku_lock_type;
typedef struct {
DB *db;
TXNID txnid;
const DBT *key_left; const DBT *key_right;
DBT key_left_copy, key_right_copy;
toku_lock_type type;
toku_lock_tree *tree;
int complete_r;
toku_lock_request_state state;
toku_pthread_cond_t wait;
bool wait_initialized;
} toku_lock_request;
// a lock request contains the db, the key range, the lock type, and the transaction id that describes a potential row range lock.
// the typical use case is:
// - initialize a lock request
// - start to try to acquire the lock
// - do something else
// - wait for the lock request to be resolved on the wait condition variable and a timeout.
// - destroy the lock request
// a lock request is resolved when its state is no longer pending, or when it becomes granted, or timedout, or deadlocked.
// when resolved, the state of the lock request is changed and any waiting threads are awakened.
// initialize a lock request (default initializer).
void toku_lock_request_default_init(toku_lock_request *lock_request);
// initialize the lock request parameters.
// this API allows a lock request to be reused.
void toku_lock_request_set(toku_lock_request *lock_request, DB *db, TXNID txnid, const DBT *key_left, const DBT *key_right, toku_lock_type type);
// initialize and set the parameters for a lock request. it is equivalent to _default_init followed by _set.
void toku_lock_request_init(toku_lock_request *lock_request, DB *db, TXNID txnid, const DBT *key_left, const DBT *key_right, toku_lock_type type);
// destroy a lock request.
void toku_lock_request_destroy(toku_lock_request *lock_request);
// try to acquire a lock described by a lock request.
// if the lock is granted, then set the lock request state to granted
// otherwise, add the lock request to the lock request tree and check for deadlocks
// returns 0 (success), DB_LOCK_NOTGRANTED, DB_LOCK_DEADLOCK
int toku_lock_request_start(toku_lock_request *lock_request, toku_lock_tree *tree, bool copy_keys_if_not_granted);
// try to acquire a lock described by a lock request.
// if the lock is not granted and copy_keys_if_not_granted is true, then make a copy of the keys in the key range.
// this is necessary when used in the ydb cursor callbacks where the keys are only valid when in the callback function.
// called with the lock tree already locked.
int toku_lock_request_start_locked(toku_lock_request *lock_request, toku_lock_tree *tree, bool copy_keys_if_not_granted);
// sleep on the lock request until it becomes resolved or the wait time occurs.
// if the wait time is not specified, then wait for as long as it takes.
int toku_lock_request_wait(toku_lock_request *lock_request, toku_lock_tree *tree, struct timeval *wait_time);
// use the default timeouts set in the ltm
int toku_lock_request_wait_with_default_timeout(toku_lock_request *lock_request, toku_lock_tree *tree);
// wakeup any threads that are waiting on a lock request.
void toku_lock_request_wakeup(toku_lock_request *lock_request, toku_lock_tree *tree);
// returns the lock request state
toku_lock_request_state toku_lock_request_get_state(toku_lock_request *lock_request);
// a lock request tree contains pending lock requests.
// initialize a lock request tree.
void toku_lock_request_tree_init(toku_lock_tree *tree);
// destroy a lock request tree.
// the tree must be empty when destroyed.
void toku_lock_request_tree_destroy(toku_lock_tree *tree);
// insert a lock request into the tree.
void toku_lock_request_tree_insert(toku_lock_tree *tree, toku_lock_request *lock_request);
// delete a lock request from the tree.
void toku_lock_request_tree_delete(toku_lock_tree *tree, toku_lock_request *lock_request);
// find a lock request for a given transaction id.
toku_lock_request *toku_lock_request_tree_find(toku_lock_tree *tree, TXNID id);
// retry all pending lock requests.
// for all lock requests, if the lock request is resolved, then wakeup any threads waiting on the lock request.
// called with the lock tree already locked.
void toku_lt_retry_lock_requests_locked(toku_lock_tree *tree);
// try to acquire a lock described by a lock request. if the lock is granted then return success.
// otherwise wait on the lock request until the lock request is resolved (either granted or
// deadlocks), or the given timer has expired.
// returns 0 (success), DB_LOCK_NOTGRANTED
int toku_lt_acquire_lock_request_with_timeout(toku_lock_tree *tree, toku_lock_request *lock_request, struct timeval *wait_time);
// called with the lock tree already locked
int toku_lt_acquire_lock_request_with_timeout_locked(toku_lock_tree *tree, toku_lock_request *lock_request, struct timeval *wait_time);
// call acquire_lock_request_with_timeout with the default lock wait timeout
int toku_lt_acquire_lock_request_with_default_timeout(toku_lock_tree *tree, toku_lock_request *lock_request);
// called with the lock tree already locked
int toku_lt_acquire_lock_request_with_default_timeout_locked (toku_lock_tree *tree, toku_lock_request *lock_request);
// check if a given lock request could deadlock with any granted locks.
void toku_lt_check_deadlock(toku_lock_tree *tree, toku_lock_request *lock_request);
#include "txnid_set.h"
// internal function that finds all transactions that conflict with a given lock request
// for read lock requests
// conflicts = all transactions in the BWT that conflict with the lock request
// for write lock requests
// conflicts = all transactions in the GRT that conflict with the lock request UNION
// all transactions in the BWT that conflict with the lock request
// adds all of the conflicting transactions to the conflicts transaction set
// returns an error code (0 == success)
int toku_lt_get_lock_request_conflicts(toku_lock_tree *tree, toku_lock_request *lock_request, txnid_set *conflicts);
// set the ltm mutex (used to override the internal mutex) and use a user supplied mutex instead to protect the
// lock tree). the first use is to use the ydb mutex to protect the lock tree. eventually, the ydb code will
// be refactored to use the ltm mutex instead.
void toku_ltm_set_mutex(toku_ltm *ltm, toku_pthread_mutex_t *use_lock);
// lock the lock tree
void toku_ltm_lock_mutex(toku_ltm *mgr);
// unlock the lock tree
void toku_ltm_unlock_mutex(toku_ltm *mgr);
// set the default lock timeout. units are microseconds.
void toku_ltm_set_lock_wait_time(toku_ltm *mgr, uint64_t lock_wait_time_usec);
// get the default lock timeout
void toku_ltm_get_lock_wait_time(toku_ltm *mgr, uint64_t *lock_wait_time_usec);
#if defined(__cplusplus)
}
#endif
......
......@@ -22,6 +22,7 @@ SRCS = $(wildcard *.c)
LOG_TESTS = $(patsubst %.c,%.log$(BINSUF),$(SRCS))
TLOG_TESTS = $(patsubst %.c,%.tlog$(BINSUF),$(SRCS))
LIN_TESTS = $(patsubst %.c,%.lin$(BINSUF),$(SRCS))
WFG_TESTS = $(patsubst %.c,%.lin$(BINSUF), $(wildcard test_wfg*.c))
ALL_TESTS = $(LIN_TESTS) $(TLOG_TESTS) #$(LOG_TESTS)
......@@ -29,6 +30,7 @@ RUN_LOG_TESTS = $(patsubst %.log$(BINSUF),%.logrun,$(LOG_TESTS))
RUN_TLOG_TESTS = $(patsubst %.tlog$(BINSUF),%.tlogrun,$(TLOG_TESTS))
RUN_LIN_TESTS = $(patsubst %.lin$(BINSUF),%.linrun,$(LIN_TESTS))
RUN_ALL_TESTS = $(RUN_LIN_TESTS) $(RUN_TLOG_TESTS) #$(RUN_LOG_TESTS)
RUN_WFG_TESTS = $(patsubst %.lin$(BINSUF),%.linrun,$(WFG_TESTS))
.PHONY: default all check tests check.lin check.tlog check.log tests.lin tests.log tests.tlog
......@@ -42,6 +44,7 @@ tests.tlog: $(TLOG_TESTS)
check.tlog: $(RUN_TLOG_TESTS)
tests.log: $(LOG_TESTS)
check.log: $(RUN_LOG_TESTS)
check.wfg: $(RUN_WFG_TESTS)
.PHONY: %.linrun %.logrun %.run %.tlogrun
# STUFF!!!!
......
......@@ -5,6 +5,7 @@
#include <brttypes.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <toku_assert.h>
#include <errno.h>
......
// T(A) gets R(TABLE)
// T(B) gets R(TABLE)
// T(C) trys W(L) blocked
// T(C) gets conflicts { A, B }
// T(A) releases locks
// T(C) gets conflicts { B }
// T(B) releases locks
// T(C) gets W(L)
#include "test.h"
static void sortit(txnid_set *txns) {
size_t n = txnid_set_size(txns);
for (size_t i = 1; i < n; i++)
assert(txnid_set_get(txns, i) > txnid_set_get(txns, i-1));
}
int main(int argc, const char *argv[]) {
int r;
uint32_t max_locks = 2;
uint64_t max_lock_memory = 4096;
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;
}
assert(0);
}
// setup
toku_ltm *ltm = NULL;
r = toku_ltm_create(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
DBT key_l; dbt_init(&key_l, "L", 1);
txnid_set conflicts;
const TXNID txn_a = 1;
toku_lock_request a_r_t; toku_lock_request_init(&a_r_t, (DB *)1, txn_a, toku_lt_neg_infinity, toku_lt_infinity, LOCK_REQUEST_READ);
r = toku_lock_request_start(&a_r_t, lt, false); assert(r == 0);
assert(a_r_t.state == LOCK_REQUEST_COMPLETE && a_r_t.complete_r == 0);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &a_r_t, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 0);
txnid_set_destroy(&conflicts);
toku_lock_request_destroy(&a_r_t);
const TXNID txn_b = 2;
toku_lock_request b_r_l; toku_lock_request_init(&b_r_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&b_r_l, lt, false); assert(r == 0);
assert(b_r_l.state == LOCK_REQUEST_COMPLETE && b_r_l.complete_r == 0);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &b_r_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 0);
txnid_set_destroy(&conflicts);
toku_lock_request_destroy(&b_r_l);
const TXNID txn_c = 3;
toku_lock_request c_w_l; toku_lock_request_init(&c_w_l, (DB *)1, txn_c, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&c_w_l, lt, false); assert(r != 0);
assert(c_w_l.state == LOCK_REQUEST_PENDING);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &c_w_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 2);
sortit(&conflicts);
assert(txnid_set_get(&conflicts, 0) == txn_a);
assert(txnid_set_get(&conflicts, 1) == txn_b);
txnid_set_destroy(&conflicts);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
assert(c_w_l.state == LOCK_REQUEST_PENDING);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &c_w_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 1);
assert(txnid_set_get(&conflicts, 0) == txn_b);
txnid_set_destroy(&conflicts);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
assert(c_w_l.state == LOCK_REQUEST_COMPLETE && c_w_l.complete_r == 0);
toku_lock_request_destroy(&c_w_l);
r = toku_lt_unlock(lt, txn_c); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}
// T(A) gets R(L)
// T(B) gets R(L)
// T(C) trys W(L) blocked
// T(C) gets conflicts { A, B }
// T(A) releases locks
// T(C) gets conflicts { B }
// T(B) releases locks
// T(C) gets W(L)
#include "test.h"
static void sortit(txnid_set *txns) {
size_t n = txnid_set_size(txns);
for (size_t i = 1; i < n; i++)
assert(txnid_set_get(txns, i) > txnid_set_get(txns, i-1));
}
int main(int argc, const char *argv[]) {
int r;
uint32_t max_locks = 2;
uint64_t max_lock_memory = 4096;
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;
}
assert(0);
}
// setup
toku_ltm *ltm = NULL;
r = toku_ltm_create(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
DBT key_l; dbt_init(&key_l, "L", 1);
txnid_set conflicts;
const TXNID txn_a = 1;
toku_lock_request a_r_l; toku_lock_request_init(&a_r_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&a_r_l, lt, false); assert(r == 0);
assert(a_r_l.state == LOCK_REQUEST_COMPLETE && a_r_l.complete_r == 0);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &a_r_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 0);
txnid_set_destroy(&conflicts);
toku_lock_request_destroy(&a_r_l);
const TXNID txn_b = 2;
toku_lock_request b_r_l; toku_lock_request_init(&b_r_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&b_r_l, lt, false); assert(r == 0);
assert(b_r_l.state == LOCK_REQUEST_COMPLETE && b_r_l.complete_r == 0
);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &b_r_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 0);
txnid_set_destroy(&conflicts);
toku_lock_request_destroy(&b_r_l);
const TXNID txn_c = 3;
toku_lock_request c_w_l; toku_lock_request_init(&c_w_l, (DB *)1, txn_c, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&c_w_l, lt, false); assert(r != 0);
assert(c_w_l.state == LOCK_REQUEST_PENDING);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &c_w_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 2);
sortit(&conflicts);
assert(txnid_set_get(&conflicts, 0) == txn_a);
assert(txnid_set_get(&conflicts, 1) == txn_b);
txnid_set_destroy(&conflicts);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
assert(c_w_l.state == LOCK_REQUEST_PENDING);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &c_w_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 1);
assert(txnid_set_get(&conflicts, 0) == txn_b);
txnid_set_destroy(&conflicts);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
assert(c_w_l.state == LOCK_REQUEST_COMPLETE && c_w_l.complete_r == 0);
toku_lock_request_destroy(&c_w_l);
r = toku_lt_unlock(lt, txn_c); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}
// T(A) gets W(L)
// T(B) trys R(L) blocked, conflicts {A}
// T(A) releases locks
// T(B) gets R(L)
#include "test.h"
int main(int argc, const char *argv[]) {
int r;
uint32_t max_locks = 2;
uint64_t max_lock_memory = 4096;
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;
}
assert(0);
}
// setup
toku_ltm *ltm = NULL;
r = toku_ltm_create(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
DBT key_l; dbt_init(&key_l, "L", 1);
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l, lt, false); assert(r == 0);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
txnid_set conflicts;
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &a_w_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 0);
txnid_set_destroy(&conflicts);
toku_lock_request_destroy(&a_w_l);
const TXNID txn_b = 2;
toku_lock_request b_r_l; toku_lock_request_init(&b_r_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&b_r_l, lt, false); assert(r != 0);
assert(b_r_l.state == LOCK_REQUEST_PENDING);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &b_r_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 1);
assert(txnid_set_get(&conflicts, 0) == txn_a);
txnid_set_destroy(&conflicts);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
assert(b_r_l.state == LOCK_REQUEST_COMPLETE && b_r_l.complete_r == 0);
toku_lock_request_destroy(&b_r_l);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}
// T(A) gets W(TABLE)
// T(B) trys R(L) blocked, conflicts {A}
// T(A) releases locks
// T(B) gets R(L)
#include "test.h"
int main(int argc, const char *argv[]) {
int r;
uint32_t max_locks = 2;
uint64_t max_lock_memory = 4096;
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;
}
assert(0);
}
// setup
toku_ltm *ltm = NULL;
r = toku_ltm_create(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
DBT key_l; dbt_init(&key_l, "L", 1);
const TXNID txn_a = 1;
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, toku_lt_neg_infinity, toku_lt_infinity, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l, lt, false); assert(r == 0);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
txnid_set conflicts;
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &a_w_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 0);
txnid_set_destroy(&conflicts);
toku_lock_request_destroy(&a_w_l);
const TXNID txn_b = 2;
toku_lock_request b_r_l; toku_lock_request_init(&b_r_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&b_r_l, lt, false); assert(r != 0);
assert(b_r_l.state == LOCK_REQUEST_PENDING);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &b_r_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 1);
assert(txnid_set_get(&conflicts, 0) == txn_a);
txnid_set_destroy(&conflicts);
toku_lock_request_destroy(&b_r_l);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}
// T(A) gets W(L)
// T(B) trys W(L) blocked
// T(B) gets conflicts { A }
// T(A) releases locks
#include "test.h"
int main(int argc, const char *argv[]) {
int r;
uint32_t max_locks = 2;
uint64_t max_lock_memory = 4096;
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;
}
assert(0);
}
// setup
toku_ltm *ltm = NULL;
r = toku_ltm_create(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
DBT key_l; dbt_init(&key_l, "L", 1);
const TXNID txn_a = 1;
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l, lt, false); assert(r == 0);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
txnid_set conflicts;
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &a_w_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 0);
txnid_set_destroy(&conflicts);
toku_lock_request_destroy(&a_w_l);
const TXNID txn_b = 2;
toku_lock_request b_w_l; toku_lock_request_init(&b_w_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&b_w_l, lt, false); assert(r != 0);
assert(b_w_l.state == LOCK_REQUEST_PENDING);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &b_w_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 1);
assert(txnid_set_get(&conflicts, 0) == txn_a);
txnid_set_destroy(&conflicts);
toku_lock_request_destroy(&b_w_l);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}
// T(A) gets W(L)
// T(B) tries W(L) with timeout, gets DB_LOCK_NOTGRANTED
// T(B) releases locks
#include "test.h"
static int read_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, (DB*)1, txnid, &key, &key, LOCK_REQUEST_READ);
int r = toku_lt_acquire_lock_request_with_default_timeout(lt, &lr);
toku_lock_request_destroy(&lr);
return r;
}
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, (DB*)1, txnid, &key, &key, LOCK_REQUEST_WRITE);
int r = toku_lt_acquire_lock_request_with_default_timeout(lt, &lr);
toku_lock_request_destroy(&lr);
return r;
}
int main(int argc, const char *argv[]) {
int r;
uint32_t max_locks = 1;
uint64_t max_lock_memory = 4096;
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;
}
assert(0);
}
// setup
toku_ltm *ltm = NULL;
r = toku_ltm_create(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
const TXNID txn_b = 2;
r = write_lock(lt, txn_a, "L"); assert(r == 0);
for (int t = 1; t < 10; t++) {
toku_ltm_set_lock_wait_time(ltm, t * 1000000);
r = read_lock(lt, txn_b, "L");
assert(r == DB_LOCK_NOTGRANTED);
r = write_lock(lt, txn_b, "L");
assert(r == DB_LOCK_NOTGRANTED);
}
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}
// verify that the lock tree global timeout APIs work
#include "test.h"
int main(int argc, const char *argv[]) {
int r;
uint32_t max_locks = 2;
uint64_t max_lock_memory = 4096;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
if (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;
}
assert(0);
}
// setup
toku_ltm *ltm = NULL;
r = toku_ltm_create(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
uint64_t target_wait_time, the_wait_time;
toku_ltm_get_lock_wait_time(ltm, &the_wait_time);
assert(the_wait_time == 0);
target_wait_time = 1*1000000 + 0;
toku_ltm_set_lock_wait_time(ltm, target_wait_time);
toku_ltm_get_lock_wait_time(ltm, &the_wait_time);
assert(the_wait_time == target_wait_time);
target_wait_time = 2*1000000 + 3;
toku_ltm_set_lock_wait_time(ltm, target_wait_time);
toku_ltm_get_lock_wait_time(ltm, &the_wait_time);
assert(the_wait_time == target_wait_time);
target_wait_time = ~0;
toku_ltm_set_lock_wait_time(ltm, target_wait_time);
toku_ltm_get_lock_wait_time(ltm, &the_wait_time);
assert(the_wait_time == target_wait_time);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}
// T(A) gets W(L)
// T(B) tries W(L) with timeout, gets DB_LOCK_NOTGRANTED
// T(B) releases locks
#include "test.h"
static int read_lock(toku_lock_tree *lt, TXNID txnid, char *k, struct timeval *wait_time) {
DBT key; dbt_init(&key, k, strlen(k));
toku_lock_request lr;
toku_lock_request_init(&lr, (DB*)1, txnid, &key, &key, LOCK_REQUEST_READ);
int r = toku_lt_acquire_lock_request_with_timeout(lt, &lr, wait_time);
toku_lock_request_destroy(&lr);
return r;
}
static int write_lock(toku_lock_tree *lt, TXNID txnid, char *k, struct timeval *wait_time) {
DBT key; dbt_init(&key, k, strlen(k));
toku_lock_request lr;
toku_lock_request_init(&lr, (DB*)1, txnid, &key, &key, LOCK_REQUEST_WRITE);
int r = toku_lt_acquire_lock_request_with_timeout(lt, &lr, wait_time);
toku_lock_request_destroy(&lr);
return r;
}
int main(int argc, const char *argv[]) {
int r;
uint32_t max_locks = 1;
uint64_t max_lock_memory = 4096;
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;
}
assert(0);
}
// setup
toku_ltm *ltm = NULL;
r = toku_ltm_create(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
const TXNID txn_b = 2;
r = write_lock(lt, txn_a, "L", NULL); assert(r == 0);
for (int t = 1; t < 10; t++) {
struct timeval wait_time = { t, 0 };
r = read_lock(lt, txn_b, "L", &wait_time);
assert(r == DB_LOCK_NOTGRANTED);
r = write_lock(lt, txn_b, "L", &wait_time);
assert(r == DB_LOCK_NOTGRANTED);
}
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}
// T(A) gets W(L)
// T(B) tries R(L), gets DB_LOCK_NOTGRANTED
// T(C) tries R(L), gets DB_LOCK_NOTGRANTED
// T(A) releases locks
// T(B) gets R(L)
// T(C) gets R(L)
// T(B) releases locks
// T(C) releases locks
#include "test.h"
static int read_lock(toku_lock_tree *lt, TXNID txnid, char *k) {
DBT key; dbt_init(&key, k, strlen(k));
int r = toku_lt_acquire_read_lock(lt, (DB*)1, txnid, &key);
return r;
}
static int write_lock(toku_lock_tree *lt, TXNID txnid, char *k) {
DBT key; dbt_init(&key, k, strlen(k));
int r = toku_lt_acquire_write_lock(lt, (DB*)1, txnid, &key);
return r;
}
int main(int argc, const char *argv[]) {
int r;
uint32_t max_locks = 2;
uint64_t max_lock_memory = 4096;
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;
}
assert(0);
}
// setup
toku_ltm *ltm = NULL;
r = toku_ltm_create(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
r = write_lock(lt, txn_a, "L"); assert(r == 0);
const TXNID txn_b = 2;
r = read_lock(lt, txn_b, "L"); assert(r == DB_LOCK_NOTGRANTED);
const TXNID txn_c = 3;
r = read_lock(lt, txn_c, "L"); assert(r == DB_LOCK_NOTGRANTED);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
r = read_lock(lt, txn_b, "L"); assert(r == 0);
r = read_lock(lt, txn_c, "L"); assert(r == 0);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
r = toku_lt_unlock(lt, txn_c); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}
// T(A) gets W(L)
// T(B) tries R(L), gets DB_LOCK_NOTGRANTED
// T(C) tries R(L), gets DB_LOCK_NOTGRANTED
// T(A) releases locks
// T(B) gets R(L)
// T(C) still pending (since we only have 1 lock
// T(B) releases lock
// T(C) gets R(L)
// T(C) releases locks
#include "test.h"
int main(int argc, const char *argv[]) {
int r;
uint32_t max_locks = 1;
uint64_t max_lock_memory = 4096;
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;
}
assert(0);
}
// setup
toku_ltm *ltm = NULL;
r = toku_ltm_create(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
DBT key_l; dbt_init(&key_l, "L", 1);
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l, lt, false); assert(r == 0);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
toku_lock_request_destroy(&a_w_l);
const TXNID txn_b = 2;
toku_lock_request b_w_l; toku_lock_request_init(&b_w_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&b_w_l, lt, false); assert(r != 0);
assert(b_w_l.state == LOCK_REQUEST_PENDING);
const TXNID txn_c = 3;
toku_lock_request c_w_l; toku_lock_request_init(&c_w_l, (DB *)1, txn_c, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&c_w_l, lt, false); assert(r != 0);
assert(c_w_l.state == LOCK_REQUEST_PENDING);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
assert(b_w_l.state == LOCK_REQUEST_COMPLETE && b_w_l.complete_r == 0);
assert(c_w_l.state == LOCK_REQUEST_COMPLETE && c_w_l.complete_r == TOKUDB_OUT_OF_LOCKS);
toku_lock_request_destroy(&b_w_l);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
toku_lock_request_destroy(&c_w_l);
r = toku_lt_unlock(lt, txn_c); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}
// T(A) gets W(L)
// T(B) tries R(L), gets DB_LOCK_NOTGRANTED
// T(C) tries R(L), gets DB_LOCK_NOTGRANTED
// T(A) releases locks
// T(B) gets R(L)
// T(C) gets R(L)
// T(B) releases locks
// T(C) releases locks
#include "test.h"
int main(int argc, const char *argv[]) {
int r;
uint32_t max_locks = 2;
uint64_t max_lock_memory = 4096;
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;
}
assert(0);
}
// setup
toku_ltm *ltm = NULL;
r = toku_ltm_create(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
DBT key_l; dbt_init(&key_l, "L", 1);
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l, lt, false); assert(r == 0);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
toku_lock_request_destroy(&a_w_l);
const TXNID txn_b = 2;
toku_lock_request b_w_l; toku_lock_request_init(&b_w_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&b_w_l, lt, false); assert(r != 0);
assert(b_w_l.state == LOCK_REQUEST_PENDING);
const TXNID txn_c = 3;
toku_lock_request c_w_l; toku_lock_request_init(&c_w_l, (DB *)1, txn_c, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&c_w_l, lt, false); assert(r != 0);
assert(c_w_l.state == LOCK_REQUEST_PENDING);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
assert(b_w_l.state == LOCK_REQUEST_COMPLETE && b_w_l.complete_r == 0);
assert(c_w_l.state == LOCK_REQUEST_COMPLETE && c_w_l.complete_r == 0);;
toku_lock_request_destroy(&b_w_l);
toku_lock_request_destroy(&c_w_l);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
r = toku_lt_unlock(lt, txn_c); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}
// verify that a user supplied mutex works
// T(A) gets W(L)
// T(B) tries W(L), gets lock request blocked
// T(B) lock request W(L) times out
// T(A) releases locks
// T(B) releases locks
#include "test.h"
int
main(int argc, const char *argv[]) {
int r;
uint32_t max_locks = 2;
uint64_t max_lock_memory = 4096;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
if (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;
}
assert(0);
}
// setup
toku_ltm *ltm = NULL;
r = toku_ltm_create(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_ltm_set_lock_wait_time(ltm, 5000000);
toku_pthread_mutex_t my_mutex = TOKU_PTHREAD_MUTEX_INITIALIZER;
toku_ltm_set_mutex(ltm, &my_mutex);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
const TXNID txn_b = 2;
DBT key_l; dbt_init(&key_l, "L", 1);
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
toku_ltm_lock_mutex(ltm);
r = toku_lock_request_start_locked(&a_w_l, lt, false); assert(r == 0);
toku_ltm_unlock_mutex(ltm);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
toku_lock_request b_w_l; toku_lock_request_init(&b_w_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_WRITE);
toku_ltm_lock_mutex(ltm);
r = toku_lock_request_start_locked(&b_w_l, lt, false); assert(r != 0);
toku_ltm_unlock_mutex(ltm);
assert(b_w_l.state == LOCK_REQUEST_PENDING);
toku_ltm_lock_mutex(ltm);
r = toku_lock_request_wait_with_default_timeout(&b_w_l, lt);
toku_ltm_unlock_mutex(ltm);
assert(r == DB_LOCK_NOTGRANTED);
assert(b_w_l.state == LOCK_REQUEST_COMPLETE);
toku_lock_request_destroy(&a_w_l);
toku_lock_request_destroy(&b_w_l);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}
// T(A) gets W(L)
// T(B) tries W(L) with timeout, gets DB_LOCK_NOTGRANTED
// T(B) releases locks
#include "test.h"
static int read_lock(toku_ltm *ltm, 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, (DB*)1, txnid, &key, &key, LOCK_REQUEST_READ);
toku_ltm_lock_mutex(ltm);
int r = toku_lt_acquire_lock_request_with_default_timeout_locked(lt, &lr);
toku_ltm_unlock_mutex(ltm);
toku_lock_request_destroy(&lr);
return r;
}
static int write_lock(toku_ltm *ltm, 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, (DB*)1, txnid, &key, &key, LOCK_REQUEST_WRITE);
toku_ltm_lock_mutex(ltm);
int r = toku_lt_acquire_lock_request_with_default_timeout_locked(lt, &lr);
toku_ltm_unlock_mutex(ltm);
toku_lock_request_destroy(&lr);
return r;
}
int main(int argc, const char *argv[]) {
int r;
uint32_t max_locks = 1;
uint64_t max_lock_memory = 4096;
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;
}
assert(0);
}
// setup
toku_ltm *ltm = NULL;
r = toku_ltm_create(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_pthread_mutex_t my_mutex = TOKU_PTHREAD_MUTEX_INITIALIZER;
toku_ltm_set_mutex(ltm, &my_mutex);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
const TXNID txn_b = 2;
r = write_lock(ltm, lt, txn_a, "L"); assert(r == 0);
for (int t = 1; t < 10; t++) {
toku_ltm_set_lock_wait_time(ltm, t * 1000000);
r = read_lock(ltm, lt, txn_b, "L");
assert(r == DB_LOCK_NOTGRANTED);
r = write_lock(ltm, lt, txn_b, "L");
assert(r == DB_LOCK_NOTGRANTED);
}
toku_ltm_lock_mutex(ltm);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
toku_ltm_unlock_mutex(ltm);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}
// T(A) gets W(L)
// T(B) gets W(M)
// T(A) tries W(M), gets blocked
// T(B) tries W(L), gets deadlock
// T(B) releases locks
// T(A) granted W(M)
// T(A) releases locks
#include "test.h"
int main(int argc, const char *argv[]) {
int r;
uint32_t max_locks = 2;
uint64_t max_lock_memory = 4096;
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;
}
assert(0);
}
// setup
toku_ltm *ltm = NULL;
r = toku_ltm_create(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
DBT key_l; dbt_init(&key_l, "L", 1);
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l, lt, false); assert(r == 0);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
toku_lock_request_destroy(&a_w_l);
const TXNID txn_b = 2;
DBT key_m; dbt_init(&key_m, "M", 1);
toku_lock_request b_w_m; toku_lock_request_init(&b_w_m, (DB *)1, txn_b, &key_m, &key_m, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&b_w_m, lt, false); assert(r == 0);
assert(b_w_m.state == LOCK_REQUEST_COMPLETE && b_w_m.complete_r == 0);
toku_lock_request_destroy(&b_w_m);
toku_lock_request a_w_m; toku_lock_request_init(&a_w_m, (DB *)1, txn_a, &key_m, &key_m, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_m, lt, false); assert(r == DB_LOCK_NOTGRANTED);
assert(a_w_m.state == LOCK_REQUEST_PENDING);
toku_lock_request b_w_l; toku_lock_request_init(&b_w_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&b_w_l, lt, false); assert(r == DB_LOCK_DEADLOCK);
assert(b_w_l.state == LOCK_REQUEST_COMPLETE && b_w_l.complete_r == DB_LOCK_DEADLOCK);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
toku_lock_request_destroy(&b_w_l);
assert(a_w_m.state == LOCK_REQUEST_COMPLETE && a_w_m.complete_r == 0
);
toku_lock_request_destroy(&a_w_m);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}
// verify that the txnid set works
#include "test.h"
#include "wfg.h"
int main(int argc, const char *argv[]) {
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;
}
assert(0);
}
struct txnid_set set_static;
struct txnid_set *set = &set_static;
txnid_set_init(set);
const int max_ids = 1000;
int ids[max_ids];
for (int i = 0; i < max_ids; i++)
ids[i] = i+1;
// verify random adds
for (int n = max_ids; n > 0; n--) {
int idx = random() % n;
int id = ids[idx];
txnid_set_add(set, id);
assert(txnid_set_size(set) == (size_t) (max_ids - n + 1));
ids[idx] = ids[n-1];
}
// verify duplicate set add
for (int id = 1; id <= max_ids; id++) {
txnid_set_add(set, id);
assert(txnid_set_size(set) == (size_t) max_ids);
}
// verify sorted set
for (int ith = 0; ith < max_ids; ith++)
assert(txnid_set_get(set, ith) == (TXNID) (ith+1));
// verify deletes of non members
txnid_set_delete(set, 0);
txnid_set_delete(set, max_ids+1);
// verify random delete
for (int i = 0; i < max_ids; i++)
ids[i] = i+1;
for (int n = max_ids; n > 0; n--) {
assert(txnid_set_size(set) == (size_t) n);
int idx = random() % n;
int id = ids[idx];
txnid_set_delete(set, id);
assert(!txnid_set_is_member(set, id));
ids[idx] = ids[n-1];
for (int i = 0; i < n-1; i++)
assert(txnid_set_is_member(set, ids[i]));
txnid_set_delete(set, id); // try it again
}
assert(txnid_set_size(set) == 0);
txnid_set_destroy(set);
return 0;
}
// T(A) gets R(L)
// T(B) gets R(L)
// T(A) tries W(L), gets blocked
// T(B) tries W(L), gets deadlock
// T(B) releases locks
// T(A) granted W(L)
// T(A) releases locks
#include "test.h"
int main(int argc, const char *argv[]) {
int r;
uint32_t max_locks = 2;
uint64_t max_lock_memory = 4096;
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;
}
assert(0);
}
// setup
toku_ltm *ltm = NULL;
r = toku_ltm_create(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
DBT key_l; dbt_init(&key_l, "L", 1);
toku_lock_request a_r_l; toku_lock_request_init(&a_r_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&a_r_l, lt, false); assert(r == 0);
assert(a_r_l.state == LOCK_REQUEST_COMPLETE && a_r_l.complete_r == 0);
toku_lock_request_destroy(&a_r_l);
const TXNID txn_b = 2;
toku_lock_request b_r_l; toku_lock_request_init(&b_r_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&b_r_l, lt, false); assert(r == 0);
assert(b_r_l.state == LOCK_REQUEST_COMPLETE && b_r_l.complete_r == 0);
toku_lock_request_destroy(&b_r_l);
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l, lt, false); assert(r == DB_LOCK_NOTGRANTED);
assert(a_w_l.state == LOCK_REQUEST_PENDING);
toku_lock_request b_w_l; toku_lock_request_init(&b_w_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&b_w_l, lt, false); assert(r == DB_LOCK_DEADLOCK);
assert(b_w_l.state == LOCK_REQUEST_COMPLETE && b_w_l.complete_r == DB_LOCK_DEADLOCK);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
toku_lock_request_destroy(&b_w_l);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
toku_lock_request_destroy(&a_w_l);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}
// T(A) gets R(L)
// T(B) gets R(L)
// T(A) tries W(L), gets blocked
// T(B) tries W(L), gets deadlock
// T(B) releases locks
// T(A) granted W(L)
// T(A) releases locks
#include "test.h"
int main(int argc, const char *argv[]) {
int r;
uint32_t max_locks = 2;
uint64_t max_lock_memory = 4096;
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;
}
assert(0);
}
// setup
toku_ltm *ltm = NULL;
r = toku_ltm_create(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
DBT key_l; dbt_init(&key_l, "L", 1);
toku_lock_request a_r_l; toku_lock_request_init(&a_r_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&a_r_l, lt, false); assert(r == 0);
assert(a_r_l.state == LOCK_REQUEST_COMPLETE && a_r_l.complete_r == 0);
toku_lock_request_destroy(&a_r_l);
const TXNID txn_b = 2;
toku_lock_request b_r_l; toku_lock_request_init(&b_r_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&b_r_l, lt, true); assert(r == 0);
assert(b_r_l.state == LOCK_REQUEST_COMPLETE && b_r_l.complete_r == 0);
toku_lock_request_destroy(&b_r_l);
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l, lt, true); assert(r == DB_LOCK_NOTGRANTED);
assert(a_w_l.state == LOCK_REQUEST_PENDING);
toku_lock_request b_w_l; toku_lock_request_init(&b_w_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&b_w_l, lt, true); assert(r == DB_LOCK_DEADLOCK);
assert(b_w_l.state == LOCK_REQUEST_COMPLETE && b_w_l.complete_r == DB_LOCK_DEADLOCK);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
toku_lock_request_destroy(&b_w_l);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
toku_lock_request_destroy(&a_w_l);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}
// find cycles in a 1000 node WFG
#include "test.h"
#include "wfg.h"
struct verify_extra {
int next_id;
TXNID ids[2];
};
static int verify_nodes(TXNID id, void *extra) {
struct verify_extra *verify_extra = (struct verify_extra *) extra;
assert(verify_extra->next_id < 2 && verify_extra->ids[verify_extra->next_id] == id);
verify_extra->next_id++;
return 0;
}
static void verify_nodes_in_cycle(struct wfg *cycles, TXNID a, TXNID b) {
struct verify_extra verify_extra = { .next_id = a, .ids = { a, b } };
wfg_apply_nodes(cycles, verify_nodes, &verify_extra);
assert(verify_extra.next_id == 2);
}
int main(int argc, const char *argv[]) {
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;
}
assert(0);
}
struct wfg *wfg = wfg_new();
struct wfg *cycles = wfg_new();
for (int id = 1; id <= 1000; id++)
wfg_add_edge(wfg, 0, id);
for (int id = 0; id <= 1000; id++) {
wfg_reinit(cycles);
assert(wfg_find_cycles_from_txnid(wfg, id, cycles) == 0);
}
for (int id = 1; id <= 1000; id++) {
wfg_add_edge(wfg, id, 0);
wfg_reinit(cycles);
assert(wfg_find_cycles_from_txnid(wfg, 0, cycles) == id);
wfg_reinit(cycles);
assert(wfg_find_cycles_from_txnid(wfg, id, cycles) == 1);
verify_nodes_in_cycle(cycles, 0, id);
}
wfg_free(wfg);
wfg_free(cycles);
return 0;
}
// find cycles in a simple WFG
#include "test.h"
#include "wfg.h"
struct print_node_extra {
TXNID max_id;
};
static int print_some_nodes(TXNID id, void *extra) {
struct print_node_extra *print_node_extra = (struct print_node_extra *) extra;
if (verbose) printf("%lu ", id);
return id == print_node_extra->max_id ? -1 : 0;
}
struct print_edge_extra {
TXNID max_id;
};
static int print_some_edges(TXNID node_id, TXNID edge_id, void *extra) {
struct print_edge_extra *print_edge_extra = (struct print_edge_extra *) extra;
if (verbose) printf("(%lu %lu) ", node_id, edge_id);
return edge_id == print_edge_extra->max_id ? -1 : 0;
}
int main(int argc, const char *argv[]) {
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;
}
assert(0);
}
struct wfg *wfg = wfg_new();
const int max_ids = 10;
for (int id = 0; id < max_ids; id++)
for (int edge_id = 0; edge_id < max_ids; edge_id++)
wfg_add_edge(wfg, id, edge_id);
struct print_node_extra print_node_extra = { max_ids/2 };
wfg_apply_nodes(wfg, print_some_nodes, &print_node_extra);
if (verbose) printf("\n");
struct print_edge_extra print_edge_extra = { max_ids/2 };
wfg_apply_edges(wfg, max_ids/2, print_some_edges, &print_edge_extra);
if (verbose) printf("\n");
wfg_free(wfg);
return 0;
}
// find cycles in a simple WFG
#include "test.h"
#include "wfg.h"
int main(int argc, const char *argv[]) {
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;
}
assert(0);
}
// setup
struct wfg *wfg = wfg_new();
struct wfg *cycles = wfg_new();
wfg_add_edge(wfg, 1, 2);
for (TXNID i = 1; i <= 2; i++) {
assert(!wfg_exist_cycle_from_txnid(wfg, i));
wfg_reinit(cycles);
assert(wfg_find_cycles_from_txnid(wfg, i, cycles) == 0);
}
wfg_add_edge(wfg, 2, 3);
for (TXNID i = 1; i <= 3; i++) {
assert(!wfg_exist_cycle_from_txnid(wfg, i));
wfg_reinit(cycles);
assert(wfg_find_cycles_from_txnid(wfg, i, cycles) == 0);
}
wfg_add_edge(wfg, 3, 4);
for (TXNID i = 1; i <= 4; i++) {
assert(!wfg_exist_cycle_from_txnid(wfg, i));
wfg_reinit(cycles);
assert(wfg_find_cycles_from_txnid(wfg, i, cycles) == 0);
}
wfg_add_edge(wfg, 4, 1);
for (TXNID i = 1; i <= 4; i++) {
assert(wfg_exist_cycle_from_txnid(wfg, i));
wfg_reinit(cycles);
assert(wfg_find_cycles_from_txnid(wfg, i, cycles) == 1);
if (verbose) wfg_print(cycles);
}
wfg_free(wfg);
wfg_free(cycles);
return 0;
}
// find cycles in a simple WFG
#include "test.h"
#include "wfg.h"
int main(int argc, const char *argv[]) {
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;
}
assert(0);
}
// setup
struct wfg *wfg = wfg_new();
struct wfg *cycles = wfg_new();
wfg_add_edge(wfg, 1, 2);
for (TXNID i = 1; i <= 2; i++) {
assert(!wfg_exist_cycle_from_txnid(wfg, i));
wfg_reinit(cycles);
assert(wfg_find_cycles_from_txnid(wfg, i, cycles) == 0);
}
wfg_add_edge(wfg, 2, 3);
for (TXNID i = 1; i <= 3; i++) {
assert(!wfg_exist_cycle_from_txnid(wfg, i));
wfg_reinit(cycles);
assert(wfg_find_cycles_from_txnid(wfg, i, cycles) == 0);
}
wfg_add_edge(wfg, 3, 4);
for (TXNID i = 1; i <= 4; i++) {
assert(!wfg_exist_cycle_from_txnid(wfg, i));
wfg_reinit(cycles);
assert(wfg_find_cycles_from_txnid(wfg, i, cycles) == 0);
}
wfg_add_edge(wfg, 4, 1);
for (TXNID i = 1; i <= 4; i++) {
assert(wfg_exist_cycle_from_txnid(wfg, i));
wfg_reinit(cycles);
assert(wfg_find_cycles_from_txnid(wfg, i, cycles) == 1);
if (verbose) wfg_print(cycles);
}
wfg_add_edge(wfg, 1, 5);
wfg_add_edge(wfg, 5, 6);
for (TXNID i = 1; i <= 4; i++) {
assert(wfg_exist_cycle_from_txnid(wfg, i));
wfg_reinit(cycles);
assert(wfg_find_cycles_from_txnid(wfg, i, cycles) == 1);
if (verbose) wfg_print(cycles);
}
for (TXNID i = 5; i <= 6; i++) {
assert(!wfg_exist_cycle_from_txnid(wfg, i));
wfg_reinit(cycles);
assert(wfg_find_cycles_from_txnid(wfg, i, cycles) == 0);
if (verbose) wfg_print(cycles);
}
wfg_add_edge(wfg, 6, 1);
assert(wfg_exist_cycle_from_txnid(wfg, 1));
wfg_reinit(cycles);
assert(wfg_find_cycles_from_txnid(wfg, 1, cycles) == 2);
if (verbose) wfg_print(cycles);
wfg_free(wfg);
wfg_free(cycles);
return 0;
}
// find no cycles in an empty WFG
#include "test.h"
#include "wfg.h"
int main(int argc, const char *argv[]) {
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;
}
assert(0);
}
// setup
struct wfg *wfg = wfg_new();
struct wfg *cycles = wfg_new();
assert(!wfg_exist_cycle_from_txnid(wfg, 0)); assert(wfg_find_cycles_from_txnid(wfg, 0, cycles) == 0);
if (verbose) {
wfg_print(wfg); wfg_print(cycles);
}
assert(!wfg_exist_cycle_from_txnid(wfg, 1)); assert(wfg_find_cycles_from_txnid(wfg, 1, cycles) == 0);
if (verbose) {
wfg_print(wfg); wfg_print(cycles);
}
wfg_free(wfg);
wfg_free(cycles);
return 0;
}
// test the wfg_node_exists function
#include "test.h"
#include "wfg.h"
int main(int argc, const char *argv[]) {
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;
}
assert(0);
}
// setup
struct wfg *wfg = wfg_new();
TXNID max_ids = 1000;
for (TXNID id = 1; id < max_ids; id++)
wfg_add_edge(wfg, id, id+1);
assert(!wfg_node_exists(wfg, 0));
for (TXNID id = 1; id <= max_ids; id++)
assert(wfg_node_exists(wfg, id));
assert(!wfg_node_exists(wfg, max_ids+2));
wfg_free(wfg);
return 0;
}
// find no cycles in an empty WFG
#include "test.h"
#include "wfg.h"
int main(int argc, const char *argv[]) {
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;
}
assert(0);
}
// setup
struct wfg *wfg = wfg_new();
struct wfg *cycles = wfg_new();
assert(!wfg_exist_cycle_from_txnid(wfg, 0)); assert(wfg_find_cycles_from_txnid(wfg, 0, cycles) == 0);
wfg_print(wfg); wfg_print(cycles);
wfg_add_edge(wfg, 1, 2);
wfg_add_edge(wfg, 2, 1);
assert(wfg_exist_cycle_from_txnid(wfg, 1)); assert(wfg_find_cycles_from_txnid(wfg, 1, cycles) != 0);
wfg_print(wfg); wfg_print(cycles);
wfg_free(wfg);
wfg_free(cycles);
return 0;
}
// find no cycles in an empty WFG
#include "test.h"
#include "wfg.h"
int main(int argc, const char *argv[]) {
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;
}
assert(0);
}
// setup
struct wfg *wfg = wfg_new();
struct wfg *cycles = wfg_new();
assert(!wfg_exist_cycle_from_txnid(wfg, 1));
wfg_reinit(cycles);
assert(wfg_find_cycles_from_txnid(wfg, 1, cycles) == 0);
if (verbose) wfg_print(cycles);
wfg_add_edge(wfg, 1, 1);
assert(wfg_exist_cycle_from_txnid(wfg, 1));
wfg_reinit(cycles);
assert(wfg_find_cycles_from_txnid(wfg, 1, cycles) == 1);
if (verbose) wfg_print(cycles);
wfg_free(wfg);
wfg_free(cycles);
return 0;
}
// find cycles in a simple WFG
#include "test.h"
#include "wfg.h"
struct verify_extra {
TXNID next_id;
};
static int verify_nodes(TXNID id, void *extra) {
struct verify_extra *verify_extra = (struct verify_extra *) extra;
assert(verify_extra->next_id == id);
verify_extra->next_id++;
return 0;
}
static void verify_nodes_in_cycle_12(struct wfg *cycles) {
struct verify_extra verify_extra = { 1 };
wfg_apply_nodes(cycles, verify_nodes, &verify_extra);
assert(verify_extra.next_id == 3);
}
int main(int argc, const char *argv[]) {
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;
}
assert(0);
}
// setup
struct wfg *wfg = wfg_new();
struct wfg *cycles = wfg_new();
wfg_add_edge(wfg, 1, 2);
assert(!wfg_exist_cycle_from_txnid(wfg, 1)); wfg_reinit(cycles); assert(wfg_find_cycles_from_txnid(wfg, 1, cycles) == 0);
assert(!wfg_exist_cycle_from_txnid(wfg, 2)); wfg_reinit(cycles); assert(wfg_find_cycles_from_txnid(wfg, 1, cycles) == 0);
wfg_add_edge(wfg, 2, 1);
assert(wfg_exist_cycle_from_txnid(wfg, 1)); wfg_reinit(cycles); assert(wfg_find_cycles_from_txnid(wfg, 1, cycles) == 1);
if (verbose) {
wfg_print(wfg); wfg_print(cycles);
}
verify_nodes_in_cycle_12(cycles);
assert(wfg_exist_cycle_from_txnid(wfg, 2)); wfg_reinit(cycles); assert(wfg_find_cycles_from_txnid(wfg, 2, cycles) == 1);
if (verbose) {
wfg_print(wfg); wfg_print(cycles);
}
verify_nodes_in_cycle_12(cycles);
wfg_add_edge(wfg, 1, 3);
assert(!wfg_exist_cycle_from_txnid(wfg, 3)); wfg_reinit(cycles); assert(wfg_find_cycles_from_txnid(wfg, 3, cycles) == 0);
assert(wfg_exist_cycle_from_txnid(wfg, 1)); wfg_reinit(cycles); assert(wfg_find_cycles_from_txnid(wfg, 1, cycles) == 1);
if (verbose) {
wfg_print(wfg); wfg_print(cycles);
}
verify_nodes_in_cycle_12(cycles);
assert(wfg_exist_cycle_from_txnid(wfg, 2));wfg_reinit(cycles); assert(wfg_find_cycles_from_txnid(wfg, 2, cycles) == 1);
if (verbose) {
wfg_print(wfg); wfg_print(cycles);
}
verify_nodes_in_cycle_12(cycles);
wfg_free(wfg);
wfg_free(cycles);
return 0;
}
// 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, (DB*)1, txnid, &key, &key, LOCK_REQUEST_WRITE);
int r = toku_lt_acquire_lock_request_with_timeout(lt, &lr, NULL);
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(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, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
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(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
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}
// 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));
int r = toku_lt_acquire_write_lock(lt, (DB*)1, txnid, &key);
return r;
}
int main(int argc, const char *argv[]) {
int r;
uint32_t max_locks = 1;
uint64_t max_lock_memory = 4096;
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;
}
assert(0);
}
// setup
toku_ltm *ltm = NULL;
r = toku_ltm_create(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
const TXNID txn_b = 2;
r = write_lock(lt, txn_a, "L"); assert(r == 0);
r = write_lock(lt, txn_b, "L"); assert(r == DB_LOCK_NOTGRANTED);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
r = write_lock(lt, txn_b, "L"); assert(r == 0);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}
// T(A) gets W(L)
// T(B) tries W(L), gets lock request blocked
// T(A) releases locks
// T(B) lock request W(L) granted
// T(B) releases locks
#include "test.h"
int main(int argc, const char *argv[]) {
int r;
uint32_t max_locks = 1;
uint64_t max_lock_memory = 4096;
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;
}
assert(0);
}
// setup
toku_ltm *ltm = NULL;
r = toku_ltm_create(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
const TXNID txn_b = 2;
DBT key_l; dbt_init(&key_l, "L", 1);
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l, lt, false); assert(r == 0);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
toku_lock_request_destroy(&a_w_l);
toku_lock_request b_w_l; toku_lock_request_init(&b_w_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&b_w_l, lt, false); assert(r != 0);
assert(b_w_l.state == LOCK_REQUEST_PENDING);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
assert(b_w_l.state == LOCK_REQUEST_COMPLETE && b_w_l.complete_r == 0);
toku_lock_request_destroy(&b_w_l);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}
// T(A) gets W(L)
// T(A) gets R(L)
// T(A) gets W(L)
// T(A) releases locks
#include "test.h"
int main(int argc, const char *argv[]) {
int r;
uint32_t max_locks = 2;
uint64_t max_lock_memory = 4096;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
if (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;
}
assert(0);
}
// setup
toku_ltm *ltm = NULL;
r = toku_ltm_create(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
DBT key_l; dbt_init(&key_l, "L", 1);
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l, lt, false); assert(r == 0);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
toku_lock_request_destroy(&a_w_l);
toku_lock_request a_r_l; toku_lock_request_init(&a_r_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&a_r_l, lt, false); assert(r == 0);
assert(a_r_l.state == LOCK_REQUEST_COMPLETE && a_r_l.complete_r == 0);
toku_lock_request_destroy(&a_r_l);
toku_lock_request a_w_l_2; toku_lock_request_init(&a_w_l_2, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l_2, lt, false); assert(r == 0);
assert(a_w_l_2.state == LOCK_REQUEST_COMPLETE && a_w_l_2.complete_r == 0);
toku_lock_request_destroy(&a_w_l_2);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}
#include <stdbool.h>
#include <stdio.h>
#include "brttypes.h"
#include "memory.h"
#include "txnid_set.h"
#include "toku_assert.h"
void
txnid_set_init(txnid_set *txnids) {
int r = toku_omt_create(&txnids->ids);
assert_zero(r);
}
void
txnid_set_destroy(txnid_set *txnids) {
toku_omt_destroy(&txnids->ids);
}
static int
txnid_compare(OMTVALUE a, void *b) {
TXNID a_txnid = (TXNID) a;
TXNID b_txnid = * (TXNID *) b;
int r;
if (a_txnid < b_txnid)
r = -1;
else if (a_txnid > b_txnid)
r = +1;
else
r = 0;
return r;
}
void
txnid_set_add(txnid_set *txnids, TXNID id) {
OMTVALUE v = (OMTVALUE) id;
int r = toku_omt_insert(txnids->ids, v, txnid_compare, &id, NULL);
assert(r == 0 || r == DB_KEYEXIST);
}
void
txnid_set_delete(txnid_set *txnids, TXNID id) {
int r;
OMTVALUE v;
uint32_t idx;
r = toku_omt_find_zero(txnids->ids, txnid_compare, &id, &v, &idx);
if (r == 0) {
r = toku_omt_delete_at(txnids->ids, idx);
assert_zero(r);
}
}
bool
txnid_set_is_member(txnid_set *txnids, TXNID id) {
int r;
OMTVALUE v;
uint32_t idx;
r = toku_omt_find_zero(txnids->ids, txnid_compare, &id, &v, &idx);
return r == 0;
}
size_t
txnid_set_size(txnid_set *txnids) {
return toku_omt_size(txnids->ids);
}
TXNID
txnid_set_get(txnid_set *txnids, size_t ith) {
OMTVALUE v;
int r = toku_omt_fetch(txnids->ids, ith, &v); assert_zero(r);
return (TXNID) v;
}
#include "omt.h"
typedef struct txnid_set {
OMT ids; // private: set of ids
} txnid_set;
void txnid_set_init(txnid_set *txnids);
void txnid_set_destroy(txnid_set *txnids);
// Return true if the given transaction id is a member of the set.
// Otherwise, return false.
bool txnid_set_is_member(txnid_set *txnids, TXNID id);
// Add a given id to the set of ids.
void txnid_set_add(txnid_set *txnids, TXNID id);
// Delete a given id from the set.
void txnid_set_delete(txnid_set *txnids, TXNID id);
// Return the number of id's in the set
size_t txnid_set_size(txnid_set *txnids);
// Get the ith id in the set, assuming that the set is sorted.
TXNID txnid_set_get(txnid_set *txnids, size_t ith);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "toku_assert.h"
#include "brttypes.h"
#include "memory.h"
#include "toku_list.h"
#include "txnid_set.h"
#include "wfg.h"
struct wfg_node {
txnid_set edges; // set of edges
TXNID id;
bool visited;
};
static struct wfg_node *
wfg_new_node(TXNID id) {
struct wfg_node *node = (struct wfg_node *) toku_xmalloc(sizeof (struct wfg_node));
node->id = id;
node->visited = false;
txnid_set_init(&node->edges);
return node;
}
static void
wfg_node_free(struct wfg_node *node) {
txnid_set_destroy(&node->edges);
toku_free(node);
}
struct wfg *
wfg_new(void) {
struct wfg *wfg = (struct wfg *) toku_xmalloc(sizeof (struct wfg));
wfg_init(wfg);
return wfg;
}
void
wfg_free(struct wfg *wfg) {
wfg_destroy(wfg);
toku_free(wfg);
}
void
wfg_init(struct wfg *wfg) {
int r = toku_omt_create(&wfg->nodes);
assert_zero(r);
}
void
wfg_destroy(struct wfg *wfg) {
size_t n_nodes = toku_omt_size(wfg->nodes);
for (size_t i = 0; i < n_nodes; i++) {
OMTVALUE v;
int r = toku_omt_fetch(wfg->nodes, i, &v);
assert_zero(r);
struct wfg_node *node = (struct wfg_node *) v;
wfg_node_free(node);
}
toku_omt_destroy(&wfg->nodes);
}
void
wfg_reinit(struct wfg *wfg) {
wfg_destroy(wfg);
wfg_init(wfg);
}
static int
wfg_compare_nodes(OMTVALUE a, void *b) {
struct wfg_node *a_node = (struct wfg_node *) a;
TXNID a_id = a_node->id;
TXNID b_id = * (TXNID *) b;
int r;
if (a_id < b_id)
r = -1;
else if (a_id > b_id)
r = +1;
else
r = 0;
return r;
}
// find node by id
static struct wfg_node *
wfg_find_node(struct wfg *wfg, TXNID id) {
int r;
OMTVALUE v;
u_int32_t idx;
r = toku_omt_find_zero(wfg->nodes, wfg_compare_nodes, &id, &v, &idx);
struct wfg_node *node;
if (r == DB_NOTFOUND)
node = NULL;
else
node = (struct wfg_node *) v;
return node;
}
bool
wfg_node_exists(struct wfg *wfg, TXNID id) {
struct wfg_node *node = wfg_find_node(wfg, id);
return node != NULL;
}
// insert a new node
static struct wfg_node *
wfg_find_create_node(struct wfg *wfg, TXNID id) {
int r;
OMTVALUE v;
u_int32_t idx;
r = toku_omt_find_zero(wfg->nodes, wfg_compare_nodes, &id, &v, &idx);
struct wfg_node *node;
if (r == DB_NOTFOUND) {
node = wfg_new_node(id);
assert(node);
r = toku_omt_insert_at(wfg->nodes, node, idx);
assert_zero(r);
} else {
node = (struct wfg_node *) v;
}
return node;
}
void
wfg_add_edge(struct wfg *wfg, TXNID a_id, TXNID b_id) {
struct wfg_node *a_node = wfg_find_create_node(wfg, a_id);
struct wfg_node *b_node = wfg_find_create_node(wfg, b_id);
txnid_set_add(&a_node->edges, b_node->id);
}
static int
wfg_find_cycles_from_node(struct wfg *wfg, struct wfg_node *target, struct wfg_node *head, struct wfg *cycles) {
int n_cycles = 0;
head->visited = true;
size_t n_edges = txnid_set_size(&head->edges);
for (size_t i = 0; i < n_edges; i++) {
TXNID edge_id = txnid_set_get(&head->edges, i);
if (target->id == edge_id) {
wfg_add_edge(cycles, head->id, edge_id);
n_cycles += 1;
} else {
struct wfg_node *new_head = wfg_find_node(wfg, edge_id);
if (new_head && !new_head->visited) {
int this_n_cycles = wfg_find_cycles_from_node(wfg, target, new_head, cycles);
if (this_n_cycles) {
wfg_add_edge(cycles, head->id, edge_id);
n_cycles += this_n_cycles;
}
}
}
}
head->visited = false;
return n_cycles;
}
int
wfg_find_cycles_from_txnid(struct wfg *wfg, TXNID a, struct wfg *cycles) {
struct wfg_node *a_node = wfg_find_node(wfg, a);
int n_cycles = 0;
if (a_node)
n_cycles = wfg_find_cycles_from_node(wfg, a_node, a_node, cycles);
return n_cycles;
}
static bool
wfg_exist_cycle_from_node(struct wfg *wfg, struct wfg_node *target, struct wfg_node *head) {
bool cycle_found = false;
head->visited = true;
size_t n_edges = txnid_set_size(&head->edges);
for (size_t i = 0; i < n_edges && !cycle_found; i++) {
TXNID edge_id = txnid_set_get(&head->edges, i);
if (target->id == edge_id) {
cycle_found = true;
} else {
struct wfg_node *new_head = wfg_find_node(wfg, edge_id);
if (new_head && !new_head->visited)
cycle_found = wfg_exist_cycle_from_node(wfg, target, new_head);
}
}
head->visited = false;
return cycle_found;
}
bool
wfg_exist_cycle_from_txnid(struct wfg *wfg, TXNID a) {
struct wfg_node *a_node = wfg_find_node(wfg, a);
bool cycles_found = false;
if (a_node)
cycles_found = wfg_exist_cycle_from_node(wfg, a_node, a_node);
return cycles_found;
}
static int
print_node_id(TXNID node_id, void *extra UU()) {
printf("%lu ", node_id);
return 0;
}
static int
print_edge(TXNID node_id, TXNID edge_id, void *extra UU()) {
printf("(%lu %lu) ", node_id, edge_id);
return 0;
}
static int
print_all_edges_from_node(TXNID node_id, void *extra) {
struct wfg *wfg = (struct wfg *) extra;
wfg_apply_edges(wfg, node_id, print_edge, extra);
return 0;
}
void
wfg_print(struct wfg *wfg) {
printf("nodes: ");
wfg_apply_nodes(wfg, print_node_id, wfg);
printf("\n");
printf("edges: ");
wfg_apply_nodes(wfg, print_all_edges_from_node, wfg);
printf("\n");
}
void
wfg_apply_nodes(struct wfg *wfg, int (*f)(TXNID id, void *extra), void *extra) {
int r;
size_t n_nodes = toku_omt_size(wfg->nodes);
for (size_t i = 0; i < n_nodes; i++) {
OMTVALUE v;
r = toku_omt_fetch(wfg->nodes, i, &v);
assert_zero(r);
struct wfg_node *i_node = (struct wfg_node *) v;
r = f(i_node->id, extra);
if (r != 0)
break;
}
}
void
wfg_apply_edges(struct wfg *wfg, TXNID node_id, int (*f)(TXNID node_id, TXNID edge_id, void *extra), void *extra) {
struct wfg_node *node = wfg_find_node(wfg, node_id);
if (node) {
size_t n_edges = txnid_set_size(&node->edges);
for (size_t i = 0; i < n_edges; i++) {
int r = f(node_id, txnid_set_get(&node->edges, i), extra);
if (r != 0)
break;
}
}
}
#include "omt.h"
struct wfg {
OMT nodes; // private: set of nodes
};
// Allocate and initialize a wfg
struct wfg *wfg_new(void);
// Destroy and free a wfg
void wfg_free(struct wfg *wfg);
void wfg_init(struct wfg *wfg);
void wfg_reinit(struct wfg *wfg);
void wfg_destroy(struct wfg *wfg);
void wfg_print(struct wfg *wfg);
// Add an edge (a_id, b_id) to the graph
void wfg_add_edge(struct wfg *wfg, TXNID a_id, TXNID b_id);
// Return true if there exists a cycle from a given transaction id in the graph.
// Return false otherwise.
bool wfg_exist_cycle_from_txnid(struct wfg *wfg, TXNID id);
// Find all cycles rooted with the given transaction id.
// Return the number of cycles found.
// Return a subset of the graph that covers the cycles
int wfg_find_cycles_from_txnid(struct wfg *wfg, TXNID id, struct wfg *cycles);
// Return true if a node with the given transaction id exists in the graph.
// Return false otherwise.
bool wfg_node_exists(struct wfg *wfg, TXNID id);
// Apply a given function f to all of the nodes in the graph. The apply function
// returns when the function f is called for all of the nodes in the graph, or the
// function f returns non-zero.
void wfg_apply_nodes(struct wfg *wfg, int (*f)(TXNID id, void *extra), void *extra);
// Apply a given function f to all of the edges whose origin is a given node id. The apply function
// returns when the function f is called for all edges in the graph rooted at node id, or the
// function f returns non-zero.
void wfg_apply_edges(struct wfg *wfg, TXNID node_id, int (*f)(TXNID node_id, TXNID edge_id, void *extra), void *extra);
// Delete the node associated with the given transaction id from the graph.
// Delete all edges to the node from all other nodes.
void wfg_delete_node_for_txnid(struct wfg *wfg, TXNID id);
/* -*- mode: C; c-basic-offset: 4 -*- */
#ident "Copyright (c) 2007-8 Tokutek Inc. All rights reserved."
#ident "Copyright (c) 2007-2011 Tokutek Inc. All rights reserved."
#ident "The technology is licensed by the Massachusetts Institute of Technology, Rutgers State University of New Jersey, and the Research Foundation of State University of New York at Stony Brook under United States of America Serial No. 11/760379 and to the patents and/or patent applications resulting from it."
......@@ -164,8 +164,6 @@ toku_rt_find(toku_range_tree* tree, toku_interval* query, u_int32_t k,
if (!tree || !query || !buf || !buflen || !numfound)
return EINVAL;
if (*buflen == 0)
return EINVAL;
u_int32_t temp_numfound = 0;
for (u_int32_t i = 0; i < tree->numelements; i++) {
......
/* -*- mode: C; c-basic-offset: 4 -*- */
#ident "Copyright (c) 2007-8 Tokutek Inc. All rights reserved."
#ident "Copyright (c) 2007-2011 Tokutek Inc. All rights reserved."
#ident "The technology is licensed by the Massachusetts Institute of Technology, Rutgers State University of New Jersey, and the Research Foundation of State University of New York at Stony Brook under United States of America Serial No. 11/760379 and to the patents and/or patent applications resulting from it."
......@@ -142,7 +142,7 @@ toku_rt_find(toku_range_tree* tree, toku_interval* query, u_int32_t k,
toku_range** buf, u_int32_t* buflen, u_int32_t* numfound) {
int r = ENOSYS;
if (!tree || !query || !buf || !buflen || !numfound || *buflen == 0) {
if (!tree || !query || !buf || !buflen || !numfound) {
r = EINVAL; goto cleanup;
}
assert(!tree->allow_overlaps);
......
/* -*- mode: C; c-basic-offset: 4 -*- */
#ident "Copyright (c) 2007-8 Tokutek Inc. All rights reserved."
#ident "Copyright (c) 2007-2011 Tokutek Inc. All rights reserved."
#ident "The technology is licensed by the Massachusetts Institute of Technology, Rutgers State University of New Jersey, and the Research Foundation of State University of New York at Stony Brook under United States of America Serial No. 11/760379 and to the patents and/or patent applications resulting from it."
......@@ -59,11 +58,14 @@ static inline int toku__rt_increase_buffer(toku_range_tree* tree, toku_range** b
assert(buf);
//TODO: SOME ATTRIBUTE TO REMOVE NEVER EXECUTABLE ERROR: assert(buflen);
if (*buflen < num) {
u_int32_t temp_len = *buflen;
u_int32_t temp_len = *buflen;
if (temp_len == 0)
temp_len = 1;
while (temp_len < num)
temp_len *= 2;
toku_range* temp_buf = tree->realloc(*buf, temp_len * sizeof(toku_range));
if (!temp_buf) return errno;
if (!temp_buf)
return errno;
*buf = temp_buf;
*buflen = temp_len;
}
......
......@@ -86,8 +86,10 @@ int main(int argc, const char *argv[]) {
unsigned oldbufsize = bufsize;
bufsize = 0;
#if 0
r = toku_rt_find(tree, &range.ends, 2, &buf, &bufsize, &found);
CKERR2(r, EINVAL);
#endif
bufsize = oldbufsize;
r = toku_rt_find(tree, &range.ends, 2, &buf, &bufsize, NULL);
......
......@@ -78,12 +78,17 @@ WINDOWS_BDB_DONTRUN_TESTS += \
test_error \
#\ ends prev line
TDB_DONTRUN_SRCS = \
$(wildcard bdb-simple-deadlock*.c) \
TDB_DONTRUN_TESTS = $(patsubst %.c,%,$(TDB_DONTRUN_SRCS))
ifeq ($(OS_CHOICE),windows)
TDB_BINS = $(patsubst %.c,%.tdb$(BINSUF),$(filter-out $(patsubst %,%.c,$(WINDOWS_DONTRUN_TESTS)),$(SRCS)))
TDB_TESTS = $(patsubst %.c,%.tdb$(BINSUF),$(filter-out $(patsubst %,%.c,$(WINDOWS_DONTRUN_TESTS)),$(filter-out $(NONSTANDARD_SRCS),$(SRCS))))
else
TDB_BINS = $(patsubst %.c,%.tdb$(BINSUF),$(SRCS))
TDB_TESTS = $(patsubst %.c,%.tdb$(BINSUF),$(filter-out $(NONSTANDARD_SRCS),$(SRCS)))
TDB_BINS = $(patsubst %.c,%.tdb$(BINSUF),$(filter-out $(TDB_DONTRUN_SRCS),$(SRCS)))
TDB_TESTS = $(patsubst %.c,%.tdb$(BINSUF),$(filter-out $(NONSTANDARD_SRCS),$(filter-out $(TDB_DONTRUN_SRCS),$(SRCS))))
endif
# For diskfull.bdb: db-4.6 seems OK, but db-4.3 segfaults
......@@ -910,3 +915,12 @@ clean:
rm -f dump.bdb.1426 dump.tdb.1426 test1426.bdb
rm -f *.bdb *.tdb
rm -f *.fastlog
BLOCKING_SRCS = $(wildcard blocking-*.c)
BLOCKING_TDB_TESTS = $(patsubst %.c,%.tdbrun,$(BLOCKING_SRCS))
BLOCKING_BDB_TESTS = $(patsubst %.c,%.bdbrun,$(BLOCKING_SRCS))
check_blocking_tdb: $(BLOCKING_TDB_TESTS)
check_blocking_bdb: $(BLOCKING_BDB_TESTS)
// verify that the BDB locker can detect deadlocks on the fly and allow
// the deadlock to be unwound by the deadlocked threads. the main thread
// polls for deadlocks with the lock_detect function.
//
// A write locks L
// B write locks M
// A tries to write lock M, gets blocked
// B tries to write lock L, gets DEADLOCK error
// B releases its lock on M
// A resumes
#include "test.h"
#include "toku_pthread.h"
struct test_seq {
int state;
toku_pthread_mutex_t lock;
toku_pthread_cond_t cv;
};
static void test_seq_init(struct test_seq *seq) {
seq->state = 0;
int r;
r = toku_pthread_mutex_init(&seq->lock, NULL); assert(r == 0);
r = toku_pthread_cond_init(&seq->cv, NULL); assert(r == 0);
}
static void test_seq_destroy(struct test_seq *seq) {
int r;
r = toku_pthread_mutex_destroy(&seq->lock); assert(r == 0);
r = toku_pthread_cond_destroy(&seq->cv); assert(r == 0);
}
static void test_seq_sleep(struct test_seq *seq, int new_state) {
int r;
r = toku_pthread_mutex_lock(&seq->lock); assert(r == 0);
while (seq->state != new_state) {
r = toku_pthread_cond_wait(&seq->cv, &seq->lock); assert(r == 0);
}
r = toku_pthread_mutex_unlock(&seq->lock); assert(r == 0);
}
static void test_seq_next_state(struct test_seq *seq) {
int r;
r = toku_pthread_mutex_lock(&seq->lock);
seq->state++;
r = toku_pthread_cond_broadcast(&seq->cv); assert(r == 0);
r = toku_pthread_mutex_unlock(&seq->lock); assert(r == 0);
}
struct locker_args {
DB_ENV *db_env;
struct test_seq *test_seq;
};
static void *run_locker_a(void *arg) {
struct locker_args *locker_args = (struct locker_args *) arg;
DB_ENV *db_env = locker_args->db_env;
struct test_seq *test_seq = locker_args->test_seq;
int r;
u_int32_t locker_a;
r = db_env->lock_id(db_env, &locker_a); assert(r == 0);
DBT object_l = { .data = "L", .size = 1 };
DBT object_m = { .data = "M", .size = 1 };
test_seq_sleep(test_seq, 0);
DB_LOCK lock_a_l;
r = db_env->lock_get(db_env, locker_a, DB_LOCK_NOWAIT, &object_l, DB_LOCK_WRITE, &lock_a_l); assert(r == 0);
test_seq_next_state(test_seq);
test_seq_sleep(test_seq, 2);
DB_LOCK lock_a_m;
r = db_env->lock_get(db_env, locker_a, 0, &object_m, DB_LOCK_WRITE, &lock_a_m); assert(r == 0);
r = db_env->lock_put(db_env, &lock_a_l); assert(r == 0);
r = db_env->lock_put(db_env, &lock_a_m); assert(r == 0);
r = db_env->lock_id_free(db_env, locker_a); assert(r == 0);
return arg;
}
static void *run_locker_b(void *arg) {
struct locker_args *locker_args = (struct locker_args *) arg;
DB_ENV *db_env = locker_args->db_env;
struct test_seq *test_seq = locker_args->test_seq;
int r;
u_int32_t locker_b;
r = db_env->lock_id(db_env, &locker_b); assert(r == 0);
DBT object_l = { .data = "L", .size = 1 };
DBT object_m = { .data = "M", .size = 1 };
test_seq_sleep(test_seq, 1);
DB_LOCK lock_b_m;
r = db_env->lock_get(db_env, locker_b, DB_LOCK_NOWAIT, &object_m, DB_LOCK_WRITE, &lock_b_m); assert(r == 0);
test_seq_next_state(test_seq);
test_seq_sleep(test_seq, 2);
DB_LOCK lock_b_l;
r = db_env->lock_get(db_env, locker_b, 0, &object_l, DB_LOCK_WRITE, &lock_b_l); assert(r == DB_LOCK_DEADLOCK);
r = db_env->lock_put(db_env, &lock_b_m); assert(r == 0);
r = db_env->lock_id_free(db_env, locker_b); assert(r == 0);
return arg;
}
static void simple_deadlock(DB_ENV *db_env) {
int r;
struct test_seq test_seq; test_seq_init(&test_seq);
toku_pthread_t tid_a;
struct locker_args args_a = { db_env, &test_seq };
r = toku_pthread_create(&tid_a, NULL, run_locker_a, &args_a); assert(r == 0);
toku_pthread_t tid_b;
struct locker_args args_b = { db_env, &test_seq };
r = toku_pthread_create(&tid_b, NULL, run_locker_b, &args_b); assert(r == 0);
while (1) {
sleep(10);
int rejected = 0;
r = db_env->lock_detect(db_env, 0, DB_LOCK_YOUNGEST, &rejected); assert(r == 0);
if (verbose)
printf("%s %d\n", __FUNCTION__, rejected);
if (rejected == 0)
break;
}
void *ret = NULL;
r = toku_pthread_join(tid_a, &ret); assert(r == 0);
r = toku_pthread_join(tid_b, &ret); assert(r == 0);
test_seq_destroy(&test_seq);
}
int test_main(int argc, char * const argv[]) {
uint64_t cachesize = 0;
int do_txn = 1;
#if defined(USE_TDB)
char *db_env_dir = "dir." __FILE__ ".tokudb";
#elif defined(USE_BDB)
char *db_env_dir = "dir." __FILE__ ".bdb";
#else
#error
#endif
int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
// parse_args(argc, argv);
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;
}
assert(0);
}
// setup env
int r;
char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
r = system(rm_cmd); assert(r == 0);
r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
DB_ENV *db_env = NULL;
r = db_env_create(&db_env, 0); assert(r == 0);
if (cachesize) {
const u_int64_t gig = 1 << 30;
r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
}
if (!do_txn)
db_env_open_flags &= ~(DB_INIT_TXN | DB_INIT_LOG);
r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
// run test
simple_deadlock(db_env);
// close env
r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
return 0;
}
// verify that a simle write lock deadlock is detected by the BDB locker
// A write locks L
// B write locks M
// A tries to write lock M, gets DB_LOCK_NOTGRANTED
// B tries to write lock L, gets DB_LOCK_NOTGRANTED
#include "test.h"
static void simple_deadlock(DB_ENV *db_env) {
int r;
u_int32_t locker_a;
r = db_env->lock_id(db_env, &locker_a); assert(r == 0);
u_int32_t locker_b;
r = db_env->lock_id(db_env, &locker_b); assert(r == 0);
DBT object_l = { .data = "L", .size = 1 };
DBT object_m = { .data = "M", .size = 1 };
DB_LOCK lock_a_l;
r = db_env->lock_get(db_env, locker_a, DB_LOCK_NOWAIT, &object_l, DB_LOCK_WRITE, &lock_a_l); assert(r == 0);
DB_LOCK lock_b_m;
r = db_env->lock_get(db_env, locker_b, DB_LOCK_NOWAIT, &object_m, DB_LOCK_WRITE, &lock_b_m); assert(r == 0);
DB_LOCK lock_a_m;
r = db_env->lock_get(db_env, locker_a, DB_LOCK_NOWAIT, &object_m, DB_LOCK_WRITE, &lock_a_m); assert(r == DB_LOCK_NOTGRANTED);
DB_LOCK lock_b_l;
r = db_env->lock_get(db_env, locker_b, DB_LOCK_NOWAIT, &object_l, DB_LOCK_WRITE, &lock_b_l); assert(r == DB_LOCK_NOTGRANTED);
r = db_env->lock_put(db_env, &lock_a_l); assert(r == 0);
r = db_env->lock_put(db_env, &lock_b_m); assert(r == 0);
r = db_env->lock_id_free(db_env, locker_a); assert(r == 0);
r = db_env->lock_id_free(db_env, locker_b); assert(r == 0);
}
int test_main(int argc, char * const argv[]) {
uint64_t cachesize = 0;
int do_txn = 1;
#if defined(USE_TDB)
char *db_env_dir = "dir." __FILE__ ".tokudb";
#elif defined(USE_BDB)
char *db_env_dir = "dir." __FILE__ ".bdb";
#else
#error
#endif
int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
// parse_args(argc, argv);
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;
}
assert(0);
}
// setup env
int r;
char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
r = system(rm_cmd); assert(r == 0);
r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
DB_ENV *db_env = NULL;
r = db_env_create(&db_env, 0); assert(r == 0);
if (cachesize) {
const u_int64_t gig = 1 << 30;
r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
}
if (!do_txn)
db_env_open_flags &= ~(DB_INIT_TXN | DB_INIT_LOG);
r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
#if 0 && defined(USE_BDB)
r = db_env->set_lk_detect(db_env, DB_LOCK_YOUNGEST); assert(r == 0);
#endif
// run test
simple_deadlock(db_env);
// close env
r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
return 0;
}
// verify that the BDB locker can detect deadlocks on the fly and allow
// the deadlock to be unwound by the deadlocked threads. we use the
// set_lk_detect function to force the locker to check for deadlocks.
//
// A write locks L
// B write locks M
// A tries to write lock M, gets blocked
// B tries to write lock L, gets DEADLOCK error
// B releases its lock on M
// A resumes
#include "test.h"
#include "toku_pthread.h"
struct test_seq {
int state;
toku_pthread_mutex_t lock;
toku_pthread_cond_t cv;
};
static void test_seq_init(struct test_seq *seq) {
seq->state = 0;
int r;
r = toku_pthread_mutex_init(&seq->lock, NULL); assert(r == 0);
r = toku_pthread_cond_init(&seq->cv, NULL); assert(r == 0);
}
static void test_seq_destroy(struct test_seq *seq) {
int r;
r = toku_pthread_mutex_destroy(&seq->lock); assert(r == 0);
r = toku_pthread_cond_destroy(&seq->cv); assert(r == 0);
}
static void test_seq_sleep(struct test_seq *seq, int new_state) {
int r;
r = toku_pthread_mutex_lock(&seq->lock); assert(r == 0);
while (seq->state != new_state) {
r = toku_pthread_cond_wait(&seq->cv, &seq->lock); assert(r == 0);
}
r = toku_pthread_mutex_unlock(&seq->lock); assert(r == 0);
}
static void test_seq_next_state(struct test_seq *seq) {
int r;
r = toku_pthread_mutex_lock(&seq->lock);
seq->state++;
r = toku_pthread_cond_broadcast(&seq->cv); assert(r == 0);
r = toku_pthread_mutex_unlock(&seq->lock); assert(r == 0);
}
struct locker_args {
DB_ENV *db_env;
struct test_seq *test_seq;
};
static void *run_locker_a(void *arg) {
struct locker_args *locker_args = (struct locker_args *) arg;
DB_ENV *db_env = locker_args->db_env;
struct test_seq *test_seq = locker_args->test_seq;
int r;
u_int32_t locker_a;
r = db_env->lock_id(db_env, &locker_a); assert(r == 0);
DBT object_l = { .data = "L", .size = 1 };
DBT object_m = { .data = "M", .size = 1 };
test_seq_sleep(test_seq, 0);
DB_LOCK lock_a_l;
r = db_env->lock_get(db_env, locker_a, DB_LOCK_NOWAIT, &object_l, DB_LOCK_WRITE, &lock_a_l); assert(r == 0);
test_seq_next_state(test_seq);
test_seq_sleep(test_seq, 2);
DB_LOCK lock_a_m;
r = db_env->lock_get(db_env, locker_a, 0, &object_m, DB_LOCK_WRITE, &lock_a_m); assert(r == 0);
r = db_env->lock_put(db_env, &lock_a_l); assert(r == 0);
r = db_env->lock_put(db_env, &lock_a_m); assert(r == 0);
r = db_env->lock_id_free(db_env, locker_a); assert(r == 0);
return arg;
}
static void *run_locker_b(void *arg) {
struct locker_args *locker_args = (struct locker_args *) arg;
DB_ENV *db_env = locker_args->db_env;
struct test_seq *test_seq = locker_args->test_seq;
int r;
u_int32_t locker_b;
r = db_env->lock_id(db_env, &locker_b); assert(r == 0);
DBT object_l = { .data = "L", .size = 1 };
DBT object_m = { .data = "M", .size = 1 };
test_seq_sleep(test_seq, 1);
DB_LOCK lock_b_m;
r = db_env->lock_get(db_env, locker_b, DB_LOCK_NOWAIT, &object_m, DB_LOCK_WRITE, &lock_b_m); assert(r == 0);
test_seq_next_state(test_seq);
test_seq_sleep(test_seq, 2);
DB_LOCK lock_b_l;
r = db_env->lock_get(db_env, locker_b, 0, &object_l, DB_LOCK_WRITE, &lock_b_l); assert(r == DB_LOCK_DEADLOCK);
r = db_env->lock_put(db_env, &lock_b_m); assert(r == 0);
r = db_env->lock_id_free(db_env, locker_b); assert(r == 0);
return arg;
}
static void simple_deadlock(DB_ENV *db_env) {
int r;
struct test_seq test_seq; test_seq_init(&test_seq);
toku_pthread_t tid_a;
struct locker_args args_a = { db_env, &test_seq };
r = toku_pthread_create(&tid_a, NULL, run_locker_a, &args_a); assert(r == 0);
toku_pthread_t tid_b;
struct locker_args args_b = { db_env, &test_seq };
r = toku_pthread_create(&tid_b, NULL, run_locker_b, &args_b); assert(r == 0);
void *ret = NULL;
r = toku_pthread_join(tid_a, &ret); assert(r == 0);
r = toku_pthread_join(tid_b, &ret); assert(r == 0);
test_seq_destroy(&test_seq);
}
int test_main(int argc, char * const argv[]) {
uint64_t cachesize = 0;
int do_txn = 1;
#if defined(USE_TDB)
char *db_env_dir = "dir." __FILE__ ".tokudb";
#elif defined(USE_BDB)
char *db_env_dir = "dir." __FILE__ ".bdb";
#else
#error
#endif
int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
// parse_args(argc, argv);
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;
}
assert(0);
}
// setup env
int r;
char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
r = system(rm_cmd); assert(r == 0);
r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
DB_ENV *db_env = NULL;
r = db_env_create(&db_env, 0); assert(r == 0);
if (cachesize) {
const u_int64_t gig = 1 << 30;
r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
}
if (!do_txn)
db_env_open_flags &= ~(DB_INIT_TXN | DB_INIT_LOG);
r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
#if defined(USE_BDB)
r = db_env->set_lk_detect(db_env, DB_LOCK_YOUNGEST); assert(r == 0);
#endif
// run test
simple_deadlock(db_env);
// close env
r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
return 0;
}
// verify that cursor deletes without write locks can detect deadlocks.
#include "test.h"
#include "toku_pthread.h"
static void populate(DB_ENV *db_env, DB *db, uint64_t nrows) {
int r;
DB_TXN *txn = NULL;
r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
for (uint64_t i = 0; i < nrows; i++) {
uint64_t k = htonl(i);
uint64_t v = i;
DBT key = { .data = &k, .size = sizeof k };
DBT val = { .data = &v, .size = sizeof v };
r = db->put(db, txn, &key, &val, 0); assert(r == 0);
}
r = txn->commit(txn, 0); assert(r == 0);
}
struct my_callback_context {
DBT key;
DBT val;
};
#if TOKUDB
static void copy_dbt(DBT *dest, DBT const *src) {
assert(dest->flags == DB_DBT_REALLOC);
dest->size = src->size;
dest->data = toku_xrealloc(dest->data, dest->size);
memcpy(dest->data, src->data, dest->size);
}
static int blocking_c_del_callback(DBT const *a UU(), DBT const *b UU(), void *e UU()) {
DBT const *found_key = a;
DBT const *found_val = b;
struct my_callback_context *context = (struct my_callback_context *) e;
copy_dbt(&context->key, found_key);
copy_dbt(&context->val, found_val);
return 0;
}
#endif
static void blocking_c_del(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime) {
int r;
struct my_callback_context context;
context.key = (DBT) { .data = NULL, .size = 0, .flags = DB_DBT_REALLOC };
context.val = (DBT) { .data = NULL, .size = 0, .flags = DB_DBT_REALLOC };
for (uint64_t i = 0; i < nrows; i++) {
DB_TXN *txn = NULL;
r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
DBC *cursor = NULL;
r = db->cursor(db, txn, &cursor, 0); assert(r == 0);
uint64_t k = htonl(i);
DBT key = { .data = &k, .size = sizeof k };
#if TOKUDB
r = cursor->c_getf_set(cursor, 0, &key, blocking_c_del_callback, &context);
#else
r = cursor->c_get(cursor, &key, &context.val, DB_SET);
#endif
assert(r == 0 || r == DB_NOTFOUND);
if (r == 0) {
usleep(sleeptime);
if (verbose) {
uint64_t kk;
#if TOKUDB
assert(context.key.size == sizeof kk);
memcpy(&kk, context.key.data, sizeof kk);
#else
assert(key.size == sizeof kk);
memcpy(&kk, key.data, sizeof kk);
#endif
printf("%lu deleting %lu\n", toku_pthread_self(), (long unsigned) htonl(kk));
}
r = cursor->c_del(cursor, 0);
assert(r == 0 || r == DB_LOCK_DEADLOCK);
}
{ int rr = cursor->c_close(cursor); assert(rr == 0); }
if (r == 0) {
if (verbose) printf("%lu commit\n", toku_pthread_self());
r = txn->commit(txn, 0);
} else {
if (verbose) printf("%lu abort\n", toku_pthread_self());
r = txn->abort(txn);
}
assert(r == 0);
if (verbose)
printf("%lu %lu\n", toku_pthread_self(), i);
}
toku_free(context.key.data);
toku_free(context.val.data);
}
struct blocking_c_del_args {
DB_ENV *db_env;
DB *db;
uint64_t nrows;
long sleeptime;
};
static void *blocking_c_del_thread(void *arg) {
struct blocking_c_del_args *a = (struct blocking_c_del_args *) arg;
blocking_c_del(a->db_env, a->db, a->nrows, a->sleeptime);
return arg;
}
static void run_test(DB_ENV *db_env, DB *db, int nthreads, uint64_t nrows, long sleeptime) {
int r;
toku_pthread_t tids[nthreads];
for (int i = 0; i < nthreads-1; i++) {
struct blocking_c_del_args a = { db_env, db, nrows, sleeptime };
r = toku_pthread_create(&tids[i], NULL, blocking_c_del_thread, &a); assert(r == 0);
}
blocking_c_del(db_env, db, nrows, sleeptime);
for (int i = 0; i < nthreads-1; i++) {
void *ret;
r = toku_pthread_join(tids[i], &ret); assert(r == 0);
}
}
int test_main(int argc, char * const argv[]) {
uint64_t cachesize = 0;
uint32_t pagesize = 0;
uint64_t nrows = 10;
int nthreads = 2;
long sleeptime = 100000;
#if defined(USE_TDB)
char *db_env_dir = "dir." __FILE__ ".tokudb";
#elif defined(USE_BDB)
char *db_env_dir = "dir." __FILE__ ".bdb";
#else
#error
#endif
char *db_filename = "test.db";
int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
// parse_args(argc, argv);
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], "--nrows") == 0 && i+1 < argc) {
nrows = atoll(argv[++i]);
continue;
}
if (strcmp(argv[i], "--nthreads") == 0 && i+1 < argc) {
nthreads = atoi(argv[++i]);
continue;
}
if (strcmp(argv[i], "--sleeptime") == 0 && i+1 < argc) {
sleeptime = atol(argv[++i]);
continue;
}
assert(0);
}
// setup env
int r;
char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
r = system(rm_cmd); assert(r == 0);
r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
DB_ENV *db_env = NULL;
r = db_env_create(&db_env, 0); assert(r == 0);
if (cachesize) {
const u_int64_t gig = 1 << 30;
r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
}
r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
#if TOKUDB
r = db_env->set_lock_timeout(db_env, 30 * 1000000); assert(r == 0);
#else
r = db_env->set_lk_detect(db_env, DB_LOCK_YOUNGEST); assert(r == 0);
#endif
// create the db
DB *db = NULL;
r = db_create(&db, db_env, 0); assert(r == 0);
DB_TXN *create_txn = NULL;
r = db_env->txn_begin(db_env, NULL, &create_txn, 0); assert(r == 0);
if (pagesize) {
r = db->set_pagesize(db, pagesize); assert(r == 0);
}
r = db->open(db, create_txn, db_filename, NULL, DB_BTREE, DB_CREATE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
r = create_txn->commit(create_txn, 0); assert(r == 0);
// populate the db
populate(db_env, db, nrows);
run_test(db_env, db, nthreads, nrows, sleeptime);
// close env
r = db->close(db, 0); assert(r == 0); db = NULL;
r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
return 0;
}
// verify that cursor deletes with write locking cause transactions with lock conflicts to
// suspend the conflicting threads.
#include "test.h"
#include "toku_pthread.h"
static void populate(DB_ENV *db_env, DB *db, uint64_t nrows) {
int r;
DB_TXN *txn = NULL;
r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
for (uint64_t i = 0; i < nrows; i++) {
uint64_t k = htonl(i);
uint64_t v = i;
DBT key = { .data = &k, .size = sizeof k };
DBT val = { .data = &v, .size = sizeof v };
r = db->put(db, txn, &key, &val, 0); assert(r == 0);
}
r = txn->commit(txn, 0); assert(r == 0);
}
struct my_callback_context {
DBT key;
DBT val;
};
#if TOKUDB
static void copy_dbt(DBT *dest, DBT const *src) {
assert(dest->flags == DB_DBT_REALLOC);
dest->size = src->size;
dest->data = toku_xrealloc(dest->data, dest->size);
memcpy(dest->data, src->data, dest->size);
}
static int blocking_c_del_callback(DBT const *a UU(), DBT const *b UU(), void *e UU()) {
DBT const *found_key = a;
DBT const *found_val = b;
struct my_callback_context *context = (struct my_callback_context *) e;
copy_dbt(&context->key, found_key);
copy_dbt(&context->val, found_val);
return 0;
}
#endif
static void blocking_c_del(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime) {
int r;
struct my_callback_context context;
context.key = (DBT) { .data = NULL, .size = 0, .flags = DB_DBT_REALLOC };
context.val = (DBT) { .data = NULL, .size = 0, .flags = DB_DBT_REALLOC };
for (uint64_t i = 0; i < nrows; i++) {
DB_TXN *txn = NULL;
r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
DBC *cursor = NULL;
r = db->cursor(db, txn, &cursor, 0); assert(r == 0);
uint64_t k = htonl(i);
DBT key = { .data = &k, .size = sizeof k };
#if TOKUDB
r = cursor->c_getf_set(cursor, DB_RMW, &key, blocking_c_del_callback, &context);
#else
r = cursor->c_get(cursor, &key, &context.val, DB_SET + DB_RMW);
#endif
assert(r == 0 || r == DB_NOTFOUND);
if (r == 0) {
usleep(sleeptime);
if (verbose) {
uint64_t kk;
#if TOKUDB
assert(context.key.size == sizeof kk);
memcpy(&kk, context.key.data, sizeof kk);
#else
assert(key.size == sizeof kk);
memcpy(&kk, key.data, sizeof kk);
#endif
printf("%lu deleting %lu\n", toku_pthread_self(), (long unsigned) htonl(kk));
}
r = cursor->c_del(cursor, 0);
assert(r == 0 || r == DB_LOCK_DEADLOCK);
}
{ int rr = cursor->c_close(cursor); assert(rr == 0); }
if (r == 0)
r = txn->commit(txn, 0);
else
r = txn->abort(txn);
assert(r == 0);
if (verbose)
printf("%lu %lu\n", toku_pthread_self(), i);
}
toku_free(context.key.data);
toku_free(context.val.data);
}
struct blocking_c_del_args {
DB_ENV *db_env;
DB *db;
uint64_t nrows;
long sleeptime;
};
static void *blocking_c_del_thread(void *arg) {
struct blocking_c_del_args *a = (struct blocking_c_del_args *) arg;
blocking_c_del(a->db_env, a->db, a->nrows, a->sleeptime);
return arg;
}
static void run_test(DB_ENV *db_env, DB *db, int nthreads, uint64_t nrows, long sleeptime) {
int r;
toku_pthread_t tids[nthreads];
for (int i = 0; i < nthreads-1; i++) {
struct blocking_c_del_args a = { db_env, db, nrows, sleeptime };
r = toku_pthread_create(&tids[i], NULL, blocking_c_del_thread, &a); assert(r == 0);
}
blocking_c_del(db_env, db, nrows, sleeptime);
for (int i = 0; i < nthreads-1; i++) {
void *ret;
r = toku_pthread_join(tids[i], &ret); assert(r == 0);
}
}
int test_main(int argc, char * const argv[]) {
uint64_t cachesize = 0;
uint32_t pagesize = 0;
uint64_t nrows = 10;
int nthreads = 2;
long sleeptime = 100000;
#if defined(USE_TDB)
char *db_env_dir = "dir." __FILE__ ".tokudb";
#elif defined(USE_BDB)
char *db_env_dir = "dir." __FILE__ ".bdb";
#else
#error
#endif
char *db_filename = "test.db";
int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
// parse_args(argc, argv);
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], "--nrows") == 0 && i+1 < argc) {
nrows = atoll(argv[++i]);
continue;
}
if (strcmp(argv[i], "--nthreads") == 0 && i+1 < argc) {
nthreads = atoi(argv[++i]);
continue;
}
if (strcmp(argv[i], "--sleeptime") == 0 && i+1 < argc) {
sleeptime = atol(argv[++i]);
continue;
}
assert(0);
}
// setup env
int r;
char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
r = system(rm_cmd); assert(r == 0);
r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
DB_ENV *db_env = NULL;
r = db_env_create(&db_env, 0); assert(r == 0);
if (cachesize) {
const u_int64_t gig = 1 << 30;
r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
}
r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
#if TOKUDB
r = db_env->set_lock_timeout(db_env, 30 * 1000000); assert(r == 0);
#else
r = db_env->set_lk_detect(db_env, DB_LOCK_YOUNGEST); assert(r == 0);
#endif
// create the db
DB *db = NULL;
r = db_create(&db, db_env, 0); assert(r == 0);
DB_TXN *create_txn = NULL;
r = db_env->txn_begin(db_env, NULL, &create_txn, 0); assert(r == 0);
if (pagesize) {
r = db->set_pagesize(db, pagesize); assert(r == 0);
}
r = db->open(db, create_txn, db_filename, NULL, DB_BTREE, DB_CREATE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
r = create_txn->commit(create_txn, 0); assert(r == 0);
// populate the db
populate(db_env, db, nrows);
run_test(db_env, db, nthreads, nrows, sleeptime);
// close env
r = db->close(db, 0); assert(r == 0); db = NULL;
r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
return 0;
}
// verify that cursor first on an empty tree with a write lock suspends the conflicting threads.
#include "test.h"
#include "toku_pthread.h"
struct my_callback_context {
DBT key;
DBT val;
};
#if TOKUDB
static void copy_dbt(DBT *dest, DBT const *src) {
assert(dest->flags == DB_DBT_REALLOC);
dest->size = src->size;
dest->data = toku_xrealloc(dest->data, dest->size);
memcpy(dest->data, src->data, dest->size);
}
static int blocking_first_callback(DBT const *a UU(), DBT const *b UU(), void *e UU()) {
DBT const *found_key = a;
DBT const *found_val = b;
struct my_callback_context *context = (struct my_callback_context *) e;
copy_dbt(&context->key, found_key);
copy_dbt(&context->val, found_val);
return 0;
}
#endif
static void blocking_first(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime) {
int r;
struct my_callback_context context;
context.key = (DBT) { .data = NULL, .size = 0, .flags = DB_DBT_REALLOC };
context.val = (DBT) { .data = NULL, .size = 0, .flags = DB_DBT_REALLOC };
for (uint64_t i = 0; i < nrows; i++) {
DB_TXN *txn = NULL;
r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
DBC *cursor = NULL;
r = db->cursor(db, txn, &cursor, 0); assert(r == 0); // get a write lock on -inf +inf
#if TOKUDB
r = cursor->c_getf_first(cursor, DB_RMW, blocking_first_callback, &context); assert(r == DB_NOTFOUND);
#else
r = cursor->c_get(cursor, &context.key, &context.val, DB_FIRST + DB_RMW); assert(r == DB_NOTFOUND);
#endif
usleep(sleeptime);
r = cursor->c_close(cursor); assert(r == 0);
r = txn->commit(txn, 0); assert(r == 0);
if (verbose)
printf("%lu %lu\n", toku_pthread_self(), i);
}
toku_free(context.key.data);
toku_free(context.val.data);
}
struct blocking_first_args {
DB_ENV *db_env;
DB *db;
uint64_t nrows;
long sleeptime;
};
static void *blocking_first_thread(void *arg) {
struct blocking_first_args *a = (struct blocking_first_args *) arg;
blocking_first(a->db_env, a->db, a->nrows, a->sleeptime);
return arg;
}
static void run_test(DB_ENV *db_env, DB *db, int nthreads, uint64_t nrows, long sleeptime) {
int r;
toku_pthread_t tids[nthreads];
for (int i = 0; i < nthreads-1; i++) {
struct blocking_first_args a = { db_env, db, nrows, sleeptime };
r = toku_pthread_create(&tids[i], NULL, blocking_first_thread, &a); assert(r == 0);
}
blocking_first(db_env, db, nrows, sleeptime);
for (int i = 0; i < nthreads-1; i++) {
void *ret;
r = toku_pthread_join(tids[i], &ret); assert(r == 0);
}
}
int test_main(int argc, char * const argv[]) {
uint64_t cachesize = 0;
uint32_t pagesize = 0;
uint64_t nrows = 10;
int nthreads = 2;
long sleeptime = 100000;
#if defined(USE_TDB)
char *db_env_dir = "dir." __FILE__ ".tokudb";
#elif defined(USE_BDB)
char *db_env_dir = "dir." __FILE__ ".bdb";
#else
#error
#endif
char *db_filename = "test.db";
int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
// parse_args(argc, argv);
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], "--nrows") == 0 && i+1 < argc) {
nrows = atoll(argv[++i]);
continue;
}
if (strcmp(argv[i], "--nthreads") == 0 && i+1 < argc) {
nthreads = atoi(argv[++i]);
continue;
}
if (strcmp(argv[i], "--sleeptime") == 0 && i+1 < argc) {
sleeptime = atol(argv[++i]);
continue;
}
assert(0);
}
// setup env
int r;
char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
r = system(rm_cmd); assert(r == 0);
r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
DB_ENV *db_env = NULL;
r = db_env_create(&db_env, 0); assert(r == 0);
if (cachesize) {
const u_int64_t gig = 1 << 30;
r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
}
r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
#if TOKUDB
r = db_env->set_lock_timeout(db_env, 30 * 1000000); assert(r == 0);
#endif
// create the db
DB *db = NULL;
r = db_create(&db, db_env, 0); assert(r == 0);
DB_TXN *create_txn = NULL;
r = db_env->txn_begin(db_env, NULL, &create_txn, 0); assert(r == 0);
if (pagesize) {
r = db->set_pagesize(db, pagesize); assert(r == 0);
}
r = db->open(db, create_txn, db_filename, NULL, DB_BTREE, DB_CREATE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
r = create_txn->commit(create_txn, 0); assert(r == 0);
run_test(db_env, db, nthreads, nrows, sleeptime);
// close env
r = db->close(db, 0); assert(r == 0); db = NULL;
r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
return 0;
}
// verify that cursor first with a write lock suspends the conflicting threads.
#include "test.h"
#include "toku_pthread.h"
static void populate(DB_ENV *db_env, DB *db, uint64_t nrows) {
int r;
DB_TXN *txn = NULL;
r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
for (uint64_t i = 0; i < nrows; i++) {
uint64_t k = htonl(i);
uint64_t v = i;
DBT key = { .data = &k, .size = sizeof k };
DBT val = { .data = &v, .size = sizeof v };
r = db->put(db, txn, &key, &val, 0); assert(r == 0);
}
r = txn->commit(txn, 0); assert(r == 0);
}
struct my_callback_context {
DBT key;
DBT val;
};
#if TOKUDB
static void copy_dbt(DBT *dest, DBT const *src) {
assert(dest->flags == DB_DBT_REALLOC);
dest->size = src->size;
dest->data = toku_xrealloc(dest->data, dest->size);
memcpy(dest->data, src->data, dest->size);
}
static int blocking_first_callback(DBT const *a UU(), DBT const *b UU(), void *e UU()) {
DBT const *found_key = a;
DBT const *found_val = b;
struct my_callback_context *context = (struct my_callback_context *) e;
copy_dbt(&context->key, found_key);
copy_dbt(&context->val, found_val);
return 0;
}
#endif
static void blocking_first(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime) {
int r;
struct my_callback_context context;
context.key = (DBT) { .data = NULL, .size = 0, .flags = DB_DBT_REALLOC };
context.val = (DBT) { .data = NULL, .size = 0, .flags = DB_DBT_REALLOC };
for (uint64_t i = 0; i < nrows; i++) {
DB_TXN *txn = NULL;
r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
DBC *cursor = NULL;
r = db->cursor(db, txn, &cursor, 0); assert(r == 0); // get a write lock on -inf ... 0
#if TOKUDB
r = cursor->c_getf_first(cursor, DB_RMW, blocking_first_callback, &context); assert(r == 0);
#else
r = cursor->c_get(cursor, &context.key, &context.val, DB_FIRST + DB_RMW); assert(r == 0);
#endif
usleep(sleeptime);
r = cursor->c_close(cursor); assert(r == 0);
r = txn->commit(txn, 0); assert(r == 0);
if (verbose)
printf("%lu %lu\n", toku_pthread_self(), i);
}
toku_free(context.key.data);
toku_free(context.val.data);
}
struct blocking_first_args {
DB_ENV *db_env;
DB *db;
uint64_t nrows;
long sleeptime;
};
static void *blocking_first_thread(void *arg) {
struct blocking_first_args *a = (struct blocking_first_args *) arg;
blocking_first(a->db_env, a->db, a->nrows, a->sleeptime);
return arg;
}
static void run_test(DB_ENV *db_env, DB *db, int nthreads, uint64_t nrows, long sleeptime) {
int r;
toku_pthread_t tids[nthreads];
for (int i = 0; i < nthreads-1; i++) {
struct blocking_first_args a = { db_env, db, nrows, sleeptime };
r = toku_pthread_create(&tids[i], NULL, blocking_first_thread, &a); assert(r == 0);
}
blocking_first(db_env, db, nrows, sleeptime);
for (int i = 0; i < nthreads-1; i++) {
void *ret;
r = toku_pthread_join(tids[i], &ret); assert(r == 0);
}
}
int test_main(int argc, char * const argv[]) {
uint64_t cachesize = 0;
uint32_t pagesize = 0;
uint64_t nrows = 10;
int nthreads = 2;
long sleeptime = 100000;
#if defined(USE_TDB)
char *db_env_dir = "dir." __FILE__ ".tokudb";
#elif defined(USE_BDB)
char *db_env_dir = "dir." __FILE__ ".bdb";
#else
#error
#endif
char *db_filename = "test.db";
int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
// parse_args(argc, argv);
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], "--nrows") == 0 && i+1 < argc) {
nrows = atoll(argv[++i]);
continue;
}
if (strcmp(argv[i], "--nthreads") == 0 && i+1 < argc) {
nthreads = atoi(argv[++i]);
continue;
}
if (strcmp(argv[i], "--sleeptime") == 0 && i+1 < argc) {
sleeptime = atol(argv[++i]);
continue;
}
assert(0);
}
// setup env
int r;
char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
r = system(rm_cmd); assert(r == 0);
r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
DB_ENV *db_env = NULL;
r = db_env_create(&db_env, 0); assert(r == 0);
if (cachesize) {
const u_int64_t gig = 1 << 30;
r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
}
r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
#if TOKUDB
r = db_env->set_lock_timeout(db_env, 30 * 1000000); assert(r == 0);
#endif
// create the db
DB *db = NULL;
r = db_create(&db, db_env, 0); assert(r == 0);
DB_TXN *create_txn = NULL;
r = db_env->txn_begin(db_env, NULL, &create_txn, 0); assert(r == 0);
if (pagesize) {
r = db->set_pagesize(db, pagesize); assert(r == 0);
}
r = db->open(db, create_txn, db_filename, NULL, DB_BTREE, DB_CREATE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
r = create_txn->commit(create_txn, 0); assert(r == 0);
// populate the db
populate(db_env, db, nrows);
run_test(db_env, db, nthreads, nrows, sleeptime);
// close env
r = db->close(db, 0); assert(r == 0); db = NULL;
r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
return 0;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -124,6 +124,7 @@ int toku_ydb_lock_destroy(void);
void toku_ydb_lock(void);
void toku_ydb_unlock(void);
void toku_ydb_unlock_and_yield(unsigned long useconds);
toku_pthread_mutex_t *toku_ydb_mutex(void);
void toku_ydb_lock_get_status(SCHEDULE_STATUS statp);
......
This diff is collapsed.
......@@ -121,7 +121,6 @@ typedef struct memory_status {
void toku_memory_get_status(MEMORY_STATUS s);
#if defined(__cplusplus) || defined(__cilkplusplus)
}
#endif
......
......@@ -68,7 +68,6 @@ static inline struct toku_list *toku_list_pop_head(struct toku_list *head) {
return toku_list;
}
//What does this do?
static inline void toku_list_move(struct toku_list *newhead, struct toku_list *oldhead) {
struct toku_list *first = oldhead->next;
struct toku_list *last = oldhead->prev;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment