- linkedListNTA is now re-entrant.
- linearHashNTA now uses nested top actions, is re-entrant, and supports a non-re-entrant iterator - 'recursive' nested top actions allow nested top actions to be nested within each other.
This commit is contained in:
parent
7a990886d2
commit
ac0d413e58
10 changed files with 417 additions and 45 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -61,6 +61,6 @@ terms specified in this license.
|
|||
|
||||
#include <lladd/operations.h>
|
||||
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
|
||||
|
|
|
@ -2,7 +2,19 @@
|
|||
#include <lladd/hash.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#define __USE_GNU
|
||||
#include <pthread.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#define __USE_GNU
|
||||
#include <pthread.h>
|
||||
|
||||
/** 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;
|
||||
}
|
||||
|
|
|
@ -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. ;)
|
||||
|
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -49,9 +49,11 @@ terms specified in this license.
|
|||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#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);
|
||||
|
||||
/* --------------------------------------------- */
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ terms specified in this license.
|
|||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue