diff --git a/lladd/constants.h b/lladd/constants.h index 0067e47..ed14fbb 100644 --- a/lladd/constants.h +++ b/lladd/constants.h @@ -122,6 +122,9 @@ terms specified in this license. #define OPERATION_SET_RANGE_INVERSE 28 #define OPERATION_LINKED_LIST_INSERT 29 #define OPERATION_LINKED_LIST_REMOVE 30 +#define OPERATION_LINEAR_HASH_INSERT 31 +#define OPERATION_LINEAR_HASH_REMOVE 32 + /* number above should be less than number below */ #define MAX_OPERATIONS 40 diff --git a/lladd/operations/linearHashNTA.h b/lladd/operations/linearHashNTA.h index 0601ce8..bee2acd 100644 --- a/lladd/operations/linearHashNTA.h +++ b/lladd/operations/linearHashNTA.h @@ -2,11 +2,15 @@ #define __LINEAR_HASH_NTA_H typedef struct { - long current_hashBucket; - recordid current_recordid; + recordid hashHeader; + recordid bucket; + int numBuckets; + int keySize; + int valueSize; + lladd_linkedList_iterator * it; } lladd_hash_iterator; -/** Implementaiton of a linear hash table that makes use of nested top actions. */ +/** Implementation of a linear hash table that makes use of nested top actions. */ recordid ThashCreate(int xid, int keySize, int valSize); void ThashDelete(int xid, recordid hash); @@ -19,9 +23,11 @@ int ThashRemove(int xid, recordid hash, const byte* key, int keySize); (a return value of zero means the key is associated with an empty value.) */ int ThashLookup(int xid, recordid hash, const byte* key, int keySize, byte ** value); -lladd_hash_iterator * ThashIterator(int xid, recordid hash); -int ThashIteratorNext(int xid, recordid hash, lladd_hash_iterator * it, byte ** key, byte** value); -void ThashIteratorFree(int xid, lladd_hash_iterator * it); +lladd_hash_iterator * ThashIterator(int xid, recordid hash, int keySize, int valueSize); +int ThashNext(int xid, lladd_hash_iterator * it, byte ** key, int * keySize, byte** value, int * valueSize); + +Operation getLinearHashInsert(); +Operation getLinearHashRemove(); //Support 16 entries by default. #define HASH_INIT_BITS 4 diff --git a/lladd/operations/nestedTopActions.h b/lladd/operations/nestedTopActions.h index d7050f9..536c04b 100644 --- a/lladd/operations/nestedTopActions.h +++ b/lladd/operations/nestedTopActions.h @@ -61,6 +61,6 @@ terms specified in this license. #include void initNestedTopActions(); -void TbeginNestedTopAction(int xid, int op, const byte* log_arguments, int log_arguments_length); -lsn_t TendNestedTopAction(int xid); +void * TbeginNestedTopAction(int xid, int op, const byte* log_arguments, int log_arguments_length); +lsn_t TendNestedTopAction(int xid, void * handle); #endif diff --git a/src/lladd/operations/linearHashNTA.c b/src/lladd/operations/linearHashNTA.c index 8f0d893..fe51db7 100644 --- a/src/lladd/operations/linearHashNTA.c +++ b/src/lladd/operations/linearHashNTA.c @@ -2,7 +2,19 @@ #include #include #include +#include +#define __USE_GNU +#include +static pthread_mutex_t linear_hash_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; + +/** + re-entrant implementation of a linear hash hable, using nensted top actions. + + @file + + @todo Improve concurrency of linearHashNTA and linkedListNTA. +*/ typedef struct { recordid buckets; @@ -49,9 +61,81 @@ void ThashDelete(int xid, recordid hash) { abort(); } +static int __ThashInsert(int xid, recordid hashHeader, const byte* key, int keySize, const byte* value, int valueSize); +static int __ThashRemove(int xid, recordid hashHeader, const byte * key, int keySize); + +typedef struct { + recordid hashHeader; + int keySize; +} linearHash_insert_arg; + +typedef struct { + recordid hashHeader; + int keySize; + int valueSize; +} linearHash_remove_arg; + +static int operateInsert(int xid, Page *p, lsn_t lsn, recordid rid, const void *dat) { + const linearHash_remove_arg * args = dat; + recordid hashHeader = args->hashHeader; + int keySize = args->keySize; + int valueSize = args->valueSize; + + byte * key = (byte*)(args+1); + byte * value = ((byte*)(args+1))+ keySize; + pthread_mutex_lock(&linear_hash_mutex); + __ThashInsert(xid, hashHeader, key, keySize, value, valueSize); + pthread_mutex_unlock(&linear_hash_mutex); + return 0; +} +static int operateRemove(int xid, Page *p, lsn_t lsn, recordid rid, const void *dat) { + const linearHash_insert_arg * args = dat; + recordid hashHeader = args->hashHeader; + int keySize = args->keySize; + + byte * key = (byte*)(args + 1); + + pthread_mutex_lock(&linear_hash_mutex); + __ThashRemove(xid, hashHeader, key, keySize); + pthread_mutex_unlock(&linear_hash_mutex); + return 0; +} +Operation getLinearHashInsert() { + Operation o = { + OPERATION_NOOP, + SIZEIS_PAGEID, + OPERATION_LINEAR_HASH_REMOVE, + &operateInsert + }; + return o; +} +Operation getLinearHashRemove() { + Operation o = { + OPERATION_NOOP, + SIZEIS_PAGEID, + OPERATION_LINEAR_HASH_INSERT, + &operateRemove + }; + return o; +} int ThashInsert(int xid, recordid hashHeader, const byte* key, int keySize, const byte* value, int valueSize) { + pthread_mutex_lock(&linear_hash_mutex); + int argSize = sizeof(linearHash_insert_arg)+keySize; + linearHash_insert_arg * arg = malloc(argSize); + arg->hashHeader = hashHeader; + arg->keySize = keySize; + memcpy(arg+1, key, keySize); + void * handle = TbeginNestedTopAction(xid, OPERATION_LINEAR_HASH_INSERT, (byte*)arg, argSize); + free(arg); + int ret = __ThashInsert(xid, hashHeader, key, keySize, value, valueSize); + TendNestedTopAction(xid, handle); + pthread_mutex_unlock(&linear_hash_mutex); + return ret; +} +static int __ThashInsert(int xid, recordid hashHeader, const byte* key, int keySize, const byte* value, int valueSize) { lladd_hash_header lhh; + Tread(xid, hashHeader, &lhh); lhh.numEntries ++; @@ -59,20 +143,45 @@ int ThashInsert(int xid, recordid hashHeader, const byte* key, int keySize, cons if(lhh.numEntries > (int)((double)(lhh.nextSplit + twoToThe(lhh.bits-1)) * HASH_FILL_FACTOR)) { ThashSplitBucket(xid, hashHeader, &lhh); } - - Tset(xid, hashHeader, &lhh); - assert(lhh.keySize == keySize); assert(lhh.valueSize == valueSize); recordid bucket = lhh.buckets; bucket.slot = hash(key, keySize, lhh.bits, lhh.nextSplit); int ret = TlinkedListInsert(xid, bucket, key, keySize, value, valueSize); + if(ret) { lhh.numEntries--; } + Tset(xid, hashHeader, &lhh); + + return ret; +} +int ThashRemove(int xid, recordid hashHeader, const byte * key, int keySize) { + pthread_mutex_lock(&linear_hash_mutex); + byte * value; + int valueSize = ThashLookup(xid, hashHeader, key, keySize, &value); + if(valueSize == -1) { + pthread_mutex_unlock(&linear_hash_mutex); + return 0; + } + int argSize = sizeof(linearHash_remove_arg) + keySize + valueSize; + linearHash_remove_arg * arg = malloc(argSize); + arg->hashHeader = hashHeader; + arg->keySize = keySize; + arg->valueSize = valueSize; + memcpy(arg+1, key, keySize); + memcpy((byte*)(arg+1)+keySize, value, valueSize); + + void * handle = TbeginNestedTopAction(xid, OPERATION_LINEAR_HASH_REMOVE, (byte*)arg, argSize); + free(arg); + free(value); + int ret = __ThashRemove(xid, hashHeader, key, keySize); + + TendNestedTopAction(xid, handle); + pthread_mutex_unlock(&linear_hash_mutex); return ret; } -int ThashRemove(int xid, recordid hashHeader, const byte * key, int keySize) { +static int __ThashRemove(int xid, recordid hashHeader, const byte * key, int keySize) { lladd_hash_header lhh; Tread(xid, hashHeader, &lhh); @@ -86,12 +195,12 @@ int ThashRemove(int xid, recordid hashHeader, const byte * key, int keySize) { bucket.slot = hash(key, keySize, lhh.bits, lhh.nextSplit); int ret = TlinkedListRemove(xid, bucket, key, keySize); - return ret; } int ThashLookup(int xid, recordid hashHeader, const byte * key, int keySize, byte ** value) { lladd_hash_header lhh; + pthread_mutex_lock(&linear_hash_mutex); Tread(xid, hashHeader, &lhh); assert(lhh.keySize == keySize); @@ -100,10 +209,9 @@ int ThashLookup(int xid, recordid hashHeader, const byte * key, int keySize, byt bucket.slot = hash(key, keySize, lhh.bits, lhh.nextSplit); int ret = TlinkedListFind(xid, bucket, key, keySize, value); - + pthread_mutex_unlock(&linear_hash_mutex); return ret; } -/** @todo Write ThashSplitBucket */ static void ThashSplitBucket(int xid, recordid hashHeader, lladd_hash_header * lhh) { long old_bucket = lhh->nextSplit; long new_bucket = old_bucket + twoToThe(lhh->bits-1); @@ -111,7 +219,7 @@ static void ThashSplitBucket(int xid, recordid hashHeader, lladd_hash_header * l recordid new_bucket_rid = lhh->buckets; old_bucket_rid.slot = old_bucket; new_bucket_rid.slot = new_bucket; - + // void * handle = TbeginNestedTopAction(xid, OPERATION_NOOP, NULL, 0); TarrayListExtend(xid, lhh->buckets, 1); byte * entry = calloc(1, lhh->buckets.size); Tset(xid, new_bucket_rid, entry); @@ -135,5 +243,33 @@ static void ThashSplitBucket(int xid, recordid hashHeader, lladd_hash_header * l free(key); free(value); } +// TendNestedTopAction(xid, handle); return; } +lladd_hash_iterator * ThashIterator(int xid, recordid hashHeader, int keySize, int valueSize) { + lladd_hash_iterator * it = malloc(sizeof(lladd_hash_iterator)); + it->hashHeader = hashHeader; + lladd_hash_header lhh; + Tread(xid, hashHeader, &lhh); + it->bucket = lhh.buckets; + it->numBuckets = lhh.nextSplit +twoToThe(lhh.bits-1); + it->bucket.slot = 0; + it->keySize = keySize; + it->valueSize = valueSize; + it->it = TlinkedListIterator(xid, it->bucket, it->keySize, it->valueSize); + + return it; +} + +int ThashNext(int xid, lladd_hash_iterator * it, byte ** key, int * keySize, byte** value, int * valueSize) { + while(!TlinkedListNext(xid, it->it, key, keySize, value, valueSize)) { + it->bucket.slot++; + if(it->bucket.slot < it->numBuckets) { + it->it = TlinkedListIterator(xid, it->bucket, it->keySize, it->valueSize); + } else { + free(it); + return 0; + } + } + return 1; +} diff --git a/src/lladd/operations/linkedListNTA.c b/src/lladd/operations/linkedListNTA.c index dda846f..4e34c04 100644 --- a/src/lladd/operations/linkedListNTA.c +++ b/src/lladd/operations/linkedListNTA.c @@ -3,6 +3,9 @@ #include #include #include +#define __USE_GNU +#include + /** A quick note on the format of linked lists. Each entry consists of a struct with some variable length data appended to it. @@ -28,8 +31,9 @@ @file */ +static pthread_mutex_t linked_list_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; static void __TlinkedListInsert(int xid, recordid list, const byte * key, int keySize, const byte * value, int valueSize); - +static int __TlinkedListRemove(int xid, recordid list, const byte * key, int keySize); typedef struct { recordid list; int keySize; @@ -52,11 +56,12 @@ static int operateInsert(int xid, Page *p, lsn_t lsn, recordid rid, const void valueSize = log->valueSize; key = (byte*)(log+1); value = ((byte*)(log+1))+keySize; - + pthread_mutex_lock(&linked_list_mutex); // printf("Operate insert called: rid.page = %d keysize = %d valuesize = %d %d {%d %d %d}\n", rid.page, log->keySize, log->valueSize, *(int*)key, value->page, value->slot, value->size); // Skip writing the undo! Recovery will write a CLR after we're done, effectively // wrapping this in a nested top action, so we needn't worry about that either. __TlinkedListInsert(xid, log->list, key, keySize, value, valueSize); + pthread_mutex_unlock(&linked_list_mutex); return 0; } @@ -69,11 +74,11 @@ static int operateRemove(int xid, Page *p, lsn_t lsn, recordid rid, const void keySize = log->keySize; key = (byte*)(log+1); - + pthread_mutex_lock(&linked_list_mutex); // printf("Operate remove called: %d\n", *(int*)key); // Don't call the version that writes an undo entry! __TlinkedListRemove(xid, log->list, key, keySize); - + pthread_mutex_unlock(&linked_list_mutex); return 0; } @@ -85,13 +90,14 @@ int TlinkedListInsert(int xid, recordid list, const byte * key, int keySize, con undoLog->list = list; undoLog->keySize = keySize; memcpy(undoLog+1, key, keySize); - - TbeginNestedTopAction(xid, OPERATION_LINKED_LIST_INSERT, + pthread_mutex_lock(&linked_list_mutex); + void * handle = TbeginNestedTopAction(xid, OPERATION_LINKED_LIST_INSERT, (byte*)undoLog, sizeof(lladd_linkedListInsert_log) + keySize); - + free(undoLog); __TlinkedListInsert(xid, list, key, keySize, value, valueSize); - TendNestedTopAction(xid); + TendNestedTopAction(xid, handle); + pthread_mutex_unlock(&linked_list_mutex); return ret; } @@ -142,10 +148,12 @@ static void __TlinkedListInsert(int xid, recordid list, const byte * key, int ke int TlinkedListFind(int xid, recordid list, const byte * key, int keySize, byte ** value) { lladd_linkedList_entry * entry = malloc(list.size); + pthread_mutex_lock(&linked_list_mutex); Tread(xid, list, entry); if(!entry->next.size) { free(entry); + pthread_mutex_unlock(&linked_list_mutex); return -1; // empty list } while(1) { @@ -155,6 +163,7 @@ int TlinkedListFind(int xid, recordid list, const byte * key, int keySize, byte *value = malloc(valueSize); memcpy(*value, ((byte*)(entry+1))+keySize, valueSize); free(entry); + pthread_mutex_unlock(&linked_list_mutex); return valueSize; } if(entry->next.size != -1) { @@ -165,19 +174,22 @@ int TlinkedListFind(int xid, recordid list, const byte * key, int keySize, byte } } free(entry); + pthread_mutex_unlock(&linked_list_mutex); return -1; } -static int __TlinkedListRemove(int xid, recordid list, const byte * key, int keySize); + int TlinkedListRemove(int xid, recordid list, const byte * key, int keySize) { byte * value; int valueSize; + pthread_mutex_lock(&linked_list_mutex); int ret = TlinkedListFind(xid, list, key, keySize, &value); if(ret != -1) { valueSize = ret; } else { + pthread_mutex_unlock(&linked_list_mutex); return 0; } int entrySize = sizeof(lladd_linkedListRemove_log) + keySize + valueSize; @@ -191,24 +203,25 @@ int TlinkedListRemove(int xid, recordid list, const byte * key, int keySize) { memcpy(((byte*)(undoLog+1))+keySize, value, valueSize); // printf("entry size %d sizeof(remove_log)%d keysize %d valuesize %d sizeof(rid) %d key %d value {%d %d %ld}\n", // entrySize, sizeof(lladd_linkedListRemove_log), keySize, valueSize, sizeof(recordid), key, value->page, value->slot, value->size); - TbeginNestedTopAction(xid, OPERATION_LINKED_LIST_REMOVE, + void * handle = TbeginNestedTopAction(xid, OPERATION_LINKED_LIST_REMOVE, (byte*)undoLog, entrySize); free(value); - + free(undoLog); __TlinkedListRemove(xid, list, key, keySize); - TendNestedTopAction(xid); - + TendNestedTopAction(xid, handle); + pthread_mutex_unlock(&linked_list_mutex); return 1; } static int __TlinkedListRemove(int xid, recordid list, const byte * key, int keySize) { lladd_linkedList_entry * entry = malloc(list.size); - + pthread_mutex_lock(&linked_list_mutex); Tread(xid, list, entry); if(entry->next.size == 0) { //Empty List. free(entry); + pthread_mutex_unlock(&linked_list_mutex); return 0; } int listRoot = 1; @@ -240,6 +253,7 @@ static int __TlinkedListRemove(int xid, recordid list, const byte * key, int key free (entry2); } free(entry); + pthread_mutex_unlock(&linked_list_mutex); return 1; } else { // Entry doesn't match the key we're looking for. if(entry->next.size != -1) { @@ -254,17 +268,21 @@ static int __TlinkedListRemove(int xid, recordid list, const byte * key, int key } } free(entry); + pthread_mutex_unlock(&linked_list_mutex); return 0; } /*** @todo TlinkedListMove could be much faster, but this is good enough for a first pass */ int TlinkedListMove(int xid, recordid start_list, recordid end_list, const byte *key, int keySize) { byte * value; + pthread_mutex_lock(&linked_list_mutex); int valueSize = TlinkedListFind(xid, start_list, key, keySize, &value); if(valueSize == -1) { + pthread_mutex_unlock(&linked_list_mutex); return 0; } else { TlinkedListRemove(xid, start_list, key, keySize); TlinkedListInsert(xid, end_list, key, keySize, value, valueSize); + pthread_mutex_unlock(&linked_list_mutex); return 1; } } @@ -308,7 +326,7 @@ lladd_linkedList_iterator * TlinkedListIterator(int xid, recordid list, int keyS int TlinkedListNext(int xid, lladd_linkedList_iterator * it, byte ** key, int * keySize, byte **value, int * valueSize) { if(it->next.size == -1) { free(it); return 0; } - + pthread_mutex_lock(&linked_list_mutex); if(it->first == -1) { it->first = 1; } else if(it->first) { @@ -321,7 +339,9 @@ int TlinkedListNext(int xid, lladd_linkedList_iterator * it, byte ** key, int * //The root entry was removed. Reset the iterator. it->first = -1; it->next = it->listRoot; - return TlinkedListNext(xid, it, key, keySize, value, valueSize); + int ret = TlinkedListNext(xid, it, key, keySize, value, valueSize); + pthread_mutex_unlock(&linked_list_mutex); + return ret; } else { //continue as normal. it->first = 0; @@ -332,6 +352,7 @@ int TlinkedListNext(int xid, lladd_linkedList_iterator * it, byte ** key, int * lladd_linkedList_entry * entry = malloc(it->next.size); Tread(xid, it->next, entry); + int ret; if(entry->next.size) { *keySize = it->keySize; *valueSize = it->valueSize; @@ -343,12 +364,14 @@ int TlinkedListNext(int xid, lladd_linkedList_iterator * it, byte ** key, int * memcpy(*key, entry+1, *keySize); memcpy(*value, ((byte*)(entry + 1))+*keySize, *valueSize); - free(entry); - return 1; + ret = 1; } else { // This entry was empty (this case occurs with empty lists) - free(it); - free(entry); - return 0; + free(it); + + ret = 0; } + free(entry); + pthread_mutex_unlock(&linked_list_mutex); + return ret; } diff --git a/src/lladd/operations/nestedTopActions.c b/src/lladd/operations/nestedTopActions.c index 410553f..d8d51c4 100644 --- a/src/lladd/operations/nestedTopActions.c +++ b/src/lladd/operations/nestedTopActions.c @@ -74,19 +74,26 @@ void initNestedTopActions() { } /** @todo TbeginNestedTopAction's API might not be quite right. Are there cases where we need to pass a recordid in? + + @return a handle that must be passed into TendNestedTopAction */ -void TbeginNestedTopAction(int xid, int op, const byte * dat, int datSize) { +void * TbeginNestedTopAction(int xid, int op, const byte * dat, int datSize) { recordid rid = NULLRID; + rid.page = datSize; LogEntry * e = LogUpdate(&XactionTable[xid % MAX_TRANSACTIONS], NULL, rid, op, dat); DEBUG("Begin Nested Top Action e->LSN: %ld\n", e->LSN); lsn_t * prevLSN = malloc(sizeof(lsn_t)); *prevLSN = e->LSN; pthread_mutex_lock(&transactional_2_mutex); - assert(!pblHtLookup(nestedTopActions, &xid, sizeof(int))); + void * ret = pblHtLookup(nestedTopActions, &xid, sizeof(int)); + if(ret) { + pblHtRemove(nestedTopActions, &xid, sizeof(int)); + } pblHtInsert(nestedTopActions, &xid, sizeof(int), prevLSN); pthread_mutex_unlock(&transactional_2_mutex); free(e); + return ret; } /** @@ -97,12 +104,15 @@ void TbeginNestedTopAction(int xid, int op, const byte * dat, int datSize) { @todo LogCLR()'s API is useless. Make it private, and implement a better public version. (Then rewrite TendNestedTopAction) */ -lsn_t TendNestedTopAction(int xid) { +lsn_t TendNestedTopAction(int xid, void * handle) { pthread_mutex_lock(&transactional_2_mutex); lsn_t * prevLSN = pblHtLookup(nestedTopActions, &xid, sizeof(int)); pblHtRemove(nestedTopActions, &xid, sizeof(int)); + if(handle) { + pblHtInsert(nestedTopActions, &xid, sizeof(int), handle); + } // This action wasn't really undone -- This is a nested top action! lsn_t undoneLSN = XactionTable[xid].prevLSN; recordid undoneRID = NULLRID; // Not correct, but this field is unused anyway. ;) diff --git a/src/lladd/transactional2.c b/src/lladd/transactional2.c index adefb75..8068f2c 100644 --- a/src/lladd/transactional2.c +++ b/src/lladd/transactional2.c @@ -71,6 +71,9 @@ void setupOperationsTable() { operationsTable[OPERATION_LINKED_LIST_INSERT] = getLinkedListInsert(); operationsTable[OPERATION_LINKED_LIST_REMOVE] = getLinkedListRemove(); + operationsTable[OPERATION_LINEAR_HASH_INSERT] = getLinearHashInsert(); + operationsTable[OPERATION_LINEAR_HASH_REMOVE] = getLinearHashRemove(); + } diff --git a/test/lladd/check_linearHashNTA.c b/test/lladd/check_linearHashNTA.c index 0343638..b52230b 100644 --- a/test/lladd/check_linearHashNTA.c +++ b/test/lladd/check_linearHashNTA.c @@ -49,9 +49,11 @@ terms specified in this license. #include #include #include - +#include #define LOG_NAME "check_linearHashNTA.log" #define NUM_ENTRIES 100000 +/** @test +*/ START_TEST(linearHashNTAtest) { Tinit(); @@ -108,6 +110,123 @@ START_TEST(linearHashNTAtest) Tcommit(xid); Tdeinit(); } END_TEST +#define NUM_THREADS 100 +#define NUM_T_ENTRIES 1000 +typedef struct { + int thread; + recordid rid; +} linear_hash_worker_args; +recordid makekey(int thread, int i) { + recordid ret; + ret.page = thread * NUM_T_ENTRIES + i; + ret.slot = thread * NUM_T_ENTRIES + i * 2; + ret.size= thread * NUM_T_ENTRIES + i * 3; + return ret; +} +void * worker(void* arg) { + linear_hash_worker_args * args = arg; + int thread = args->thread; + recordid hash = args->rid; + + int xid = Tbegin(); + + int i; + + for(i = 0; i < NUM_T_ENTRIES; i++) { + int value = i + thread * NUM_T_ENTRIES; + recordid key = makekey(thread,i); + ThashInsert(xid, hash, (byte*)&key, sizeof(recordid), (byte*)&value, sizeof(int)); + } + + Tcommit(xid); + xid = Tbegin(); + + for(i = 0; i < NUM_T_ENTRIES; i+=10) { + int * value; + recordid key = makekey(thread,i); + assert(ThashRemove(xid, hash, (byte*)&key, sizeof(recordid))); + assert(-1==ThashLookup(xid, hash, (byte*)&key, sizeof(recordid), (byte**)&value)); + assert(!ThashRemove(xid, hash, (byte*)&key, sizeof(recordid))); + } + + Tabort(xid); + xid = Tbegin(); + + for(i = 0; i < NUM_T_ENTRIES; i+=10) { + recordid key = makekey(thread,i); + int * value; + assert(sizeof(int) == ThashLookup(xid, hash, (byte*)&key, sizeof(recordid), (byte**)&value)); + assert(*value == i + thread * NUM_T_ENTRIES); + free (value); + } + Tcommit(xid); + return NULL; +} +START_TEST(linearHashNTAThreadedTest) { + Tinit(); + int xid = Tbegin(); + recordid rid = ThashCreate(xid, sizeof(recordid), sizeof(int)); + int i; + Tcommit(xid); + pthread_t threads[NUM_THREADS]; + for(i = 0; i < NUM_THREADS; i++) { + linear_hash_worker_args * args = malloc(sizeof(linear_hash_worker_args)); + args->thread = i; + args->rid= rid; + pthread_create(&threads[i], NULL, &worker, args); + } + for(i = 0; i < NUM_THREADS; i++) { + void * ret; + pthread_join(threads[i], ret); + } + Tdeinit(); +} END_TEST + +START_TEST(linearHashNTAIteratortest) { + Tinit(); + int xid = Tbegin(); + + recordid hash = ThashCreate(xid, sizeof(int), sizeof(recordid)); + + int i = 0; + + for(i = 0; i < NUM_ENTRIES; i++) { + recordid value = makekey(0, i); + assert(!ThashInsert(xid, hash, (byte*)&i, sizeof(int), (byte*)&value, sizeof(recordid))); + } + + int seen[NUM_ENTRIES]; + + for(i = 0; i < NUM_ENTRIES; i++) { + seen[i] = 0; + } + + lladd_hash_iterator * it = ThashIterator(xid, hash, sizeof(int), sizeof(recordid)); + + int * key; + recordid * value; + int keySize; + int valueSize; + + while(ThashNext(xid, it, (byte**)&key, &keySize, (byte**)&value, &valueSize)) { + + recordid check = makekey(0, *key); + assert(!memcmp(value, &check, sizeof(recordid))); + + assert(!seen[*key]); + seen[*key]++; + + free(key); + free(value); + } + + for(i = 0 ; i < NUM_ENTRIES; i++) { + assert(seen[i] == 1); + } + + Tcommit(xid); + Tdeinit(); +} END_TEST Suite * check_suite(void) { Suite *s = suite_create("linearHashNTA"); @@ -117,7 +236,9 @@ Suite * check_suite(void) { /* Sub tests are added, one per line, here */ + tcase_add_test(tc, linearHashNTAIteratortest); tcase_add_test(tc, linearHashNTAtest); + tcase_add_test(tc, linearHashNTAThreadedTest); /* --------------------------------------------- */ diff --git a/test/lladd/check_linkedListNTA.c b/test/lladd/check_linkedListNTA.c index c91e2fe..b0db740 100644 --- a/test/lladd/check_linkedListNTA.c +++ b/test/lladd/check_linkedListNTA.c @@ -49,6 +49,7 @@ terms specified in this license. #include #include #include +#include #define LOG_NAME "check_linkedListNTA.log" START_TEST(linkedListNTAtest) @@ -99,8 +100,77 @@ START_TEST(linkedListNTAtest) Tcommit(xid); Tdeinit(); } END_TEST +#define NUM_THREADS 100 +#define NUM_T_ENTRIES 100 +static recordid makekey(int thread, int i) { + recordid rid; + rid.page = thread*NUM_THREADS+i; + rid.slot = thread*NUM_THREADS+i+1; + rid.size = thread*NUM_THREADS+i+1; + return rid; +} +typedef struct { + int thread; + recordid listRoot; +} workerarg; +static void * worker(void * arg) { + workerarg * arguments = (workerarg*) arg; + int thread = arguments->thread; + recordid listRoot = arguments->listRoot; + free(arg); + int xid = Tbegin(); + int i; + for(i = 0; i < NUM_T_ENTRIES; i++) { + recordid key = makekey(thread, i); + int value = i + thread * NUM_THREADS; + int ret = TlinkedListInsert(xid, listRoot, (byte*)&key, sizeof(recordid), (byte*)&value, sizeof(int)); + assert(!ret); + } + Tcommit(xid); + xid = Tbegin(); + for(i = 0; i < NUM_T_ENTRIES; i+=10) { + recordid key = makekey(thread, i); + int ret = TlinkedListRemove(xid, listRoot, (byte*)&key, sizeof(recordid)); + assert(ret); + ret = TlinkedListRemove(xid, listRoot, (byte*)&key, sizeof(recordid)); + assert(!ret); + } + Tabort(xid); + xid = Tbegin(); + for(i = 0; i < NUM_T_ENTRIES; i++) { + recordid key = makekey(thread, i); + int * value; + int ret = TlinkedListFind(xid, listRoot, (byte*)&key, sizeof(recordid), (byte**)&value); + assert(ret == sizeof(int)); + assert(*value == i+thread*NUM_THREADS); + free(value); + } + Tcommit(xid); + return NULL; +} - +START_TEST ( linkedListMultiThreadedNTA ) { + Tinit(); + int xid = Tbegin(); + recordid listRoot = TlinkedListCreate(xid, sizeof(recordid), sizeof(int)); + Tcommit(xid); + int i; + pthread_t threads[NUM_THREADS]; + + for(i = 0; i < NUM_THREADS; i++) { + workerarg * arg = malloc(sizeof(workerarg)); + arg->thread = i; + arg->listRoot = listRoot; + pthread_create(&threads[i],NULL, &worker, arg); + } + for(i = 0; i < NUM_THREADS; i++) { + void * ptr; + pthread_join(threads[i], &ptr); + } + + + Tdeinit(); +} END_TEST Suite * check_suite(void) { Suite *s = suite_create("linkedListNTA"); @@ -111,7 +181,7 @@ Suite * check_suite(void) { /* Sub tests are added, one per line, here */ tcase_add_test(tc, linkedListNTAtest); - + tcase_add_test(tc, linkedListMultiThreadedNTA); /* --------------------------------------------- */ tcase_add_checked_fixture(tc, setup, teardown); diff --git a/test/lladd/check_operations.c b/test/lladd/check_operations.c index 20f2e89..826f971 100644 --- a/test/lladd/check_operations.c +++ b/test/lladd/check_operations.c @@ -368,14 +368,14 @@ START_TEST(operation_nestedTopAction) { *dat = 10; Tset(xid, rid1, dat); - TbeginNestedTopAction(xid, OPERATION_NOOP, NULL, 0); + void * handle = TbeginNestedTopAction(xid, OPERATION_NOOP, NULL, 0); *dat = 20; Tset(xid, rid2, dat); *dat = 30; Tset(xid, rid3, dat); - TendNestedTopAction(xid); + TendNestedTopAction(xid, handle); // printf("B"); fflush(NULL); *dat = 40;