Commit d1e31549 authored by Yoni Fogel's avatar Yoni Fogel

Addresses #307

Added test cases for the hashtable.
Now has 100% line and branch coverage of hashtable except for asserts.

git-svn-id: file:///svn/tokudb@2077 c7de825b-a66e-492c-adef-691d508d4ae1
parent 016a5b8f
...@@ -203,6 +203,7 @@ static int __toku_lt_selfread(toku_lock_tree* tree, DB_TXN* txn, ...@@ -203,6 +203,7 @@ static int __toku_lt_selfread(toku_lock_tree* tree, DB_TXN* txn,
toku_rt_forest* forest = toku_rth_find(tree->rth, txn); toku_rt_forest* forest = toku_rth_find(tree->rth, txn);
if (!forest) { if (!forest) {
/* Neither selfread nor selfwrite exist. */
r = toku_rth_insert(tree->rth, txn); r = toku_rth_insert(tree->rth, txn);
if (r!=0) return r; if (r!=0) return r;
forest = toku_rth_find(tree->rth, txn); forest = toku_rth_find(tree->rth, txn);
...@@ -1147,20 +1148,19 @@ static int __toku_lt_border_delete(toku_lock_tree* tree, toku_range_tree* rt) { ...@@ -1147,20 +1148,19 @@ static int __toku_lt_border_delete(toku_lock_tree* tree, toku_range_tree* rt) {
return 0; return 0;
} }
/*
TODO: delete selfread and selfwrite from txn and/or local list
*/
int toku_lt_unlock(toku_lock_tree* tree, DB_TXN* txn) { int toku_lt_unlock(toku_lock_tree* tree, DB_TXN* txn) {
assert(tree && txn);
int r; int r;
toku_range_tree *selfwrite; toku_range_tree *selfwrite = __toku_lt_ifexist_selfwrite(tree, txn);
toku_range_tree *selfread = __toku_lt_ifexist_selfread (tree, txn);
r = __toku_lt_free_contents(tree, __toku_lt_ifexist_selfread(tree, txn), r = __toku_lt_free_contents(tree, selfread, tree->mainread);
tree->mainread);
if (r!=0) return __toku_lt_panic(tree); if (r!=0) return __toku_lt_panic(tree);
selfwrite = __toku_lt_ifexist_selfwrite(tree, txn);
r = __toku_lt_border_delete(tree, selfwrite); r = __toku_lt_border_delete(tree, selfwrite);
if (r!=0) return __toku_lt_panic(tree); if (r!=0) return __toku_lt_panic(tree);
if (selfread || selfwrite) toku_rth_delete(tree->rth, txn);
return 0; return 0;
} }
...@@ -57,7 +57,7 @@ typedef struct { ...@@ -57,7 +57,7 @@ typedef struct {
BOOL duplicates; BOOL duplicates;
toku_range_tree* mainread; /**< See design document */ toku_range_tree* mainread; /**< See design document */
toku_range_tree* borderwrite; /**< See design document */ toku_range_tree* borderwrite; /**< See design document */
toku_rt_hashtable* rth; toku_rth* rth;
/** A temporary area where we store the results of various find on /** A temporary area where we store the results of various find on
the range trees that this lock tree owns */ the range trees that this lock tree owns */
toku_range* buf; toku_range* buf;
......
...@@ -17,22 +17,22 @@ ...@@ -17,22 +17,22 @@
/* TODO: reallocate the hash table if it grows too big. Perhaps, use toku_get_prime in newbrt/primes.c */ /* TODO: reallocate the hash table if it grows too big. Perhaps, use toku_get_prime in newbrt/primes.c */
const uint32 __toku_rth_init_size = 521; const uint32 __toku_rth_init_size = 521;
static uint32 __toku_rth_hash(toku_rt_hashtable* table, DB_TXN* key) { static inline uint32 __toku_rth_hash(toku_rth* table, DB_TXN* key) {
assert(table);
size_t tmp = (size_t)key; size_t tmp = (size_t)key;
return tmp % table->array_size; return tmp % table->array_size;
} }
static inline void __toku_invalidate_scan(toku_rt_hashtable* table) { static inline void __toku_invalidate_scan(toku_rth* table) {
table->finger_end = TRUE; table->finger_end = TRUE;
} }
int toku_rth_create(toku_rt_hashtable** ptable, int toku_rth_create(toku_rth** ptable,
void* (*user_malloc) (size_t), void* (*user_malloc) (size_t),
void (*user_free) (void*), void (*user_free) (void*),
void* (*user_realloc)(void*, size_t)) { void* (*user_realloc)(void*, size_t)) {
assert(ptable && user_malloc && user_free && user_realloc);
int r; int r;
toku_rt_hashtable* tmp = (toku_rt_hashtable*)user_malloc(sizeof(*tmp)); toku_rth* tmp = (toku_rth*)user_malloc(sizeof(*tmp));
if (0) { died1: user_free(tmp); return r; } if (0) { died1: user_free(tmp); return r; }
if (!tmp) return errno; if (!tmp) return errno;
...@@ -50,7 +50,7 @@ int toku_rth_create(toku_rt_hashtable** ptable, ...@@ -50,7 +50,7 @@ int toku_rth_create(toku_rt_hashtable** ptable,
return 0; return 0;
} }
toku_rt_forest* toku_rth_find(toku_rt_hashtable* table, DB_TXN* key) { toku_rt_forest* toku_rth_find(toku_rth* table, DB_TXN* key) {
assert(table && key); assert(table && key);
uint32 index = __toku_rth_hash(table, key); uint32 index = __toku_rth_hash(table, key);
...@@ -59,14 +59,14 @@ toku_rt_forest* toku_rth_find(toku_rt_hashtable* table, DB_TXN* key) { ...@@ -59,14 +59,14 @@ toku_rt_forest* toku_rth_find(toku_rt_hashtable* table, DB_TXN* key) {
return element ? &element->value : NULL; return element ? &element->value : NULL;
} }
void toku_rth_start_scan(toku_rt_hashtable* table) { void toku_rth_start_scan(toku_rth* table) {
assert(table); assert(table);
table->finger_index = 0; table->finger_index = 0;
table->finger_ptr = table->table[table->finger_index]; table->finger_ptr = table->table[table->finger_index];
table->finger_end = FALSE; table->finger_end = FALSE;
} }
toku_rth_elt* __toku_rth_next(toku_rt_hashtable* table) { static inline toku_rth_elt* __toku_rth_next(toku_rth* table) {
assert(table); assert(table);
assert(!table->finger_end); assert(!table->finger_end);
...@@ -78,88 +78,57 @@ toku_rth_elt* __toku_rth_next(toku_rt_hashtable* table) { ...@@ -78,88 +78,57 @@ toku_rth_elt* __toku_rth_next(toku_rt_hashtable* table) {
return table->finger_ptr; return table->finger_ptr;
} }
toku_rt_forest* toku_rth_next(toku_rt_hashtable* table) { toku_rt_forest* toku_rth_next(toku_rth* table) {
assert(table); assert(table);
toku_rth_elt* next = __toku_rth_next(table); toku_rth_elt* next = __toku_rth_next(table);
return next ? &next->value : NULL; return next ? &next->value : NULL;
} }
int toku_rth_delete(toku_rt_hashtable* table, DB_TXN* key) { /* Element MUST exist. */
void toku_rth_delete(toku_rth* table, DB_TXN* key) {
assert(table && key); assert(table && key);
__toku_invalidate_scan(table); __toku_invalidate_scan(table);
/* No elements. */ /* Must have elements. */
if (!table->num_keys) return EDOM; assert(table->num_keys);
uint32 index = __toku_rth_hash(table, key); uint32 index = __toku_rth_hash(table, key);
toku_rth_elt* element = table->table[index]; toku_rth_elt* element = table->table[index];
/* No elements of the right hash. */ /* Elements of the right hash must exist. */
if (!element) return EDOM; assert(element);
/* Case where it is the first element. */ /* Case where it is the first element. */
if (element->key == key) { if (element->key == key) {
table->table[index] = element->next; table->table[index] = element->next;
goto recycle; table->free(element);
table->num_keys--;
return;
} }
toku_rth_elt* prev; toku_rth_elt* prev;
/* Case where it is not the first element. */ /* Case where it is not the first element. */
do { do {
assert(element);
prev = element; prev = element;
element = element->next; element = element->next;
} while (element && element->key != key); } while (element->key != key);
/* Not found. */ /* Must be found. */
if (!element) return EDOM; assert(element);
prev->next = element->next; prev->next = element->next;
goto recycle;
recycle:
element->next = table->free_list;
table->free_list = element;
table->num_keys--;
return 0;
}
void toku_rth_close(toku_rt_hashtable* table) {
toku_rth_elt* element;
toku_rth_elt* next = NULL;
toku_rth_start_scan(table);
next = __toku_rth_next(table);
while (next) {
element = next;
next = __toku_rth_next(table);
table->free(element); table->free(element);
} table->num_keys--;
return;
next = table->free_list;
while (next) {
element = next;
next = next->next;
table->free(element);
}
table->free(table->table);
table->free(table);
} }
/* Will allow you to insert it over and over. You need to keep track. */ /* Will allow you to insert it over and over. You need to keep track. */
int toku_rth_insert(toku_rt_hashtable* table, DB_TXN* key) { int toku_rth_insert(toku_rth* table, DB_TXN* key) {
assert(table && key); assert(table && key);
__toku_invalidate_scan(table); __toku_invalidate_scan(table);
uint32 index = __toku_rth_hash(table, key); uint32 index = __toku_rth_hash(table, key);
/* Recycle */
toku_rth_elt* element;
if (table->free_list) {
element = table->free_list;
table->free_list = table->free_list->next;
}
else {
/* Allocate a new one. */ /* Allocate a new one. */
element = (toku_rth_elt*)table->malloc(sizeof(*element)); toku_rth_elt* element = (toku_rth_elt*)table->malloc(sizeof(*element));
if (!element) return errno; if (!element) return errno;
}
memset(&element->value, 0, sizeof(toku_rt_forest)); memset(&element->value, 0, sizeof(toku_rt_forest));
element->key = key; element->key = key;
element->next = table->table[index]; element->next = table->table[index];
...@@ -167,3 +136,20 @@ int toku_rth_insert(toku_rt_hashtable* table, DB_TXN* key) { ...@@ -167,3 +136,20 @@ int toku_rth_insert(toku_rt_hashtable* table, DB_TXN* key) {
table->num_keys++; table->num_keys++;
return 0; return 0;
} }
void toku_rth_close(toku_rth* table) {
assert(table);
toku_rth_elt* element;
toku_rth_elt* next = NULL;
toku_rth_start_scan(table);
next = __toku_rth_next(table);
while (next) {
element = next;
next = __toku_rth_next(table);
table->free(element);
}
table->free(table->table);
table->free(table);
}
...@@ -29,15 +29,14 @@ struct __toku_rth_elt { ...@@ -29,15 +29,14 @@ struct __toku_rth_elt {
toku_rth_elt* next; toku_rth_elt* next;
}; };
typedef struct __toku_rt_hashtable toku_rt_hashtable; typedef struct __toku_rth toku_rth;
struct __toku_rt_hashtable { struct __toku_rth {
toku_rth_elt** table; toku_rth_elt** table;
uint32 num_keys; uint32 num_keys;
uint32 array_size; uint32 array_size;
uint32 finger_index; uint32 finger_index;
toku_rth_elt* finger_ptr; toku_rth_elt* finger_ptr;
BOOL finger_end; BOOL finger_end;
toku_rth_elt* free_list;
/** The user malloc function */ /** The user malloc function */
void* (*malloc) (size_t); void* (*malloc) (size_t);
/** The user free function */ /** The user free function */
...@@ -46,19 +45,19 @@ struct __toku_rt_hashtable { ...@@ -46,19 +45,19 @@ struct __toku_rt_hashtable {
void* (*realloc)(void*, size_t); void* (*realloc)(void*, size_t);
}; };
int toku_rth_create(toku_rt_hashtable** ptable, int toku_rth_create(toku_rth** ptable,
void* (*user_malloc) (size_t), void* (*user_malloc) (size_t),
void (*user_free) (void*), void (*user_free) (void*),
void* (*user_realloc)(void*, size_t)); void* (*user_realloc)(void*, size_t));
toku_rt_forest* toku_rth_find(toku_rt_hashtable* table, DB_TXN* key); toku_rt_forest* toku_rth_find (toku_rth* table, DB_TXN* key);
void toku_rth_start_scan(toku_rt_hashtable* table); void toku_rth_start_scan (toku_rth* table);
toku_rt_forest* toku_rth_next(toku_rt_hashtable* table); toku_rt_forest* toku_rth_next (toku_rth* table);
int toku_rth_delete(toku_rt_hashtable* table, DB_TXN* key); void toku_rth_delete (toku_rth* table, DB_TXN* key);
void toku_rth_close(toku_rt_hashtable* table); void toku_rth_close (toku_rth* table);
int toku_rth_insert(toku_rt_hashtable* table, DB_TXN* key); int toku_rth_insert (toku_rth* table, DB_TXN* key);
...@@ -112,3 +112,14 @@ void init_point(toku_point* point, toku_lock_tree* tree) { ...@@ -112,3 +112,14 @@ void init_point(toku_point* point, toku_lock_tree* tree) {
memset(point, 0, sizeof(toku_point)); memset(point, 0, sizeof(toku_point));
point->lt = tree; point->lt = tree;
} }
int mallocced = 0;
int failon = -1;
void* fail_malloc(size_t size) {
if (++mallocced == failon) {
errno = ENOMEM;
return NULL;
}
return malloc(size);
}
/* We are going to test whether create and close properly check their input. */
#include "test.h"
toku_rth* rth;
int main(int argc, const char *argv[]) {
int r;
parse_args(argc, argv);
rth = NULL;
for (failon = 1; failon <= 2; failon++) {
mallocced = 0;
r = toku_rth_create(&rth, fail_malloc, toku_free, toku_realloc);
CKERR2(r, ENOMEM);
assert(rth==NULL);
}
r = toku_rth_create(&rth, toku_malloc, toku_free, toku_realloc);
CKERR(r);
assert(rth);
toku_rth_close(rth);
rth = NULL;
size_t i;
size_t iterations = 512 << 2;
r = toku_rth_create(&rth, toku_malloc, toku_free, toku_realloc);
CKERR(r);
assert(rth);
for (i = 1; i < iterations; i++) {
r = toku_rth_insert(rth, (DB_TXN*)i);
CKERR(r);
}
toku_rt_forest* f;
for (i = 1; i < iterations; i++) {
f = toku_rth_find(rth, (DB_TXN*)i);
assert(f);
}
f = toku_rth_find(rth, (DB_TXN*)i);
assert(!f);
for (i = iterations - 1; i >= 1; i--) {
toku_rth_delete(rth, (DB_TXN*)i);
}
toku_rth_close(rth);
rth = NULL;
r = toku_rth_create(&rth, toku_malloc, toku_free, toku_realloc);
CKERR(r);
assert(rth);
for (i = 1; i < iterations; i++) {
r = toku_rth_insert(rth, (DB_TXN*)i);
CKERR(r);
}
for (i = 1; i < iterations; i++) {
toku_rth_delete(rth, (DB_TXN*)i);
}
toku_rth_close(rth);
rth = NULL;
r = toku_rth_create(&rth, toku_malloc, toku_free, toku_realloc);
CKERR(r);
assert(rth);
for (i = iterations - 1; i >= 1; i--) {
r = toku_rth_insert(rth, (DB_TXN*)i);
CKERR(r);
}
toku_rth_close(rth);
rth = NULL;
failon = 3;
mallocced = 0;
r = toku_rth_create(&rth, fail_malloc, toku_free, toku_realloc);
CKERR(r);
assert(rth);
r = toku_rth_insert(rth, (DB_TXN*)1);
CKERR2(r, ENOMEM);
toku_rth_close(rth);
rth = NULL;
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment