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,
toku_rt_forest* forest = toku_rth_find(tree->rth, txn);
if (!forest) {
/* Neither selfread nor selfwrite exist. */
r = toku_rth_insert(tree->rth, txn);
if (r!=0) return r;
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) {
return 0;
}
/*
TODO: delete selfread and selfwrite from txn and/or local list
*/
int toku_lt_unlock(toku_lock_tree* tree, DB_TXN* txn) {
assert(tree && txn);
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),
tree->mainread);
r = __toku_lt_free_contents(tree, selfread, tree->mainread);
if (r!=0) return __toku_lt_panic(tree);
selfwrite = __toku_lt_ifexist_selfwrite(tree, txn);
r = __toku_lt_border_delete(tree, selfwrite);
if (r!=0) return __toku_lt_panic(tree);
if (selfread || selfwrite) toku_rth_delete(tree->rth, txn);
return 0;
}
......@@ -57,27 +57,27 @@ typedef struct {
BOOL duplicates;
toku_range_tree* mainread; /**< 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
the range trees that this lock tree owns */
toku_range* buf;
u_int32_t buflen; /**< The length of buf */
u_int32_t buflen; /**< The length of buf */
/** The maximum amount of memory to be used for DBT payloads. */
size_t payload_capacity;
/** The current amount of memory used for DBT payloads. */
size_t payload_used;
/** The key compare function */
int (*compare_fun)(DB*,const DBT*,const DBT*);
int (*compare_fun)(DB*,const DBT*,const DBT*);
/** The data compare function */
int (*dup_compare)(DB*,const DBT*,const DBT*);
int (*dup_compare)(DB*,const DBT*,const DBT*);
/** The panic function */
int (*panic)(DB*);
int (*panic)(DB*);
/** The user malloc function */
void* (*malloc) (size_t);
void* (*malloc) (size_t);
/** The user free function */
void (*free) (void*);
void (*free) (void*);
/** The user realloc function */
void* (*realloc)(void*, size_t);
void* (*realloc)(void*, size_t);
} toku_lock_tree;
......
......@@ -17,22 +17,22 @@
/* 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;
static uint32 __toku_rth_hash(toku_rt_hashtable* table, DB_TXN* key) {
assert(table);
static inline uint32 __toku_rth_hash(toku_rth* table, DB_TXN* key) {
size_t tmp = (size_t)key;
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;
}
int toku_rth_create(toku_rt_hashtable** ptable,
int toku_rth_create(toku_rth** ptable,
void* (*user_malloc) (size_t),
void (*user_free) (void*),
void* (*user_realloc)(void*, size_t)) {
assert(ptable && user_malloc && user_free && user_realloc);
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 (!tmp) return errno;
......@@ -50,7 +50,7 @@ int toku_rth_create(toku_rt_hashtable** ptable,
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);
uint32 index = __toku_rth_hash(table, key);
......@@ -59,14 +59,14 @@ toku_rt_forest* toku_rth_find(toku_rt_hashtable* table, DB_TXN* key) {
return element ? &element->value : NULL;
}
void toku_rth_start_scan(toku_rt_hashtable* table) {
void toku_rth_start_scan(toku_rth* table) {
assert(table);
table->finger_index = 0;
table->finger_ptr = table->table[table->finger_index];
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->finger_end);
......@@ -78,48 +78,67 @@ toku_rth_elt* __toku_rth_next(toku_rt_hashtable* table) {
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);
toku_rth_elt* next = __toku_rth_next(table);
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);
__toku_invalidate_scan(table);
/* No elements. */
if (!table->num_keys) return EDOM;
/* Must have elements. */
assert(table->num_keys);
uint32 index = __toku_rth_hash(table, key);
toku_rth_elt* element = table->table[index];
/* No elements of the right hash. */
if (!element) return EDOM;
/* Elements of the right hash must exist. */
assert(element);
/* Case where it is the first element. */
if (element->key == key) {
table->table[index] = element->next;
goto recycle;
table->free(element);
table->num_keys--;
return;
}
toku_rth_elt* prev;
/* Case where it is not the first element. */
do {
assert(element);
prev = element;
element = element->next;
} while (element && element->key != key);
/* Not found. */
if (!element) return EDOM;
} while (element->key != key);
/* Must be found. */
assert(element);
prev->next = element->next;
goto recycle;
recycle:
element->next = table->free_list;
table->free_list = element;
table->free(element);
table->num_keys--;
return 0;
return;
}
void toku_rth_close(toku_rt_hashtable* table) {
/* Will allow you to insert it over and over. You need to keep track. */
int toku_rth_insert(toku_rth* table, DB_TXN* key) {
assert(table && key);
__toku_invalidate_scan(table);
uint32 index = __toku_rth_hash(table, key);
/* Allocate a new one. */
toku_rth_elt* element = (toku_rth_elt*)table->malloc(sizeof(*element));
if (!element) return errno;
memset(&element->value, 0, sizeof(toku_rt_forest));
element->key = key;
element->next = table->table[index];
table->table[index] = element;
table->num_keys++;
return 0;
}
void toku_rth_close(toku_rth* table) {
assert(table);
toku_rth_elt* element;
toku_rth_elt* next = NULL;
......@@ -131,39 +150,6 @@ void toku_rth_close(toku_rt_hashtable* table) {
table->free(element);
}
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. */
int toku_rth_insert(toku_rt_hashtable* table, DB_TXN* key) {
assert(table && key);
__toku_invalidate_scan(table);
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. */
element = (toku_rth_elt*)table->malloc(sizeof(*element));
if (!element) return errno;
}
memset(&element->value, 0, sizeof(toku_rt_forest));
element->key = key;
element->next = table->table[index];
table->table[index] = element;
table->num_keys++;
return 0;
}
......@@ -29,36 +29,35 @@ struct __toku_rth_elt {
toku_rth_elt* next;
};
typedef struct __toku_rt_hashtable toku_rt_hashtable;
struct __toku_rt_hashtable {
typedef struct __toku_rth toku_rth;
struct __toku_rth {
toku_rth_elt** table;
uint32 num_keys;
uint32 array_size;
uint32 finger_index;
toku_rth_elt* finger_ptr;
BOOL finger_end;
toku_rth_elt* free_list;
/** The user malloc function */
void* (*malloc) (size_t);
void* (*malloc) (size_t);
/** The user free function */
void (*free) (void*);
void (*free) (void*);
/** The user realloc function */
void* (*realloc)(void*, size_t);
void* (*realloc)(void*, size_t);
};
int toku_rth_create(toku_rt_hashtable** ptable,
void* (*user_malloc) (size_t),
void (*user_free) (void*),
void* (*user_realloc)(void*, size_t));
int toku_rth_create(toku_rth** ptable,
void* (*user_malloc) (size_t),
void (*user_free) (void*),
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) {
memset(point, 0, sizeof(toku_point));
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