mirror of
https://github.com/MariaDB/server.git
synced 2025-01-22 06:44:16 +01:00
#3452 new row locking APIs refs[t:3452]
git-svn-id: file:///svn/toku/tokudb@35392 c7de825b-a66e-492c-adef-691d508d4ae1
This commit is contained in:
parent
0be8bf6210
commit
dc4960612a
81 changed files with 7716 additions and 309 deletions
|
@ -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)
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
/* TODO: During integration, make sure we first verify the NULL CONSISTENCY,
|
||||
(return EINVAL if necessary) before making lock tree calls. */
|
||||
|
||||
#if DEBUG
|
||||
static int toku_lt_debug = 0;
|
||||
#endif
|
||||
|
||||
static inline int lt_panic(toku_lock_tree *tree, int r) {
|
||||
return tree->panic(tree->db, r);
|
||||
|
@ -41,6 +44,40 @@ static const DBT __toku_lt_neg_infinity;
|
|||
const DBT* const toku_lt_infinity = &__toku_lt_infinity;
|
||||
const DBT* const toku_lt_neg_infinity = &__toku_lt_neg_infinity;
|
||||
|
||||
static toku_pthread_mutex_t *
|
||||
toku_ltm_get_mutex(toku_ltm *ltm) {
|
||||
toku_pthread_mutex_t *lock = ltm->use_lock;
|
||||
if (lock == NULL)
|
||||
lock = <m->lock;
|
||||
return lock;
|
||||
}
|
||||
|
||||
void
|
||||
toku_ltm_set_mutex(toku_ltm *ltm, toku_pthread_mutex_t *use_lock) {
|
||||
ltm->use_lock = use_lock;
|
||||
}
|
||||
|
||||
static void
|
||||
toku_ltm_init_mutex(toku_ltm *ltm) {
|
||||
int r = toku_pthread_mutex_init(<m->lock, NULL); assert_zero(r);
|
||||
ltm->use_lock = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
toku_ltm_destroy_mutex(toku_ltm *ltm) {
|
||||
int r = toku_pthread_mutex_destroy(<m->lock); assert_zero(r);
|
||||
}
|
||||
|
||||
void
|
||||
toku_ltm_lock_mutex(toku_ltm *ltm) {
|
||||
int r = toku_pthread_mutex_lock(toku_ltm_get_mutex(ltm)); assert_zero(r);
|
||||
}
|
||||
|
||||
void
|
||||
toku_ltm_unlock_mutex(toku_ltm *ltm) {
|
||||
int r = toku_pthread_mutex_unlock(toku_ltm_get_mutex(ltm)); assert_zero(r);
|
||||
}
|
||||
|
||||
char* toku_lt_strerror(TOKU_LT_ERROR r) {
|
||||
if (r >= 0)
|
||||
return strerror(r);
|
||||
|
@ -64,12 +101,14 @@ static inline int infinite_compare(const DBT* a, const DBT* b) {
|
|||
}
|
||||
|
||||
static inline BOOL lt_is_infinite(const DBT* p) {
|
||||
BOOL r;
|
||||
if (p == toku_lt_infinity || p == toku_lt_neg_infinity) {
|
||||
DBT* dbt = (DBT*)p;
|
||||
assert(!dbt->data && !dbt->size);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
r = TRUE;
|
||||
} else
|
||||
r = FALSE;
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Verifies that NULL data and size are consistent.
|
||||
|
@ -172,6 +211,7 @@ int toku_ltm_create(toku_ltm** pmgr,
|
|||
if (!tmp_mgr->idlth) {
|
||||
r = ENOMEM; goto cleanup;
|
||||
}
|
||||
toku_ltm_init_mutex(tmp_mgr);
|
||||
r = 0;
|
||||
*pmgr = tmp_mgr;
|
||||
cleanup:
|
||||
|
@ -204,6 +244,7 @@ int toku_ltm_close(toku_ltm* mgr) {
|
|||
}
|
||||
toku_lth_close(mgr->lth);
|
||||
toku_idlth_close(mgr->idlth);
|
||||
toku_ltm_destroy_mutex(mgr);
|
||||
mgr->free(mgr);
|
||||
|
||||
r = first_error;
|
||||
|
@ -1016,7 +1057,7 @@ static inline int lt_free_contents(toku_lock_tree* tree, toku_range_tree* rt, BO
|
|||
r = 0;
|
||||
toku_rt_clear(rt);
|
||||
}
|
||||
assert(r == 0);
|
||||
assert_zero(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -1303,6 +1344,7 @@ int toku_lt_create(toku_lock_tree** ptree,
|
|||
r = toku_omt_create(&tmp_tree->dbs);
|
||||
if (r != 0)
|
||||
goto cleanup;
|
||||
toku_lock_request_tree_init(tmp_tree);
|
||||
|
||||
tmp_tree->ref_count = 1;
|
||||
*ptree = tmp_tree;
|
||||
|
@ -1419,6 +1461,7 @@ int toku_lt_close(toku_lock_tree* tree) {
|
|||
if (!tree) {
|
||||
r = EINVAL; goto cleanup;
|
||||
}
|
||||
toku_lock_request_tree_destroy(tree);
|
||||
r = toku_rt_close(tree->borderwrite);
|
||||
if (!first_error && r != 0)
|
||||
first_error = r;
|
||||
|
@ -1711,7 +1754,7 @@ static int lt_do_escalation(toku_lock_tree* lt) {
|
|||
|
||||
assert(toku_omt_size(lt->dbs) > 0); // there is at least one db associated with this locktree
|
||||
r = toku_omt_fetch(lt->dbs, 0, &dbv);
|
||||
assert(r == 0);
|
||||
assert_zero(r);
|
||||
db = dbv;
|
||||
lt_set_comparison_functions(lt, db);
|
||||
|
||||
|
@ -1791,6 +1834,7 @@ int toku_lt_acquire_range_read_lock(toku_lock_tree* tree, DB* db, TXNID txn,
|
|||
return r;
|
||||
}
|
||||
|
||||
|
||||
static int lt_try_acquire_range_write_lock(toku_lock_tree* tree,
|
||||
DB* db, TXNID txn,
|
||||
const DBT* key_left,
|
||||
|
@ -2084,11 +2128,16 @@ int toku_lt_unlock(toku_lock_tree* tree, TXNID txn) {
|
|||
if (!tree) {
|
||||
r = EINVAL; goto cleanup;
|
||||
}
|
||||
#if DEBUG
|
||||
if (toku_lt_debug)
|
||||
printf("%s:%u %lu\n", __FUNCTION__, __LINE__, txn);
|
||||
#endif
|
||||
r = lt_defer_unlocking_txn(tree, txn);
|
||||
if (r != 0)
|
||||
goto cleanup;
|
||||
if (toku_rth_is_empty(tree->txns_still_locked))
|
||||
lt_clear(tree);
|
||||
toku_lt_retry_lock_requests_locked(tree);
|
||||
r = 0;
|
||||
cleanup:
|
||||
return r;
|
||||
|
@ -2170,7 +2219,8 @@ void toku_lt_remove_db_ref(toku_lock_tree* tree, DB *db) {
|
|||
assert_zero(r);
|
||||
}
|
||||
|
||||
static void lt_verify(toku_lock_tree *lt) {
|
||||
static void
|
||||
lt_verify(toku_lock_tree *lt) {
|
||||
// verify the borderwrite tree
|
||||
toku_rt_verify(lt->borderwrite);
|
||||
|
||||
|
@ -2185,8 +2235,432 @@ static void lt_verify(toku_lock_tree *lt) {
|
|||
}
|
||||
}
|
||||
|
||||
void toku_lt_verify(toku_lock_tree *lt, DB *db) {
|
||||
void
|
||||
toku_lt_verify(toku_lock_tree *lt, DB *db) {
|
||||
lt_set_comparison_functions(lt, db);
|
||||
lt_verify(lt);
|
||||
lt_clear_comparison_functions(lt);
|
||||
}
|
||||
|
||||
static void
|
||||
toku_lock_request_init_wait(toku_lock_request *lock_request) {
|
||||
if (!lock_request->wait_initialized) {
|
||||
int r = toku_pthread_cond_init(&lock_request->wait, NULL); assert_zero(r);
|
||||
lock_request->wait_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
toku_lock_request_destroy_wait(toku_lock_request *lock_request) {
|
||||
if (lock_request->wait_initialized) {
|
||||
int r = toku_pthread_cond_destroy(&lock_request->wait); assert_zero(r);
|
||||
lock_request->wait_initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
toku_lock_request_default_init(toku_lock_request *lock_request) {
|
||||
lock_request->db = NULL;
|
||||
lock_request->txnid = 0;
|
||||
lock_request->key_left = lock_request->key_right = NULL;
|
||||
lock_request->key_left_copy = (DBT) { .data = NULL, .size = 0, .flags = DB_DBT_REALLOC };
|
||||
lock_request->key_right_copy = (DBT) { .data = NULL, .size = 0, .flags = DB_DBT_REALLOC };
|
||||
lock_request->state = LOCK_REQUEST_INIT;
|
||||
lock_request->complete_r = 0;
|
||||
lock_request->type = LOCK_REQUEST_UNKNOWN;
|
||||
lock_request->tree = NULL;
|
||||
lock_request->wait_initialized = false;
|
||||
}
|
||||
|
||||
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 lock_type) {
|
||||
assert(lock_request->state != LOCK_REQUEST_PENDING);
|
||||
lock_request->db = db;
|
||||
lock_request->txnid = txnid;
|
||||
lock_request->key_left = key_left;
|
||||
lock_request->key_right = key_right;
|
||||
lock_request->type = lock_type;
|
||||
lock_request->state = LOCK_REQUEST_INIT;
|
||||
}
|
||||
|
||||
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 lock_type) {
|
||||
toku_lock_request_default_init(lock_request);
|
||||
toku_lock_request_set(lock_request, db, txnid, key_left, key_right, lock_type);
|
||||
}
|
||||
|
||||
void
|
||||
toku_lock_request_destroy(toku_lock_request *lock_request) {
|
||||
if (lock_request->state == LOCK_REQUEST_PENDING)
|
||||
toku_lock_request_tree_delete(lock_request->tree, lock_request);
|
||||
toku_lock_request_destroy_wait(lock_request);
|
||||
toku_free(lock_request->key_left_copy.data);
|
||||
toku_free(lock_request->key_right_copy.data);
|
||||
}
|
||||
|
||||
static void
|
||||
toku_lock_request_complete(toku_lock_request *lock_request, int complete_r) {
|
||||
lock_request->state = LOCK_REQUEST_COMPLETE;
|
||||
lock_request->complete_r = complete_r;
|
||||
}
|
||||
|
||||
static const struct timeval max_timeval = { ~0, 0 };
|
||||
|
||||
int
|
||||
toku_lock_request_wait(toku_lock_request *lock_request, toku_lock_tree *tree, struct timeval *wait_time) {
|
||||
#if DEBUG
|
||||
if (toku_lt_debug)
|
||||
printf("%s:%u %lu\n", __FUNCTION__, __LINE__, lock_request->txnid);
|
||||
#endif
|
||||
int r = 0;
|
||||
if (wait_time && wait_time->tv_sec != max_timeval.tv_sec) {
|
||||
struct timeval now;
|
||||
r = gettimeofday(&now, NULL); assert_zero(r);
|
||||
long int sec = now.tv_sec + wait_time->tv_sec;
|
||||
long int usec = now.tv_usec + wait_time->tv_usec;
|
||||
long int d_sec = usec / 1000000;
|
||||
long int d_usec = usec % 1000000;
|
||||
struct timespec ts = { sec + d_sec, d_usec * 1000 };
|
||||
while (lock_request->state == LOCK_REQUEST_PENDING) {
|
||||
toku_lock_request_init_wait(lock_request);
|
||||
r = pthread_cond_timedwait(&lock_request->wait, toku_ltm_get_mutex(tree->mgr), &ts);
|
||||
assert(r == 0 || r == ETIMEDOUT);
|
||||
if (r == ETIMEDOUT && lock_request->state == LOCK_REQUEST_PENDING) {
|
||||
toku_lock_request_tree_delete(tree, lock_request);
|
||||
toku_lock_request_complete(lock_request, DB_LOCK_NOTGRANTED);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (lock_request->state == LOCK_REQUEST_PENDING) {
|
||||
toku_lock_request_init_wait(lock_request);
|
||||
r = toku_pthread_cond_wait(&lock_request->wait, toku_ltm_get_mutex(tree->mgr)); assert_zero(r);
|
||||
}
|
||||
}
|
||||
assert(lock_request->state == LOCK_REQUEST_COMPLETE);
|
||||
return lock_request->complete_r;
|
||||
}
|
||||
|
||||
int
|
||||
toku_lock_request_wait_with_default_timeout(toku_lock_request *lock_request, toku_lock_tree *tree) {
|
||||
return toku_lock_request_wait(lock_request, tree, &tree->mgr->lock_wait_time);
|
||||
}
|
||||
|
||||
void
|
||||
toku_lock_request_wakeup(toku_lock_request *lock_request, toku_lock_tree *tree UU()) {
|
||||
if (lock_request->wait_initialized) {
|
||||
int r = toku_pthread_cond_broadcast(&lock_request->wait); assert_zero(r);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
toku_lock_request_tree_init(toku_lock_tree *tree) {
|
||||
int r = toku_omt_create(&tree->lock_requests); assert_zero(r);
|
||||
}
|
||||
|
||||
void
|
||||
toku_lock_request_tree_destroy(toku_lock_tree *tree) {
|
||||
assert(toku_omt_size(tree->lock_requests) == 0);
|
||||
toku_omt_destroy(&tree->lock_requests);
|
||||
}
|
||||
|
||||
static int
|
||||
compare_lock_request(OMTVALUE a, void *b) {
|
||||
toku_lock_request *a_lock_request = (toku_lock_request *) a;
|
||||
TXNID b_id = * (TXNID *) b;
|
||||
if (a_lock_request->txnid < b_id)
|
||||
return -1;
|
||||
if (a_lock_request->txnid > b_id)
|
||||
return +1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
toku_lock_request_tree_insert(toku_lock_tree *tree, toku_lock_request *lock_request) {
|
||||
lock_request->tree = tree;
|
||||
int r;
|
||||
OMTVALUE v;
|
||||
u_int32_t idx;
|
||||
r = toku_omt_find_zero(tree->lock_requests, compare_lock_request, &lock_request->txnid, &v, &idx); assert(r == DB_NOTFOUND);
|
||||
r = toku_omt_insert_at(tree->lock_requests, lock_request, idx); assert_zero(r);
|
||||
}
|
||||
|
||||
void
|
||||
toku_lock_request_tree_delete(toku_lock_tree *tree, toku_lock_request *lock_request) {
|
||||
int r;
|
||||
OMTVALUE v;
|
||||
u_int32_t idx;
|
||||
r = toku_omt_find_zero(tree->lock_requests, compare_lock_request, &lock_request->txnid, &v, &idx);
|
||||
if (r == 0) {
|
||||
r = toku_omt_delete_at(tree->lock_requests, idx); assert_zero(r);
|
||||
}
|
||||
}
|
||||
|
||||
toku_lock_request *
|
||||
toku_lock_request_tree_find(toku_lock_tree *tree, TXNID id) {
|
||||
int r;
|
||||
OMTVALUE v;
|
||||
u_int32_t idx;
|
||||
r = toku_omt_find_zero(tree->lock_requests, compare_lock_request, &id, &v, &idx);
|
||||
toku_lock_request *lock_request = NULL;
|
||||
if (r == 0)
|
||||
lock_request = (toku_lock_request *) v;
|
||||
return lock_request;
|
||||
}
|
||||
|
||||
static void
|
||||
copy_dbt(DBT *dest, const DBT *src) {
|
||||
dest->size = src->size;
|
||||
if (dest->size > 0) {
|
||||
dest->data = toku_xrealloc(dest->data, dest->size);
|
||||
memcpy(dest->data, src->data, dest->size);
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
#include <ctype.h>
|
||||
static void print_key(const char *sp, const DBT *k) {
|
||||
printf("%s", sp);
|
||||
if (k == toku_lt_neg_infinity)
|
||||
printf("-inf");
|
||||
else if (k == toku_lt_infinity)
|
||||
printf("inf");
|
||||
else {
|
||||
char *data = (char *) k->data;
|
||||
for (unsigned i = 0; i < k->size; i++)
|
||||
printf("%2.2x", data[i]);
|
||||
printf(" ");
|
||||
for (unsigned i = 0; i < k->size; i++) {
|
||||
int c = data[i];
|
||||
printf("%c", isprint(c) ? c : '.');
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
toku_lock_request_start_locked(toku_lock_request *lock_request, toku_lock_tree *tree, bool copy_keys_if_not_granted) {
|
||||
int r;
|
||||
assert(lock_request->state == LOCK_REQUEST_INIT);
|
||||
if (lock_request->type == LOCK_REQUEST_READ) {
|
||||
r = toku_lt_acquire_range_read_lock(tree, lock_request->db, lock_request->txnid, lock_request->key_left, lock_request->key_right);
|
||||
} else if (lock_request->type == LOCK_REQUEST_WRITE) {
|
||||
r = toku_lt_acquire_range_write_lock(tree, lock_request->db, lock_request->txnid, lock_request->key_left, lock_request->key_right);
|
||||
} else
|
||||
assert(0);
|
||||
#if DEBUG
|
||||
if (toku_lt_debug) {
|
||||
printf("%s:%u %lu db=%p %s %u\n", __FUNCTION__, __LINE__, lock_request->txnid, lock_request->db, lock_request->type == LOCK_REQUEST_READ ? "r" : "w", lock_request->state);
|
||||
print_key("left=", lock_request->key_left);
|
||||
print_key("right=", lock_request->key_right);
|
||||
}
|
||||
#endif
|
||||
if (r == DB_LOCK_NOTGRANTED) {
|
||||
lock_request->state = LOCK_REQUEST_PENDING;
|
||||
if (copy_keys_if_not_granted) {
|
||||
copy_dbt(&lock_request->key_left_copy, lock_request->key_left);
|
||||
if (!lt_is_infinite(lock_request->key_left))
|
||||
lock_request->key_left = &lock_request->key_left_copy;
|
||||
copy_dbt(&lock_request->key_right_copy, lock_request->key_right);
|
||||
if (!lt_is_infinite(lock_request->key_right))
|
||||
lock_request->key_right = &lock_request->key_right_copy;
|
||||
}
|
||||
toku_lock_request_tree_insert(tree, lock_request);
|
||||
|
||||
// check for deadlock
|
||||
toku_lt_check_deadlock(tree, lock_request);
|
||||
if (lock_request->state == LOCK_REQUEST_COMPLETE)
|
||||
r = lock_request->complete_r;
|
||||
} else
|
||||
toku_lock_request_complete(lock_request, r);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
toku_lock_request_start(toku_lock_request *lock_request, toku_lock_tree *tree, bool copy_keys_if_not_granted) {
|
||||
toku_ltm_lock_mutex(tree->mgr);
|
||||
int r = toku_lock_request_start_locked(lock_request, tree, copy_keys_if_not_granted);
|
||||
toku_ltm_unlock_mutex(tree->mgr);
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
toku_lt_acquire_lock_request_with_timeout_locked(toku_lock_tree *tree, toku_lock_request *lock_request, struct timeval *wait_time) {
|
||||
int r = toku_lock_request_start_locked(lock_request, tree, false);
|
||||
if (r == DB_LOCK_NOTGRANTED)
|
||||
r = toku_lock_request_wait(lock_request, tree, wait_time);
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
toku_lt_acquire_lock_request_with_timeout(toku_lock_tree *tree, toku_lock_request *lock_request, struct timeval *wait_time) {
|
||||
toku_ltm_lock_mutex(tree->mgr);
|
||||
int r = toku_lt_acquire_lock_request_with_timeout_locked(tree, lock_request, wait_time);
|
||||
toku_ltm_unlock_mutex(tree->mgr);
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
toku_lt_acquire_lock_request_with_default_timeout_locked(toku_lock_tree *tree, toku_lock_request *lock_request) {
|
||||
return toku_lt_acquire_lock_request_with_timeout_locked(tree, lock_request, &tree->mgr->lock_wait_time);
|
||||
}
|
||||
|
||||
int
|
||||
toku_lt_acquire_lock_request_with_default_timeout(toku_lock_tree *tree, toku_lock_request *lock_request) {
|
||||
toku_ltm_lock_mutex(tree->mgr);
|
||||
int r = toku_lt_acquire_lock_request_with_timeout_locked(tree, lock_request, &tree->mgr->lock_wait_time);
|
||||
toku_ltm_unlock_mutex(tree->mgr);
|
||||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
toku_lt_retry_lock_requests_locked(toku_lock_tree *tree) {
|
||||
int r;
|
||||
for (uint32_t i = 0; i < toku_omt_size(tree->lock_requests); ) {
|
||||
OMTVALUE v;
|
||||
r = toku_omt_fetch(tree->lock_requests, i, &v); assert_zero(r);
|
||||
toku_lock_request *lock_request = (toku_lock_request *) v;
|
||||
assert(lock_request->state == LOCK_REQUEST_PENDING);
|
||||
lock_request->state = LOCK_REQUEST_INIT;
|
||||
toku_omt_delete_at(tree->lock_requests, i);
|
||||
r = toku_lock_request_start_locked(lock_request, tree, false);
|
||||
if (lock_request->state == LOCK_REQUEST_COMPLETE) {
|
||||
toku_lock_request_wakeup(lock_request, tree);
|
||||
} else {
|
||||
assert(lock_request->state == LOCK_REQUEST_PENDING);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "wfg.h"
|
||||
|
||||
// build the WFG for a given lock request
|
||||
//
|
||||
// for each transaction B that blocks A's lock request
|
||||
// if B is blocked then add (A,T) to the WFG and if B is new, fill in the WFG from B
|
||||
static void
|
||||
build_wfg_for_a_lock_request(toku_lock_tree *tree, struct wfg *wfg, toku_lock_request *a_lock_request) {
|
||||
txnid_set conflicts; txnid_set_init(&conflicts);
|
||||
int r = toku_lt_get_lock_request_conflicts(tree, a_lock_request, &conflicts); assert_zero(r);
|
||||
size_t n_conflicts = txnid_set_size(&conflicts);
|
||||
for (size_t i = 0; i < n_conflicts; i++) {
|
||||
TXNID b = txnid_set_get(&conflicts, i);
|
||||
toku_lock_request *b_lock_request = toku_lock_request_tree_find(tree, b);
|
||||
if (b_lock_request) {
|
||||
bool b_exists = wfg_node_exists(wfg, b);
|
||||
wfg_add_edge(wfg, a_lock_request->txnid, b);
|
||||
if (!b_exists)
|
||||
build_wfg_for_a_lock_request(tree, wfg, b_lock_request);
|
||||
}
|
||||
}
|
||||
txnid_set_destroy(&conflicts);
|
||||
}
|
||||
|
||||
// check if a given lock request could deadlock with any granted locks.
|
||||
void
|
||||
toku_lt_check_deadlock(toku_lock_tree *tree, toku_lock_request *a_lock_request) {
|
||||
// init the wfg
|
||||
struct wfg wfg_static;
|
||||
struct wfg *wfg = &wfg_static; wfg_init(wfg);
|
||||
|
||||
// build the wfg rooted with a lock request
|
||||
build_wfg_for_a_lock_request(tree, wfg, a_lock_request);
|
||||
|
||||
// find cycles in the wfg rooted with A
|
||||
// if cycles exists then
|
||||
// pick all necessary transactions T from the cycle needed to break the cycle
|
||||
// hand T the deadlock error
|
||||
// remove T's lock request
|
||||
// set the lock request state to deadlocked
|
||||
// wakeup T's lock request
|
||||
if (wfg_exist_cycle_from_txnid(wfg, a_lock_request->txnid)) {
|
||||
assert(a_lock_request->state == LOCK_REQUEST_PENDING);
|
||||
toku_lock_request_complete(a_lock_request, DB_LOCK_DEADLOCK);
|
||||
toku_lock_request_tree_delete(tree, a_lock_request);
|
||||
toku_lock_request_wakeup(a_lock_request, tree);
|
||||
}
|
||||
|
||||
// destroy the wfg
|
||||
wfg_destroy(wfg);
|
||||
}
|
||||
|
||||
static void
|
||||
add_conflicts(txnid_set *conflicts, toku_range *ranges, uint32_t nranges, TXNID id) {
|
||||
for (uint32_t i = 0; i < nranges; i++)
|
||||
if (ranges[i].data != id)
|
||||
txnid_set_add(conflicts, ranges[i].data);
|
||||
}
|
||||
|
||||
static void
|
||||
find_read_conflicts(toku_lock_tree *tree, toku_interval *query, TXNID id, txnid_set *conflicts, toku_range **range_ptr, uint32_t *n_expected_ranges_ptr) {
|
||||
uint32_t numfound;
|
||||
toku_rth_start_scan(tree->rth);
|
||||
rt_forest *forest;
|
||||
while ((forest = toku_rth_next(tree->rth)) != NULL) {
|
||||
if (forest->self_read != NULL && lt_txn_cmp(forest->hash_key, id)) {
|
||||
numfound = 0;
|
||||
int r = toku_rt_find(forest->self_read, query, 0, range_ptr, n_expected_ranges_ptr, &numfound);
|
||||
if (r == 0)
|
||||
add_conflicts(conflicts, *range_ptr, numfound, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// find 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
|
||||
int
|
||||
toku_lt_get_lock_request_conflicts(toku_lock_tree *tree, toku_lock_request *lock_request, txnid_set *conflicts) {
|
||||
int r;
|
||||
|
||||
// build a query from the lock request
|
||||
toku_point left; init_point(&left, tree, lock_request->key_left);
|
||||
toku_point right; init_point(&right, tree, lock_request->key_right);
|
||||
toku_interval query; init_query(&query, &left, &right);
|
||||
lt_set_comparison_functions(tree, lock_request->db);
|
||||
|
||||
uint32_t n_expected_ranges = 0;
|
||||
toku_range *ranges = NULL;
|
||||
|
||||
if (lock_request->type == LOCK_REQUEST_WRITE) {
|
||||
// check conflicts with read locks
|
||||
find_read_conflicts(tree, &query, lock_request->txnid, conflicts, &ranges, &n_expected_ranges);
|
||||
}
|
||||
|
||||
// check conflicts with write locks
|
||||
uint32_t numfound = 0;
|
||||
r = toku_rt_find(tree->borderwrite, &query, 0, &ranges, &n_expected_ranges, &numfound);
|
||||
if (r == 0) {
|
||||
for (uint32_t i = 0; i < numfound; i++)
|
||||
if (ranges[i].data != lock_request->txnid)
|
||||
txnid_set_add(conflicts, ranges[i].data);
|
||||
}
|
||||
|
||||
if (ranges)
|
||||
tree->free(ranges);
|
||||
|
||||
lt_clear_comparison_functions(tree);
|
||||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
toku_ltm_set_lock_wait_time(toku_ltm *mgr, uint64_t lock_wait_time_usec) {
|
||||
if (lock_wait_time_usec == UINT64_MAX)
|
||||
mgr->lock_wait_time = max_timeval;
|
||||
else
|
||||
mgr->lock_wait_time = (struct timeval) { lock_wait_time_usec / 1000000, lock_wait_time_usec % 1000000 };
|
||||
}
|
||||
|
||||
void
|
||||
toku_ltm_get_lock_wait_time(toku_ltm *mgr, uint64_t *lock_wait_time_usec) {
|
||||
if (mgr->lock_wait_time.tv_sec == max_timeval.tv_sec && mgr->lock_wait_time.tv_usec == max_timeval.tv_usec)
|
||||
*lock_wait_time_usec = UINT64_MAX;
|
||||
else
|
||||
*lock_wait_time_usec = mgr->lock_wait_time.tv_sec * 1000000 + mgr->lock_wait_time.tv_usec;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
112
src/lock_tree/tests/test_conflict_read_table_write.c
Normal file
112
src/lock_tree/tests/test_conflict_read_table_write.c
Normal file
|
@ -0,0 +1,112 @@
|
|||
// 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(<m, 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(<, 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;
|
||||
}
|
113
src/lock_tree/tests/test_conflict_read_write.c
Normal file
113
src/lock_tree/tests/test_conflict_read_write.c
Normal file
|
@ -0,0 +1,113 @@
|
|||
// 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(<m, 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(<, 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;
|
||||
}
|
81
src/lock_tree/tests/test_conflict_write_read.c
Normal file
81
src/lock_tree/tests/test_conflict_write_read.c
Normal file
|
@ -0,0 +1,81 @@
|
|||
// 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(<m, 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(<, 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;
|
||||
}
|
81
src/lock_tree/tests/test_conflict_write_table_read.c
Normal file
81
src/lock_tree/tests/test_conflict_write_table_read.c
Normal file
|
@ -0,0 +1,81 @@
|
|||
// 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(<m, 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(<, 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;
|
||||
}
|
81
src/lock_tree/tests/test_conflict_write_write.c
Normal file
81
src/lock_tree/tests/test_conflict_write_write.c
Normal file
|
@ -0,0 +1,81 @@
|
|||
// 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(<m, 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(<, 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;
|
||||
}
|
78
src/lock_tree/tests/test_default_lock_timeout.c
Normal file
78
src/lock_tree/tests/test_default_lock_timeout.c
Normal file
|
@ -0,0 +1,78 @@
|
|||
// 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(<m, 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(<, 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;
|
||||
}
|
59
src/lock_tree/tests/test_default_timeout.c
Normal file
59
src/lock_tree/tests/test_default_timeout.c
Normal file
|
@ -0,0 +1,59 @@
|
|||
// 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(<m, 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;
|
||||
}
|
78
src/lock_tree/tests/test_lock_timeout.c
Normal file
78
src/lock_tree/tests/test_lock_timeout.c
Normal file
|
@ -0,0 +1,78 @@
|
|||
// 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(<m, 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(<, 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;
|
||||
}
|
79
src/lock_tree/tests/test_read_notgranted.c
Normal file
79
src/lock_tree/tests/test_read_notgranted.c
Normal file
|
@ -0,0 +1,79 @@
|
|||
// 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(<m, 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(<, 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;
|
||||
}
|
78
src/lock_tree/tests/test_read_out_of_locks.c
Normal file
78
src/lock_tree/tests/test_read_out_of_locks.c
Normal file
|
@ -0,0 +1,78 @@
|
|||
// 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(<m, 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(<, 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;
|
||||
}
|
77
src/lock_tree/tests/test_read_request_blocked.c
Normal file
77
src/lock_tree/tests/test_read_request_blocked.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
// 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(<m, 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(<, 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;
|
||||
}
|
84
src/lock_tree/tests/test_set_mutex.c
Normal file
84
src/lock_tree/tests/test_set_mutex.c
Normal file
|
@ -0,0 +1,84 @@
|
|||
// 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(<m, 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(<, 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;
|
||||
}
|
87
src/lock_tree/tests/test_set_mutex_default_lock_timeout.c
Normal file
87
src/lock_tree/tests/test_set_mutex_default_lock_timeout.c
Normal file
|
@ -0,0 +1,87 @@
|
|||
// 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(<m, 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(<, 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;
|
||||
}
|
82
src/lock_tree/tests/test_simple_deadlock.c
Normal file
82
src/lock_tree/tests/test_simple_deadlock.c
Normal file
|
@ -0,0 +1,82 @@
|
|||
// 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(<m, 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(<, 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;
|
||||
}
|
71
src/lock_tree/tests/test_txnid_set.c
Normal file
71
src/lock_tree/tests/test_txnid_set.c
Normal file
|
@ -0,0 +1,71 @@
|
|||
// 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;
|
||||
}
|
80
src/lock_tree/tests/test_update_deadlock.c
Normal file
80
src/lock_tree/tests/test_update_deadlock.c
Normal file
|
@ -0,0 +1,80 @@
|
|||
// 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(<m, 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(<, 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;
|
||||
}
|
80
src/lock_tree/tests/test_update_deadlock_copy_keys.c
Normal file
80
src/lock_tree/tests/test_update_deadlock_copy_keys.c
Normal file
|
@ -0,0 +1,80 @@
|
|||
// 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(<m, 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(<, 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;
|
||||
}
|
62
src/lock_tree/tests/test_wfg_1000.c
Normal file
62
src/lock_tree/tests/test_wfg_1000.c
Normal file
|
@ -0,0 +1,62 @@
|
|||
// 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;
|
||||
}
|
59
src/lock_tree/tests/test_wfg_apply_funcs.c
Normal file
59
src/lock_tree/tests/test_wfg_apply_funcs.c
Normal file
|
@ -0,0 +1,59 @@
|
|||
// 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;
|
||||
}
|
57
src/lock_tree/tests/test_wfg_circular_cycle.c
Normal file
57
src/lock_tree/tests/test_wfg_circular_cycle.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
// 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;
|
||||
}
|
78
src/lock_tree/tests/test_wfg_circular_cycle_2.c
Normal file
78
src/lock_tree/tests/test_wfg_circular_cycle_2.c
Normal file
|
@ -0,0 +1,78 @@
|
|||
// 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;
|
||||
}
|
37
src/lock_tree/tests/test_wfg_empty.c
Normal file
37
src/lock_tree/tests/test_wfg_empty.c
Normal file
|
@ -0,0 +1,37 @@
|
|||
// 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;
|
||||
}
|
34
src/lock_tree/tests/test_wfg_node_exists.c
Normal file
34
src/lock_tree/tests/test_wfg_node_exists.c
Normal file
|
@ -0,0 +1,34 @@
|
|||
// 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;
|
||||
}
|
35
src/lock_tree/tests/test_wfg_print.c
Normal file
35
src/lock_tree/tests/test_wfg_print.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
// 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;
|
||||
}
|
39
src/lock_tree/tests/test_wfg_self_cycle.c
Normal file
39
src/lock_tree/tests/test_wfg_self_cycle.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
// 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;
|
||||
}
|
74
src/lock_tree/tests/test_wfg_simple_cycle.c
Normal file
74
src/lock_tree/tests/test_wfg_simple_cycle.c
Normal file
|
@ -0,0 +1,74 @@
|
|||
// 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;
|
||||
}
|
101
src/lock_tree/tests/test_write_conflict_with_threads.c
Normal file
101
src/lock_tree/tests/test_write_conflict_with_threads.c
Normal file
|
@ -0,0 +1,101 @@
|
|||
// 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(<m, 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(<, 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;
|
||||
}
|
64
src/lock_tree/tests/test_write_notgranted.c
Normal file
64
src/lock_tree/tests/test_write_notgranted.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
// 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(<m, 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(<, 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;
|
||||
}
|
67
src/lock_tree/tests/test_write_request_blocked.c
Normal file
67
src/lock_tree/tests/test_write_request_blocked.c
Normal file
|
@ -0,0 +1,67 @@
|
|||
// 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(<m, 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(<, 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;
|
||||
}
|
67
src/lock_tree/tests/test_wrw.c
Normal file
67
src/lock_tree/tests/test_wrw.c
Normal file
|
@ -0,0 +1,67 @@
|
|||
// 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(<m, 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(<, 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;
|
||||
}
|
71
src/lock_tree/txnid_set.c
Normal file
71
src/lock_tree/txnid_set.c
Normal file
|
@ -0,0 +1,71 @@
|
|||
#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;
|
||||
}
|
26
src/lock_tree/txnid_set.h
Normal file
26
src/lock_tree/txnid_set.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#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);
|
||||
|
252
src/lock_tree/wfg.c
Normal file
252
src/lock_tree/wfg.c
Normal file
|
@ -0,0 +1,252 @@
|
|||
#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
50
src/lock_tree/wfg.h
Normal file
50
src/lock_tree/wfg.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
#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);
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/* -*- 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++) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* -*- 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);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
|
||||
/* -*- 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)
|
||||
|
||||
|
|
195
src/tests/bdb-simple-deadlock-detect.c
Normal file
195
src/tests/bdb-simple-deadlock-detect.c
Normal file
|
@ -0,0 +1,195 @@
|
|||
// 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;
|
||||
}
|
93
src/tests/bdb-simple-deadlock-nowait.c
Normal file
93
src/tests/bdb-simple-deadlock-nowait.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
// 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;
|
||||
}
|
188
src/tests/bdb-simple-deadlock-on-the-fly.c
Normal file
188
src/tests/bdb-simple-deadlock-on-the-fly.c
Normal file
|
@ -0,0 +1,188 @@
|
|||
// 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;
|
||||
}
|
218
src/tests/blocking-c-del-deadlock.c
Normal file
218
src/tests/blocking-c-del-deadlock.c
Normal file
|
@ -0,0 +1,218 @@
|
|||
// 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;
|
||||
}
|
216
src/tests/blocking-c-del.c
Normal file
216
src/tests/blocking-c-del.c
Normal file
|
@ -0,0 +1,216 @@
|
|||
// 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;
|
||||
}
|
167
src/tests/blocking-first-empty.c
Normal file
167
src/tests/blocking-first-empty.c
Normal file
|
@ -0,0 +1,167 @@
|
|||
// 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;
|
||||
}
|
187
src/tests/blocking-first.c
Normal file
187
src/tests/blocking-first.c
Normal file
|
@ -0,0 +1,187 @@
|
|||
// 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;
|
||||
}
|
187
src/tests/blocking-last.c
Normal file
187
src/tests/blocking-last.c
Normal file
|
@ -0,0 +1,187 @@
|
|||
// verify that cursor last operations with conflicting locks suspend the calling 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_last_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_last(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_last(cursor, DB_RMW, blocking_last_callback, &context); assert(r == 0);
|
||||
#else
|
||||
r = cursor->c_get(cursor, &context.key, &context.val, DB_LAST + 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_last_args {
|
||||
DB_ENV *db_env;
|
||||
DB *db;
|
||||
uint64_t nrows;
|
||||
long sleeptime;
|
||||
};
|
||||
|
||||
static void *blocking_last_thread(void *arg) {
|
||||
struct blocking_last_args *a = (struct blocking_last_args *) arg;
|
||||
blocking_last(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_last_args a = { db_env, db, nrows, sleeptime };
|
||||
r = toku_pthread_create(&tids[i], NULL, blocking_last_thread, &a); assert(r == 0);
|
||||
}
|
||||
blocking_last(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;
|
||||
}
|
258
src/tests/blocking-next-prev-deadlock.c
Normal file
258
src/tests/blocking-next-prev-deadlock.c
Normal file
|
@ -0,0 +1,258 @@
|
|||
// verify that two transactions doing cursor next and prev operations detect a deadlock when
|
||||
// using write locking cursor operations.
|
||||
|
||||
#include "test.h"
|
||||
#include "toku_pthread.h"
|
||||
|
||||
static uint64_t get_key(DBT *key) {
|
||||
uint64_t k = 0;
|
||||
assert(key->size == sizeof k);
|
||||
memcpy(&k, key->data, key->size);
|
||||
return htonl(k);
|
||||
}
|
||||
|
||||
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_next_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_next(DB_ENV *db_env, DB *db, uint64_t nrows UU(), 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 };
|
||||
|
||||
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 i;
|
||||
for (i = 0; ; i++) {
|
||||
#if TOKUDB
|
||||
r = cursor->c_getf_next(cursor, DB_RMW, blocking_next_callback, &context);
|
||||
#else
|
||||
r = cursor->c_get(cursor, &context.key, &context.val, DB_NEXT + DB_RMW);
|
||||
#endif
|
||||
if (r != 0)
|
||||
break;
|
||||
if (verbose)
|
||||
printf("%lu next %lu\n", toku_pthread_self(), get_key(&context.key));
|
||||
usleep(sleeptime);
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
printf("%lu next=%d\n", toku_pthread_self(), r);
|
||||
assert(r == DB_NOTFOUND || r == DB_LOCK_DEADLOCK);
|
||||
|
||||
int rr = cursor->c_close(cursor); assert(rr == 0);
|
||||
|
||||
if (r == DB_NOTFOUND) {
|
||||
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);
|
||||
|
||||
toku_free(context.key.data);
|
||||
toku_free(context.val.data);
|
||||
}
|
||||
|
||||
static void blocking_prev(DB_ENV *db_env, DB *db, uint64_t nrows UU(), 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 };
|
||||
|
||||
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 i;
|
||||
for (i = 0; ; i++) {
|
||||
#if TOKUDB
|
||||
r = cursor->c_getf_prev(cursor, DB_RMW, blocking_next_callback, &context);
|
||||
#else
|
||||
r = cursor->c_get(cursor, &context.key, &context.val, DB_PREV + DB_RMW);
|
||||
#endif
|
||||
if (r != 0)
|
||||
break;
|
||||
if (verbose)
|
||||
printf("%lu prev %lu\n", toku_pthread_self(), get_key(&context.key));
|
||||
usleep(sleeptime);
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
printf("%lu prev=%d\n", toku_pthread_self(), r);
|
||||
assert(r == DB_NOTFOUND || r == DB_LOCK_DEADLOCK);
|
||||
|
||||
int rr = cursor->c_close(cursor); assert(rr == 0);
|
||||
|
||||
if (r == DB_NOTFOUND) {
|
||||
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);
|
||||
|
||||
toku_free(context.key.data);
|
||||
toku_free(context.val.data);
|
||||
}
|
||||
|
||||
struct blocking_next_args {
|
||||
DB_ENV *db_env;
|
||||
DB *db;
|
||||
uint64_t nrows;
|
||||
long sleeptime;
|
||||
};
|
||||
|
||||
static void *blocking_next_thread(void *arg) {
|
||||
struct blocking_next_args *a = (struct blocking_next_args *) arg;
|
||||
blocking_next(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_next_args a = { db_env, db, nrows, sleeptime };
|
||||
r = toku_pthread_create(&tids[i], NULL, blocking_next_thread, &a); assert(r == 0);
|
||||
}
|
||||
blocking_prev(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;
|
||||
}
|
252
src/tests/blocking-next-prev.c
Normal file
252
src/tests/blocking-next-prev.c
Normal file
|
@ -0,0 +1,252 @@
|
|||
// verify that two transactions doing cursor next and prev operations on a tree do not conflict.
|
||||
|
||||
#include "test.h"
|
||||
#include "toku_pthread.h"
|
||||
|
||||
static uint64_t get_key(DBT *key) {
|
||||
uint64_t k = 0;
|
||||
assert(key->size == sizeof k);
|
||||
memcpy(&k, key->data, key->size);
|
||||
return htonl(k);
|
||||
}
|
||||
|
||||
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_next_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_next(DB_ENV *db_env, DB *db, uint64_t nrows UU(), 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 };
|
||||
|
||||
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 i;
|
||||
for (i = 0; ; i++) {
|
||||
#if TOKUDB
|
||||
r = cursor->c_getf_next(cursor, 0, blocking_next_callback, &context);
|
||||
#else
|
||||
r = cursor->c_get(cursor, &context.key, &context.val, DB_NEXT);
|
||||
#endif
|
||||
if (r != 0)
|
||||
break;
|
||||
if (verbose)
|
||||
printf("%lu next %lu\n", toku_pthread_self(), get_key(&context.key));
|
||||
usleep(sleeptime);
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
printf("%lu next=%d\n", toku_pthread_self(), r);
|
||||
assert(r == DB_NOTFOUND || r == DB_LOCK_DEADLOCK);
|
||||
|
||||
int rr = cursor->c_close(cursor); assert(rr == 0);
|
||||
|
||||
if (r == DB_NOTFOUND) {
|
||||
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);
|
||||
|
||||
toku_free(context.key.data);
|
||||
toku_free(context.val.data);
|
||||
}
|
||||
|
||||
static void blocking_prev(DB_ENV *db_env, DB *db, uint64_t nrows UU(), 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 };
|
||||
|
||||
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 i;
|
||||
for (i = 0; ; i++) {
|
||||
#if TOKUDB
|
||||
r = cursor->c_getf_prev(cursor, 0, blocking_next_callback, &context);
|
||||
#else
|
||||
r = cursor->c_get(cursor, &context.key, &context.val, DB_PREV);
|
||||
#endif
|
||||
if (r != 0)
|
||||
break;
|
||||
if (verbose)
|
||||
printf("%lu prev %lu\n", toku_pthread_self(), get_key(&context.key));
|
||||
usleep(sleeptime);
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
printf("%lu prev=%d\n", toku_pthread_self(), r);
|
||||
assert(r == DB_NOTFOUND);
|
||||
|
||||
int rr = cursor->c_close(cursor); assert(rr == 0);
|
||||
|
||||
if (r == DB_NOTFOUND) {
|
||||
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);
|
||||
|
||||
toku_free(context.key.data);
|
||||
toku_free(context.val.data);
|
||||
}
|
||||
|
||||
struct blocking_next_args {
|
||||
DB_ENV *db_env;
|
||||
DB *db;
|
||||
uint64_t nrows;
|
||||
long sleeptime;
|
||||
};
|
||||
|
||||
static void *blocking_next_thread(void *arg) {
|
||||
struct blocking_next_args *a = (struct blocking_next_args *) arg;
|
||||
blocking_next(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_next_args a = { db_env, db, nrows, sleeptime };
|
||||
r = toku_pthread_create(&tids[i], NULL, blocking_next_thread, &a); assert(r == 0);
|
||||
}
|
||||
blocking_prev(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);
|
||||
|
||||
// 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;
|
||||
}
|
131
src/tests/blocking-prelock-range.c
Normal file
131
src/tests/blocking-prelock-range.c
Normal file
|
@ -0,0 +1,131 @@
|
|||
// verify that conflicting range locks works suspend the conflicting threads.
|
||||
|
||||
#include "test.h"
|
||||
#include "toku_pthread.h"
|
||||
|
||||
static void blocking_range_lock(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime) {
|
||||
int r;
|
||||
|
||||
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, DB_RMW); assert(r == 0);
|
||||
|
||||
uint64_t k = 0;
|
||||
DBT key = { .data = &k, .size = sizeof k};
|
||||
r = cursor->c_pre_acquire_range_lock(cursor, &key, &key); assert(r == 0);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
struct blocking_range_lock_args {
|
||||
DB_ENV *db_env;
|
||||
DB *db;
|
||||
uint64_t nrows;
|
||||
long sleeptime;
|
||||
};
|
||||
|
||||
static void *blocking_range_lock_thread(void *arg) {
|
||||
struct blocking_range_lock_args *a = (struct blocking_range_lock_args *) arg;
|
||||
blocking_range_lock(a->db_env, a->db, a->nrows, a->sleeptime);
|
||||
return arg;
|
||||
}
|
||||
|
||||
int test_main(int argc, char * const argv[]) {
|
||||
uint64_t cachesize = 0;
|
||||
uint32_t pagesize = 0;
|
||||
uint64_t nrows = 100;
|
||||
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);
|
||||
|
||||
toku_pthread_t tids[nthreads];
|
||||
for (int i = 0; i < nthreads-1; i++) {
|
||||
struct blocking_range_lock_args a = { db_env, db, nrows, sleeptime };
|
||||
r = toku_pthread_create(&tids[i], NULL, blocking_range_lock_thread, &a); assert(r == 0);
|
||||
}
|
||||
blocking_range_lock(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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
172
src/tests/blocking-put-timeout.c
Normal file
172
src/tests/blocking-put-timeout.c
Normal file
|
@ -0,0 +1,172 @@
|
|||
// verify that blocking lock waits eventually time out if the lock owner never releases the lock.
|
||||
|
||||
// A begin txn
|
||||
// A write locks 0
|
||||
// A sleeps
|
||||
// B begin txn
|
||||
// B tries to write lock 0, blocks
|
||||
// B's write lock times out, B aborts its txn
|
||||
// A wakes up and commits its txn
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
static void t_a(DB_ENV *db_env, DB *db, struct test_seq *seq) {
|
||||
int r;
|
||||
test_seq_sleep(seq, 0);
|
||||
int k = 0;
|
||||
DB_TXN *txn_a = NULL;
|
||||
r = db_env->txn_begin(db_env, NULL, &txn_a, 0); assert(r == 0);
|
||||
DBT key = { .data = &k, .size = sizeof k };
|
||||
DBT val = { .data = &k, .size = sizeof k };
|
||||
r = db->put(db, txn_a, &key, &val, 0); assert(r == 0);
|
||||
test_seq_next_state(seq);
|
||||
sleep(10);
|
||||
r = txn_a->commit(txn_a, 0); assert(r == 0);
|
||||
}
|
||||
|
||||
static void t_b(DB_ENV *db_env, DB *db, struct test_seq *seq) {
|
||||
int r;
|
||||
test_seq_sleep(seq, 1);
|
||||
int k = 0;
|
||||
DB_TXN *txn_b = NULL;
|
||||
r = db_env->txn_begin(db_env, NULL, &txn_b, 0); assert(r == 0);
|
||||
DBT key = { .data = &k, .size = sizeof k };
|
||||
DBT val = { .data = &k, .size = sizeof k };
|
||||
r = db->put(db, txn_b, &key, &val, 0); assert(r == DB_LOCK_NOTGRANTED);
|
||||
r = txn_b->abort(txn_b); assert(r == 0);
|
||||
}
|
||||
|
||||
struct t_a_args {
|
||||
DB_ENV *env;
|
||||
DB *db;
|
||||
struct test_seq *seq;
|
||||
};
|
||||
|
||||
static void *t_a_thread(void *arg) {
|
||||
struct t_a_args *a = (struct t_a_args *) arg;
|
||||
t_a(a->env, a->db, a->seq);
|
||||
return arg;
|
||||
}
|
||||
|
||||
int test_main(int argc, char * const argv[]) {
|
||||
uint64_t cachesize = 0;
|
||||
uint32_t pagesize = 0;
|
||||
#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;
|
||||
}
|
||||
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 USE_BDB
|
||||
db_timeout_t t;
|
||||
r = db_env->get_timeout(db_env, &t, DB_SET_LOCK_TIMEOUT); assert(r == 0);
|
||||
printf("lock %d\n", t);
|
||||
r = db_env->get_timeout(db_env, &t, DB_SET_TXN_TIMEOUT); assert(r == 0);
|
||||
printf("txn %d\n", t);
|
||||
|
||||
r = db_env->set_timeout(db_env, 5, DB_SET_LOCK_TIMEOUT); assert(r == 0);
|
||||
r = db_env->set_timeout(db_env, 5, DB_SET_TXN_TIMEOUT); assert(r == 0);
|
||||
|
||||
r = db_env->get_timeout(db_env, &t, DB_SET_LOCK_TIMEOUT); assert(r == 0);
|
||||
printf("lock %d\n", t);
|
||||
r = db_env->get_timeout(db_env, &t, DB_SET_TXN_TIMEOUT); assert(r == 0);
|
||||
printf("txn %d\n", t);
|
||||
#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
|
||||
struct test_seq seq; test_seq_init(&seq);
|
||||
toku_pthread_t t_a_id;
|
||||
struct t_a_args t_a_args = { db_env, db, &seq };
|
||||
r = toku_pthread_create(&t_a_id, NULL, t_a_thread, &t_a_args); assert(r == 0);
|
||||
t_b(db_env, db, &seq);
|
||||
void *ret;
|
||||
r = toku_pthread_join(t_a_id, &ret); assert(r == 0);
|
||||
test_seq_destroy(&seq);
|
||||
|
||||
// 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;
|
||||
}
|
161
src/tests/blocking-put-wakeup.c
Normal file
161
src/tests/blocking-put-wakeup.c
Normal file
|
@ -0,0 +1,161 @@
|
|||
// verify that a blocking lock gets granted when the owning transaction commits.
|
||||
|
||||
// A begin txn
|
||||
// A write locks 0
|
||||
// A sleeps
|
||||
// B begin txn
|
||||
// B tries to write lock 0, blocks
|
||||
// A wakes up and commits its txn
|
||||
// B's write lock is granted, B's thread resumes,
|
||||
// B commits its txn
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
static void t_a(DB_ENV *db_env, DB *db, struct test_seq *seq) {
|
||||
int r;
|
||||
test_seq_sleep(seq, 0);
|
||||
int k = 0;
|
||||
DB_TXN *txn_a = NULL;
|
||||
r = db_env->txn_begin(db_env, NULL, &txn_a, 0); assert(r == 0);
|
||||
DBT key = { .data = &k, .size = sizeof k };
|
||||
DBT val = { .data = &k, .size = sizeof k };
|
||||
r = db->put(db, txn_a, &key, &val, 0); assert(r == 0);
|
||||
test_seq_next_state(seq);
|
||||
sleep(10);
|
||||
r = txn_a->commit(txn_a, 0); assert(r == 0);
|
||||
}
|
||||
|
||||
static void t_b(DB_ENV *db_env, DB *db, struct test_seq *seq) {
|
||||
int r;
|
||||
test_seq_sleep(seq, 1);
|
||||
int k = 0;
|
||||
DB_TXN *txn_b = NULL;
|
||||
r = db_env->txn_begin(db_env, NULL, &txn_b, 0); assert(r == 0);
|
||||
DBT key = { .data = &k, .size = sizeof k };
|
||||
DBT val = { .data = &k, .size = sizeof k };
|
||||
r = db->put(db, txn_b, &key, &val, 0); assert(r == 0);
|
||||
r = txn_b->commit(txn_b, 0); assert(r == 0);
|
||||
}
|
||||
|
||||
struct t_a_args {
|
||||
DB_ENV *env;
|
||||
DB *db;
|
||||
struct test_seq *seq;
|
||||
};
|
||||
|
||||
static void *t_a_thread(void *arg) {
|
||||
struct t_a_args *a = (struct t_a_args *) arg;
|
||||
t_a(a->env, a->db, a->seq);
|
||||
return arg;
|
||||
}
|
||||
|
||||
int test_main(int argc, char * const argv[]) {
|
||||
uint64_t cachesize = 0;
|
||||
uint32_t pagesize = 0;
|
||||
#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;
|
||||
}
|
||||
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
|
||||
struct test_seq seq; test_seq_init(&seq);
|
||||
toku_pthread_t t_a_id;
|
||||
struct t_a_args t_a_args = { db_env, db, &seq };
|
||||
r = toku_pthread_create(&t_a_id, NULL, t_a_thread, &t_a_args); assert(r == 0);
|
||||
t_b(db_env, db, &seq);
|
||||
void *ret;
|
||||
r = toku_pthread_join(t_a_id, &ret); assert(r == 0);
|
||||
test_seq_destroy(&seq);
|
||||
|
||||
// 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;
|
||||
}
|
130
src/tests/blocking-put.c
Normal file
130
src/tests/blocking-put.c
Normal file
|
@ -0,0 +1,130 @@
|
|||
// have multiple threads try to put key 0 into the same db. one thread should gain a write lock on the key.
|
||||
// the other threads should block until the thread that owns the lock commits its transaction. then, one
|
||||
// of the blocked transactions should gain the lock and its owning thread resumed.
|
||||
|
||||
#include "test.h"
|
||||
#include "toku_pthread.h"
|
||||
|
||||
// pound on key == 0 and hold the write lock for a time less than the lock timeout
|
||||
static void blocking_put(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime) {
|
||||
int r;
|
||||
|
||||
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);
|
||||
|
||||
uint64_t k = 0;
|
||||
DBT key = { .data = &k, .size = sizeof k};
|
||||
DBT val = { .data = &k, .size = sizeof k};
|
||||
r = db->put(db, txn, &key, &val, 0); assert(r == 0);
|
||||
|
||||
usleep(sleeptime);
|
||||
|
||||
r = txn->commit(txn, 0); assert(r == 0);
|
||||
if (verbose)
|
||||
printf("%lu %lu\n", toku_pthread_self(), i);
|
||||
}
|
||||
}
|
||||
|
||||
struct blocking_put_args {
|
||||
DB_ENV *db_env;
|
||||
DB *db;
|
||||
uint64_t nrows;
|
||||
long sleeptime;
|
||||
};
|
||||
|
||||
static void *blocking_put_thread(void *arg) {
|
||||
struct blocking_put_args *a = (struct blocking_put_args *) arg;
|
||||
blocking_put(a->db_env, a->db, a->nrows, a->sleeptime);
|
||||
return arg;
|
||||
}
|
||||
|
||||
int test_main(int argc, char * const argv[]) {
|
||||
uint64_t cachesize = 0;
|
||||
uint32_t pagesize = 0;
|
||||
uint64_t nrows = 100;
|
||||
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);
|
||||
|
||||
toku_pthread_t tids[nthreads];
|
||||
for (int i = 0; i < nthreads-1; i++) {
|
||||
struct blocking_put_args a = { db_env, db, nrows, sleeptime };
|
||||
r = toku_pthread_create(&tids[i], NULL, blocking_put_thread, &a); assert(r == 0);
|
||||
}
|
||||
blocking_put(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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
199
src/tests/blocking-set-range-0.c
Normal file
199
src/tests/blocking-set-range-0.c
Normal file
|
@ -0,0 +1,199 @@
|
|||
// verify that cursor set range operations suspend the conflicting threads when another transaction
|
||||
// owns a lock on the key. the test uses keys 0, nrows/2, and nrows-1.
|
||||
|
||||
#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_set_range_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_set_range(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime, uint64_t the_key) {
|
||||
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 the key
|
||||
|
||||
uint64_t k = htonl(the_key);
|
||||
DBT key = { .data = &k, .size = sizeof k };
|
||||
#if TOKUDB
|
||||
r = cursor->c_getf_set_range(cursor, DB_RMW, &key, blocking_set_range_callback, &context); assert(r == 0);
|
||||
#else
|
||||
r = cursor->c_get(cursor, &key, &context.val, DB_SET_RANGE + DB_RMW); assert(r == 0);
|
||||
#endif
|
||||
uint64_t v;
|
||||
assert(context.val.size == sizeof v);
|
||||
memcpy(&v, context.val.data, context.val.size);
|
||||
assert(v == the_key); // verify the value
|
||||
|
||||
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_set_range_args {
|
||||
DB_ENV *db_env;
|
||||
DB *db;
|
||||
uint64_t nrows;
|
||||
long sleeptime;
|
||||
uint64_t the_key;
|
||||
};
|
||||
|
||||
static void *blocking_set_range_thread(void *arg) {
|
||||
struct blocking_set_range_args *a = (struct blocking_set_range_args *) arg;
|
||||
blocking_set_range(a->db_env, a->db, a->nrows, a->sleeptime, a->the_key);
|
||||
return arg;
|
||||
}
|
||||
|
||||
static void run_test(DB_ENV *db_env, DB *db, int nthreads, uint64_t nrows, long sleeptime, uint64_t the_key) {
|
||||
int r;
|
||||
toku_pthread_t tids[nthreads];
|
||||
for (int i = 0; i < nthreads-1; i++) {
|
||||
struct blocking_set_range_args a = { db_env, db, nrows, sleeptime, the_key };
|
||||
r = toku_pthread_create(&tids[i], NULL, blocking_set_range_thread, &a); assert(r == 0);
|
||||
}
|
||||
blocking_set_range(db_env, db, nrows, sleeptime, the_key);
|
||||
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, 0);
|
||||
run_test(db_env, db, nthreads, nrows, sleeptime, nrows/2);
|
||||
run_test(db_env, db, nthreads, nrows, sleeptime, nrows-1);
|
||||
|
||||
// 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;
|
||||
}
|
192
src/tests/blocking-set-range-n.c
Normal file
192
src/tests/blocking-set-range-n.c
Normal file
|
@ -0,0 +1,192 @@
|
|||
// verify that cursor set range operations suspend the conflicting threads when another transaction
|
||||
// owns a lock on the key. the key is at the right edge of the key space.
|
||||
|
||||
#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_set_range_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_set_range(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime, uint64_t the_key) {
|
||||
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 the key
|
||||
|
||||
uint64_t k = htonl(the_key);
|
||||
DBT key = { .data = &k, .size = sizeof k };
|
||||
#if TOKUDB
|
||||
r = cursor->c_getf_set_range(cursor, DB_RMW, &key, blocking_set_range_callback, &context); assert(r == DB_NOTFOUND);
|
||||
#else
|
||||
r = cursor->c_get(cursor, &key, &context.val, DB_SET_RANGE + 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_set_range_args {
|
||||
DB_ENV *db_env;
|
||||
DB *db;
|
||||
uint64_t nrows;
|
||||
long sleeptime;
|
||||
uint64_t the_key;
|
||||
};
|
||||
|
||||
static void *blocking_set_range_thread(void *arg) {
|
||||
struct blocking_set_range_args *a = (struct blocking_set_range_args *) arg;
|
||||
blocking_set_range(a->db_env, a->db, a->nrows, a->sleeptime, a->the_key);
|
||||
return arg;
|
||||
}
|
||||
|
||||
static void run_test(DB_ENV *db_env, DB *db, int nthreads, uint64_t nrows, long sleeptime, uint64_t the_key) {
|
||||
int r;
|
||||
toku_pthread_t tids[nthreads];
|
||||
for (int i = 0; i < nthreads-1; i++) {
|
||||
struct blocking_set_range_args a = { db_env, db, nrows, sleeptime, the_key };
|
||||
r = toku_pthread_create(&tids[i], NULL, blocking_set_range_thread, &a); assert(r == 0);
|
||||
}
|
||||
blocking_set_range(db_env, db, nrows, sleeptime, the_key);
|
||||
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, nrows);
|
||||
|
||||
// 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;
|
||||
}
|
195
src/tests/blocking-set-range-reverse-0.c
Normal file
195
src/tests/blocking-set-range-reverse-0.c
Normal file
|
@ -0,0 +1,195 @@
|
|||
// verify that cursor set range reverse operations suspend the conflicting threads when another transaction
|
||||
// owns a lock on the key.
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
struct my_callback_context {
|
||||
DBT key;
|
||||
DBT val;
|
||||
};
|
||||
|
||||
static int blocking_set_range_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;
|
||||
}
|
||||
|
||||
static void blocking_set_range(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime, uint64_t the_key) {
|
||||
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, DB_RMW); assert(r == 0); // get a write lock on the key
|
||||
|
||||
uint64_t k = htonl(the_key);
|
||||
DBT key = { .data = &k, .size = sizeof k };
|
||||
#if TOKUDB
|
||||
r = cursor->c_getf_set_range_reverse(cursor, 0, &key, blocking_set_range_callback, &context); assert(r == 0);
|
||||
#else
|
||||
r = cursor->c_get(cursor, &key, &context.val, DB_SET_RANGE_REVERSE); assert(r == 0);
|
||||
#endif
|
||||
uint64_t v;
|
||||
assert(context.val.size == sizeof v);
|
||||
memcpy(&v, context.val.data, context.val.size);
|
||||
assert(v == 0); // verify the value
|
||||
|
||||
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_set_range_args {
|
||||
DB_ENV *db_env;
|
||||
DB *db;
|
||||
uint64_t nrows;
|
||||
long sleeptime;
|
||||
uint64_t the_key;
|
||||
};
|
||||
|
||||
static void *blocking_set_range_thread(void *arg) {
|
||||
struct blocking_set_range_args *a = (struct blocking_set_range_args *) arg;
|
||||
blocking_set_range(a->db_env, a->db, a->nrows, a->sleeptime, a->the_key);
|
||||
return arg;
|
||||
}
|
||||
|
||||
static void run_test(DB_ENV *db_env, DB *db, int nthreads, uint64_t nrows, long sleeptime, uint64_t the_key) {
|
||||
int r;
|
||||
toku_pthread_t tids[nthreads];
|
||||
for (int i = 0; i < nthreads-1; i++) {
|
||||
struct blocking_set_range_args a = { db_env, db, nrows, sleeptime, the_key };
|
||||
r = toku_pthread_create(&tids[i], NULL, blocking_set_range_thread, &a); assert(r == 0);
|
||||
}
|
||||
blocking_set_range(db_env, db, nrows, sleeptime, the_key);
|
||||
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, 0);
|
||||
|
||||
// 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;
|
||||
}
|
178
src/tests/blocking-set.c
Normal file
178
src/tests/blocking-set.c
Normal file
|
@ -0,0 +1,178 @@
|
|||
// verify that cursor set operations suspend the conflicting threads when another transaction
|
||||
// owns a lock on the key.
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
#if TOKUDB
|
||||
static int blocking_set_callback(DBT const *a UU(), DBT const *b UU(), void *e UU()) {
|
||||
// DBT const *found_key = a;
|
||||
DBT const *found_val = b;
|
||||
DBT *my_val = (DBT *) e;
|
||||
assert(my_val->flags == DB_DBT_REALLOC);
|
||||
my_val->data = toku_xrealloc(my_val->data, found_val->size);
|
||||
my_val->size = found_val->size;
|
||||
memcpy(my_val->data, found_val->data, found_val->size);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void blocking_set(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime) {
|
||||
int r;
|
||||
|
||||
DBT val = { .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 the key
|
||||
|
||||
uint64_t k = htonl(0); // set to key 0
|
||||
DBT key = { .data = &k, .size = sizeof k };
|
||||
#if TOKUDB
|
||||
r = cursor->c_getf_set(cursor, DB_RMW, &key, blocking_set_callback, &val); assert(r == 0);
|
||||
#else
|
||||
r = cursor->c_get(cursor, &key, &val, DB_SET + DB_RMW); assert(r == 0);
|
||||
#endif
|
||||
uint64_t v;
|
||||
assert(val.size == sizeof v);
|
||||
memcpy(&v, val.data, val.size);
|
||||
assert(v == 0); // verify the value
|
||||
|
||||
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(val.data);
|
||||
}
|
||||
|
||||
struct blocking_set_args {
|
||||
DB_ENV *db_env;
|
||||
DB *db;
|
||||
uint64_t nrows;
|
||||
long sleeptime;
|
||||
};
|
||||
|
||||
static void *blocking_set_thread(void *arg) {
|
||||
struct blocking_set_args *a = (struct blocking_set_args *) arg;
|
||||
blocking_set(a->db_env, a->db, a->nrows, a->sleeptime);
|
||||
return arg;
|
||||
}
|
||||
|
||||
int test_main(int argc, char * const argv[]) {
|
||||
uint64_t cachesize = 0;
|
||||
uint32_t pagesize = 0;
|
||||
uint64_t nrows = 100;
|
||||
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);
|
||||
|
||||
toku_pthread_t tids[nthreads];
|
||||
for (int i = 0; i < nthreads-1; i++) {
|
||||
struct blocking_set_args a = { db_env, db, nrows, sleeptime };
|
||||
r = toku_pthread_create(&tids[i], NULL, blocking_set_thread, &a); assert(r == 0);
|
||||
}
|
||||
blocking_set(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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
124
src/tests/blocking-table-lock.c
Normal file
124
src/tests/blocking-table-lock.c
Normal file
|
@ -0,0 +1,124 @@
|
|||
// verify that table locks used by multiple transactions suspend the conflicting thread rather than just return DB_LOCK_NOTGRANTED.
|
||||
|
||||
#include "test.h"
|
||||
#include "toku_pthread.h"
|
||||
|
||||
static void blocking_table_lock(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime) {
|
||||
int r;
|
||||
|
||||
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);
|
||||
|
||||
r = db->pre_acquire_table_lock(db, txn); assert(r == 0);
|
||||
|
||||
usleep(sleeptime);
|
||||
|
||||
r = txn->commit(txn, 0); assert(r == 0);
|
||||
if (verbose)
|
||||
printf("%lu %lu\n", toku_pthread_self(), i);
|
||||
}
|
||||
}
|
||||
|
||||
struct blocking_table_lock_args {
|
||||
DB_ENV *db_env;
|
||||
DB *db;
|
||||
uint64_t nrows;
|
||||
long sleeptime;
|
||||
};
|
||||
|
||||
static void *blocking_table_lock_thread(void *arg) {
|
||||
struct blocking_table_lock_args *a = (struct blocking_table_lock_args *) arg;
|
||||
blocking_table_lock(a->db_env, a->db, a->nrows, a->sleeptime);
|
||||
return arg;
|
||||
}
|
||||
|
||||
int test_main(int argc, char * const argv[]) {
|
||||
uint64_t cachesize = 0;
|
||||
uint32_t pagesize = 0;
|
||||
uint64_t nrows = 100;
|
||||
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);
|
||||
|
||||
toku_pthread_t tids[nthreads];
|
||||
for (int i = 0; i < nthreads-1; i++) {
|
||||
struct blocking_table_lock_args a = { db_env, db, nrows, sleeptime };
|
||||
r = toku_pthread_create(&tids[i], NULL, blocking_table_lock_thread, &a); assert(r == 0);
|
||||
}
|
||||
blocking_table_lock(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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
221
src/tests/db-put-simple-deadlock-threads.c
Normal file
221
src/tests/db-put-simple-deadlock-threads.c
Normal file
|
@ -0,0 +1,221 @@
|
|||
// T(a) put 0
|
||||
// T(b) put N-1
|
||||
// T(a) put N-1, should wait on lock W(N-1)
|
||||
// T(b) put 0, should return deadlock
|
||||
// T(b) abort
|
||||
// T(a) gets lock W(N-1)
|
||||
// T(A) commit
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
static void insert_row(DB *db, DB_TXN *txn, int k, int v, int expect_r) {
|
||||
DBT key; dbt_init(&key, &k, sizeof k);
|
||||
DBT value; dbt_init(&value, &v, sizeof v);
|
||||
int r = db->put(db, txn, &key, &value, 0); assert(r == expect_r);
|
||||
}
|
||||
|
||||
struct run_txn_b_arg {
|
||||
struct test_seq *test_seq;
|
||||
DB_TXN *txn_b;
|
||||
DB *db;
|
||||
int n;
|
||||
};
|
||||
|
||||
static void *run_txn_b(void *arg) {
|
||||
struct run_txn_b_arg *b_arg = (struct run_txn_b_arg *) arg;
|
||||
struct test_seq *test_seq = b_arg->test_seq;
|
||||
DB_TXN *txn_b = b_arg->txn_b;
|
||||
DB *db = b_arg->db;
|
||||
int n = b_arg->n;
|
||||
|
||||
test_seq_sleep(test_seq, 1);
|
||||
insert_row(db, txn_b, htonl(n-1), n-1, 0);
|
||||
test_seq_next_state(test_seq);
|
||||
|
||||
#if defined(USE_TDB)
|
||||
test_seq_sleep(test_seq, 3);
|
||||
insert_row(db, txn_b, htonl(0), 0, DB_LOCK_NOTGRANTED);
|
||||
int r = txn_b->commit(txn_b, 0); assert(r == 0);
|
||||
#elif defined(USE_BDB)
|
||||
test_seq_sleep(test_seq, 2);
|
||||
insert_row(db, txn_b, htonl(0), 0, DB_LOCK_DEADLOCK);
|
||||
int r = txn_b->abort(txn_b); assert(r == 0);
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
static void simple_deadlock(DB_ENV *db_env, DB *db, int do_txn, int n) {
|
||||
int r;
|
||||
|
||||
DB_TXN *txn_init = NULL;
|
||||
if (do_txn) {
|
||||
r = db_env->txn_begin(db_env, NULL, &txn_init, 0); assert(r == 0);
|
||||
}
|
||||
|
||||
for (int k = 0; k < n; k++) {
|
||||
insert_row(db, txn_init, htonl(k), k, 0);
|
||||
}
|
||||
|
||||
if (do_txn) {
|
||||
r = txn_init->commit(txn_init, 0); assert(r == 0);
|
||||
}
|
||||
|
||||
|
||||
DB_TXN *txn_a = NULL;
|
||||
if (do_txn) {
|
||||
r = db_env->txn_begin(db_env, NULL, &txn_a, 0); assert(r == 0);
|
||||
}
|
||||
|
||||
DB_TXN *txn_b = NULL;
|
||||
if (do_txn) {
|
||||
r = db_env->txn_begin(db_env, NULL, &txn_b, 0); assert(r == 0);
|
||||
}
|
||||
|
||||
struct test_seq test_seq; test_seq_init(&test_seq);
|
||||
|
||||
toku_pthread_t tid;
|
||||
struct run_txn_b_arg arg = { &test_seq, txn_b, db, n};
|
||||
r = toku_pthread_create(&tid, NULL, run_txn_b, &arg);
|
||||
|
||||
test_seq_sleep(&test_seq, 0);
|
||||
insert_row(db, txn_a, htonl(0), 0, 0);
|
||||
test_seq_next_state(&test_seq);
|
||||
|
||||
test_seq_sleep(&test_seq, 2);
|
||||
#if defined(USE_TDB)
|
||||
insert_row(db, txn_a, htonl(n-1), n-1, DB_LOCK_NOTGRANTED);
|
||||
#elif defined(USE_BDB)
|
||||
insert_row(db, txn_a, htonl(n-1), n-1, 0);
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
test_seq_next_state(&test_seq);
|
||||
|
||||
void *ret = NULL;
|
||||
r = toku_pthread_join(tid, &ret); assert(r == 0);
|
||||
|
||||
if (do_txn) {
|
||||
r = txn_a->commit(txn_a, 0); assert(r == 0);
|
||||
}
|
||||
|
||||
test_seq_destroy(&test_seq);
|
||||
}
|
||||
|
||||
int test_main(int argc, char * const argv[]) {
|
||||
uint64_t cachesize = 0;
|
||||
uint32_t pagesize = 0;
|
||||
int do_txn = 1;
|
||||
int nrows = 1000;
|
||||
#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 = "simple_deadlock";
|
||||
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], "-n") == 0 && i+1 < argc) {
|
||||
nrows = atoi(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);
|
||||
}
|
||||
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
|
||||
|
||||
// create the db
|
||||
DB *db = NULL;
|
||||
r = db_create(&db, db_env, 0); assert(r == 0);
|
||||
DB_TXN *create_txn = NULL;
|
||||
if (do_txn) {
|
||||
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);
|
||||
if (do_txn) {
|
||||
r = create_txn->commit(create_txn, 0); assert(r == 0);
|
||||
}
|
||||
|
||||
// run test
|
||||
simple_deadlock(db_env, db, do_txn, nrows);
|
||||
|
||||
// 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;
|
||||
}
|
124
src/tests/db-put-simple-deadlock.c
Normal file
124
src/tests/db-put-simple-deadlock.c
Normal file
|
@ -0,0 +1,124 @@
|
|||
#include "test.h"
|
||||
|
||||
static void insert_row(DB *db, DB_TXN *txn, int k, int v, int expect_r) {
|
||||
DBT key; dbt_init(&key, &k, sizeof k);
|
||||
DBT value; dbt_init(&value, &v, sizeof v);
|
||||
int r = db->put(db, txn, &key, &value, 0); assert(r == expect_r);
|
||||
}
|
||||
|
||||
static void simple_deadlock(DB_ENV *db_env, DB *db, int do_txn, int n) {
|
||||
int r;
|
||||
|
||||
DB_TXN *txn_init = NULL;
|
||||
if (do_txn) {
|
||||
r = db_env->txn_begin(db_env, NULL, &txn_init, 0); assert(r == 0);
|
||||
}
|
||||
|
||||
for (int k = 0; k < n; k++) {
|
||||
insert_row(db, txn_init, htonl(k), k, 0);
|
||||
}
|
||||
|
||||
if (do_txn) {
|
||||
r = txn_init->commit(txn_init, 0); assert(r == 0);
|
||||
}
|
||||
|
||||
|
||||
DB_TXN *txn_a = NULL;
|
||||
if (do_txn) {
|
||||
r = db_env->txn_begin(db_env, NULL, &txn_a, 0); assert(r == 0);
|
||||
}
|
||||
|
||||
DB_TXN *txn_b = NULL;
|
||||
if (do_txn) {
|
||||
r = db_env->txn_begin(db_env, NULL, &txn_b, 0); assert(r == 0);
|
||||
}
|
||||
|
||||
insert_row(db, txn_a, htonl(0), 0, 0);
|
||||
insert_row(db, txn_b, htonl(n-1), n-1, 0);
|
||||
insert_row(db, txn_a, htonl(n-1), n-1, DB_LOCK_NOTGRANTED);
|
||||
insert_row(db, txn_b, htonl(0), 0, DB_LOCK_NOTGRANTED);
|
||||
|
||||
if (do_txn) {
|
||||
r = txn_a->commit(txn_a, 0); assert(r == 0);
|
||||
r = txn_b->commit(txn_b, 0); assert(r == 0);
|
||||
}
|
||||
}
|
||||
|
||||
int test_main(int argc, char * const argv[]) {
|
||||
uint64_t cachesize = 0;
|
||||
uint32_t pagesize = 0;
|
||||
int do_txn = 1;
|
||||
int nrows = 2;
|
||||
#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 = "simple_deadlock";
|
||||
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], "-n") == 0 && i+1 < argc) {
|
||||
nrows = atoi(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);
|
||||
}
|
||||
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
|
||||
|
||||
// create the db
|
||||
DB *db = NULL;
|
||||
r = db_create(&db, db_env, 0); assert(r == 0);
|
||||
DB_TXN *create_txn = NULL;
|
||||
if (do_txn) {
|
||||
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);
|
||||
if (do_txn) {
|
||||
r = create_txn->commit(create_txn, 0); assert(r == 0);
|
||||
}
|
||||
|
||||
// run test
|
||||
simple_deadlock(db_env, db, do_txn, nrows);
|
||||
|
||||
// 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;
|
||||
}
|
161
src/tests/db-put-simple-lockwait.c
Normal file
161
src/tests/db-put-simple-lockwait.c
Normal file
|
@ -0,0 +1,161 @@
|
|||
// T(a) put 0
|
||||
// T(b) put 0, should block
|
||||
// T(c) put 0, should block
|
||||
// T(a) commit
|
||||
// T(b) put 0 succeeds
|
||||
// T(b) commit
|
||||
// T(c) put 0 succeeds
|
||||
|
||||
#include "test.h"
|
||||
#include "toku_pthread.h"
|
||||
|
||||
static void insert_row(DB *db, DB_TXN *txn, int k, int v, int expect_r) {
|
||||
DBT key; dbt_init(&key, &k, sizeof k);
|
||||
DBT value; dbt_init(&value, &v, sizeof v);
|
||||
int r = db->put(db, txn, &key, &value, 0); assert(r == expect_r);
|
||||
}
|
||||
|
||||
struct insert_one_arg {
|
||||
DB_TXN *txn;
|
||||
DB *db;
|
||||
};
|
||||
|
||||
static void *insert_one(void *arg) {
|
||||
struct insert_one_arg *f_arg = (struct insert_one_arg *) arg;
|
||||
DB_TXN *txn = f_arg->txn;
|
||||
DB *db = f_arg->db;
|
||||
|
||||
insert_row(db, txn, htonl(0), 0, 0);
|
||||
if (txn) {
|
||||
int r = txn->commit(txn, 0); assert(r == 0);
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
static void simple_lockwait(DB_ENV *db_env, DB *db, int do_txn, int nrows, int ntxns) {
|
||||
int r;
|
||||
|
||||
DB_TXN *txn_init = NULL;
|
||||
if (do_txn) {
|
||||
r = db_env->txn_begin(db_env, NULL, &txn_init, 0); assert(r == 0);
|
||||
}
|
||||
for (int k = 0; k < nrows; k++) {
|
||||
insert_row(db, txn_init, htonl(k), k, 0);
|
||||
}
|
||||
if (do_txn) {
|
||||
r = txn_init->commit(txn_init, 0); assert(r == 0);
|
||||
}
|
||||
|
||||
DB_TXN *txns[ntxns];
|
||||
for (int i = 0; i < ntxns; i++) {
|
||||
txns[i] = NULL;
|
||||
if (do_txn) {
|
||||
r = db_env->txn_begin(db_env, NULL, &txns[i], 0); assert(r == 0);
|
||||
}
|
||||
}
|
||||
|
||||
insert_row(db, txns[0], htonl(0), 0, 0);
|
||||
|
||||
toku_pthread_t tids[ntxns];
|
||||
for (int i = 1 ; i < ntxns; i++) {
|
||||
struct insert_one_arg *arg = toku_malloc(sizeof (struct insert_one_arg));
|
||||
*arg = (struct insert_one_arg) { txns[i], db};
|
||||
r = toku_pthread_create(&tids[i], NULL, insert_one, arg);
|
||||
}
|
||||
|
||||
sleep(10);
|
||||
if (do_txn) {
|
||||
r = txns[0]->commit(txns[0], 0); assert(r == 0);
|
||||
}
|
||||
|
||||
for (int i = 1; i < ntxns; i++) {
|
||||
void *ret = NULL;
|
||||
r = toku_pthread_join(tids[i], &ret); assert(r == 0); toku_free(ret);
|
||||
}
|
||||
}
|
||||
|
||||
int test_main(int argc, char * const argv[]) {
|
||||
uint64_t cachesize = 0;
|
||||
uint32_t pagesize = 0;
|
||||
int do_txn = 1;
|
||||
int nrows = 1000;
|
||||
int ntxns = 2;
|
||||
#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 = "simple_lockwait";
|
||||
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 = atoi(argv[++i]);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(argv[i], "--ntxns") == 0 && i+1 < argc) {
|
||||
ntxns = atoi(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);
|
||||
}
|
||||
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 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;
|
||||
if (do_txn) {
|
||||
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);
|
||||
if (do_txn) {
|
||||
r = create_txn->commit(create_txn, 0); assert(r == 0);
|
||||
}
|
||||
|
||||
// run test
|
||||
simple_lockwait(db_env, db, do_txn, nrows, ntxns);
|
||||
|
||||
// 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;
|
||||
}
|
202
src/tests/db-put-update-deadlock.c
Normal file
202
src/tests/db-put-update-deadlock.c
Normal file
|
@ -0,0 +1,202 @@
|
|||
// for all i: T(i) reads 0, gets a read lock on 0
|
||||
// for all i: T(i) writes 0, enters a deadlock
|
||||
// run deadlock detector until forward progress is possible
|
||||
|
||||
#include "test.h"
|
||||
#include "toku_pthread.h"
|
||||
|
||||
static void write_row(DB *db, DB_TXN *txn, int k, int v, int expect_r) {
|
||||
DBT key; dbt_init(&key, &k, sizeof k);
|
||||
DBT value; dbt_init(&value, &v, sizeof v);
|
||||
int r = db->put(db, txn, &key, &value, 0); assert(r == expect_r);
|
||||
}
|
||||
|
||||
static void read_row(DB *db, DB_TXN *txn, int k, int expect_r) {
|
||||
DBT key; dbt_init(&key, &k, sizeof k);
|
||||
DBT value; dbt_init_malloc(&value);
|
||||
int r = db->get(db, txn, &key, &value, 0); assert(r == expect_r);
|
||||
toku_free(value.data);
|
||||
}
|
||||
|
||||
static volatile int n_txns;
|
||||
|
||||
struct write_one_arg {
|
||||
DB_TXN *txn;
|
||||
DB *db;
|
||||
int k;
|
||||
int v;
|
||||
};
|
||||
|
||||
static void *write_one_f(void *arg) {
|
||||
struct write_one_arg *f_arg = (struct write_one_arg *) arg;
|
||||
DB_TXN *txn = f_arg->txn;
|
||||
DB *db = f_arg->db;
|
||||
int k = f_arg->k;
|
||||
int v = f_arg->v;
|
||||
|
||||
DBT key; dbt_init(&key, &k, sizeof k);
|
||||
DBT value; dbt_init(&value, &v, sizeof v);
|
||||
int r = db->put(db, txn, &key, &value, 0);
|
||||
if (verbose)
|
||||
printf("%s %p %d\n", __FUNCTION__, arg, r);
|
||||
assert(r == 0 || r == DB_LOCK_DEADLOCK);
|
||||
if (r == 0) {
|
||||
r = txn->commit(txn, 0); assert(r == 0);
|
||||
} else {
|
||||
r = txn->abort(txn); assert(r == 0);
|
||||
}
|
||||
(void) __sync_fetch_and_sub(&n_txns, 1);
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
static void update_deadlock(DB_ENV *db_env, DB *db, int do_txn, int nrows, int ntxns, int poll_deadlock UU()) {
|
||||
int r;
|
||||
|
||||
// populate the initial tree
|
||||
DB_TXN *txn_init = NULL;
|
||||
if (do_txn) {
|
||||
r = db_env->txn_begin(db_env, NULL, &txn_init, 0); assert(r == 0);
|
||||
}
|
||||
for (int k = 0; k < nrows; k++) {
|
||||
write_row(db, txn_init, htonl(k), k, 0);
|
||||
}
|
||||
if (do_txn) {
|
||||
r = txn_init->commit(txn_init, 0); assert(r == 0);
|
||||
}
|
||||
|
||||
// create the transactions
|
||||
n_txns = ntxns;
|
||||
DB_TXN *txns[ntxns];
|
||||
for (int i = 0; i < ntxns; i++) {
|
||||
txns[i] = NULL;
|
||||
if (do_txn) {
|
||||
r = db_env->txn_begin(db_env, NULL, &txns[i], 0); assert(r == 0);
|
||||
}
|
||||
}
|
||||
|
||||
// get read locks
|
||||
for (int i = 0; i < ntxns; i++) {
|
||||
read_row(db, txns[i], htonl(0), 0);
|
||||
}
|
||||
|
||||
// get write locks
|
||||
toku_pthread_t tids[ntxns];
|
||||
for (int i = 0 ; i < ntxns; i++) {
|
||||
struct write_one_arg *arg = toku_malloc(sizeof (struct write_one_arg));
|
||||
*arg = (struct write_one_arg) { txns[i], db, htonl(0), 0};
|
||||
r = toku_pthread_create(&tids[i], NULL, write_one_f, arg);
|
||||
}
|
||||
|
||||
#if defined(USE_BDB)
|
||||
// check for deadlocks
|
||||
if (poll_deadlock) {
|
||||
while (n_txns > 0) {
|
||||
sleep(10);
|
||||
int rejected = 0;
|
||||
r = db_env->lock_detect(db_env, 0, DB_LOCK_YOUNGEST, &rejected); assert(r == 0);
|
||||
printf("%s rejected %d\n", __FUNCTION__, rejected);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// cleanup
|
||||
for (int i = 0; i < ntxns; i++) {
|
||||
void *ret = NULL;
|
||||
r = toku_pthread_join(tids[i], &ret); assert(r == 0); toku_free(ret);
|
||||
}
|
||||
}
|
||||
|
||||
int test_main(int argc, char * const argv[]) {
|
||||
uint64_t cachesize = 0;
|
||||
uint32_t pagesize = 0;
|
||||
int do_txn = 1;
|
||||
int nrows = 1000;
|
||||
int ntxns = 2;
|
||||
int poll_deadlock = 0;
|
||||
#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 = "simple_deadlock";
|
||||
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 = atoi(argv[++i]);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(argv[i], "--ntxns") == 0 && i+1 < argc) {
|
||||
ntxns = atoi(argv[++i]);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(argv[i], "--poll") == 0) {
|
||||
poll_deadlock = 1;
|
||||
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(TOKUDB)
|
||||
r = db_env->set_lock_timeout(db_env, 30 * 1000000); assert(r == 0);
|
||||
#endif
|
||||
#if defined(USE_BDB)
|
||||
if (!poll_deadlock) {
|
||||
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;
|
||||
if (do_txn) {
|
||||
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);
|
||||
if (do_txn) {
|
||||
r = create_txn->commit(create_txn, 0); assert(r == 0);
|
||||
}
|
||||
|
||||
// run test
|
||||
update_deadlock(db_env, db, do_txn, nrows, ntxns, poll_deadlock);
|
||||
|
||||
// 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;
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
562
src/ydb.c
562
src/ydb.c
|
@ -1761,6 +1761,34 @@ locked_env_set_redzone(DB_ENV *env, int redzone) {
|
|||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
env_get_lock_timeout(DB_ENV *env, uint64_t *lock_timeout_usec) {
|
||||
toku_ltm_get_lock_wait_time(env->i->ltm, lock_timeout_usec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
locked_env_get_lock_timeout(DB_ENV *env, uint64_t *lock_timeout_usec) {
|
||||
toku_ydb_lock();
|
||||
int r = env_get_lock_timeout(env, lock_timeout_usec);
|
||||
toku_ydb_unlock();
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
env_set_lock_timeout(DB_ENV *env, uint64_t lock_timeout_usec) {
|
||||
toku_ltm_set_lock_wait_time(env->i->ltm, lock_timeout_usec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
locked_env_set_lock_timeout(DB_ENV *env, uint64_t lock_timeout_usec) {
|
||||
toku_ydb_lock();
|
||||
int r = env_set_lock_timeout(env, lock_timeout_usec);
|
||||
toku_ydb_unlock();
|
||||
return r;
|
||||
}
|
||||
|
||||
static void
|
||||
format_time(const time_t *timer, char *buf) {
|
||||
ctime_r(timer, buf);
|
||||
|
@ -2329,6 +2357,8 @@ toku_env_create(DB_ENV ** envp, u_int32_t flags) {
|
|||
SENV(set_redzone);
|
||||
SENV(create_indexer);
|
||||
SENV(create_loader);
|
||||
SENV(get_lock_timeout);
|
||||
SENV(set_lock_timeout);
|
||||
#undef SENV
|
||||
|
||||
MALLOC(result->i);
|
||||
|
@ -2347,6 +2377,7 @@ toku_env_create(DB_ENV ** envp, u_int32_t flags) {
|
|||
toku_db_get_compare_fun,
|
||||
toku_malloc, toku_free, toku_realloc);
|
||||
if (r!=0) { goto cleanup; }
|
||||
toku_ltm_set_mutex(result->i->ltm, toku_ydb_mutex());
|
||||
|
||||
{
|
||||
r = toku_logger_create(&result->i->logger);
|
||||
|
@ -3184,7 +3215,10 @@ toku_c_get(DBC* c, DBT* key, DBT* val, u_int32_t flag) {
|
|||
|
||||
static int
|
||||
locked_c_getf_first(DBC *c, u_int32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) {
|
||||
toku_ydb_lock(); int r = toku_c_getf_first(c, flag, f, extra); toku_ydb_unlock(); return r;
|
||||
toku_ydb_lock();
|
||||
int r = toku_c_getf_first(c, flag, f, extra);
|
||||
toku_ydb_unlock();
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -3227,82 +3261,51 @@ locked_c_getf_set_range_reverse(DBC *c, u_int32_t flag, DBT * key, YDB_CALLBACK_
|
|||
toku_ydb_lock(); int r = toku_c_getf_set_range_reverse(c, flag, key, f, extra); toku_ydb_unlock(); return r;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
BOOL is_write_lock;
|
||||
DB_TXN *txn;
|
||||
DB *db;
|
||||
toku_lock_tree *lt;
|
||||
DBT const *left_key;
|
||||
DBT const *right_key;
|
||||
} *RANGE_LOCK_REQUEST, RANGE_LOCK_REQUEST_S;
|
||||
|
||||
static void
|
||||
range_lock_request_init(RANGE_LOCK_REQUEST request,
|
||||
BOOL is_write_lock,
|
||||
DB_TXN *txn,
|
||||
DB *db,
|
||||
DBT const *left_key,
|
||||
DBT const *right_key) {
|
||||
request->is_write_lock = is_write_lock;
|
||||
request->txn = txn;
|
||||
request->db = db;
|
||||
request->lt = db->i->lt;
|
||||
request->left_key = left_key;
|
||||
request->right_key = right_key;
|
||||
}
|
||||
|
||||
static void
|
||||
read_lock_request_init(RANGE_LOCK_REQUEST request,
|
||||
DB_TXN *txn,
|
||||
DB *db,
|
||||
DBT const *left_key,
|
||||
DBT const *right_key) {
|
||||
range_lock_request_init(request, FALSE, txn, db, left_key, right_key);
|
||||
}
|
||||
|
||||
static void
|
||||
write_lock_request_init(RANGE_LOCK_REQUEST request,
|
||||
DB_TXN *txn,
|
||||
DB *db,
|
||||
DBT const *left_key,
|
||||
DBT const *right_key) {
|
||||
range_lock_request_init(request, TRUE, txn, db, left_key, right_key);
|
||||
}
|
||||
|
||||
static int
|
||||
grab_range_lock(RANGE_LOCK_REQUEST request) {
|
||||
get_range_lock(DB *db, DB_TXN *txn, const DBT *left_key, const DBT *right_key, toku_lock_type lock_type) {
|
||||
int r;
|
||||
//TODO: (Multithreading) Grab lock protecting lock tree
|
||||
DB_TXN *txn_anc = toku_txn_ancestor(request->txn);
|
||||
r = toku_txn_add_lt(txn_anc, request->lt);
|
||||
if (r==0) {
|
||||
DB_TXN *txn_anc = toku_txn_ancestor(txn);
|
||||
r = toku_txn_add_lt(txn_anc, db->i->lt);
|
||||
if (r == 0) {
|
||||
TXNID txn_anc_id = toku_txn_get_txnid(db_txn_struct_i(txn_anc)->tokutxn);
|
||||
if (request->is_write_lock)
|
||||
r = toku_lt_acquire_range_write_lock(request->lt, request->db, txn_anc_id,
|
||||
request->left_key, request->right_key);
|
||||
else
|
||||
r = toku_lt_acquire_range_read_lock(request->lt, request->db, txn_anc_id,
|
||||
request->left_key, request->right_key);
|
||||
toku_lock_request lock_request;
|
||||
toku_lock_request_init(&lock_request, db, txn_anc_id, left_key, right_key, lock_type);
|
||||
r = toku_lt_acquire_lock_request_with_default_timeout_locked(db->i->lt, &lock_request);
|
||||
toku_lock_request_destroy(&lock_request);
|
||||
}
|
||||
//TODO: (Multithreading) Release lock protecting lock tree
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
get_range_lock_request(DB *db, DB_TXN *txn, const DBT *left_key, const DBT *right_key, toku_lock_type lock_type, toku_lock_request *lock_request) {
|
||||
int r;
|
||||
DB_TXN *txn_anc = toku_txn_ancestor(txn);
|
||||
r = toku_txn_add_lt(txn_anc, db->i->lt);
|
||||
if (r == 0) {
|
||||
TXNID txn_anc_id = toku_txn_get_txnid(db_txn_struct_i(txn_anc)->tokutxn);
|
||||
toku_lock_request_set(lock_request, db, txn_anc_id, left_key, right_key, lock_type);
|
||||
r = toku_lock_request_start_locked(lock_request, db->i->lt, true);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
get_point_write_lock(DB *db, DB_TXN *txn, const DBT *key) {
|
||||
int r = get_range_lock(db, txn, key, key, LOCK_REQUEST_WRITE);
|
||||
return r;
|
||||
}
|
||||
|
||||
// assume ydb is locked
|
||||
int
|
||||
toku_grab_read_lock_on_directory (DB* db, DB_TXN * txn) {
|
||||
char * dname = db->i->dname;
|
||||
DBT key_in_directory;
|
||||
|
||||
// bad hack because some environment dictionaries do not have a dname
|
||||
char *dname = db->i->dname;
|
||||
if (!dname || (db->dbenv->i->directory->i->lt == NULL))
|
||||
return 0;
|
||||
|
||||
toku_fill_dbt(&key_in_directory, dname, strlen(dname)+1);
|
||||
//Left end of range == right end of range (point lock)
|
||||
RANGE_LOCK_REQUEST_S request;
|
||||
read_lock_request_init(&request, txn, db->dbenv->i->directory,
|
||||
&key_in_directory, &key_in_directory);
|
||||
int r = grab_range_lock(&request);
|
||||
DBT key_in_directory = { .data = dname, .size = strlen(dname)+1 };
|
||||
int r = get_range_lock(db->dbenv->i->directory, txn, &key_in_directory, &key_in_directory, LOCK_REQUEST_READ);
|
||||
if (r == 0)
|
||||
directory_read_locks++;
|
||||
else
|
||||
|
@ -3317,29 +3320,30 @@ typedef struct query_context_base_t {
|
|||
BRT_CURSOR c;
|
||||
DB_TXN *txn;
|
||||
DB *db;
|
||||
YDB_CALLBACK_FUNCTION f;
|
||||
void *f_extra;
|
||||
int r_user_callback;
|
||||
BOOL do_locking;
|
||||
BOOL is_write_op;
|
||||
toku_lock_request lock_request;
|
||||
} *QUERY_CONTEXT_BASE, QUERY_CONTEXT_BASE_S;
|
||||
|
||||
typedef struct query_context_t {
|
||||
QUERY_CONTEXT_BASE_S base;
|
||||
YDB_CALLBACK_FUNCTION f;
|
||||
} *QUERY_CONTEXT, QUERY_CONTEXT_S;
|
||||
|
||||
typedef struct query_context_with_input_t {
|
||||
QUERY_CONTEXT_BASE_S base;
|
||||
YDB_CALLBACK_FUNCTION f;
|
||||
DBT *input_key;
|
||||
DBT *input_val;
|
||||
} *QUERY_CONTEXT_WITH_INPUT, QUERY_CONTEXT_WITH_INPUT_S;
|
||||
|
||||
static void
|
||||
query_context_base_init(QUERY_CONTEXT_BASE context, DBC *c, u_int32_t flag, BOOL is_write_op, void *extra) {
|
||||
query_context_base_init(QUERY_CONTEXT_BASE context, DBC *c, u_int32_t flag, BOOL is_write_op, YDB_CALLBACK_FUNCTION f, void *extra) {
|
||||
context->c = dbc_struct_i(c)->c;
|
||||
context->txn = dbc_struct_i(c)->txn;
|
||||
context->db = c->dbp;
|
||||
context->f = f;
|
||||
context->f_extra = extra;
|
||||
context->is_write_op = is_write_op;
|
||||
u_int32_t lock_flags = get_cursor_prelocked_flags(flag, c);
|
||||
|
@ -3347,28 +3351,31 @@ query_context_base_init(QUERY_CONTEXT_BASE context, DBC *c, u_int32_t flag, BOOL
|
|||
lock_flags &= DB_PRELOCKED_WRITE; // Only care about whether already locked for write
|
||||
context->do_locking = (BOOL)(context->db->i->lt!=NULL && !(lock_flags & (DB_PRELOCKED|DB_PRELOCKED_WRITE)));
|
||||
context->r_user_callback = 0;
|
||||
toku_lock_request_default_init(&context->lock_request);
|
||||
}
|
||||
|
||||
static void
|
||||
query_context_base_destroy(QUERY_CONTEXT_BASE context) {
|
||||
toku_lock_request_destroy(&context->lock_request);
|
||||
}
|
||||
|
||||
static void
|
||||
query_context_init_read(QUERY_CONTEXT context, DBC *c, u_int32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) {
|
||||
BOOL is_write = FALSE;
|
||||
query_context_base_init(&context->base, c, flag, is_write, extra);
|
||||
context->f = f;
|
||||
query_context_base_init(&context->base, c, flag, is_write, f, extra);
|
||||
}
|
||||
|
||||
static void
|
||||
query_context_init_write(QUERY_CONTEXT context, DBC *c, u_int32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) {
|
||||
BOOL is_write = TRUE;
|
||||
query_context_base_init(&context->base, c, flag, is_write, extra);
|
||||
context->f = f;
|
||||
query_context_base_init(&context->base, c, flag, is_write, f, extra);
|
||||
}
|
||||
|
||||
static void
|
||||
query_context_with_input_init(QUERY_CONTEXT_WITH_INPUT context, DBC *c, u_int32_t flag, DBT *key, DBT *val, YDB_CALLBACK_FUNCTION f, void *extra) {
|
||||
// grab write locks if the DB_RMW flag is set or the cursor was created with the DB_RMW flag
|
||||
BOOL is_write = ((flag & DB_RMW) != 0) || dbc_struct_i(c)->rmw;
|
||||
query_context_base_init(&context->base, c, flag, is_write, extra);
|
||||
context->f = f;
|
||||
query_context_base_init(&context->base, c, flag, is_write, f, extra);
|
||||
context->input_key = key;
|
||||
context->input_val = val;
|
||||
}
|
||||
|
@ -3390,13 +3397,21 @@ toku_c_del(DBC * c, u_int32_t flags) {
|
|||
BOOL do_locking = (BOOL)(c->dbp->i->lt && !(lock_flags&DB_PRELOCKED_WRITE));
|
||||
|
||||
int r = 0;
|
||||
if (unchecked_flags!=0) r = EINVAL;
|
||||
if (unchecked_flags!=0)
|
||||
r = EINVAL;
|
||||
else {
|
||||
if (do_locking) {
|
||||
QUERY_CONTEXT_S context;
|
||||
query_context_init_write(&context, c, lock_flags, NULL, NULL);
|
||||
//We do not need a read lock, we must already have it.
|
||||
r = toku_c_getf_current_binding(c, DB_PRELOCKED, c_del_callback, &context);
|
||||
while (r == 0) {
|
||||
//We do not need a read lock, we must already have it.
|
||||
r = toku_c_getf_current_binding(c, DB_PRELOCKED, c_del_callback, &context);
|
||||
if (r == DB_LOCK_NOTGRANTED)
|
||||
r = toku_lock_request_wait_with_default_timeout(&context.base.lock_request, c->dbp->i->lt);
|
||||
else
|
||||
break;
|
||||
}
|
||||
query_context_base_destroy(&context.base);
|
||||
}
|
||||
if (r==0) {
|
||||
//Do the actual delete.
|
||||
|
@ -3419,11 +3434,10 @@ c_del_callback(DBT const *key, DBT const *val, void *extra) {
|
|||
assert(context->is_write_op);
|
||||
assert(key!=NULL);
|
||||
assert(val!=NULL);
|
||||
|
||||
//Lock:
|
||||
// left(key,val)==right(key,val) == (key, val);
|
||||
RANGE_LOCK_REQUEST_S request;
|
||||
write_lock_request_init(&request, context->txn, context->db, key, key);
|
||||
r = grab_range_lock(&request);
|
||||
r = get_range_lock_request(context->db, context->txn, key, key, LOCK_REQUEST_WRITE, &context->lock_request);
|
||||
|
||||
//Give brt-layer an error (if any) to return from toku_c_getf_current_binding
|
||||
return r;
|
||||
|
@ -3431,7 +3445,8 @@ c_del_callback(DBT const *key, DBT const *val, void *extra) {
|
|||
|
||||
static int c_getf_first_callback(ITEMLEN keylen, bytevec key, ITEMLEN vallen, bytevec val, void *extra);
|
||||
|
||||
static void c_query_context_init(QUERY_CONTEXT context, DBC *c, u_int32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) {
|
||||
static void
|
||||
c_query_context_init(QUERY_CONTEXT context, DBC *c, u_int32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) {
|
||||
BOOL is_write_op = FALSE;
|
||||
// grab write locks if the DB_RMW flag is set or the cursor was created with the DB_RMW flag
|
||||
if ((flag & DB_RMW) || dbc_struct_i(c)->rmw)
|
||||
|
@ -3442,16 +3457,31 @@ static void c_query_context_init(QUERY_CONTEXT context, DBC *c, u_int32_t flag,
|
|||
query_context_init_read(context, c, flag, f, extra);
|
||||
}
|
||||
|
||||
static void
|
||||
c_query_context_destroy(QUERY_CONTEXT context) {
|
||||
query_context_base_destroy(&context->base);
|
||||
}
|
||||
|
||||
static int
|
||||
toku_c_getf_first(DBC *c, u_int32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) {
|
||||
HANDLE_PANICKED_DB(c->dbp);
|
||||
HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
|
||||
num_point_queries++; // accountability
|
||||
int r = 0;
|
||||
QUERY_CONTEXT_S context; //Describes the context of this query.
|
||||
c_query_context_init(&context, c, flag, f, extra);
|
||||
//toku_brt_cursor_first will call c_getf_first_callback(..., context) (if query is successful)
|
||||
int r = toku_brt_cursor_first(dbc_struct_i(c)->c, c_getf_first_callback, &context);
|
||||
if (r == TOKUDB_USER_CALLBACK_ERROR) r = context.base.r_user_callback;
|
||||
while (r == 0) {
|
||||
//toku_brt_cursor_first will call c_getf_first_callback(..., context) (if query is successful)
|
||||
r = toku_brt_cursor_first(dbc_struct_i(c)->c, c_getf_first_callback, &context);
|
||||
if (r == DB_LOCK_NOTGRANTED)
|
||||
r = toku_lock_request_wait_with_default_timeout(&context.base.lock_request, c->dbp->i->lt);
|
||||
else {
|
||||
if (r == TOKUDB_USER_CALLBACK_ERROR)
|
||||
r = context.base.r_user_callback;
|
||||
break;
|
||||
}
|
||||
}
|
||||
c_query_context_destroy(&context);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -3462,28 +3492,20 @@ c_getf_first_callback(ITEMLEN keylen, bytevec key, ITEMLEN vallen, bytevec val,
|
|||
QUERY_CONTEXT_BASE context = &super_context->base;
|
||||
|
||||
int r;
|
||||
|
||||
DBT found_key;
|
||||
DBT found_val;
|
||||
toku_fill_dbt(&found_key, key, keylen);
|
||||
toku_fill_dbt(&found_val, val, vallen);
|
||||
DBT found_key = { .data = (void *) key, .size = keylen };
|
||||
DBT found_val = { .data = (void *) val, .size = vallen };
|
||||
|
||||
if (context->do_locking) {
|
||||
RANGE_LOCK_REQUEST_S request;
|
||||
if (key!=NULL) {
|
||||
range_lock_request_init(&request, context->is_write_op, context->txn, context->db,
|
||||
toku_lt_neg_infinity, &found_key);
|
||||
} else {
|
||||
range_lock_request_init(&request, context->is_write_op, context->txn, context->db,
|
||||
toku_lt_neg_infinity, toku_lt_infinity);
|
||||
}
|
||||
r = grab_range_lock(&request);
|
||||
}
|
||||
else r = 0;
|
||||
const DBT *left_key = toku_lt_neg_infinity;
|
||||
const DBT *right_key = key != NULL ? &found_key : toku_lt_infinity;
|
||||
r = get_range_lock_request(context->db, context->txn, left_key, right_key,
|
||||
context->is_write_op ? LOCK_REQUEST_WRITE : LOCK_REQUEST_READ, &context->lock_request);
|
||||
} else
|
||||
r = 0;
|
||||
|
||||
//Call application-layer callback if found and locks were successfully obtained.
|
||||
if (r==0 && key!=NULL) {
|
||||
context->r_user_callback = super_context->f(&found_key, &found_val, context->f_extra);
|
||||
context->r_user_callback = context->f(&found_key, &found_val, context->f_extra);
|
||||
r = context->r_user_callback;
|
||||
}
|
||||
|
||||
|
@ -3498,11 +3520,21 @@ toku_c_getf_last(DBC *c, u_int32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) {
|
|||
HANDLE_PANICKED_DB(c->dbp);
|
||||
HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
|
||||
num_point_queries++; // accountability
|
||||
int r = 0;
|
||||
QUERY_CONTEXT_S context; //Describes the context of this query.
|
||||
c_query_context_init(&context, c, flag, f, extra);
|
||||
//toku_brt_cursor_last will call c_getf_last_callback(..., context) (if query is successful)
|
||||
int r = toku_brt_cursor_last(dbc_struct_i(c)->c, c_getf_last_callback, &context);
|
||||
if (r == TOKUDB_USER_CALLBACK_ERROR) r = context.base.r_user_callback;
|
||||
while (r == 0) {
|
||||
//toku_brt_cursor_last will call c_getf_last_callback(..., context) (if query is successful)
|
||||
r = toku_brt_cursor_last(dbc_struct_i(c)->c, c_getf_last_callback, &context);
|
||||
if (r == DB_LOCK_NOTGRANTED)
|
||||
r = toku_lock_request_wait_with_default_timeout(&context.base.lock_request, c->dbp->i->lt);
|
||||
else {
|
||||
if (r == TOKUDB_USER_CALLBACK_ERROR)
|
||||
r = context.base.r_user_callback;
|
||||
break;
|
||||
}
|
||||
}
|
||||
c_query_context_destroy(&context);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -3513,28 +3545,20 @@ c_getf_last_callback(ITEMLEN keylen, bytevec key, ITEMLEN vallen, bytevec val, v
|
|||
QUERY_CONTEXT_BASE context = &super_context->base;
|
||||
|
||||
int r;
|
||||
|
||||
DBT found_key;
|
||||
DBT found_val;
|
||||
toku_fill_dbt(&found_key, key, keylen);
|
||||
toku_fill_dbt(&found_val, val, vallen);
|
||||
DBT found_key = { .data = (void *) key, .size = keylen };
|
||||
DBT found_val = { .data = (void *) val, .size = vallen };
|
||||
|
||||
if (context->do_locking) {
|
||||
RANGE_LOCK_REQUEST_S request;
|
||||
if (key!=NULL) {
|
||||
range_lock_request_init(&request, context->is_write_op, context->txn, context->db,
|
||||
&found_key, toku_lt_infinity);
|
||||
} else {
|
||||
range_lock_request_init(&request, context->is_write_op, context->txn, context->db,
|
||||
toku_lt_neg_infinity, toku_lt_infinity);
|
||||
}
|
||||
r = grab_range_lock(&request);
|
||||
}
|
||||
else r = 0;
|
||||
const DBT *left_key = key != NULL ? &found_key : toku_lt_neg_infinity;
|
||||
const DBT *right_key = toku_lt_infinity;
|
||||
r = get_range_lock_request(context->db, context->txn, left_key, right_key,
|
||||
context->is_write_op ? LOCK_REQUEST_WRITE : LOCK_REQUEST_READ, &context->lock_request);
|
||||
} else
|
||||
r = 0;
|
||||
|
||||
//Call application-layer callback if found and locks were successfully obtained.
|
||||
if (r==0 && key!=NULL) {
|
||||
context->r_user_callback = super_context->f(&found_key, &found_val, context->f_extra);
|
||||
context->r_user_callback = context->f(&found_key, &found_val, context->f_extra);
|
||||
r = context->r_user_callback;
|
||||
}
|
||||
|
||||
|
@ -3549,13 +3573,24 @@ toku_c_getf_next(DBC *c, u_int32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) {
|
|||
int r;
|
||||
HANDLE_PANICKED_DB(c->dbp);
|
||||
HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
|
||||
if (toku_c_uninitialized(c)) r = toku_c_getf_first(c, flag, f, extra);
|
||||
if (toku_c_uninitialized(c))
|
||||
r = toku_c_getf_first(c, flag, f, extra);
|
||||
else {
|
||||
r = 0;
|
||||
QUERY_CONTEXT_S context; //Describes the context of this query.
|
||||
c_query_context_init(&context, c, flag, f, extra);
|
||||
//toku_brt_cursor_next will call c_getf_next_callback(..., context) (if query is successful)
|
||||
r = toku_brt_cursor_next(dbc_struct_i(c)->c, c_getf_next_callback, &context);
|
||||
if (r == TOKUDB_USER_CALLBACK_ERROR) r = context.base.r_user_callback;
|
||||
while (r == 0) {
|
||||
//toku_brt_cursor_next will call c_getf_next_callback(..., context) (if query is successful)
|
||||
r = toku_brt_cursor_next(dbc_struct_i(c)->c, c_getf_next_callback, &context);
|
||||
if (r == DB_LOCK_NOTGRANTED)
|
||||
r = toku_lock_request_wait_with_default_timeout(&context.base.lock_request, c->dbp->i->lt);
|
||||
else {
|
||||
if (r == TOKUDB_USER_CALLBACK_ERROR)
|
||||
r = context.base.r_user_callback;
|
||||
break;
|
||||
}
|
||||
}
|
||||
c_query_context_destroy(&context);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
@ -3567,29 +3602,24 @@ c_getf_next_callback(ITEMLEN keylen, bytevec key, ITEMLEN vallen, bytevec val, v
|
|||
QUERY_CONTEXT_BASE context = &super_context->base;
|
||||
|
||||
int r;
|
||||
|
||||
DBT found_key = { .data = (void *) key, .size = keylen };
|
||||
DBT found_val = { .data = (void *) val, .size = vallen };
|
||||
num_sequential_queries++; // accountability
|
||||
|
||||
DBT found_key;
|
||||
DBT found_val;
|
||||
toku_fill_dbt(&found_key, key, keylen);
|
||||
toku_fill_dbt(&found_val, val, vallen);
|
||||
|
||||
if (context->do_locking) {
|
||||
RANGE_LOCK_REQUEST_S request;
|
||||
const DBT *prevkey;
|
||||
const DBT *prevval;
|
||||
const DBT *right_key = key==NULL ? toku_lt_infinity : &found_key;
|
||||
|
||||
const DBT *prevkey, *prevval;
|
||||
toku_brt_cursor_peek(context->c, &prevkey, &prevval);
|
||||
range_lock_request_init(&request, context->is_write_op, context->txn, context->db,
|
||||
prevkey, right_key);
|
||||
r = grab_range_lock(&request);
|
||||
}
|
||||
else r = 0;
|
||||
const DBT *left_key = prevkey;
|
||||
const DBT *right_key = key != NULL ? &found_key : toku_lt_infinity;
|
||||
r = get_range_lock_request(context->db, context->txn, left_key, right_key,
|
||||
context->is_write_op ? LOCK_REQUEST_WRITE : LOCK_REQUEST_READ, &context->lock_request);
|
||||
} else
|
||||
r = 0;
|
||||
|
||||
//Call application-layer callback if found and locks were successfully obtained.
|
||||
if (r==0 && key!=NULL) {
|
||||
context->r_user_callback = super_context->f(&found_key, &found_val, context->f_extra);
|
||||
context->r_user_callback = context->f(&found_key, &found_val, context->f_extra);
|
||||
r = context->r_user_callback;
|
||||
}
|
||||
|
||||
|
@ -3604,13 +3634,24 @@ toku_c_getf_prev(DBC *c, u_int32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) {
|
|||
int r;
|
||||
HANDLE_PANICKED_DB(c->dbp);
|
||||
HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
|
||||
if (toku_c_uninitialized(c)) r = toku_c_getf_last(c, flag, f, extra);
|
||||
if (toku_c_uninitialized(c))
|
||||
r = toku_c_getf_last(c, flag, f, extra);
|
||||
else {
|
||||
r = 0;
|
||||
QUERY_CONTEXT_S context; //Describes the context of this query.
|
||||
c_query_context_init(&context, c, flag, f, extra);
|
||||
//toku_brt_cursor_prev will call c_getf_prev_callback(..., context) (if query is successful)
|
||||
r = toku_brt_cursor_prev(dbc_struct_i(c)->c, c_getf_prev_callback, &context);
|
||||
if (r == TOKUDB_USER_CALLBACK_ERROR) r = context.base.r_user_callback;
|
||||
c_query_context_init(&context, c, flag, f, extra);
|
||||
while (r == 0) {
|
||||
//toku_brt_cursor_prev will call c_getf_prev_callback(..., context) (if query is successful)
|
||||
r = toku_brt_cursor_prev(dbc_struct_i(c)->c, c_getf_prev_callback, &context);
|
||||
if (r == DB_LOCK_NOTGRANTED)
|
||||
r = toku_lock_request_wait_with_default_timeout(&context.base.lock_request, c->dbp->i->lt);
|
||||
else {
|
||||
if (r == TOKUDB_USER_CALLBACK_ERROR)
|
||||
r = context.base.r_user_callback;
|
||||
break;
|
||||
}
|
||||
}
|
||||
c_query_context_destroy(&context);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
@ -3622,29 +3663,23 @@ c_getf_prev_callback(ITEMLEN keylen, bytevec key, ITEMLEN vallen, bytevec val, v
|
|||
QUERY_CONTEXT_BASE context = &super_context->base;
|
||||
|
||||
int r;
|
||||
|
||||
DBT found_key = { .data = (void *) key, .size = keylen };
|
||||
DBT found_val = { .data = (void *) val, .size = vallen };
|
||||
num_sequential_queries++; // accountability
|
||||
DBT found_key;
|
||||
DBT found_val;
|
||||
toku_fill_dbt(&found_key, key, keylen);
|
||||
toku_fill_dbt(&found_val, val, vallen);
|
||||
|
||||
if (context->do_locking) {
|
||||
RANGE_LOCK_REQUEST_S request;
|
||||
const DBT *prevkey;
|
||||
const DBT *prevval;
|
||||
const DBT *left_key = key==NULL ? toku_lt_neg_infinity : &found_key;
|
||||
|
||||
const DBT *prevkey, *prevval;
|
||||
toku_brt_cursor_peek(context->c, &prevkey, &prevval);
|
||||
range_lock_request_init(&request, context->is_write_op, context->txn, context->db,
|
||||
left_key, prevkey);
|
||||
r = grab_range_lock(&request);
|
||||
}
|
||||
else r = 0;
|
||||
const DBT *left_key = key != NULL ? &found_key : toku_lt_neg_infinity;
|
||||
const DBT *right_key = prevkey;
|
||||
r = get_range_lock_request(context->db, context->txn, left_key, right_key,
|
||||
context->is_write_op ? LOCK_REQUEST_WRITE : LOCK_REQUEST_READ, &context->lock_request);
|
||||
} else
|
||||
r = 0;
|
||||
|
||||
//Call application-layer callback if found and locks were successfully obtained.
|
||||
if (r==0 && key!=NULL) {
|
||||
context->r_user_callback = super_context->f(&found_key, &found_val, context->f_extra);
|
||||
context->r_user_callback = context->f(&found_key, &found_val, context->f_extra);
|
||||
r = context->r_user_callback;
|
||||
}
|
||||
|
||||
|
@ -3665,6 +3700,7 @@ toku_c_getf_current(DBC *c, u_int32_t flag, YDB_CALLBACK_FUNCTION f, void *extra
|
|||
//toku_brt_cursor_current will call c_getf_current_callback(..., context) (if query is successful)
|
||||
int r = toku_brt_cursor_current(dbc_struct_i(c)->c, DB_CURRENT, c_getf_current_callback, &context);
|
||||
if (r == TOKUDB_USER_CALLBACK_ERROR) r = context.base.r_user_callback;
|
||||
c_query_context_destroy(&context);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -3674,17 +3710,16 @@ c_getf_current_callback(ITEMLEN keylen, bytevec key, ITEMLEN vallen, bytevec val
|
|||
QUERY_CONTEXT super_context = extra;
|
||||
QUERY_CONTEXT_BASE context = &super_context->base;
|
||||
|
||||
DBT found_key;
|
||||
DBT found_val;
|
||||
toku_fill_dbt(&found_key, key, keylen);
|
||||
toku_fill_dbt(&found_val, val, vallen);
|
||||
int r;
|
||||
DBT found_key = { .data = (void *) key, .size = keylen };
|
||||
DBT found_val = { .data = (void *) val, .size = vallen };
|
||||
|
||||
int r=0;
|
||||
//Call application-layer callback if found.
|
||||
if (key!=NULL) {
|
||||
context->r_user_callback = super_context->f(&found_key, &found_val, context->f_extra);
|
||||
context->r_user_callback = context->f(&found_key, &found_val, context->f_extra);
|
||||
r = context->r_user_callback;
|
||||
}
|
||||
} else
|
||||
r = 0;
|
||||
|
||||
//Give brt-layer an error (if any) to return from toku_brt_cursor_current
|
||||
return r;
|
||||
|
@ -3701,6 +3736,7 @@ toku_c_getf_current_binding(DBC *c, u_int32_t flag, YDB_CALLBACK_FUNCTION f, voi
|
|||
//toku_brt_cursor_current will call c_getf_current_callback(..., context) (if query is successful)
|
||||
int r = toku_brt_cursor_current(dbc_struct_i(c)->c, DB_CURRENT_BINDING, c_getf_current_callback, &context);
|
||||
if (r == TOKUDB_USER_CALLBACK_ERROR) r = context.base.r_user_callback;
|
||||
c_query_context_destroy(&context);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -3711,12 +3747,22 @@ toku_c_getf_set(DBC *c, u_int32_t flag, DBT *key, YDB_CALLBACK_FUNCTION f, void
|
|||
HANDLE_PANICKED_DB(c->dbp);
|
||||
HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
|
||||
|
||||
int r = 0;
|
||||
QUERY_CONTEXT_WITH_INPUT_S context; //Describes the context of this query.
|
||||
num_point_queries++; // accountability
|
||||
query_context_with_input_init(&context, c, flag, key, NULL, f, extra);
|
||||
//toku_brt_cursor_set will call c_getf_set_callback(..., context) (if query is successful)
|
||||
int r = toku_brt_cursor_set(dbc_struct_i(c)->c, key, c_getf_set_callback, &context);
|
||||
if (r == TOKUDB_USER_CALLBACK_ERROR) r = context.base.r_user_callback;
|
||||
while (r == 0) {
|
||||
//toku_brt_cursor_set will call c_getf_set_callback(..., context) (if query is successful)
|
||||
r = toku_brt_cursor_set(dbc_struct_i(c)->c, key, c_getf_set_callback, &context);
|
||||
if (r == DB_LOCK_NOTGRANTED)
|
||||
r = toku_lock_request_wait_with_default_timeout(&context.base.lock_request, c->dbp->i->lt);
|
||||
else {
|
||||
if (r == TOKUDB_USER_CALLBACK_ERROR)
|
||||
r = context.base.r_user_callback;
|
||||
break;
|
||||
}
|
||||
}
|
||||
query_context_base_destroy(&context.base);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -3727,26 +3773,21 @@ c_getf_set_callback(ITEMLEN keylen, bytevec key, ITEMLEN vallen, bytevec val, vo
|
|||
QUERY_CONTEXT_BASE context = &super_context->base;
|
||||
|
||||
int r;
|
||||
|
||||
DBT found_key;
|
||||
DBT found_val;
|
||||
toku_fill_dbt(&found_key, key, keylen);
|
||||
toku_fill_dbt(&found_val, val, vallen);
|
||||
DBT found_key = { .data = (void *) key, .size = keylen };
|
||||
DBT found_val = { .data = (void *) val, .size = vallen };
|
||||
|
||||
//Lock:
|
||||
// left(key,val) = (input_key, -infinity)
|
||||
// right(key,val) = (input_key, found ? found_val : infinity)
|
||||
if (context->do_locking) {
|
||||
RANGE_LOCK_REQUEST_S request;
|
||||
range_lock_request_init(&request, context->is_write_op, context->txn, context->db,
|
||||
super_context->input_key, super_context->input_key);
|
||||
r = grab_range_lock(&request);
|
||||
r = get_range_lock_request(context->db, context->txn, super_context->input_key, super_context->input_key,
|
||||
context->is_write_op ? LOCK_REQUEST_WRITE : LOCK_REQUEST_READ, &context->lock_request);
|
||||
} else
|
||||
r = 0;
|
||||
|
||||
//Call application-layer callback if found and locks were successfully obtained.
|
||||
if (r==0 && key!=NULL) {
|
||||
context->r_user_callback = super_context->f(&found_key, &found_val, context->f_extra);
|
||||
context->r_user_callback = context->f(&found_key, &found_val, context->f_extra);
|
||||
r = context->r_user_callback;
|
||||
}
|
||||
|
||||
|
@ -3761,12 +3802,22 @@ toku_c_getf_set_range(DBC *c, u_int32_t flag, DBT *key, YDB_CALLBACK_FUNCTION f,
|
|||
HANDLE_PANICKED_DB(c->dbp);
|
||||
HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
|
||||
|
||||
int r = 0;
|
||||
QUERY_CONTEXT_WITH_INPUT_S context; //Describes the context of this query.
|
||||
num_point_queries++; // accountability
|
||||
query_context_with_input_init(&context, c, flag, key, NULL, f, extra);
|
||||
//toku_brt_cursor_set_range will call c_getf_set_range_callback(..., context) (if query is successful)
|
||||
int r = toku_brt_cursor_set_range(dbc_struct_i(c)->c, key, c_getf_set_range_callback, &context);
|
||||
if (r == TOKUDB_USER_CALLBACK_ERROR) r = context.base.r_user_callback;
|
||||
while (r == 0) {
|
||||
//toku_brt_cursor_set_range will call c_getf_set_range_callback(..., context) (if query is successful)
|
||||
r = toku_brt_cursor_set_range(dbc_struct_i(c)->c, key, c_getf_set_range_callback, &context);
|
||||
if (r == DB_LOCK_NOTGRANTED)
|
||||
r = toku_lock_request_wait_with_default_timeout(&context.base.lock_request, c->dbp->i->lt);
|
||||
else {
|
||||
if (r == TOKUDB_USER_CALLBACK_ERROR)
|
||||
r = context.base.r_user_callback;
|
||||
break;
|
||||
}
|
||||
}
|
||||
query_context_base_destroy(&context.base);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -3777,31 +3828,24 @@ c_getf_set_range_callback(ITEMLEN keylen, bytevec key, ITEMLEN vallen, bytevec v
|
|||
QUERY_CONTEXT_BASE context = &super_context->base;
|
||||
|
||||
int r;
|
||||
|
||||
DBT found_key;
|
||||
DBT found_val;
|
||||
toku_fill_dbt(&found_key, key, keylen);
|
||||
toku_fill_dbt(&found_val, val, vallen);
|
||||
DBT found_key = { .data = (void *) key, .size = keylen };
|
||||
DBT found_val = { .data = (void *) val, .size = vallen };
|
||||
|
||||
//Lock:
|
||||
// left(key,val) = (input_key, -infinity)
|
||||
// right(key) = found ? found_key : infinity
|
||||
// right(val) = found ? found_val : infinity
|
||||
if (context->do_locking) {
|
||||
RANGE_LOCK_REQUEST_S request;
|
||||
if (key!=NULL)
|
||||
range_lock_request_init(&request, context->is_write_op, context->txn, context->db,
|
||||
super_context->input_key, &found_key);
|
||||
else
|
||||
range_lock_request_init(&request, context->is_write_op, context->txn, context->db,
|
||||
super_context->input_key, toku_lt_infinity);
|
||||
r = grab_range_lock(&request);
|
||||
}
|
||||
else r = 0;
|
||||
const DBT *left_key = super_context->input_key;
|
||||
const DBT *right_key = key != NULL ? &found_key : toku_lt_infinity;
|
||||
r = get_range_lock_request(context->db, context->txn, left_key, right_key,
|
||||
context->is_write_op ? LOCK_REQUEST_WRITE : LOCK_REQUEST_READ, &context->lock_request);
|
||||
} else
|
||||
r = 0;
|
||||
|
||||
//Call application-layer callback if found and locks were successfully obtained.
|
||||
if (r==0 && key!=NULL) {
|
||||
context->r_user_callback = super_context->f(&found_key, &found_val, context->f_extra);
|
||||
context->r_user_callback = context->f(&found_key, &found_val, context->f_extra);
|
||||
r = context->r_user_callback;
|
||||
}
|
||||
|
||||
|
@ -3816,12 +3860,22 @@ toku_c_getf_set_range_reverse(DBC *c, u_int32_t flag, DBT *key, YDB_CALLBACK_FUN
|
|||
HANDLE_PANICKED_DB(c->dbp);
|
||||
HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
|
||||
|
||||
int r = 0;
|
||||
QUERY_CONTEXT_WITH_INPUT_S context; //Describes the context of this query.
|
||||
num_point_queries++; // accountability
|
||||
query_context_with_input_init(&context, c, flag, key, NULL, f, extra);
|
||||
//toku_brt_cursor_set_range_reverse will call c_getf_set_range_reverse_callback(..., context) (if query is successful)
|
||||
int r = toku_brt_cursor_set_range_reverse(dbc_struct_i(c)->c, key, c_getf_set_range_reverse_callback, &context);
|
||||
if (r == TOKUDB_USER_CALLBACK_ERROR) r = context.base.r_user_callback;
|
||||
while (r == 0) {
|
||||
//toku_brt_cursor_set_range_reverse will call c_getf_set_range_reverse_callback(..., context) (if query is successful)
|
||||
r = toku_brt_cursor_set_range_reverse(dbc_struct_i(c)->c, key, c_getf_set_range_reverse_callback, &context);
|
||||
if (r == DB_LOCK_NOTGRANTED)
|
||||
r = toku_lock_request_wait_with_default_timeout(&context.base.lock_request, c->dbp->i->lt);
|
||||
else {
|
||||
if (r == TOKUDB_USER_CALLBACK_ERROR)
|
||||
r = context.base.r_user_callback;
|
||||
break;
|
||||
}
|
||||
}
|
||||
query_context_base_destroy(&context.base);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -3832,32 +3886,24 @@ c_getf_set_range_reverse_callback(ITEMLEN keylen, bytevec key, ITEMLEN vallen, b
|
|||
QUERY_CONTEXT_BASE context = &super_context->base;
|
||||
|
||||
int r;
|
||||
|
||||
DBT found_key;
|
||||
DBT found_val;
|
||||
toku_fill_dbt(&found_key, key, keylen);
|
||||
toku_fill_dbt(&found_val, val, vallen);
|
||||
DBT found_key = { .data = (void *) key, .size = keylen };
|
||||
DBT found_val = { .data = (void *) val, .size = vallen };
|
||||
|
||||
//Lock:
|
||||
// left(key) = found ? found_key : -infinity
|
||||
// left(val) = found ? found_val : -infinity
|
||||
// right(key,val) = (input_key, infinity)
|
||||
if (context->do_locking) {
|
||||
RANGE_LOCK_REQUEST_S request;
|
||||
if (key!=NULL) {
|
||||
range_lock_request_init(&request, context->is_write_op, context->txn, context->db,
|
||||
&found_key, super_context->input_key);
|
||||
} else {
|
||||
range_lock_request_init(&request, context->is_write_op, context->txn, context->db,
|
||||
toku_lt_neg_infinity, super_context->input_key);
|
||||
}
|
||||
r = grab_range_lock(&request);
|
||||
}
|
||||
else r = 0;
|
||||
const DBT *left_key = key != NULL ? &found_key : toku_lt_neg_infinity;
|
||||
const DBT *right_key = super_context->input_key;
|
||||
r = get_range_lock_request(context->db, context->txn, left_key, right_key,
|
||||
context->is_write_op ? LOCK_REQUEST_WRITE : LOCK_REQUEST_READ, &context->lock_request);
|
||||
} else
|
||||
r = 0;
|
||||
|
||||
//Call application-layer callback if found and locks were successfully obtained.
|
||||
if (r==0 && key!=NULL) {
|
||||
context->r_user_callback = super_context->f(&found_key, &found_val, context->f_extra);
|
||||
context->r_user_callback = context->f(&found_key, &found_val, context->f_extra);
|
||||
r = context->r_user_callback;
|
||||
}
|
||||
|
||||
|
@ -3878,12 +3924,6 @@ static int toku_c_close(DBC * c) {
|
|||
return r;
|
||||
}
|
||||
|
||||
static inline int
|
||||
keyeq(DBC *c, DBT *a, DBT *b) {
|
||||
DB *db = c->dbp;
|
||||
return db->i->brt->compare_fun(db, a, b) == 0;
|
||||
}
|
||||
|
||||
// Return the number of entries whose key matches the key currently
|
||||
// pointed to by the brt cursor.
|
||||
static int
|
||||
|
@ -3948,17 +3988,6 @@ db_getf_set(DB *db, DB_TXN *txn, u_int32_t flags, DBT *key, YDB_CALLBACK_FUNCTIO
|
|||
return r;
|
||||
}
|
||||
|
||||
////////////
|
||||
|
||||
static int
|
||||
get_point_lock(DB *db, DB_TXN *txn, const DBT *key) {
|
||||
RANGE_LOCK_REQUEST_S request;
|
||||
//Left end of range == right end of range (point lock)
|
||||
write_lock_request_init(&request, txn, db, key, key);
|
||||
int r = grab_range_lock(&request);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
toku_db_del(DB *db, DB_TXN *txn, DBT *key, u_int32_t flags) {
|
||||
HANDLE_PANICKED_DB(db);
|
||||
|
@ -3986,7 +4015,7 @@ toku_db_del(DB *db, DB_TXN *txn, DBT *key, u_int32_t flags) {
|
|||
}
|
||||
if (r == 0 && do_locking) {
|
||||
//Do locking if necessary.
|
||||
r = get_point_lock(db, txn, key);
|
||||
r = get_point_write_lock(db, txn, key);
|
||||
}
|
||||
if (r == 0) {
|
||||
//Do the actual deleting.
|
||||
|
@ -4142,7 +4171,7 @@ env_del_multiple(
|
|||
//Do locking if necessary.
|
||||
if (db->i->lt && !(lock_flags[which_db] & DB_PRELOCKED_WRITE)) {
|
||||
//Needs locking
|
||||
r = get_point_lock(db, txn, &del_keys[which_db]);
|
||||
r = get_point_write_lock(db, txn, &del_keys[which_db]);
|
||||
if (r != 0) goto cleanup;
|
||||
}
|
||||
brts[which_db] = db->i->brt;
|
||||
|
@ -4711,7 +4740,7 @@ toku_db_put(DB *db, DB_TXN *txn, DBT *key, DBT *val, u_int32_t flags) {
|
|||
BOOL do_locking = (BOOL)(db->i->lt && !(lock_flags&DB_PRELOCKED_WRITE));
|
||||
if (r == 0 && do_locking) {
|
||||
//Do locking if necessary.
|
||||
r = get_point_lock(db, txn, key);
|
||||
r = get_point_write_lock(db, txn, key);
|
||||
}
|
||||
if (r == 0) {
|
||||
//Insert into the brt.
|
||||
|
@ -4731,20 +4760,14 @@ toku_db_put(DB *db, DB_TXN *txn, DBT *key, DBT *val, u_int32_t flags) {
|
|||
}
|
||||
|
||||
static int toku_db_pre_acquire_fileops_lock(DB *db, DB_TXN *txn) {
|
||||
char * dname = db->i->dname;
|
||||
DBT key_in_directory;
|
||||
//
|
||||
// bad hack because some environment dictionaries do not have a dname
|
||||
//
|
||||
if (!dname) {
|
||||
char *dname = db->i->dname;
|
||||
if (!dname)
|
||||
return 0;
|
||||
}
|
||||
toku_fill_dbt(&key_in_directory, dname, strlen(dname)+1);
|
||||
|
||||
DBT key_in_directory = { .data = dname, .size = strlen(dname)+1 };
|
||||
//Left end of range == right end of range (point lock)
|
||||
RANGE_LOCK_REQUEST_S request;
|
||||
write_lock_request_init(&request, txn, db->dbenv->i->directory,
|
||||
&key_in_directory, &key_in_directory);
|
||||
int r = grab_range_lock(&request);
|
||||
int r = get_range_lock(db->dbenv->i->directory, txn, &key_in_directory, &key_in_directory, LOCK_REQUEST_WRITE);
|
||||
if (r == 0)
|
||||
directory_write_locks++;
|
||||
else
|
||||
|
@ -4752,8 +4775,6 @@ static int toku_db_pre_acquire_fileops_lock(DB *db, DB_TXN *txn) {
|
|||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int
|
||||
toku_db_update(DB *db, DB_TXN *txn,
|
||||
const DBT *key,
|
||||
|
@ -4776,7 +4797,7 @@ toku_db_update(DB *db, DB_TXN *txn,
|
|||
|
||||
BOOL do_locking = (db->i->lt && !(lock_flags & DB_PRELOCKED_WRITE));
|
||||
if (do_locking) {
|
||||
r = get_point_lock(db, txn, key);
|
||||
r = get_point_write_lock(db, txn, key);
|
||||
if (r != 0) { goto cleanup; }
|
||||
}
|
||||
|
||||
|
@ -4976,7 +4997,7 @@ env_put_multiple(
|
|||
//Do locking if necessary.
|
||||
if (db->i->lt && !(lock_flags[which_db] & DB_PRELOCKED_WRITE)) {
|
||||
//Needs locking
|
||||
r = get_point_lock(db, txn, &put_keys[which_db]);
|
||||
r = get_point_write_lock(db, txn, &put_keys[which_db]);
|
||||
if (r != 0) goto cleanup;
|
||||
}
|
||||
brts[which_db] = db->i->brt;
|
||||
|
@ -5096,7 +5117,7 @@ env_update_multiple(DB_ENV *env, DB *src_db, DB_TXN *txn,
|
|||
|
||||
// lock old key
|
||||
if (db->i->lt && !(lock_flags[which_db] & DB_PRELOCKED_WRITE)) {
|
||||
r = get_point_lock(db, txn, &curr_old_key);
|
||||
r = get_point_write_lock(db, txn, &curr_old_key);
|
||||
if (r != 0) goto cleanup;
|
||||
}
|
||||
del_dbs[n_del_dbs] = db;
|
||||
|
@ -5115,7 +5136,7 @@ env_update_multiple(DB_ENV *env, DB *src_db, DB_TXN *txn,
|
|||
|
||||
// lock new key
|
||||
if (db->i->lt) {
|
||||
r = get_point_lock(db, txn, &curr_new_key);
|
||||
r = get_point_write_lock(db, txn, &curr_new_key);
|
||||
if (r != 0) goto cleanup;
|
||||
}
|
||||
put_dbs[n_put_dbs] = db;
|
||||
|
@ -5540,9 +5561,8 @@ toku_c_pre_acquire_range_lock(DBC *dbc, const DBT *key_left, const DBT *key_righ
|
|||
if (!dbc_struct_i(dbc)->rmw && dbc_struct_i(dbc)->iso != TOKU_ISO_SERIALIZABLE)
|
||||
return 0;
|
||||
|
||||
RANGE_LOCK_REQUEST_S request;
|
||||
range_lock_request_init(&request, dbc_struct_i(dbc)->rmw, txn, db, key_left, key_right);
|
||||
int r = grab_range_lock(&request);
|
||||
toku_lock_type lock_type = dbc_struct_i(dbc)->rmw ? LOCK_REQUEST_WRITE : LOCK_REQUEST_READ;
|
||||
int r = get_range_lock(db, txn, key_left, key_right, lock_type);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -5555,12 +5575,7 @@ toku_db_pre_acquire_table_lock(DB *db, DB_TXN *txn, BOOL just_lock) {
|
|||
|
||||
int r;
|
||||
|
||||
{
|
||||
RANGE_LOCK_REQUEST_S request;
|
||||
write_lock_request_init(&request, txn, db,
|
||||
toku_lt_neg_infinity, toku_lt_infinity);
|
||||
r = grab_range_lock(&request);
|
||||
}
|
||||
r = get_range_lock(db, txn, toku_lt_neg_infinity, toku_lt_infinity, LOCK_REQUEST_WRITE);
|
||||
|
||||
if (r==0 && !just_lock &&
|
||||
!toku_brt_is_recovery_logging_suppressed(db->i->brt) &&
|
||||
|
@ -6506,13 +6521,16 @@ toku_test_get_checkpointing_user_data_status (void) {
|
|||
return toku_cachetable_get_checkpointing_user_data_status();
|
||||
}
|
||||
|
||||
// acquire a point write lock on the key for a given txn.
|
||||
// this does not block the calling thread.
|
||||
int
|
||||
toku_grab_write_lock (DB* db, DBT* key, TOKUTXN tokutxn) {
|
||||
DB_TXN * txn = toku_txn_get_container_db_txn(tokutxn);
|
||||
//Left end of range == right end of range (point lock)
|
||||
RANGE_LOCK_REQUEST_S request;
|
||||
write_lock_request_init(&request, txn, db, key, key);
|
||||
int r = grab_range_lock(&request);
|
||||
toku_grab_write_lock (DB *db, DBT *key, TOKUTXN tokutxn) {
|
||||
DB_TXN *txn = toku_txn_get_container_db_txn(tokutxn);
|
||||
DB_TXN *txn_anc = toku_txn_ancestor(txn);
|
||||
int r = toku_txn_add_lt(txn_anc, db->i->lt);
|
||||
if (r == 0) {
|
||||
TXNID txn_anc_id = toku_txn_get_txnid(db_txn_struct_i(txn_anc)->tokutxn);
|
||||
r = toku_lt_acquire_write_lock(db->i->lt, db, txn_anc_id, key);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue