#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:
Rich Prohaska 2013-04-16 23:59:50 -04:00 committed by Yoni Fogel
parent 0be8bf6210
commit dc4960612a
81 changed files with 7716 additions and 309 deletions

View file

@ -210,7 +210,7 @@ typedef struct __toku_engine_status {
uint64_t mem_requested; /* number of bytes requested via malloc/realloc */ 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_used; /* number of bytes used (obtained from malloc_usable_size()) */
uint64_t mem_freed; /* number of bytes freed */ 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; } ENGINE_STATUS;
typedef enum { typedef enum {
DB_BTREE=1, DB_BTREE=1,
@ -345,7 +345,9 @@ struct __toku_db_env {
int (*set_lk_max_memory) (DB_ENV *env, uint64_t max); int (*set_lk_max_memory) (DB_ENV *env, uint64_t max);
int (*get_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 (*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]; char __toku_dummy1[64];
void *api1_internal; /* 32-bit offset=212 size=4, 64=bit offset=360 size=8 */ void *api1_internal; /* 32-bit offset=212 size=4, 64=bit offset=360 size=8 */
void* __toku_dummy2[7]; void* __toku_dummy2[7];

View file

@ -210,7 +210,7 @@ typedef struct __toku_engine_status {
uint64_t mem_requested; /* number of bytes requested via malloc/realloc */ 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_used; /* number of bytes used (obtained from malloc_usable_size()) */
uint64_t mem_freed; /* number of bytes freed */ 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; } ENGINE_STATUS;
typedef enum { typedef enum {
DB_BTREE=1, DB_BTREE=1,
@ -347,7 +347,9 @@ struct __toku_db_env {
int (*set_lk_max_memory) (DB_ENV *env, uint64_t max); int (*set_lk_max_memory) (DB_ENV *env, uint64_t max);
int (*get_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 (*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]; char __toku_dummy1[96];
void *api1_internal; /* 32-bit offset=244 size=4, 64=bit offset=392 size=8 */ void *api1_internal; /* 32-bit offset=244 size=4, 64=bit offset=392 size=8 */
void* __toku_dummy2[7]; void* __toku_dummy2[7];

View file

@ -210,7 +210,7 @@ typedef struct __toku_engine_status {
uint64_t mem_requested; /* number of bytes requested via malloc/realloc */ 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_used; /* number of bytes used (obtained from malloc_usable_size()) */
uint64_t mem_freed; /* number of bytes freed */ 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; } ENGINE_STATUS;
typedef enum { typedef enum {
DB_BTREE=1, DB_BTREE=1,
@ -347,7 +347,9 @@ struct __toku_db_env {
int (*set_lk_max_memory) (DB_ENV *env, uint64_t max); int (*set_lk_max_memory) (DB_ENV *env, uint64_t max);
int (*get_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 (*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]; char __toku_dummy1[128];
void *api1_internal; /* 32-bit offset=336 size=4, 64=bit offset=544 size=8 */ void *api1_internal; /* 32-bit offset=336 size=4, 64=bit offset=544 size=8 */
void* __toku_dummy2[7]; void* __toku_dummy2[7];

View file

@ -210,7 +210,7 @@ typedef struct __toku_engine_status {
uint64_t mem_requested; /* number of bytes requested via malloc/realloc */ 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_used; /* number of bytes used (obtained from malloc_usable_size()) */
uint64_t mem_freed; /* number of bytes freed */ 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; } ENGINE_STATUS;
typedef enum { typedef enum {
DB_BTREE=1, DB_BTREE=1,
@ -347,7 +347,9 @@ struct __toku_db_env {
int (*set_lk_max_memory) (DB_ENV *env, uint64_t max); int (*set_lk_max_memory) (DB_ENV *env, uint64_t max);
int (*get_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 (*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]; char __toku_dummy1[128];
void *api1_internal; /* 32-bit offset=336 size=4, 64=bit offset=544 size=8 */ void *api1_internal; /* 32-bit offset=336 size=4, 64=bit offset=544 size=8 */
void* __toku_dummy2[8]; void* __toku_dummy2[8];

View file

@ -210,7 +210,7 @@ typedef struct __toku_engine_status {
uint64_t mem_requested; /* number of bytes requested via malloc/realloc */ 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_used; /* number of bytes used (obtained from malloc_usable_size()) */
uint64_t mem_freed; /* number of bytes freed */ 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; } ENGINE_STATUS;
typedef enum { typedef enum {
DB_BTREE=1, DB_BTREE=1,
@ -348,7 +348,9 @@ struct __toku_db_env {
int (*set_lk_max_memory) (DB_ENV *env, uint64_t max); int (*set_lk_max_memory) (DB_ENV *env, uint64_t max);
int (*get_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 (*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]; char __toku_dummy1[144];
void *api1_internal; /* 32-bit offset=356 size=4, 64=bit offset=568 size=8 */ void *api1_internal; /* 32-bit offset=356 size=4, 64=bit offset=568 size=8 */
void* __toku_dummy2[8]; void* __toku_dummy2[8];

View file

@ -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 (*set_lk_max_memory) (DB_ENV *env, uint64_t max)",
"int (*get_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 (*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}; NULL};
print_struct("db_env", 1, db_env_fields32, db_env_fields64, sizeof(db_env_fields32)/sizeof(db_env_fields32[0]), extra); print_struct("db_env", 1, db_env_fields32, db_env_fields64, sizeof(db_env_fields32)/sizeof(db_env_fields32[0]), extra);
} }

View file

@ -210,7 +210,7 @@ typedef struct __toku_engine_status {
uint64_t mem_requested; /* number of bytes requested via malloc/realloc */ 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_used; /* number of bytes used (obtained from malloc_usable_size()) */
uint64_t mem_freed; /* number of bytes freed */ 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; } ENGINE_STATUS;
typedef enum { typedef enum {
DB_BTREE=1, DB_BTREE=1,
@ -348,6 +348,8 @@ struct __toku_db_env {
int (*set_lk_max_memory) (DB_ENV *env, uint64_t max); int (*set_lk_max_memory) (DB_ENV *env, uint64_t max);
int (*get_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 (*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; void *api1_internal;
int (*close) (DB_ENV *, u_int32_t); int (*close) (DB_ENV *, u_int32_t);
int (*dbremove) (DB_ENV *, DB_TXN *, const char *, const char *, u_int32_t); int (*dbremove) (DB_ENV *, DB_TXN *, const char *, const char *, u_int32_t);

View file

@ -210,7 +210,7 @@ typedef struct __toku_engine_status {
uint64_t mem_requested; /* number of bytes requested via malloc/realloc */ 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_used; /* number of bytes used (obtained from malloc_usable_size()) */
uint64_t mem_freed; /* number of bytes freed */ 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; } ENGINE_STATUS;
typedef enum { typedef enum {
DB_BTREE=1, DB_BTREE=1,
@ -348,6 +348,8 @@ struct __toku_db_env {
int (*set_lk_max_memory) (DB_ENV *env, uint64_t max); int (*set_lk_max_memory) (DB_ENV *env, uint64_t max);
int (*get_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 (*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; void *api1_internal;
int (*close) (DB_ENV *, u_int32_t); int (*close) (DB_ENV *, u_int32_t);
int (*dbremove) (DB_ENV *, DB_TXN *, const char *, const char *, u_int32_t); int (*dbremove) (DB_ENV *, DB_TXN *, const char *, const char *, u_int32_t);

View file

@ -1307,7 +1307,6 @@ static void flush_and_maybe_remove (CACHETABLE ct, PAIR p) {
} }
} }
static void do_partial_eviction(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 // 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 // other than CTPAIR_IDLE so that other threads know to not hold

View file

@ -80,13 +80,13 @@ toku_ydb_lock(void) {
if (new_num_waiters > status.max_waiters) status.max_waiters = new_num_waiters; if (new_num_waiters > status.max_waiters) status.max_waiters = new_num_waiters;
status.total_time_since_start = now - ydb_big_lock.starttime; 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 static void
ydb_unlock_internal(unsigned long useconds) { ydb_unlock_internal(unsigned long useconds) {
status.ydb_lock_ctr++; 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 now = get_tokutime();
tokutime_t time_held = now - ydb_big_lock.acquired_time; tokutime_t time_held = now - ydb_big_lock.acquired_time;
@ -113,3 +113,8 @@ void
toku_ydb_unlock_and_yield(unsigned long useconds) { toku_ydb_unlock_and_yield(unsigned long useconds) {
ydb_unlock_internal(useconds); ydb_unlock_internal(useconds);
} }
toku_pthread_mutex_t *
toku_ydb_mutex(void) {
return &ydb_big_lock.lock;
}

View file

@ -20,6 +20,8 @@ OBJS_RAW = \
idlth \ idlth \
lth \ lth \
rth \ rth \
txnid_set \
wfg \
#end #end
LIBRARIES=$(LOCKTREE_LINEAR) $(LOCKTREE_TLOG) $(LOCKTREE) # $(LOCKTREE_LOG) LIBRARIES=$(LOCKTREE_LINEAR) $(LOCKTREE_TLOG) $(LOCKTREE) # $(LOCKTREE_LOG)

View file

@ -28,6 +28,9 @@
/* TODO: During integration, make sure we first verify the NULL CONSISTENCY, /* TODO: During integration, make sure we first verify the NULL CONSISTENCY,
(return EINVAL if necessary) before making lock tree calls. */ (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) { static inline int lt_panic(toku_lock_tree *tree, int r) {
return tree->panic(tree->db, 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_infinity = &__toku_lt_infinity;
const DBT* const toku_lt_neg_infinity = &__toku_lt_neg_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 = &ltm->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(&ltm->lock, NULL); assert_zero(r);
ltm->use_lock = NULL;
}
static void
toku_ltm_destroy_mutex(toku_ltm *ltm) {
int r = toku_pthread_mutex_destroy(&ltm->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) { char* toku_lt_strerror(TOKU_LT_ERROR r) {
if (r >= 0) if (r >= 0)
return strerror(r); 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) { static inline BOOL lt_is_infinite(const DBT* p) {
BOOL r;
if (p == toku_lt_infinity || p == toku_lt_neg_infinity) { if (p == toku_lt_infinity || p == toku_lt_neg_infinity) {
DBT* dbt = (DBT*)p; DBT* dbt = (DBT*)p;
assert(!dbt->data && !dbt->size); assert(!dbt->data && !dbt->size);
return TRUE; r = TRUE;
} } else
return FALSE; r = FALSE;
return r;
} }
/* Verifies that NULL data and size are consistent. /* Verifies that NULL data and size are consistent.
@ -172,6 +211,7 @@ int toku_ltm_create(toku_ltm** pmgr,
if (!tmp_mgr->idlth) { if (!tmp_mgr->idlth) {
r = ENOMEM; goto cleanup; r = ENOMEM; goto cleanup;
} }
toku_ltm_init_mutex(tmp_mgr);
r = 0; r = 0;
*pmgr = tmp_mgr; *pmgr = tmp_mgr;
cleanup: cleanup:
@ -204,6 +244,7 @@ int toku_ltm_close(toku_ltm* mgr) {
} }
toku_lth_close(mgr->lth); toku_lth_close(mgr->lth);
toku_idlth_close(mgr->idlth); toku_idlth_close(mgr->idlth);
toku_ltm_destroy_mutex(mgr);
mgr->free(mgr); mgr->free(mgr);
r = first_error; r = first_error;
@ -1016,7 +1057,7 @@ static inline int lt_free_contents(toku_lock_tree* tree, toku_range_tree* rt, BO
r = 0; r = 0;
toku_rt_clear(rt); toku_rt_clear(rt);
} }
assert(r == 0); assert_zero(r);
return r; return r;
} }
@ -1303,6 +1344,7 @@ int toku_lt_create(toku_lock_tree** ptree,
r = toku_omt_create(&tmp_tree->dbs); r = toku_omt_create(&tmp_tree->dbs);
if (r != 0) if (r != 0)
goto cleanup; goto cleanup;
toku_lock_request_tree_init(tmp_tree);
tmp_tree->ref_count = 1; tmp_tree->ref_count = 1;
*ptree = tmp_tree; *ptree = tmp_tree;
@ -1419,6 +1461,7 @@ int toku_lt_close(toku_lock_tree* tree) {
if (!tree) { if (!tree) {
r = EINVAL; goto cleanup; r = EINVAL; goto cleanup;
} }
toku_lock_request_tree_destroy(tree);
r = toku_rt_close(tree->borderwrite); r = toku_rt_close(tree->borderwrite);
if (!first_error && r != 0) if (!first_error && r != 0)
first_error = r; 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 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); r = toku_omt_fetch(lt->dbs, 0, &dbv);
assert(r == 0); assert_zero(r);
db = dbv; db = dbv;
lt_set_comparison_functions(lt, db); 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; return r;
} }
static int lt_try_acquire_range_write_lock(toku_lock_tree* tree, static int lt_try_acquire_range_write_lock(toku_lock_tree* tree,
DB* db, TXNID txn, DB* db, TXNID txn,
const DBT* key_left, const DBT* key_left,
@ -2084,11 +2128,16 @@ int toku_lt_unlock(toku_lock_tree* tree, TXNID txn) {
if (!tree) { if (!tree) {
r = EINVAL; goto cleanup; 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); r = lt_defer_unlocking_txn(tree, txn);
if (r != 0) if (r != 0)
goto cleanup; goto cleanup;
if (toku_rth_is_empty(tree->txns_still_locked)) if (toku_rth_is_empty(tree->txns_still_locked))
lt_clear(tree); lt_clear(tree);
toku_lt_retry_lock_requests_locked(tree);
r = 0; r = 0;
cleanup: cleanup:
return r; return r;
@ -2170,7 +2219,8 @@ void toku_lt_remove_db_ref(toku_lock_tree* tree, DB *db) {
assert_zero(r); assert_zero(r);
} }
static void lt_verify(toku_lock_tree *lt) { static void
lt_verify(toku_lock_tree *lt) {
// verify the borderwrite tree // verify the borderwrite tree
toku_rt_verify(lt->borderwrite); 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_set_comparison_functions(lt, db);
lt_verify(lt); lt_verify(lt);
lt_clear_comparison_functions(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;
}

View file

@ -17,7 +17,7 @@
each other, due to some system error like failed malloc, each other, due to some system error like failed malloc,
we defer to the db panic handler. Pass in another parameter to do this. we defer to the db panic handler. Pass in another parameter to do this.
*/ */
#include <stdbool.h>
#include <db.h> #include <db.h>
#include <brttypes.h> #include <brttypes.h>
#include <rangetree.h> #include <rangetree.h>
@ -25,7 +25,7 @@
#include <rth.h> #include <rth.h>
#include <idlth.h> #include <idlth.h>
#include <omt.h> #include <omt.h>
#include "toku_pthread.h"
#include "toku_assert.h" #include "toku_assert.h"
#if defined(__cplusplus) #if defined(__cplusplus)
@ -117,6 +117,8 @@ struct __toku_lock_tree {
/** DICTIONARY_ID associated with the lock tree */ /** DICTIONARY_ID associated with the lock tree */
DICTIONARY_ID dict_id; DICTIONARY_ID dict_id;
OMT dbs; //The extant dbs using this lock tree. OMT dbs; //The extant dbs using this lock tree.
OMT lock_requests;
}; };
@ -162,6 +164,10 @@ struct __toku_ltm {
void (*free) (void*); void (*free) (void*);
/** The user realloc function */ /** The user realloc function */
void* (*realloc)(void*, size_t); 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 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); 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) #if defined(__cplusplus)
} }
#endif #endif

View file

@ -22,6 +22,7 @@ SRCS = $(wildcard *.c)
LOG_TESTS = $(patsubst %.c,%.log$(BINSUF),$(SRCS)) LOG_TESTS = $(patsubst %.c,%.log$(BINSUF),$(SRCS))
TLOG_TESTS = $(patsubst %.c,%.tlog$(BINSUF),$(SRCS)) TLOG_TESTS = $(patsubst %.c,%.tlog$(BINSUF),$(SRCS))
LIN_TESTS = $(patsubst %.c,%.lin$(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) 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_TLOG_TESTS = $(patsubst %.tlog$(BINSUF),%.tlogrun,$(TLOG_TESTS))
RUN_LIN_TESTS = $(patsubst %.lin$(BINSUF),%.linrun,$(LIN_TESTS)) RUN_LIN_TESTS = $(patsubst %.lin$(BINSUF),%.linrun,$(LIN_TESTS))
RUN_ALL_TESTS = $(RUN_LIN_TESTS) $(RUN_TLOG_TESTS) #$(RUN_LOG_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 .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) check.tlog: $(RUN_TLOG_TESTS)
tests.log: $(LOG_TESTS) tests.log: $(LOG_TESTS)
check.log: $(RUN_LOG_TESTS) check.log: $(RUN_LOG_TESTS)
check.wfg: $(RUN_WFG_TESTS)
.PHONY: %.linrun %.logrun %.run %.tlogrun .PHONY: %.linrun %.logrun %.run %.tlogrun
# STUFF!!!! # STUFF!!!!

View file

@ -5,6 +5,7 @@
#include <brttypes.h> #include <brttypes.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <toku_assert.h> #include <toku_assert.h>
#include <errno.h> #include <errno.h>

View 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(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
DBT key_l; dbt_init(&key_l, "L", 1);
txnid_set conflicts;
const TXNID txn_a = 1;
toku_lock_request a_r_t; toku_lock_request_init(&a_r_t, (DB *)1, txn_a, toku_lt_neg_infinity, toku_lt_infinity, LOCK_REQUEST_READ);
r = toku_lock_request_start(&a_r_t, lt, false); assert(r == 0);
assert(a_r_t.state == LOCK_REQUEST_COMPLETE && a_r_t.complete_r == 0);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &a_r_t, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 0);
txnid_set_destroy(&conflicts);
toku_lock_request_destroy(&a_r_t);
const TXNID txn_b = 2;
toku_lock_request b_r_l; toku_lock_request_init(&b_r_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&b_r_l, lt, false); assert(r == 0);
assert(b_r_l.state == LOCK_REQUEST_COMPLETE && b_r_l.complete_r == 0);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &b_r_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 0);
txnid_set_destroy(&conflicts);
toku_lock_request_destroy(&b_r_l);
const TXNID txn_c = 3;
toku_lock_request c_w_l; toku_lock_request_init(&c_w_l, (DB *)1, txn_c, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&c_w_l, lt, false); assert(r != 0);
assert(c_w_l.state == LOCK_REQUEST_PENDING);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &c_w_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 2);
sortit(&conflicts);
assert(txnid_set_get(&conflicts, 0) == txn_a);
assert(txnid_set_get(&conflicts, 1) == txn_b);
txnid_set_destroy(&conflicts);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
assert(c_w_l.state == LOCK_REQUEST_PENDING);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &c_w_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 1);
assert(txnid_set_get(&conflicts, 0) == txn_b);
txnid_set_destroy(&conflicts);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
assert(c_w_l.state == LOCK_REQUEST_COMPLETE && c_w_l.complete_r == 0);
toku_lock_request_destroy(&c_w_l);
r = toku_lt_unlock(lt, txn_c); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}

View 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(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
DBT key_l; dbt_init(&key_l, "L", 1);
txnid_set conflicts;
const TXNID txn_a = 1;
toku_lock_request a_r_l; toku_lock_request_init(&a_r_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&a_r_l, lt, false); assert(r == 0);
assert(a_r_l.state == LOCK_REQUEST_COMPLETE && a_r_l.complete_r == 0);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &a_r_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 0);
txnid_set_destroy(&conflicts);
toku_lock_request_destroy(&a_r_l);
const TXNID txn_b = 2;
toku_lock_request b_r_l; toku_lock_request_init(&b_r_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&b_r_l, lt, false); assert(r == 0);
assert(b_r_l.state == LOCK_REQUEST_COMPLETE && b_r_l.complete_r == 0
);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &b_r_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 0);
txnid_set_destroy(&conflicts);
toku_lock_request_destroy(&b_r_l);
const TXNID txn_c = 3;
toku_lock_request c_w_l; toku_lock_request_init(&c_w_l, (DB *)1, txn_c, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&c_w_l, lt, false); assert(r != 0);
assert(c_w_l.state == LOCK_REQUEST_PENDING);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &c_w_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 2);
sortit(&conflicts);
assert(txnid_set_get(&conflicts, 0) == txn_a);
assert(txnid_set_get(&conflicts, 1) == txn_b);
txnid_set_destroy(&conflicts);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
assert(c_w_l.state == LOCK_REQUEST_PENDING);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &c_w_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 1);
assert(txnid_set_get(&conflicts, 0) == txn_b);
txnid_set_destroy(&conflicts);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
assert(c_w_l.state == LOCK_REQUEST_COMPLETE && c_w_l.complete_r == 0);
toku_lock_request_destroy(&c_w_l);
r = toku_lt_unlock(lt, txn_c); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}

View 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(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
DBT key_l; dbt_init(&key_l, "L", 1);
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l, lt, false); assert(r == 0);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
txnid_set conflicts;
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &a_w_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 0);
txnid_set_destroy(&conflicts);
toku_lock_request_destroy(&a_w_l);
const TXNID txn_b = 2;
toku_lock_request b_r_l; toku_lock_request_init(&b_r_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&b_r_l, lt, false); assert(r != 0);
assert(b_r_l.state == LOCK_REQUEST_PENDING);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &b_r_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 1);
assert(txnid_set_get(&conflicts, 0) == txn_a);
txnid_set_destroy(&conflicts);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
assert(b_r_l.state == LOCK_REQUEST_COMPLETE && b_r_l.complete_r == 0);
toku_lock_request_destroy(&b_r_l);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}

View 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(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
DBT key_l; dbt_init(&key_l, "L", 1);
const TXNID txn_a = 1;
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, toku_lt_neg_infinity, toku_lt_infinity, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l, lt, false); assert(r == 0);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
txnid_set conflicts;
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &a_w_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 0);
txnid_set_destroy(&conflicts);
toku_lock_request_destroy(&a_w_l);
const TXNID txn_b = 2;
toku_lock_request b_r_l; toku_lock_request_init(&b_r_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&b_r_l, lt, false); assert(r != 0);
assert(b_r_l.state == LOCK_REQUEST_PENDING);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &b_r_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 1);
assert(txnid_set_get(&conflicts, 0) == txn_a);
txnid_set_destroy(&conflicts);
toku_lock_request_destroy(&b_r_l);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}

View 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(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
DBT key_l; dbt_init(&key_l, "L", 1);
const TXNID txn_a = 1;
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l, lt, false); assert(r == 0);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
txnid_set conflicts;
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &a_w_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 0);
txnid_set_destroy(&conflicts);
toku_lock_request_destroy(&a_w_l);
const TXNID txn_b = 2;
toku_lock_request b_w_l; toku_lock_request_init(&b_w_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&b_w_l, lt, false); assert(r != 0);
assert(b_w_l.state == LOCK_REQUEST_PENDING);
txnid_set_init(&conflicts);
r = toku_lt_get_lock_request_conflicts(lt, &b_w_l, &conflicts);
assert(r == 0);
assert(txnid_set_size(&conflicts) == 1);
assert(txnid_set_get(&conflicts, 0) == txn_a);
txnid_set_destroy(&conflicts);
toku_lock_request_destroy(&b_w_l);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}

View 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(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
const TXNID txn_b = 2;
r = write_lock(lt, txn_a, "L"); assert(r == 0);
for (int t = 1; t < 10; t++) {
toku_ltm_set_lock_wait_time(ltm, t * 1000000);
r = read_lock(lt, txn_b, "L");
assert(r == DB_LOCK_NOTGRANTED);
r = write_lock(lt, txn_b, "L");
assert(r == DB_LOCK_NOTGRANTED);
}
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}

View 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(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
uint64_t target_wait_time, the_wait_time;
toku_ltm_get_lock_wait_time(ltm, &the_wait_time);
assert(the_wait_time == 0);
target_wait_time = 1*1000000 + 0;
toku_ltm_set_lock_wait_time(ltm, target_wait_time);
toku_ltm_get_lock_wait_time(ltm, &the_wait_time);
assert(the_wait_time == target_wait_time);
target_wait_time = 2*1000000 + 3;
toku_ltm_set_lock_wait_time(ltm, target_wait_time);
toku_ltm_get_lock_wait_time(ltm, &the_wait_time);
assert(the_wait_time == target_wait_time);
target_wait_time = ~0;
toku_ltm_set_lock_wait_time(ltm, target_wait_time);
toku_ltm_get_lock_wait_time(ltm, &the_wait_time);
assert(the_wait_time == target_wait_time);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}

View 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(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
const TXNID txn_b = 2;
r = write_lock(lt, txn_a, "L", NULL); assert(r == 0);
for (int t = 1; t < 10; t++) {
struct timeval wait_time = { t, 0 };
r = read_lock(lt, txn_b, "L", &wait_time);
assert(r == DB_LOCK_NOTGRANTED);
r = write_lock(lt, txn_b, "L", &wait_time);
assert(r == DB_LOCK_NOTGRANTED);
}
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}

View 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(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
r = write_lock(lt, txn_a, "L"); assert(r == 0);
const TXNID txn_b = 2;
r = read_lock(lt, txn_b, "L"); assert(r == DB_LOCK_NOTGRANTED);
const TXNID txn_c = 3;
r = read_lock(lt, txn_c, "L"); assert(r == DB_LOCK_NOTGRANTED);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
r = read_lock(lt, txn_b, "L"); assert(r == 0);
r = read_lock(lt, txn_c, "L"); assert(r == 0);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
r = toku_lt_unlock(lt, txn_c); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}

View 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(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
DBT key_l; dbt_init(&key_l, "L", 1);
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l, lt, false); assert(r == 0);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
toku_lock_request_destroy(&a_w_l);
const TXNID txn_b = 2;
toku_lock_request b_w_l; toku_lock_request_init(&b_w_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&b_w_l, lt, false); assert(r != 0);
assert(b_w_l.state == LOCK_REQUEST_PENDING);
const TXNID txn_c = 3;
toku_lock_request c_w_l; toku_lock_request_init(&c_w_l, (DB *)1, txn_c, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&c_w_l, lt, false); assert(r != 0);
assert(c_w_l.state == LOCK_REQUEST_PENDING);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
assert(b_w_l.state == LOCK_REQUEST_COMPLETE && b_w_l.complete_r == 0);
assert(c_w_l.state == LOCK_REQUEST_COMPLETE && c_w_l.complete_r == TOKUDB_OUT_OF_LOCKS);
toku_lock_request_destroy(&b_w_l);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
toku_lock_request_destroy(&c_w_l);
r = toku_lt_unlock(lt, txn_c); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}

View 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(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
DBT key_l; dbt_init(&key_l, "L", 1);
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l, lt, false); assert(r == 0);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
toku_lock_request_destroy(&a_w_l);
const TXNID txn_b = 2;
toku_lock_request b_w_l; toku_lock_request_init(&b_w_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&b_w_l, lt, false); assert(r != 0);
assert(b_w_l.state == LOCK_REQUEST_PENDING);
const TXNID txn_c = 3;
toku_lock_request c_w_l; toku_lock_request_init(&c_w_l, (DB *)1, txn_c, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&c_w_l, lt, false); assert(r != 0);
assert(c_w_l.state == LOCK_REQUEST_PENDING);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
assert(b_w_l.state == LOCK_REQUEST_COMPLETE && b_w_l.complete_r == 0);
assert(c_w_l.state == LOCK_REQUEST_COMPLETE && c_w_l.complete_r == 0);;
toku_lock_request_destroy(&b_w_l);
toku_lock_request_destroy(&c_w_l);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
r = toku_lt_unlock(lt, txn_c); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}

View 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(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_ltm_set_lock_wait_time(ltm, 5000000);
toku_pthread_mutex_t my_mutex = TOKU_PTHREAD_MUTEX_INITIALIZER;
toku_ltm_set_mutex(ltm, &my_mutex);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
const TXNID txn_b = 2;
DBT key_l; dbt_init(&key_l, "L", 1);
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
toku_ltm_lock_mutex(ltm);
r = toku_lock_request_start_locked(&a_w_l, lt, false); assert(r == 0);
toku_ltm_unlock_mutex(ltm);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
toku_lock_request b_w_l; toku_lock_request_init(&b_w_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_WRITE);
toku_ltm_lock_mutex(ltm);
r = toku_lock_request_start_locked(&b_w_l, lt, false); assert(r != 0);
toku_ltm_unlock_mutex(ltm);
assert(b_w_l.state == LOCK_REQUEST_PENDING);
toku_ltm_lock_mutex(ltm);
r = toku_lock_request_wait_with_default_timeout(&b_w_l, lt);
toku_ltm_unlock_mutex(ltm);
assert(r == DB_LOCK_NOTGRANTED);
assert(b_w_l.state == LOCK_REQUEST_COMPLETE);
toku_lock_request_destroy(&a_w_l);
toku_lock_request_destroy(&b_w_l);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}

View 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(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_pthread_mutex_t my_mutex = TOKU_PTHREAD_MUTEX_INITIALIZER;
toku_ltm_set_mutex(ltm, &my_mutex);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
const TXNID txn_b = 2;
r = write_lock(ltm, lt, txn_a, "L"); assert(r == 0);
for (int t = 1; t < 10; t++) {
toku_ltm_set_lock_wait_time(ltm, t * 1000000);
r = read_lock(ltm, lt, txn_b, "L");
assert(r == DB_LOCK_NOTGRANTED);
r = write_lock(ltm, lt, txn_b, "L");
assert(r == DB_LOCK_NOTGRANTED);
}
toku_ltm_lock_mutex(ltm);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
toku_ltm_unlock_mutex(ltm);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}

View 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(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
DBT key_l; dbt_init(&key_l, "L", 1);
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l, lt, false); assert(r == 0);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
toku_lock_request_destroy(&a_w_l);
const TXNID txn_b = 2;
DBT key_m; dbt_init(&key_m, "M", 1);
toku_lock_request b_w_m; toku_lock_request_init(&b_w_m, (DB *)1, txn_b, &key_m, &key_m, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&b_w_m, lt, false); assert(r == 0);
assert(b_w_m.state == LOCK_REQUEST_COMPLETE && b_w_m.complete_r == 0);
toku_lock_request_destroy(&b_w_m);
toku_lock_request a_w_m; toku_lock_request_init(&a_w_m, (DB *)1, txn_a, &key_m, &key_m, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_m, lt, false); assert(r == DB_LOCK_NOTGRANTED);
assert(a_w_m.state == LOCK_REQUEST_PENDING);
toku_lock_request b_w_l; toku_lock_request_init(&b_w_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&b_w_l, lt, false); assert(r == DB_LOCK_DEADLOCK);
assert(b_w_l.state == LOCK_REQUEST_COMPLETE && b_w_l.complete_r == DB_LOCK_DEADLOCK);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
toku_lock_request_destroy(&b_w_l);
assert(a_w_m.state == LOCK_REQUEST_COMPLETE && a_w_m.complete_r == 0
);
toku_lock_request_destroy(&a_w_m);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}

View 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;
}

View 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(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
DBT key_l; dbt_init(&key_l, "L", 1);
toku_lock_request a_r_l; toku_lock_request_init(&a_r_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&a_r_l, lt, false); assert(r == 0);
assert(a_r_l.state == LOCK_REQUEST_COMPLETE && a_r_l.complete_r == 0);
toku_lock_request_destroy(&a_r_l);
const TXNID txn_b = 2;
toku_lock_request b_r_l; toku_lock_request_init(&b_r_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&b_r_l, lt, false); assert(r == 0);
assert(b_r_l.state == LOCK_REQUEST_COMPLETE && b_r_l.complete_r == 0);
toku_lock_request_destroy(&b_r_l);
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l, lt, false); assert(r == DB_LOCK_NOTGRANTED);
assert(a_w_l.state == LOCK_REQUEST_PENDING);
toku_lock_request b_w_l; toku_lock_request_init(&b_w_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&b_w_l, lt, false); assert(r == DB_LOCK_DEADLOCK);
assert(b_w_l.state == LOCK_REQUEST_COMPLETE && b_w_l.complete_r == DB_LOCK_DEADLOCK);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
toku_lock_request_destroy(&b_w_l);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
toku_lock_request_destroy(&a_w_l);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}

View 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(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
DBT key_l; dbt_init(&key_l, "L", 1);
toku_lock_request a_r_l; toku_lock_request_init(&a_r_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&a_r_l, lt, false); assert(r == 0);
assert(a_r_l.state == LOCK_REQUEST_COMPLETE && a_r_l.complete_r == 0);
toku_lock_request_destroy(&a_r_l);
const TXNID txn_b = 2;
toku_lock_request b_r_l; toku_lock_request_init(&b_r_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&b_r_l, lt, true); assert(r == 0);
assert(b_r_l.state == LOCK_REQUEST_COMPLETE && b_r_l.complete_r == 0);
toku_lock_request_destroy(&b_r_l);
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l, lt, true); assert(r == DB_LOCK_NOTGRANTED);
assert(a_w_l.state == LOCK_REQUEST_PENDING);
toku_lock_request b_w_l; toku_lock_request_init(&b_w_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&b_w_l, lt, true); assert(r == DB_LOCK_DEADLOCK);
assert(b_w_l.state == LOCK_REQUEST_COMPLETE && b_w_l.complete_r == DB_LOCK_DEADLOCK);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
toku_lock_request_destroy(&b_w_l);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
toku_lock_request_destroy(&a_w_l);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
r = write_lock(lt, txn_a, "L"); assert(r == 0);
printf("main locked\n");
toku_pthread_t tids[max_threads];
for (int i = 0 ; i < max_threads; i++) {
struct writer_arg *writer_arg = (struct writer_arg *) toku_malloc(sizeof (struct writer_arg));
*writer_arg = (struct writer_arg) { i+10, lt, "L"};
r = toku_pthread_create(&tids[i], NULL, writer_thread, writer_arg); assert(r == 0);
}
sleep(10);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
printf("main unlocked\n");
for (int i = 0; i < max_threads; i++) {
void *retarg;
r = toku_pthread_join(tids[i], &retarg); assert(r == 0);
toku_free(retarg);
}
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}

View 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(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
const TXNID txn_b = 2;
r = write_lock(lt, txn_a, "L"); assert(r == 0);
r = write_lock(lt, txn_b, "L"); assert(r == DB_LOCK_NOTGRANTED);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
r = write_lock(lt, txn_b, "L"); assert(r == 0);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}

View 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(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
const TXNID txn_b = 2;
DBT key_l; dbt_init(&key_l, "L", 1);
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l, lt, false); assert(r == 0);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
toku_lock_request_destroy(&a_w_l);
toku_lock_request b_w_l; toku_lock_request_init(&b_w_l, (DB *)1, txn_b, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&b_w_l, lt, false); assert(r != 0);
assert(b_w_l.state == LOCK_REQUEST_PENDING);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
assert(b_w_l.state == LOCK_REQUEST_COMPLETE && b_w_l.complete_r == 0);
toku_lock_request_destroy(&b_w_l);
r = toku_lt_unlock(lt, txn_b); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}

View 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(&ltm, max_locks, max_lock_memory, dbpanic, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && ltm);
toku_lock_tree *lt = NULL;
r = toku_lt_create(&lt, dbpanic, ltm, get_compare_fun_from_db, toku_malloc, toku_free, toku_realloc);
assert(r == 0 && lt);
const TXNID txn_a = 1;
DBT key_l; dbt_init(&key_l, "L", 1);
toku_lock_request a_w_l; toku_lock_request_init(&a_w_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l, lt, false); assert(r == 0);
assert(a_w_l.state == LOCK_REQUEST_COMPLETE && a_w_l.complete_r == 0);
toku_lock_request_destroy(&a_w_l);
toku_lock_request a_r_l; toku_lock_request_init(&a_r_l, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_READ);
r = toku_lock_request_start(&a_r_l, lt, false); assert(r == 0);
assert(a_r_l.state == LOCK_REQUEST_COMPLETE && a_r_l.complete_r == 0);
toku_lock_request_destroy(&a_r_l);
toku_lock_request a_w_l_2; toku_lock_request_init(&a_w_l_2, (DB *)1, txn_a, &key_l, &key_l, LOCK_REQUEST_WRITE);
r = toku_lock_request_start(&a_w_l_2, lt, false); assert(r == 0);
assert(a_w_l_2.state == LOCK_REQUEST_COMPLETE && a_w_l_2.complete_r == 0);
toku_lock_request_destroy(&a_w_l_2);
r = toku_lt_unlock(lt, txn_a); assert(r == 0);
// shutdown
r = toku_lt_close(lt); assert(r == 0);
r = toku_ltm_close(ltm); assert(r == 0);
return 0;
}

71
src/lock_tree/txnid_set.c Normal file
View 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
View 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
View 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
View 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);

View file

@ -1,5 +1,5 @@
/* -*- mode: C; c-basic-offset: 4 -*- */ /* -*- 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." #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) if (!tree || !query || !buf || !buflen || !numfound)
return EINVAL; return EINVAL;
if (*buflen == 0)
return EINVAL;
u_int32_t temp_numfound = 0; u_int32_t temp_numfound = 0;
for (u_int32_t i = 0; i < tree->numelements; i++) { for (u_int32_t i = 0; i < tree->numelements; i++) {

View file

@ -1,5 +1,5 @@
/* -*- mode: C; c-basic-offset: 4 -*- */ /* -*- 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." #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) { toku_range** buf, u_int32_t* buflen, u_int32_t* numfound) {
int r = ENOSYS; int r = ENOSYS;
if (!tree || !query || !buf || !buflen || !numfound || *buflen == 0) { if (!tree || !query || !buf || !buflen || !numfound) {
r = EINVAL; goto cleanup; r = EINVAL; goto cleanup;
} }
assert(!tree->allow_overlaps); assert(!tree->allow_overlaps);

View file

@ -1,6 +1,5 @@
/* -*- mode: C; c-basic-offset: 4 -*- */ /* -*- 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." #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); assert(buf);
//TODO: SOME ATTRIBUTE TO REMOVE NEVER EXECUTABLE ERROR: assert(buflen); //TODO: SOME ATTRIBUTE TO REMOVE NEVER EXECUTABLE ERROR: assert(buflen);
if (*buflen < num) { 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) while (temp_len < num)
temp_len *= 2; temp_len *= 2;
toku_range* temp_buf = tree->realloc(*buf, temp_len * sizeof(toku_range)); 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; *buf = temp_buf;
*buflen = temp_len; *buflen = temp_len;
} }

View file

@ -86,8 +86,10 @@ int main(int argc, const char *argv[]) {
unsigned oldbufsize = bufsize; unsigned oldbufsize = bufsize;
bufsize = 0; bufsize = 0;
#if 0
r = toku_rt_find(tree, &range.ends, 2, &buf, &bufsize, &found); r = toku_rt_find(tree, &range.ends, 2, &buf, &bufsize, &found);
CKERR2(r, EINVAL); CKERR2(r, EINVAL);
#endif
bufsize = oldbufsize; bufsize = oldbufsize;
r = toku_rt_find(tree, &range.ends, 2, &buf, &bufsize, NULL); r = toku_rt_find(tree, &range.ends, 2, &buf, &bufsize, NULL);

View file

@ -78,12 +78,17 @@ WINDOWS_BDB_DONTRUN_TESTS += \
test_error \ test_error \
#\ ends prev line #\ ends prev line
TDB_DONTRUN_SRCS = \
$(wildcard bdb-simple-deadlock*.c) \
TDB_DONTRUN_TESTS = $(patsubst %.c,%,$(TDB_DONTRUN_SRCS))
ifeq ($(OS_CHOICE),windows) ifeq ($(OS_CHOICE),windows)
TDB_BINS = $(patsubst %.c,%.tdb$(BINSUF),$(filter-out $(patsubst %,%.c,$(WINDOWS_DONTRUN_TESTS)),$(SRCS))) 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)))) TDB_TESTS = $(patsubst %.c,%.tdb$(BINSUF),$(filter-out $(patsubst %,%.c,$(WINDOWS_DONTRUN_TESTS)),$(filter-out $(NONSTANDARD_SRCS),$(SRCS))))
else else
TDB_BINS = $(patsubst %.c,%.tdb$(BINSUF),$(SRCS)) TDB_BINS = $(patsubst %.c,%.tdb$(BINSUF),$(filter-out $(TDB_DONTRUN_SRCS),$(SRCS)))
TDB_TESTS = $(patsubst %.c,%.tdb$(BINSUF),$(filter-out $(NONSTANDARD_SRCS),$(SRCS))) TDB_TESTS = $(patsubst %.c,%.tdb$(BINSUF),$(filter-out $(NONSTANDARD_SRCS),$(filter-out $(TDB_DONTRUN_SRCS),$(SRCS))))
endif endif
# For diskfull.bdb: db-4.6 seems OK, but db-4.3 segfaults # 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 dump.bdb.1426 dump.tdb.1426 test1426.bdb
rm -f *.bdb *.tdb rm -f *.bdb *.tdb
rm -f *.fastlog 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)

View 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;
}

View 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;
}

View 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;
}

View 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
View 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;
}

View 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
View 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
View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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
View 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;
}

View 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;
}

View 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;
}

View 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
View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View file

@ -124,6 +124,7 @@ int toku_ydb_lock_destroy(void);
void toku_ydb_lock(void); void toku_ydb_lock(void);
void toku_ydb_unlock(void); void toku_ydb_unlock(void);
void toku_ydb_unlock_and_yield(unsigned long useconds); 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); void toku_ydb_lock_get_status(SCHEDULE_STATUS statp);

562
src/ydb.c
View file

@ -1761,6 +1761,34 @@ locked_env_set_redzone(DB_ENV *env, int redzone) {
return r; 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 static void
format_time(const time_t *timer, char *buf) { format_time(const time_t *timer, char *buf) {
ctime_r(timer, buf); ctime_r(timer, buf);
@ -2329,6 +2357,8 @@ toku_env_create(DB_ENV ** envp, u_int32_t flags) {
SENV(set_redzone); SENV(set_redzone);
SENV(create_indexer); SENV(create_indexer);
SENV(create_loader); SENV(create_loader);
SENV(get_lock_timeout);
SENV(set_lock_timeout);
#undef SENV #undef SENV
MALLOC(result->i); MALLOC(result->i);
@ -2347,6 +2377,7 @@ toku_env_create(DB_ENV ** envp, u_int32_t flags) {
toku_db_get_compare_fun, toku_db_get_compare_fun,
toku_malloc, toku_free, toku_realloc); toku_malloc, toku_free, toku_realloc);
if (r!=0) { goto cleanup; } if (r!=0) { goto cleanup; }
toku_ltm_set_mutex(result->i->ltm, toku_ydb_mutex());
{ {
r = toku_logger_create(&result->i->logger); 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 static int
locked_c_getf_first(DBC *c, u_int32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) { 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 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; 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 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; int r;
//TODO: (Multithreading) Grab lock protecting lock tree DB_TXN *txn_anc = toku_txn_ancestor(txn);
DB_TXN *txn_anc = toku_txn_ancestor(request->txn); r = toku_txn_add_lt(txn_anc, db->i->lt);
r = toku_txn_add_lt(txn_anc, request->lt); if (r == 0) {
if (r==0) {
TXNID txn_anc_id = toku_txn_get_txnid(db_txn_struct_i(txn_anc)->tokutxn); TXNID txn_anc_id = toku_txn_get_txnid(db_txn_struct_i(txn_anc)->tokutxn);
if (request->is_write_lock) toku_lock_request lock_request;
r = toku_lt_acquire_range_write_lock(request->lt, request->db, txn_anc_id, toku_lock_request_init(&lock_request, db, txn_anc_id, left_key, right_key, lock_type);
request->left_key, request->right_key); r = toku_lt_acquire_lock_request_with_default_timeout_locked(db->i->lt, &lock_request);
else toku_lock_request_destroy(&lock_request);
r = toku_lt_acquire_range_read_lock(request->lt, request->db, txn_anc_id,
request->left_key, request->right_key);
} }
//TODO: (Multithreading) Release lock protecting lock tree
return r; 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 int
toku_grab_read_lock_on_directory (DB* db, DB_TXN * txn) { 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 // 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)) if (!dname || (db->dbenv->i->directory->i->lt == NULL))
return 0; return 0;
toku_fill_dbt(&key_in_directory, dname, strlen(dname)+1);
//Left end of range == right end of range (point lock) //Left end of range == right end of range (point lock)
RANGE_LOCK_REQUEST_S request; DBT key_in_directory = { .data = dname, .size = strlen(dname)+1 };
read_lock_request_init(&request, txn, db->dbenv->i->directory, int r = get_range_lock(db->dbenv->i->directory, txn, &key_in_directory, &key_in_directory, LOCK_REQUEST_READ);
&key_in_directory, &key_in_directory);
int r = grab_range_lock(&request);
if (r == 0) if (r == 0)
directory_read_locks++; directory_read_locks++;
else else
@ -3317,29 +3320,30 @@ typedef struct query_context_base_t {
BRT_CURSOR c; BRT_CURSOR c;
DB_TXN *txn; DB_TXN *txn;
DB *db; DB *db;
YDB_CALLBACK_FUNCTION f;
void *f_extra; void *f_extra;
int r_user_callback; int r_user_callback;
BOOL do_locking; BOOL do_locking;
BOOL is_write_op; BOOL is_write_op;
toku_lock_request lock_request;
} *QUERY_CONTEXT_BASE, QUERY_CONTEXT_BASE_S; } *QUERY_CONTEXT_BASE, QUERY_CONTEXT_BASE_S;
typedef struct query_context_t { typedef struct query_context_t {
QUERY_CONTEXT_BASE_S base; QUERY_CONTEXT_BASE_S base;
YDB_CALLBACK_FUNCTION f;
} *QUERY_CONTEXT, QUERY_CONTEXT_S; } *QUERY_CONTEXT, QUERY_CONTEXT_S;
typedef struct query_context_with_input_t { typedef struct query_context_with_input_t {
QUERY_CONTEXT_BASE_S base; QUERY_CONTEXT_BASE_S base;
YDB_CALLBACK_FUNCTION f;
DBT *input_key; DBT *input_key;
DBT *input_val; DBT *input_val;
} *QUERY_CONTEXT_WITH_INPUT, QUERY_CONTEXT_WITH_INPUT_S; } *QUERY_CONTEXT_WITH_INPUT, QUERY_CONTEXT_WITH_INPUT_S;
static void 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->c = dbc_struct_i(c)->c;
context->txn = dbc_struct_i(c)->txn; context->txn = dbc_struct_i(c)->txn;
context->db = c->dbp; context->db = c->dbp;
context->f = f;
context->f_extra = extra; context->f_extra = extra;
context->is_write_op = is_write_op; context->is_write_op = is_write_op;
u_int32_t lock_flags = get_cursor_prelocked_flags(flag, c); 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 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->do_locking = (BOOL)(context->db->i->lt!=NULL && !(lock_flags & (DB_PRELOCKED|DB_PRELOCKED_WRITE)));
context->r_user_callback = 0; 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 static void
query_context_init_read(QUERY_CONTEXT context, DBC *c, u_int32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) { query_context_init_read(QUERY_CONTEXT context, DBC *c, u_int32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) {
BOOL is_write = FALSE; BOOL is_write = FALSE;
query_context_base_init(&context->base, c, flag, is_write, extra); query_context_base_init(&context->base, c, flag, is_write, f, extra);
context->f = f;
} }
static void static void
query_context_init_write(QUERY_CONTEXT context, DBC *c, u_int32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) { query_context_init_write(QUERY_CONTEXT context, DBC *c, u_int32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) {
BOOL is_write = TRUE; BOOL is_write = TRUE;
query_context_base_init(&context->base, c, flag, is_write, extra); query_context_base_init(&context->base, c, flag, is_write, f, extra);
context->f = f;
} }
static void 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) { 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 // 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; BOOL is_write = ((flag & DB_RMW) != 0) || dbc_struct_i(c)->rmw;
query_context_base_init(&context->base, c, flag, is_write, extra); query_context_base_init(&context->base, c, flag, is_write, f, extra);
context->f = f;
context->input_key = key; context->input_key = key;
context->input_val = val; 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)); BOOL do_locking = (BOOL)(c->dbp->i->lt && !(lock_flags&DB_PRELOCKED_WRITE));
int r = 0; int r = 0;
if (unchecked_flags!=0) r = EINVAL; if (unchecked_flags!=0)
r = EINVAL;
else { else {
if (do_locking) { if (do_locking) {
QUERY_CONTEXT_S context; QUERY_CONTEXT_S context;
query_context_init_write(&context, c, lock_flags, NULL, NULL); query_context_init_write(&context, c, lock_flags, NULL, NULL);
//We do not need a read lock, we must already have it. while (r == 0) {
r = toku_c_getf_current_binding(c, DB_PRELOCKED, c_del_callback, &context); //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) { if (r==0) {
//Do the actual delete. //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(context->is_write_op);
assert(key!=NULL); assert(key!=NULL);
assert(val!=NULL); assert(val!=NULL);
//Lock: //Lock:
// left(key,val)==right(key,val) == (key, val); // left(key,val)==right(key,val) == (key, val);
RANGE_LOCK_REQUEST_S request; r = get_range_lock_request(context->db, context->txn, key, key, LOCK_REQUEST_WRITE, &context->lock_request);
write_lock_request_init(&request, context->txn, context->db, key, key);
r = grab_range_lock(&request);
//Give brt-layer an error (if any) to return from toku_c_getf_current_binding //Give brt-layer an error (if any) to return from toku_c_getf_current_binding
return r; 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 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; 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 // 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) 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); 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 static int
toku_c_getf_first(DBC *c, u_int32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) { toku_c_getf_first(DBC *c, u_int32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) {
HANDLE_PANICKED_DB(c->dbp); HANDLE_PANICKED_DB(c->dbp);
HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c); HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
num_point_queries++; // accountability num_point_queries++; // accountability
int r = 0;
QUERY_CONTEXT_S context; //Describes the context of this query. QUERY_CONTEXT_S context; //Describes the context of this query.
c_query_context_init(&context, c, flag, f, extra); c_query_context_init(&context, c, flag, f, extra);
//toku_brt_cursor_first will call c_getf_first_callback(..., context) (if query is successful) while (r == 0) {
int r = toku_brt_cursor_first(dbc_struct_i(c)->c, c_getf_first_callback, &context); //toku_brt_cursor_first will call c_getf_first_callback(..., context) (if query is successful)
if (r == TOKUDB_USER_CALLBACK_ERROR) r = context.base.r_user_callback; 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; 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; QUERY_CONTEXT_BASE context = &super_context->base;
int r; int r;
DBT found_key = { .data = (void *) key, .size = keylen };
DBT found_key; DBT found_val = { .data = (void *) val, .size = vallen };
DBT found_val;
toku_fill_dbt(&found_key, key, keylen);
toku_fill_dbt(&found_val, val, vallen);
if (context->do_locking) { if (context->do_locking) {
RANGE_LOCK_REQUEST_S request; const DBT *left_key = toku_lt_neg_infinity;
if (key!=NULL) { const DBT *right_key = key != NULL ? &found_key : toku_lt_infinity;
range_lock_request_init(&request, context->is_write_op, context->txn, context->db, r = get_range_lock_request(context->db, context->txn, left_key, right_key,
toku_lt_neg_infinity, &found_key); context->is_write_op ? LOCK_REQUEST_WRITE : LOCK_REQUEST_READ, &context->lock_request);
} else { } else
range_lock_request_init(&request, context->is_write_op, context->txn, context->db, r = 0;
toku_lt_neg_infinity, toku_lt_infinity);
}
r = grab_range_lock(&request);
}
else r = 0;
//Call application-layer callback if found and locks were successfully obtained. //Call application-layer callback if found and locks were successfully obtained.
if (r==0 && key!=NULL) { 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; 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_PANICKED_DB(c->dbp);
HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c); HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
num_point_queries++; // accountability num_point_queries++; // accountability
int r = 0;
QUERY_CONTEXT_S context; //Describes the context of this query. QUERY_CONTEXT_S context; //Describes the context of this query.
c_query_context_init(&context, c, flag, f, extra); c_query_context_init(&context, c, flag, f, extra);
//toku_brt_cursor_last will call c_getf_last_callback(..., context) (if query is successful) while (r == 0) {
int r = toku_brt_cursor_last(dbc_struct_i(c)->c, c_getf_last_callback, &context); //toku_brt_cursor_last will call c_getf_last_callback(..., context) (if query is successful)
if (r == TOKUDB_USER_CALLBACK_ERROR) r = context.base.r_user_callback; 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; 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; QUERY_CONTEXT_BASE context = &super_context->base;
int r; int r;
DBT found_key = { .data = (void *) key, .size = keylen };
DBT found_key; DBT found_val = { .data = (void *) val, .size = vallen };
DBT found_val;
toku_fill_dbt(&found_key, key, keylen);
toku_fill_dbt(&found_val, val, vallen);
if (context->do_locking) { if (context->do_locking) {
RANGE_LOCK_REQUEST_S request; const DBT *left_key = key != NULL ? &found_key : toku_lt_neg_infinity;
if (key!=NULL) { const DBT *right_key = toku_lt_infinity;
range_lock_request_init(&request, context->is_write_op, context->txn, context->db, r = get_range_lock_request(context->db, context->txn, left_key, right_key,
&found_key, toku_lt_infinity); context->is_write_op ? LOCK_REQUEST_WRITE : LOCK_REQUEST_READ, &context->lock_request);
} else { } else
range_lock_request_init(&request, context->is_write_op, context->txn, context->db, r = 0;
toku_lt_neg_infinity, toku_lt_infinity);
}
r = grab_range_lock(&request);
}
else r = 0;
//Call application-layer callback if found and locks were successfully obtained. //Call application-layer callback if found and locks were successfully obtained.
if (r==0 && key!=NULL) { 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; 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; int r;
HANDLE_PANICKED_DB(c->dbp); HANDLE_PANICKED_DB(c->dbp);
HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c); 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 { else {
r = 0;
QUERY_CONTEXT_S context; //Describes the context of this query. QUERY_CONTEXT_S context; //Describes the context of this query.
c_query_context_init(&context, c, flag, f, extra); c_query_context_init(&context, c, flag, f, extra);
//toku_brt_cursor_next will call c_getf_next_callback(..., context) (if query is successful) while (r == 0) {
r = toku_brt_cursor_next(dbc_struct_i(c)->c, c_getf_next_callback, &context); //toku_brt_cursor_next will call c_getf_next_callback(..., context) (if query is successful)
if (r == TOKUDB_USER_CALLBACK_ERROR) r = context.base.r_user_callback; 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; 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; QUERY_CONTEXT_BASE context = &super_context->base;
int r; int r;
DBT found_key = { .data = (void *) key, .size = keylen };
DBT found_val = { .data = (void *) val, .size = vallen };
num_sequential_queries++; // accountability 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) { if (context->do_locking) {
RANGE_LOCK_REQUEST_S request; const DBT *prevkey, *prevval;
const DBT *prevkey;
const DBT *prevval;
const DBT *right_key = key==NULL ? toku_lt_infinity : &found_key;
toku_brt_cursor_peek(context->c, &prevkey, &prevval); toku_brt_cursor_peek(context->c, &prevkey, &prevval);
range_lock_request_init(&request, context->is_write_op, context->txn, context->db, const DBT *left_key = prevkey;
prevkey, right_key); const DBT *right_key = key != NULL ? &found_key : toku_lt_infinity;
r = grab_range_lock(&request); 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; } else
r = 0;
//Call application-layer callback if found and locks were successfully obtained. //Call application-layer callback if found and locks were successfully obtained.
if (r==0 && key!=NULL) { 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; 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; int r;
HANDLE_PANICKED_DB(c->dbp); HANDLE_PANICKED_DB(c->dbp);
HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c); 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 { else {
r = 0;
QUERY_CONTEXT_S context; //Describes the context of this query. QUERY_CONTEXT_S context; //Describes the context of this query.
c_query_context_init(&context, c, flag, f, extra); c_query_context_init(&context, c, flag, f, extra);
//toku_brt_cursor_prev will call c_getf_prev_callback(..., context) (if query is successful) while (r == 0) {
r = toku_brt_cursor_prev(dbc_struct_i(c)->c, c_getf_prev_callback, &context); //toku_brt_cursor_prev will call c_getf_prev_callback(..., context) (if query is successful)
if (r == TOKUDB_USER_CALLBACK_ERROR) r = context.base.r_user_callback; 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; 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; QUERY_CONTEXT_BASE context = &super_context->base;
int r; int r;
DBT found_key = { .data = (void *) key, .size = keylen };
DBT found_val = { .data = (void *) val, .size = vallen };
num_sequential_queries++; // accountability 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) { if (context->do_locking) {
RANGE_LOCK_REQUEST_S request; const DBT *prevkey, *prevval;
const DBT *prevkey;
const DBT *prevval;
const DBT *left_key = key==NULL ? toku_lt_neg_infinity : &found_key;
toku_brt_cursor_peek(context->c, &prevkey, &prevval); toku_brt_cursor_peek(context->c, &prevkey, &prevval);
range_lock_request_init(&request, context->is_write_op, context->txn, context->db, const DBT *left_key = key != NULL ? &found_key : toku_lt_neg_infinity;
left_key, prevkey); const DBT *right_key = prevkey;
r = grab_range_lock(&request); 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; } else
r = 0;
//Call application-layer callback if found and locks were successfully obtained. //Call application-layer callback if found and locks were successfully obtained.
if (r==0 && key!=NULL) { 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; 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) //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); 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; if (r == TOKUDB_USER_CALLBACK_ERROR) r = context.base.r_user_callback;
c_query_context_destroy(&context);
return r; 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 super_context = extra;
QUERY_CONTEXT_BASE context = &super_context->base; QUERY_CONTEXT_BASE context = &super_context->base;
DBT found_key; int r;
DBT found_val; DBT found_key = { .data = (void *) key, .size = keylen };
toku_fill_dbt(&found_key, key, keylen); DBT found_val = { .data = (void *) val, .size = vallen };
toku_fill_dbt(&found_val, val, vallen);
int r=0;
//Call application-layer callback if found. //Call application-layer callback if found.
if (key!=NULL) { 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; r = context->r_user_callback;
} } else
r = 0;
//Give brt-layer an error (if any) to return from toku_brt_cursor_current //Give brt-layer an error (if any) to return from toku_brt_cursor_current
return r; 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) //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); 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; if (r == TOKUDB_USER_CALLBACK_ERROR) r = context.base.r_user_callback;
c_query_context_destroy(&context);
return r; 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_PANICKED_DB(c->dbp);
HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c); HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
int r = 0;
QUERY_CONTEXT_WITH_INPUT_S context; //Describes the context of this query. QUERY_CONTEXT_WITH_INPUT_S context; //Describes the context of this query.
num_point_queries++; // accountability num_point_queries++; // accountability
query_context_with_input_init(&context, c, flag, key, NULL, f, extra); 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) while (r == 0) {
int r = toku_brt_cursor_set(dbc_struct_i(c)->c, key, c_getf_set_callback, &context); //toku_brt_cursor_set will call c_getf_set_callback(..., context) (if query is successful)
if (r == TOKUDB_USER_CALLBACK_ERROR) r = context.base.r_user_callback; 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; 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; QUERY_CONTEXT_BASE context = &super_context->base;
int r; int r;
DBT found_key = { .data = (void *) key, .size = keylen };
DBT found_key; DBT found_val = { .data = (void *) val, .size = vallen };
DBT found_val;
toku_fill_dbt(&found_key, key, keylen);
toku_fill_dbt(&found_val, val, vallen);
//Lock: //Lock:
// left(key,val) = (input_key, -infinity) // left(key,val) = (input_key, -infinity)
// right(key,val) = (input_key, found ? found_val : infinity) // right(key,val) = (input_key, found ? found_val : infinity)
if (context->do_locking) { if (context->do_locking) {
RANGE_LOCK_REQUEST_S request; r = get_range_lock_request(context->db, context->txn, super_context->input_key, super_context->input_key,
range_lock_request_init(&request, context->is_write_op, context->txn, context->db, context->is_write_op ? LOCK_REQUEST_WRITE : LOCK_REQUEST_READ, &context->lock_request);
super_context->input_key, super_context->input_key);
r = grab_range_lock(&request);
} else } else
r = 0; r = 0;
//Call application-layer callback if found and locks were successfully obtained. //Call application-layer callback if found and locks were successfully obtained.
if (r==0 && key!=NULL) { 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; 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_PANICKED_DB(c->dbp);
HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c); HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
int r = 0;
QUERY_CONTEXT_WITH_INPUT_S context; //Describes the context of this query. QUERY_CONTEXT_WITH_INPUT_S context; //Describes the context of this query.
num_point_queries++; // accountability num_point_queries++; // accountability
query_context_with_input_init(&context, c, flag, key, NULL, f, extra); 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) while (r == 0) {
int r = toku_brt_cursor_set_range(dbc_struct_i(c)->c, key, c_getf_set_range_callback, &context); //toku_brt_cursor_set_range will call c_getf_set_range_callback(..., context) (if query is successful)
if (r == TOKUDB_USER_CALLBACK_ERROR) r = context.base.r_user_callback; 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; 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; QUERY_CONTEXT_BASE context = &super_context->base;
int r; int r;
DBT found_key = { .data = (void *) key, .size = keylen };
DBT found_key; DBT found_val = { .data = (void *) val, .size = vallen };
DBT found_val;
toku_fill_dbt(&found_key, key, keylen);
toku_fill_dbt(&found_val, val, vallen);
//Lock: //Lock:
// left(key,val) = (input_key, -infinity) // left(key,val) = (input_key, -infinity)
// right(key) = found ? found_key : infinity // right(key) = found ? found_key : infinity
// right(val) = found ? found_val : infinity // right(val) = found ? found_val : infinity
if (context->do_locking) { if (context->do_locking) {
RANGE_LOCK_REQUEST_S request; const DBT *left_key = super_context->input_key;
if (key!=NULL) const DBT *right_key = key != NULL ? &found_key : toku_lt_infinity;
range_lock_request_init(&request, context->is_write_op, context->txn, context->db, r = get_range_lock_request(context->db, context->txn, left_key, right_key,
super_context->input_key, &found_key); context->is_write_op ? LOCK_REQUEST_WRITE : LOCK_REQUEST_READ, &context->lock_request);
else } else
range_lock_request_init(&request, context->is_write_op, context->txn, context->db, r = 0;
super_context->input_key, toku_lt_infinity);
r = grab_range_lock(&request);
}
else r = 0;
//Call application-layer callback if found and locks were successfully obtained. //Call application-layer callback if found and locks were successfully obtained.
if (r==0 && key!=NULL) { 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; 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_PANICKED_DB(c->dbp);
HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c); HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
int r = 0;
QUERY_CONTEXT_WITH_INPUT_S context; //Describes the context of this query. QUERY_CONTEXT_WITH_INPUT_S context; //Describes the context of this query.
num_point_queries++; // accountability num_point_queries++; // accountability
query_context_with_input_init(&context, c, flag, key, NULL, f, extra); 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) while (r == 0) {
int r = toku_brt_cursor_set_range_reverse(dbc_struct_i(c)->c, key, c_getf_set_range_reverse_callback, &context); //toku_brt_cursor_set_range_reverse will call c_getf_set_range_reverse_callback(..., context) (if query is successful)
if (r == TOKUDB_USER_CALLBACK_ERROR) r = context.base.r_user_callback; 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; 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; QUERY_CONTEXT_BASE context = &super_context->base;
int r; int r;
DBT found_key = { .data = (void *) key, .size = keylen };
DBT found_key; DBT found_val = { .data = (void *) val, .size = vallen };
DBT found_val;
toku_fill_dbt(&found_key, key, keylen);
toku_fill_dbt(&found_val, val, vallen);
//Lock: //Lock:
// left(key) = found ? found_key : -infinity // left(key) = found ? found_key : -infinity
// left(val) = found ? found_val : -infinity // left(val) = found ? found_val : -infinity
// right(key,val) = (input_key, infinity) // right(key,val) = (input_key, infinity)
if (context->do_locking) { if (context->do_locking) {
RANGE_LOCK_REQUEST_S request; const DBT *left_key = key != NULL ? &found_key : toku_lt_neg_infinity;
if (key!=NULL) { const DBT *right_key = super_context->input_key;
range_lock_request_init(&request, context->is_write_op, context->txn, context->db, r = get_range_lock_request(context->db, context->txn, left_key, right_key,
&found_key, super_context->input_key); context->is_write_op ? LOCK_REQUEST_WRITE : LOCK_REQUEST_READ, &context->lock_request);
} else { } else
range_lock_request_init(&request, context->is_write_op, context->txn, context->db, r = 0;
toku_lt_neg_infinity, super_context->input_key);
}
r = grab_range_lock(&request);
}
else r = 0;
//Call application-layer callback if found and locks were successfully obtained. //Call application-layer callback if found and locks were successfully obtained.
if (r==0 && key!=NULL) { 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; r = context->r_user_callback;
} }
@ -3878,12 +3924,6 @@ static int toku_c_close(DBC * c) {
return r; 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 // Return the number of entries whose key matches the key currently
// pointed to by the brt cursor. // pointed to by the brt cursor.
static int 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; 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 static int
toku_db_del(DB *db, DB_TXN *txn, DBT *key, u_int32_t flags) { toku_db_del(DB *db, DB_TXN *txn, DBT *key, u_int32_t flags) {
HANDLE_PANICKED_DB(db); 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) { if (r == 0 && do_locking) {
//Do locking if necessary. //Do locking if necessary.
r = get_point_lock(db, txn, key); r = get_point_write_lock(db, txn, key);
} }
if (r == 0) { if (r == 0) {
//Do the actual deleting. //Do the actual deleting.
@ -4142,7 +4171,7 @@ env_del_multiple(
//Do locking if necessary. //Do locking if necessary.
if (db->i->lt && !(lock_flags[which_db] & DB_PRELOCKED_WRITE)) { if (db->i->lt && !(lock_flags[which_db] & DB_PRELOCKED_WRITE)) {
//Needs locking //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; if (r != 0) goto cleanup;
} }
brts[which_db] = db->i->brt; 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)); BOOL do_locking = (BOOL)(db->i->lt && !(lock_flags&DB_PRELOCKED_WRITE));
if (r == 0 && do_locking) { if (r == 0 && do_locking) {
//Do locking if necessary. //Do locking if necessary.
r = get_point_lock(db, txn, key); r = get_point_write_lock(db, txn, key);
} }
if (r == 0) { if (r == 0) {
//Insert into the brt. //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) { 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 // bad hack because some environment dictionaries do not have a dname
// char *dname = db->i->dname;
if (!dname) { if (!dname)
return 0; 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) //Left end of range == right end of range (point lock)
RANGE_LOCK_REQUEST_S request; int r = get_range_lock(db->dbenv->i->directory, txn, &key_in_directory, &key_in_directory, LOCK_REQUEST_WRITE);
write_lock_request_init(&request, txn, db->dbenv->i->directory,
&key_in_directory, &key_in_directory);
int r = grab_range_lock(&request);
if (r == 0) if (r == 0)
directory_write_locks++; directory_write_locks++;
else else
@ -4752,8 +4775,6 @@ static int toku_db_pre_acquire_fileops_lock(DB *db, DB_TXN *txn) {
return r; return r;
} }
static int static int
toku_db_update(DB *db, DB_TXN *txn, toku_db_update(DB *db, DB_TXN *txn,
const DBT *key, 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)); BOOL do_locking = (db->i->lt && !(lock_flags & DB_PRELOCKED_WRITE));
if (do_locking) { if (do_locking) {
r = get_point_lock(db, txn, key); r = get_point_write_lock(db, txn, key);
if (r != 0) { goto cleanup; } if (r != 0) { goto cleanup; }
} }
@ -4976,7 +4997,7 @@ env_put_multiple(
//Do locking if necessary. //Do locking if necessary.
if (db->i->lt && !(lock_flags[which_db] & DB_PRELOCKED_WRITE)) { if (db->i->lt && !(lock_flags[which_db] & DB_PRELOCKED_WRITE)) {
//Needs locking //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; if (r != 0) goto cleanup;
} }
brts[which_db] = db->i->brt; 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 // lock old key
if (db->i->lt && !(lock_flags[which_db] & DB_PRELOCKED_WRITE)) { 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; if (r != 0) goto cleanup;
} }
del_dbs[n_del_dbs] = db; 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 // lock new key
if (db->i->lt) { 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; if (r != 0) goto cleanup;
} }
put_dbs[n_put_dbs] = db; 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) if (!dbc_struct_i(dbc)->rmw && dbc_struct_i(dbc)->iso != TOKU_ISO_SERIALIZABLE)
return 0; return 0;
RANGE_LOCK_REQUEST_S request; toku_lock_type lock_type = dbc_struct_i(dbc)->rmw ? LOCK_REQUEST_WRITE : LOCK_REQUEST_READ;
range_lock_request_init(&request, dbc_struct_i(dbc)->rmw, txn, db, key_left, key_right); int r = get_range_lock(db, txn, key_left, key_right, lock_type);
int r = grab_range_lock(&request);
return r; return r;
} }
@ -5555,12 +5575,7 @@ toku_db_pre_acquire_table_lock(DB *db, DB_TXN *txn, BOOL just_lock) {
int r; int r;
{ r = get_range_lock(db, txn, toku_lt_neg_infinity, toku_lt_infinity, LOCK_REQUEST_WRITE);
RANGE_LOCK_REQUEST_S request;
write_lock_request_init(&request, txn, db,
toku_lt_neg_infinity, toku_lt_infinity);
r = grab_range_lock(&request);
}
if (r==0 && !just_lock && if (r==0 && !just_lock &&
!toku_brt_is_recovery_logging_suppressed(db->i->brt) && !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(); 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 int
toku_grab_write_lock (DB* db, DBT* key, TOKUTXN tokutxn) { toku_grab_write_lock (DB *db, DBT *key, TOKUTXN tokutxn) {
DB_TXN * txn = toku_txn_get_container_db_txn(tokutxn); DB_TXN *txn = toku_txn_get_container_db_txn(tokutxn);
//Left end of range == right end of range (point lock) DB_TXN *txn_anc = toku_txn_ancestor(txn);
RANGE_LOCK_REQUEST_S request; int r = toku_txn_add_lt(txn_anc, db->i->lt);
write_lock_request_init(&request, txn, db, key, key); if (r == 0) {
int r = grab_range_lock(&request); 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; return r;
} }

View file

@ -121,7 +121,6 @@ typedef struct memory_status {
void toku_memory_get_status(MEMORY_STATUS s); void toku_memory_get_status(MEMORY_STATUS s);
#if defined(__cplusplus) || defined(__cilkplusplus) #if defined(__cplusplus) || defined(__cilkplusplus)
} }
#endif #endif

View file

@ -68,7 +68,6 @@ static inline struct toku_list *toku_list_pop_head(struct toku_list *head) {
return toku_list; return toku_list;
} }
//What does this do?
static inline void toku_list_move(struct toku_list *newhead, struct toku_list *oldhead) { static inline void toku_list_move(struct toku_list *newhead, struct toku_list *oldhead) {
struct toku_list *first = oldhead->next; struct toku_list *first = oldhead->next;
struct toku_list *last = oldhead->prev; struct toku_list *last = oldhead->prev;