Fixeds cht test / finished error checking within LLADD, but haven't tested error handling yet.
This commit is contained in:
parent
29dacbe7ed
commit
890a7385d0
14 changed files with 640 additions and 629 deletions
|
@ -28,15 +28,15 @@ Operation getRealloc();
|
||||||
|
|
||||||
@return the recordid of the new record.
|
@return the recordid of the new record.
|
||||||
*/
|
*/
|
||||||
recordid Talloc(int xid, long size);
|
compensated_function recordid Talloc(int xid, long size);
|
||||||
|
|
||||||
recordid TallocFromPage(int xid, long page, long size);
|
compensated_function recordid TallocFromPage(int xid, long page, long size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Free a record.
|
Free a record.
|
||||||
@todo Currently, we just leak store space on dealloc.
|
@todo Currently, we just leak store space on dealloc.
|
||||||
*/
|
*/
|
||||||
void Tdealloc(int xid, recordid rid);
|
compensated_function void Tdealloc(int xid, recordid rid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Obtain the type of a record, as returned by getRecordType.
|
Obtain the type of a record, as returned by getRecordType.
|
||||||
|
@ -51,7 +51,7 @@ void Tdealloc(int xid, recordid rid);
|
||||||
@see getRecordType
|
@see getRecordType
|
||||||
|
|
||||||
*/
|
*/
|
||||||
int TrecordType(int xid, recordid rid);
|
compensated_function int TrecordType(int xid, recordid rid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Obtain the length of the data stored in a record.
|
Obtain the length of the data stored in a record.
|
||||||
|
@ -63,9 +63,9 @@ int TrecordType(int xid, recordid rid);
|
||||||
|
|
||||||
@return -1 if the record does not exist, the size of the record otherwise.
|
@return -1 if the record does not exist, the size of the record otherwise.
|
||||||
*/
|
*/
|
||||||
int TrecordSize(int xid, recordid rid);
|
compensated_function int TrecordSize(int xid, recordid rid);
|
||||||
|
|
||||||
/** Return the number of records stored in page pageid */
|
/** Return the number of records stored in page pageid */
|
||||||
int TrecordsInPage(int xid, int pageid);
|
compensated_function int TrecordsInPage(int xid, int pageid);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -103,10 +103,10 @@ lladd_pagedList_iterator * TpagedListIterator(int xid, recordid list);
|
||||||
/** @return 1 if there was another entry to be iterated over. 0 otherwise.
|
/** @return 1 if there was another entry to be iterated over. 0 otherwise.
|
||||||
If this function returns 1, the caller must free() the malloced memory
|
If this function returns 1, the caller must free() the malloced memory
|
||||||
returned via the key and value arguments.*/
|
returned via the key and value arguments.*/
|
||||||
int TpagedListNext(int xid, lladd_pagedList_iterator * it, byte ** key, int * keySize, byte ** value, int * valueSize);
|
compensated_function int TpagedListNext(int xid, lladd_pagedList_iterator * it, byte ** key, int * keySize, byte ** value, int * valueSize);
|
||||||
recordid TpagedListAlloc(int xid);
|
compensated_function recordid TpagedListAlloc(int xid);
|
||||||
void TpagedListDelete(int xid, recordid list);
|
compensated_function void TpagedListDelete(int xid, recordid list);
|
||||||
int TpagedListSpansPages(int xid, recordid list);
|
compensated_function int TpagedListSpansPages(int xid, recordid list);
|
||||||
Operation getPagedListInsert();
|
Operation getPagedListInsert();
|
||||||
Operation getPagedListRemove();
|
Operation getPagedListRemove();
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -71,7 +71,10 @@ static int _chtEval(DfaSet * dfaSet,
|
||||||
}
|
}
|
||||||
if(ht != NULL) {
|
if(ht != NULL) {
|
||||||
memcpy(&(__header_ptr(&m)->hashTable), ht, sizeof(clusterHashTable_t));
|
memcpy(&(__header_ptr(&m)->hashTable), ht, sizeof(clusterHashTable_t));
|
||||||
}
|
} else {
|
||||||
|
|
||||||
|
//memset(&(__header_ptr(&m)->hashTable), 0, sizeof(clusterHashTable_t));
|
||||||
|
}
|
||||||
|
|
||||||
/* printf("%s <- %s\n", __header_ptr(&m)->initiator, dfaSet->networkSetup.localhost); */
|
/* printf("%s <- %s\n", __header_ptr(&m)->initiator, dfaSet->networkSetup.localhost); */
|
||||||
|
|
||||||
|
|
|
@ -32,14 +32,14 @@ static void readRawRecord(int xid, Page * p, recordid rid, void * buf, int size)
|
||||||
recordid blob_rec_rid = rid;
|
recordid blob_rec_rid = rid;
|
||||||
blob_rec_rid.size = size;
|
blob_rec_rid.size = size;
|
||||||
readRecord(xid, p, blob_rec_rid, buf);
|
readRecord(xid, p, blob_rec_rid, buf);
|
||||||
/* Tread(xid, blob_rec_rid, buf); */
|
/* T read(xid, blob_rec_rid, buf); */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeRawRecord(int xid, Page * p, recordid rid, lsn_t lsn, const void * buf, int size) {
|
static void writeRawRecord(int xid, Page * p, recordid rid, lsn_t lsn, const void * buf, int size) {
|
||||||
recordid blob_rec_rid = rid;
|
recordid blob_rec_rid = rid;
|
||||||
blob_rec_rid.size = size;
|
blob_rec_rid.size = size;
|
||||||
writeRecord(xid, p, lsn, blob_rec_rid, buf);
|
writeRecord(xid, p, lsn, blob_rec_rid, buf);
|
||||||
/* Tset(xid, blob_rec_rid, buf); - We no longer need to write a log
|
/* T set(xid, blob_rec_rid, buf); - We no longer need to write a log
|
||||||
record out here, since we're called by something that is the
|
record out here, since we're called by something that is the
|
||||||
result of a log record.*/
|
result of a log record.*/
|
||||||
}
|
}
|
||||||
|
@ -195,37 +195,47 @@ void closeBlobStore() {
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
recordid preAllocBlob(int xid, long blobSize) {
|
compensated_function recordid preAllocBlob(int xid, long blobSize) {
|
||||||
|
|
||||||
/* Allocate space for the blob entry. */
|
/* Allocate space for the blob entry. */
|
||||||
|
|
||||||
|
recordid rid;
|
||||||
|
|
||||||
DEBUG("Allocing blob (size %ld)\n", blobSize);
|
try_ret(NULLRID) {
|
||||||
|
|
||||||
|
DEBUG("Allocing blob (size %ld)\n", blobSize);
|
||||||
|
|
||||||
|
assert(blobSize > 0); /* Don't support zero length blobs right now... */
|
||||||
|
|
||||||
assert(blobSize > 0); /* Don't support zero length blobs right now... */
|
/* First in buffer manager. */
|
||||||
|
|
||||||
|
rid = Talloc(xid, BLOB_SLOT); //sizeof(blob_record_t));
|
||||||
|
|
||||||
|
rid.size = blobSize;
|
||||||
|
|
||||||
/* First in buffer manager. */
|
} end_ret(NULLRID);
|
||||||
|
|
||||||
recordid rid = Talloc(xid, BLOB_SLOT); //sizeof(blob_record_t));
|
|
||||||
|
|
||||||
rid.size = blobSize;
|
|
||||||
|
|
||||||
return rid;
|
return rid;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
recordid preAllocBlobFromPage(int xid, long page, long blobSize) {
|
compensated_function recordid preAllocBlobFromPage(int xid, long page, long blobSize) {
|
||||||
|
|
||||||
|
recordid rid;
|
||||||
|
|
||||||
/* Allocate space for the blob entry. */
|
/* Allocate space for the blob entry. */
|
||||||
|
try_ret(NULLRID) {
|
||||||
DEBUG("Allocing blob (size %ld)\n", blobSize);
|
DEBUG("Allocing blob (size %ld)\n", blobSize);
|
||||||
|
|
||||||
|
assert(blobSize > 0); /* Don't support zero length blobs right now... */
|
||||||
|
|
||||||
|
/* First in buffer manager. */
|
||||||
|
|
||||||
|
rid = TallocFromPage(xid, page, BLOB_SLOT); //sizeof(blob_record_t));
|
||||||
|
|
||||||
assert(blobSize > 0); /* Don't support zero length blobs right now... */
|
rid.size = blobSize;
|
||||||
|
|
||||||
/* First in buffer manager. */
|
} end_ret(NULLRID);
|
||||||
|
|
||||||
recordid rid = TallocFromPage(xid, page, BLOB_SLOT); //sizeof(blob_record_t));
|
|
||||||
|
|
||||||
rid.size = blobSize;
|
|
||||||
|
|
||||||
return rid;
|
return rid;
|
||||||
|
|
||||||
|
@ -280,7 +290,7 @@ void allocBlob(int xid, Page * p, lsn_t lsn, recordid rid) {
|
||||||
funlockfile(blobf0);
|
funlockfile(blobf0);
|
||||||
funlockfile(blobf1);
|
funlockfile(blobf1);
|
||||||
|
|
||||||
/* Tset() needs to know to 'do the right thing' here, since we've
|
/* T set() needs to know to 'do the right thing' here, since we've
|
||||||
changed the size it has recorded for this record, and
|
changed the size it has recorded for this record, and
|
||||||
writeRawRecord makes sure that that is the case.
|
writeRawRecord makes sure that that is the case.
|
||||||
|
|
||||||
|
@ -337,7 +347,7 @@ static FILE * getDirtyFD(int xid, Page * p, lsn_t lsn, recordid rid) {
|
||||||
|
|
||||||
/* First, determine if the blob is dirty. */
|
/* First, determine if the blob is dirty. */
|
||||||
|
|
||||||
/* Tread() raw record */
|
/* T read() raw record */
|
||||||
readRawRecord(xid, p, rid, &rec, sizeof(blob_record_t));
|
readRawRecord(xid, p, rid, &rec, sizeof(blob_record_t));
|
||||||
|
|
||||||
assert(rec.size == rid.size);
|
assert(rec.size == rid.size);
|
||||||
|
@ -352,7 +362,7 @@ static FILE * getDirtyFD(int xid, Page * p, lsn_t lsn, recordid rid) {
|
||||||
/* Flip the fd bit on the record. */
|
/* Flip the fd bit on the record. */
|
||||||
rec.fd = rec.fd ? 0 : 1;
|
rec.fd = rec.fd ? 0 : 1;
|
||||||
|
|
||||||
/* Tset() raw record */
|
/* T set() raw record */
|
||||||
writeRawRecord(xid, p, rid, lsn, &rec, sizeof(blob_record_t));
|
writeRawRecord(xid, p, rid, lsn, &rec, sizeof(blob_record_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -459,7 +469,7 @@ void abortBlobs(int xid) {
|
||||||
and undo have to reason about lazy propogation of values to the
|
and undo have to reason about lazy propogation of values to the
|
||||||
bufferManager, and also have to preserve *write* ordering, even
|
bufferManager, and also have to preserve *write* ordering, even
|
||||||
though the writes may be across many transactions, and could be
|
though the writes may be across many transactions, and could be
|
||||||
propogated in the wrong order. If we generate a Tset() (for the
|
propogated in the wrong order. If we generate a T set() (for the
|
||||||
blob record in bufferManager) for each write, things become much
|
blob record in bufferManager) for each write, things become much
|
||||||
easier.
|
easier.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -77,8 +77,8 @@ typedef struct {
|
||||||
} blob_record_t;
|
} blob_record_t;
|
||||||
|
|
||||||
|
|
||||||
recordid preAllocBlob(int xid, long blobsize);
|
compensated_function recordid preAllocBlob(int xid, long blobsize);
|
||||||
recordid preAllocBlobFromPage(int xid, long page, long blobsize);
|
compensated_function recordid preAllocBlobFromPage(int xid, long page, long blobsize);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Allocate a blob of size blobSize.
|
Allocate a blob of size blobSize.
|
||||||
|
|
|
@ -65,7 +65,10 @@ void redoUpdate(const LogEntry * e) {
|
||||||
if(e->type == UPDATELOG) {
|
if(e->type == UPDATELOG) {
|
||||||
/* lsn_t pageLSN = readLSN(e->contents.update.rid.page); */
|
/* lsn_t pageLSN = readLSN(e->contents.update.rid.page); */
|
||||||
recordid rid = e->contents.update.rid;
|
recordid rid = e->contents.update.rid;
|
||||||
Page * p = loadPage(e->xid, rid.page);
|
Page * p;
|
||||||
|
try {
|
||||||
|
p = loadPage(e->xid, rid.page);
|
||||||
|
} end;
|
||||||
lsn_t pageLSN = pageReadLSN(p);
|
lsn_t pageLSN = pageReadLSN(p);
|
||||||
|
|
||||||
if(e->LSN > pageLSN) {
|
if(e->LSN > pageLSN) {
|
||||||
|
@ -79,7 +82,10 @@ void redoUpdate(const LogEntry * e) {
|
||||||
} else if(e->type == CLRLOG) {
|
} else if(e->type == CLRLOG) {
|
||||||
LogEntry * f = readLSNEntry(e->contents.clr.thisUpdateLSN);
|
LogEntry * f = readLSNEntry(e->contents.clr.thisUpdateLSN);
|
||||||
recordid rid = f->contents.update.rid;
|
recordid rid = f->contents.update.rid;
|
||||||
Page * p = loadPage(e->xid, rid.page);
|
Page * p;
|
||||||
|
try {
|
||||||
|
p = loadPage(e->xid, rid.page);
|
||||||
|
} end;
|
||||||
|
|
||||||
assert(rid.page == e->contents.update.rid.page); /* @todo Should this always hold? */
|
assert(rid.page == e->contents.update.rid.page); /* @todo Should this always hold? */
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "../page/slotted.h"
|
#include "../page/slotted.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
//try{
|
||||||
/**
|
/**
|
||||||
@file
|
@file
|
||||||
|
|
||||||
|
@ -44,9 +45,9 @@
|
||||||
int page = Treserve(int xid, int size)
|
int page = Treserve(int xid, int size)
|
||||||
|
|
||||||
This would tell Talloc to treat the page as though 'size' bytes had
|
This would tell Talloc to treat the page as though 'size' bytes had
|
||||||
already been reserved. The 'free space' that Talloc() reasons
|
already been reserved. The 'free space' that Talloc () reasons
|
||||||
about would be: max(reservedSpace, usedSpace). A seperate call,
|
about would be: max(reservedSpace, usedSpace). A seperate call,
|
||||||
TallocFromPage(xid, page, size) already exists, and should ignore
|
TallocFromPage (xid, page, size) already exists, and should ignore
|
||||||
the presence of the 'reserved space' field.
|
the presence of the 'reserved space' field.
|
||||||
|
|
||||||
Track level locality is another problem that Talloc should address,
|
Track level locality is another problem that Talloc should address,
|
||||||
|
@ -54,8 +55,8 @@
|
||||||
|
|
||||||
Better support for locking. Consider this sequence of events:
|
Better support for locking. Consider this sequence of events:
|
||||||
|
|
||||||
recordid rid1 = Talloc(xid1, 1);
|
recordid rid1 = Talloc (xid1, 1);
|
||||||
recordid rid2 = Talloc(xid2, 1); // May deadlock if page level
|
recordid rid2 = Talloc (xid2, 1); // May deadlock if page level
|
||||||
// locking is used.
|
// locking is used.
|
||||||
|
|
||||||
The lock manager needs a 'try lock' operation that allows
|
The lock manager needs a 'try lock' operation that allows
|
||||||
|
@ -74,9 +75,9 @@
|
||||||
$Id$
|
$Id$
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
//}end
|
||||||
static int operate(int xid, Page * p, lsn_t lsn, recordid rid, const void * dat) {
|
static int operate(int xid, Page * p, lsn_t lsn, recordid rid, const void * dat) {
|
||||||
/* * @ todo Currently, Talloc() needs to clean up the page type (for recovery). Should this be elsewhere? */
|
/* * @ todo Currently, T alloc () needs to clean up the page type (for recovery). Should this be elsewhere? */
|
||||||
|
|
||||||
/* if(*page_type_ptr(p) == UNINITIALIZED_PAGE) {
|
/* if(*page_type_ptr(p) == UNINITIALIZED_PAGE) {
|
||||||
*page_type_ptr(p) = SLOTTED_PAGE;
|
*page_type_ptr(p) = SLOTTED_PAGE;
|
||||||
|
@ -149,87 +150,125 @@ Operation getRealloc() {
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
recordid Talloc(int xid, long size) {
|
compensated_function recordid Talloc(int xid, long size) {
|
||||||
recordid rid;
|
recordid rid;
|
||||||
Page * p = NULL;
|
|
||||||
if(size >= BLOB_THRESHOLD_SIZE && size != BLOB_SLOT) {
|
int isBlob = size >= BLOB_THRESHOLD_SIZE && size != BLOB_SLOT;
|
||||||
/**@todo is it OK that Talloc doesn't pin the page when a blob is alloced?*/
|
|
||||||
rid = preAllocBlob(xid, size);
|
if(isBlob) {
|
||||||
|
try_ret(NULLRID) {
|
||||||
|
rid = preAllocBlob(xid, size);
|
||||||
|
Tupdate(xid,rid, NULL, OPERATION_ALLOC);
|
||||||
|
} end_ret(NULLRID);
|
||||||
} else {
|
} else {
|
||||||
pthread_mutex_lock(&talloc_mutex);
|
|
||||||
rid = slottedPreRalloc(xid, size, &p);
|
|
||||||
assert(p != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
Tupdate(xid,rid, NULL, OPERATION_ALLOC);
|
Page * p = NULL;
|
||||||
|
|
||||||
if(p != NULL) {
|
|
||||||
/* release the page that preAllocBlob pinned for us. */
|
|
||||||
|
|
||||||
/* @todo alloc.c pins multiple pages -> Will deadlock with small buffer sizes.. */
|
begin_action_ret(pthread_mutex_unlock, &talloc_mutex, NULLRID) {
|
||||||
releasePage(p);
|
pthread_mutex_lock(&talloc_mutex);
|
||||||
pthread_mutex_unlock(&talloc_mutex);
|
rid = slottedPreRalloc(xid, size, &p);
|
||||||
|
Tupdate(xid, rid, NULL, OPERATION_ALLOC);
|
||||||
|
/** @todo does releasePage do the correct error checking? */
|
||||||
|
releasePage(p);
|
||||||
|
} compensate_ret(NULLRID);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rid;
|
return rid;
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
recordid TallocFromPage(int xid, long page, long size) {
|
/* try_ret(NULL_RID) {
|
||||||
recordid rid;
|
if(size >= BLOB_THRESHOLD_SIZE && size != BLOB_SLOT) {
|
||||||
|
///@todo is it OK that Talloc doesn't pin the page when a blob is alloced?
|
||||||
Page * p = NULL;
|
rid = pr eAllocBlob(xid, size);
|
||||||
if(size >= BLOB_THRESHOLD_SIZE && size != BLOB_SLOT) {
|
} else {
|
||||||
rid = preAllocBlobFromPage(xid, page, size);
|
pthread_mutex_lock(&talloc_mutex);
|
||||||
} else {
|
rid = slottedPreRalloc(xid, size, &p);
|
||||||
pthread_mutex_lock(&talloc_mutex);
|
assert(p != NULL);
|
||||||
rid = slottedPreRallocFromPage(xid, page, size, &p);
|
|
||||||
if(p == NULL) {
|
|
||||||
assert(rid.size == -1);
|
|
||||||
pthread_mutex_unlock(&talloc_mutex);
|
|
||||||
return rid;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Tupdate(xid,rid, NULL, OPERATION_ALLOC);
|
Tupdate(xid,rid, NULL, OPERATION_ALLOC);
|
||||||
|
|
||||||
if(p != NULL) {
|
if(p != NULL) {
|
||||||
/* release the page that preRallocFromPage pinned for us. */
|
/// release the page that preAllocBlob pinned for us.
|
||||||
/* @todo alloc.c pins multiple pages -> Will deadlock with small buffer sizes.. */
|
|
||||||
releasePage(p);
|
/// @todo alloc.c pins multiple pages -> Will deadlock with small buffer sizes..
|
||||||
pthread_mutex_unlock(&talloc_mutex);
|
releasePage(p);
|
||||||
}
|
pthread_mutex_unlock(&talloc_mutex);
|
||||||
|
|
||||||
|
}
|
||||||
|
} end_ret(NULLRID);
|
||||||
|
return rid;*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
compensated_function recordid TallocFromPage(int xid, long page, long size) {
|
||||||
|
recordid rid;
|
||||||
|
|
||||||
|
Page * p = NULL;
|
||||||
|
if(size >= BLOB_THRESHOLD_SIZE && size != BLOB_SLOT) {
|
||||||
|
try_ret(NULLRID) {
|
||||||
|
rid = preAllocBlobFromPage(xid, page, size);
|
||||||
|
Tupdate(xid,rid, NULL, OPERATION_ALLOC);
|
||||||
|
} end_ret(NULLRID);
|
||||||
|
} else {
|
||||||
|
begin_action_ret(pthread_mutex_unlock, &talloc_mutex, NULLRID) {
|
||||||
|
pthread_mutex_lock(&talloc_mutex);
|
||||||
|
rid = slottedPreRallocFromPage(xid, page, size, &p);
|
||||||
|
if(rid.size == size) {
|
||||||
|
Tupdate(xid,rid, NULL, OPERATION_ALLOC);
|
||||||
|
} else {
|
||||||
|
assert(rid.size < 0);
|
||||||
|
}
|
||||||
|
if(p) {
|
||||||
|
/* @todo alloc.c pins multiple pages -> Will deadlock with small buffer sizes.. */
|
||||||
|
releasePage(p);
|
||||||
|
}
|
||||||
|
} compensate_ret(NULLRID);
|
||||||
|
}
|
||||||
|
|
||||||
return rid;
|
return rid;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tdealloc(int xid, recordid rid) {
|
compensated_function void Tdealloc(int xid, recordid rid) {
|
||||||
void * preimage = malloc(rid.size);
|
void * preimage = malloc(rid.size);
|
||||||
Page * p = loadPage(xid, rid.page);
|
Page * p;
|
||||||
readRecord(xid, p, rid, preimage);
|
try {
|
||||||
/** @todo race in Tdealloc; do we care, or is this something that the log manager should cope with? */
|
p = loadPage(xid, rid.page);
|
||||||
Tupdate(xid, rid, preimage, OPERATION_DEALLOC);
|
} end;
|
||||||
releasePage(p);
|
begin_action(releasePage, p) {
|
||||||
|
readRecord(xid, p, rid, preimage);
|
||||||
|
/** @todo race in Tdealloc; do we care, or is this something that the log manager should cope with? */
|
||||||
|
Tupdate(xid, rid, preimage, OPERATION_DEALLOC);
|
||||||
|
} compensate;
|
||||||
free(preimage);
|
free(preimage);
|
||||||
}
|
}
|
||||||
|
|
||||||
int TrecordType(int xid, recordid rid) {
|
compensated_function int TrecordType(int xid, recordid rid) {
|
||||||
Page * p = loadPage(xid, rid.page);
|
Page * p;
|
||||||
int ret = getRecordType(xid, p, rid);
|
try_ret(compensation_error()) {
|
||||||
|
p = loadPage(xid, rid.page);
|
||||||
|
} end_ret(compensation_error());
|
||||||
|
int ret;
|
||||||
|
ret = getRecordType(xid, p, rid);
|
||||||
releasePage(p);
|
releasePage(p);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TrecordSize(int xid, recordid rid) {
|
compensated_function int TrecordSize(int xid, recordid rid) {
|
||||||
int ret;
|
int ret;
|
||||||
Page * p = loadPage(xid, rid.page);
|
Page * p;
|
||||||
|
try_ret(compensation_error()) {
|
||||||
|
p = loadPage(xid, rid.page);
|
||||||
|
} end_ret(compensation_error());
|
||||||
ret = getRecordSize(xid, p, rid);
|
ret = getRecordSize(xid, p, rid);
|
||||||
releasePage(p);
|
releasePage(p);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TrecordsInPage(int xid, int pageid) {
|
compensated_function int TrecordsInPage(int xid, int pageid) {
|
||||||
Page * p = loadPage(xid, pageid);
|
Page * p;
|
||||||
|
try_ret(compensation_error()) {
|
||||||
|
p = loadPage(xid, pageid);
|
||||||
|
} end_ret(compensation_error());
|
||||||
readlock(p->rwlatch, 187);
|
readlock(p->rwlatch, 187);
|
||||||
int ret = *numslots_ptr(p);
|
int ret = *numslots_ptr(p);
|
||||||
unlock(p->rwlatch);
|
unlock(p->rwlatch);
|
||||||
|
|
|
@ -120,149 +120,68 @@ Operation getArrayListAlloc() {
|
||||||
}
|
}
|
||||||
/*----------------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
compensated_function int TarrayListExtend(int xid, recordid rid, int slots) {
|
/** @todo locking for arrayList... this isn't pressing since currently
|
||||||
|
the only thing that calls arraylist (the hashtable
|
||||||
|
implementations) serialize bucket list operations anyway... */
|
||||||
|
|
||||||
|
static compensated_function int TarrayListExtendInternal(int xid, recordid rid, int slots, int op) {
|
||||||
Page * p;
|
Page * p;
|
||||||
try_ret(compensation_error()) {
|
try_ret(compensation_error()) {
|
||||||
p = loadPage(xid, rid.page);
|
p = loadPage(xid, rid.page);
|
||||||
} end_ret(compensation_error());
|
} end_ret(compensation_error());
|
||||||
TarrayListParameters tlp = pageToTLP(p);
|
TarrayListParameters tlp = pageToTLP(p);
|
||||||
|
|
||||||
int lastCurrentBlock;
|
|
||||||
if(tlp.maxOffset == -1) {
|
|
||||||
lastCurrentBlock = -1;
|
|
||||||
} else{
|
|
||||||
lastCurrentBlock = getBlockContainingOffset(tlp, tlp.maxOffset, NULL);
|
|
||||||
}
|
|
||||||
int lastNewBlock = getBlockContainingOffset(tlp, tlp.maxOffset+slots, NULL);
|
|
||||||
|
|
||||||
DEBUG("lastCurrentBlock = %d, lastNewBlock = %d\n", lastCurrentBlock, lastNewBlock);
|
|
||||||
|
|
||||||
recordid tmp; /* recordid of slot in base page that holds new block. */
|
|
||||||
tmp.page = rid.page;
|
|
||||||
tmp.size = sizeof(int);
|
|
||||||
|
|
||||||
recordid tmp2; /* recordid of newly created pages. */
|
|
||||||
tmp2.slot = 0;
|
|
||||||
tmp2.size = tlp.size;
|
|
||||||
/* Iterate over the (small number) of indirection blocks that need to be updated */
|
|
||||||
for(int i = lastCurrentBlock+1; i <= lastNewBlock; i++) {
|
|
||||||
/* Alloc block i */
|
|
||||||
int blockSize = tlp.initialSize * powl(tlp.multiplier, i);
|
|
||||||
int newFirstPage;
|
|
||||||
begin_action_ret(releasePage, p, compensation_error()) {
|
|
||||||
newFirstPage = TpageAllocMany(xid, blockSize);
|
|
||||||
} end_action_ret(compensation_error());
|
|
||||||
DEBUG("block %d\n", i);
|
|
||||||
/* Iterate over the storage blocks that are pointed to by our current indirection block. */
|
|
||||||
// for(int j = 0; j < blockSize; j++) {
|
|
||||||
// DEBUG("page %d (%d)\n", j, j + newFirstPage);
|
|
||||||
// tmp2.page = j + newFirstPage;
|
|
||||||
/** @todo If we were a little smarter about this, and fixed.c
|
|
||||||
could handle uninitialized blocks correctly, then we
|
|
||||||
wouldn't have to iterate over the datapages in
|
|
||||||
TarrayListExtend() (Fixed?)*/
|
|
||||||
// T update(xid, tmp2, NULL, OPERATION_INITIALIZE_FIXED_PAGE);
|
|
||||||
// }
|
|
||||||
|
|
||||||
tmp.slot = i + FIRST_DATA_PAGE_OFFSET;
|
|
||||||
/** @todo what does this do to recovery?? */
|
|
||||||
/** @todo locking for arrayList... */
|
|
||||||
/* *page_type_ptr(p) = FIXED_PAGE;
|
|
||||||
Tset(xid, tmp, &newFirstPage);
|
|
||||||
*page_type_ptr(p) = ARRAY_LIST_PAGE; */
|
|
||||||
/* @todo This is a copy of Tupdate!! Replace it.*/
|
|
||||||
begin_action_ret(releasePage, p, compensation_error()) {
|
|
||||||
alTupdate(xid, tmp, &newFirstPage, OPERATION_SET);
|
|
||||||
} end_action_ret(compensation_error());
|
|
||||||
|
|
||||||
DEBUG("Tset: {%d, %d, %d} = %d\n", tmp.page, tmp.slot, tmp.size, newFirstPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp.slot = MAX_OFFSET_POSITION;
|
|
||||||
|
|
||||||
int newMaxOffset = tlp.maxOffset+slots;
|
|
||||||
// *page_type_ptr(p) = FIXED_PAGE;
|
|
||||||
// Tset(xid, tmp, &newMaxOffset);
|
|
||||||
// *page_type_ptr(p) = ARRAY_LIST_PAGE;
|
|
||||||
// releasePage(p);
|
|
||||||
/* @todo This is a copy of Tupdate!! Replace it.*/
|
|
||||||
begin_action_ret(releasePage, p, compensation_error()) {
|
|
||||||
alTupdate(xid, tmp, &newMaxOffset, OPERATION_SET);
|
|
||||||
} compensate_ret(compensation_error());
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
/** @todo: TarrayListInstantExtend, is a hacked-up cut and paste version of TarrayListExtend */
|
|
||||||
compensated_function int TarrayListInstantExtend(int xid, recordid rid, int slots) {
|
|
||||||
Page * p = loadPage(xid, rid.page);
|
|
||||||
TarrayListParameters tlp = pageToTLP(p);
|
|
||||||
|
|
||||||
int lastCurrentBlock;
|
|
||||||
if(tlp.maxOffset == -1) {
|
|
||||||
lastCurrentBlock = -1;
|
|
||||||
} else{
|
|
||||||
lastCurrentBlock = getBlockContainingOffset(tlp, tlp.maxOffset, NULL);
|
|
||||||
}
|
|
||||||
int lastNewBlock = getBlockContainingOffset(tlp, tlp.maxOffset+slots, NULL);
|
|
||||||
|
|
||||||
DEBUG("lastCurrentBlock = %d, lastNewBlock = %d\n", lastCurrentBlock, lastNewBlock);
|
|
||||||
|
|
||||||
recordid tmp; /* recordid of slot in base page that holds new block. */
|
|
||||||
tmp.page = rid.page;
|
|
||||||
tmp.size = sizeof(int);
|
|
||||||
|
|
||||||
recordid tmp2; /* recordid of newly created pages. */
|
|
||||||
tmp2.slot = 0;
|
|
||||||
tmp2.size = tlp.size;
|
|
||||||
/* Iterate over the (small number) of indirection blocks that need to be updated */
|
|
||||||
for(int i = lastCurrentBlock+1; i <= lastNewBlock; i++) {
|
|
||||||
/* Alloc block i */
|
|
||||||
int blockSize = tlp.initialSize * powl(tlp.multiplier, i);
|
|
||||||
int newFirstPage = TpageAllocMany(xid, blockSize);
|
|
||||||
DEBUG("block %d\n", i);
|
|
||||||
/* Iterate over the storage blocks that are pointed to by our current indirection block. */
|
|
||||||
/* for(int j = 0; j < blockSize; j++) {
|
|
||||||
DEBUG("page %d (%d)\n", j, j + newFirstPage);
|
|
||||||
tmp2.page = j + newFirstPage;
|
|
||||||
/ ** @todo If we were a little smarter about this, and fixed.c
|
|
||||||
coulds handle uninitialized blocks correctly, then we
|
|
||||||
wouldn't have to iterate over the datapages in
|
|
||||||
TarrayListExtend() * /
|
|
||||||
// Tupdate(xid, tmp2, NULL, OPERATION_INITIALIZE_FIXED_PAGE);
|
|
||||||
} */
|
|
||||||
|
|
||||||
tmp.slot = i + FIRST_DATA_PAGE_OFFSET;
|
|
||||||
/** @todo what does this do to recovery?? */
|
|
||||||
/** @todo locking for arrayList... */
|
|
||||||
*page_type_ptr(p) = FIXED_PAGE;
|
|
||||||
TinstantSet(xid, tmp, &newFirstPage);
|
|
||||||
*page_type_ptr(p) = ARRAY_LIST_PAGE;
|
|
||||||
|
|
||||||
DEBUG("Tset: {%d, %d, %d} = %d\n", tmp.page, tmp.slot, tmp.size, newFirstPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp.slot = MAX_OFFSET_POSITION;
|
|
||||||
|
|
||||||
int newMaxOffset = tlp.maxOffset+slots;
|
|
||||||
/** @todo CORRECTNESS BUG: From recovery's point of view, arrayList is totally wrong! The
|
|
||||||
only reason we mess with p is beacuse TinstantSet doesn't handle
|
|
||||||
ARRAY_LIST_PAGES the way we need it to, so this won't be hard to
|
|
||||||
fix... */
|
|
||||||
*page_type_ptr(p) = FIXED_PAGE;
|
|
||||||
TinstantSet(xid, tmp, &newMaxOffset);
|
|
||||||
*page_type_ptr(p) = ARRAY_LIST_PAGE;
|
|
||||||
releasePage(p);
|
releasePage(p);
|
||||||
|
p = NULL;
|
||||||
|
|
||||||
|
int lastCurrentBlock;
|
||||||
|
if(tlp.maxOffset == -1) {
|
||||||
|
lastCurrentBlock = -1;
|
||||||
|
} else{
|
||||||
|
lastCurrentBlock = getBlockContainingOffset(tlp, tlp.maxOffset, NULL);
|
||||||
|
}
|
||||||
|
int lastNewBlock = getBlockContainingOffset(tlp, tlp.maxOffset+slots, NULL);
|
||||||
|
|
||||||
|
DEBUG("lastCurrentBlock = %d, lastNewBlock = %d\n", lastCurrentBlock, lastNewBlock);
|
||||||
|
|
||||||
|
recordid tmp; /* recordid of slot in base page that holds new block. */
|
||||||
|
tmp.page = rid.page;
|
||||||
|
tmp.size = sizeof(int);
|
||||||
|
|
||||||
|
recordid tmp2; /* recordid of newly created pages. */
|
||||||
|
tmp2.slot = 0;
|
||||||
|
tmp2.size = tlp.size;
|
||||||
|
/* Iterate over the (small number) of indirection blocks that need to be updated */
|
||||||
|
try_ret(compensation_error()) {
|
||||||
|
for(int i = lastCurrentBlock+1; i <= lastNewBlock; i++) {
|
||||||
|
/* Alloc block i */
|
||||||
|
int blockSize = tlp.initialSize * powl(tlp.multiplier, i);
|
||||||
|
int newFirstPage = TpageAllocMany(xid, blockSize);
|
||||||
|
DEBUG("block %d\n", i);
|
||||||
|
/* We used to call OPERATION_INITIALIZE_FIXED_PAGE on each page in current indirection block. */
|
||||||
|
tmp.slot = i + FIRST_DATA_PAGE_OFFSET;
|
||||||
|
alTupdate(xid, tmp, &newFirstPage, op);
|
||||||
|
DEBUG("Tset: {%d, %d, %d} = %d\n", tmp.page, tmp.slot, tmp.size, newFirstPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp.slot = MAX_OFFSET_POSITION;
|
||||||
|
|
||||||
|
int newMaxOffset = tlp.maxOffset+slots;
|
||||||
|
alTupdate(xid, tmp, &newMaxOffset, op);
|
||||||
|
} end_ret(compensation_error());
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compensated_function int TarrayListInstantExtend(int xid, recordid rid, int slots) {
|
||||||
|
return TarrayListExtendInternal(xid, rid, slots, OPERATION_INSTANT_SET);
|
||||||
|
}
|
||||||
|
compensated_function int TarrayListExtend(int xid, recordid rid, int slots) {
|
||||||
|
return TarrayListExtendInternal(xid, rid, slots, OPERATION_SET);
|
||||||
|
}
|
||||||
|
|
||||||
static int operateInitFixed(int xid, Page * p, lsn_t lsn, recordid rid, const void * dat) {
|
static int operateInitFixed(int xid, Page * p, lsn_t lsn, recordid rid, const void * dat) {
|
||||||
|
|
||||||
fixedPageInitialize(p, rid.size, recordsPerPage(rid.size));
|
fixedPageInitialize(p, rid.size, recordsPerPage(rid.size));
|
||||||
|
|
||||||
pageWriteLSN(xid, p, lsn);
|
pageWriteLSN(xid, p, lsn);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,27 +70,31 @@ int findInBucket(int xid, recordid hashRid, int bucket_number, const void * key,
|
||||||
|
|
||||||
|
|
||||||
int findInBucket(int xid, recordid hashRid, int bucket_number, const void * key, int keySize, void * val, int valSize) {
|
int findInBucket(int xid, recordid hashRid, int bucket_number, const void * key, int keySize, void * val, int valSize) {
|
||||||
|
int found;
|
||||||
hashEntry * e = malloc(sizeof(hashEntry) + keySize + valSize);
|
try_ret(compensation_error()) {
|
||||||
|
hashEntry * e = malloc(sizeof(hashEntry) + keySize + valSize);
|
||||||
recordid nextEntry;
|
|
||||||
|
recordid nextEntry;
|
||||||
hashRid.slot = bucket_number;
|
|
||||||
nextEntry = hashRid;
|
hashRid.slot = bucket_number;
|
||||||
|
nextEntry = hashRid;
|
||||||
int found = 0;
|
|
||||||
|
found = 0;
|
||||||
while(nextEntry.size != -1 && nextEntry.size != 0) {
|
|
||||||
assert(nextEntry.size == sizeof(hashEntry) + keySize + valSize);
|
while(nextEntry.size != -1 && nextEntry.size != 0) {
|
||||||
Tread(xid, nextEntry, e);
|
if(compensation_error()) { break; }
|
||||||
if(!memcmp(key, e+1, keySize) && e->next.size != 0) {
|
assert(nextEntry.size == sizeof(hashEntry) + keySize + valSize);
|
||||||
memcpy(val, ((byte*)(e+1))+keySize, valSize);
|
Tread(xid, nextEntry, e);
|
||||||
found = 1;
|
if(!memcmp(key, e+1, keySize) && e->next.size != 0) {
|
||||||
break;
|
memcpy(val, ((byte*)(e+1))+keySize, valSize);
|
||||||
}
|
found = 1;
|
||||||
nextEntry = e->next;
|
break;
|
||||||
}
|
}
|
||||||
free(e);
|
nextEntry = e->next;
|
||||||
|
}
|
||||||
|
free(e);
|
||||||
|
} end_ret(compensation_error());
|
||||||
|
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,177 +106,183 @@ void expand (int xid, recordid hash, int next_split, int i, int keySize, int val
|
||||||
#define AMORTIZE 1000
|
#define AMORTIZE 1000
|
||||||
#define FF_AM 750
|
#define FF_AM 750
|
||||||
if(count <= 0 && !(count * -1) % FF_AM) {
|
if(count <= 0 && !(count * -1) % FF_AM) {
|
||||||
recordid * headerRidB = pblHtLookup(openHashes, &(hash.page), sizeof(int));
|
try {
|
||||||
int j;
|
recordid * headerRidB = pblHtLookup(openHashes, &(hash.page), sizeof(int));
|
||||||
TarrayListExtend(xid, hash, AMORTIZE);
|
int j;
|
||||||
for(j = 0; j < AMORTIZE; j++) {
|
TarrayListExtend(xid, hash, AMORTIZE);
|
||||||
|
for(j = 0; j < AMORTIZE; j++) {
|
||||||
if(next_split >= twoToThe(i-1)+2) {
|
if(compensation_error()) { break; }
|
||||||
i++;
|
if(next_split >= twoToThe(i-1)+2) {
|
||||||
next_split = 2;
|
i++;
|
||||||
}
|
next_split = 2;
|
||||||
rehash(xid, hash, next_split, i, keySize, valSize);
|
}
|
||||||
next_split++;
|
rehash(xid, hash, next_split, i, keySize, valSize);
|
||||||
headerNextSplit = next_split;
|
next_split++;
|
||||||
headerHashBits = i;
|
headerNextSplit = next_split;
|
||||||
}
|
headerHashBits = i;
|
||||||
update_hash_header(xid, hash, i, next_split);
|
}
|
||||||
|
update_hash_header(xid, hash, i, next_split);
|
||||||
|
} end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_hash_header(int xid, recordid hash, int i, int next_split) {
|
void update_hash_header(int xid, recordid hash, int i, int next_split) {
|
||||||
hashEntry * he = pblHtLookup(openHashes, &(hash.page), sizeof(int));
|
try {
|
||||||
assert(he);
|
hashEntry * he = pblHtLookup(openHashes, &(hash.page), sizeof(int));
|
||||||
recordid * headerRidB = &he->next;
|
assert(he);
|
||||||
|
recordid * headerRidB = &he->next;
|
||||||
assert(headerRidB);
|
|
||||||
|
assert(headerRidB);
|
||||||
headerHashBits = i;
|
|
||||||
headerNextSplit = next_split;
|
headerHashBits = i;
|
||||||
hash.slot = 1;
|
headerNextSplit = next_split;
|
||||||
Tset(xid, hash, headerRidB);
|
hash.slot = 1;
|
||||||
|
|
||||||
|
Tset(xid, hash, headerRidB);
|
||||||
|
} end;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rehash(int xid, recordid hashRid, int next_split, int i, int keySize, int valSize) {
|
void rehash(int xid, recordid hashRid, int next_split, int i, int keySize, int valSize) {
|
||||||
int firstA = 1; // Is 'A' the recordid of a bucket?
|
try {
|
||||||
int firstD = 1; // What about 'D'?
|
int firstA = 1; // Is 'A' the recordid of a bucket?
|
||||||
|
int firstD = 1; // What about 'D'?
|
||||||
assert(hashRid.size == sizeof(hashEntry) + keySize + valSize);
|
|
||||||
recordid ba = hashRid; ba.slot = next_split;
|
|
||||||
recordid bb = hashRid; bb.slot = next_split + twoToThe(i-1);
|
|
||||||
|
|
||||||
hashEntry * D_contents = calloc(1,sizeof(hashEntry) + keySize + valSize);
|
|
||||||
hashEntry * A_contents = calloc(1,sizeof(hashEntry) + keySize + valSize);
|
|
||||||
hashEntry * B_contents = calloc(1,sizeof(hashEntry) + keySize + valSize);
|
|
||||||
|
|
||||||
Tread(xid, ba, A_contents);
|
|
||||||
Tread(xid, bb, D_contents);
|
|
||||||
recordid A = ba; //ba_contents;
|
|
||||||
recordid D = bb; //bb_contents;
|
|
||||||
recordid B = A_contents->next;
|
|
||||||
recordid C;
|
|
||||||
|
|
||||||
if(!A_contents->next.size) {
|
|
||||||
/* Bucket A is empty, so we're done. */
|
|
||||||
free(D_contents);
|
|
||||||
free(A_contents);
|
|
||||||
free(B_contents);
|
|
||||||
/* printf("Expand was a noop.\n");
|
|
||||||
fflush(NULL); */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int old_hash;
|
|
||||||
int new_hash = hash(A_contents+1, keySize, i, ULONG_MAX) + 2;
|
|
||||||
|
|
||||||
while(new_hash != next_split) {
|
|
||||||
// Need a record in A that belongs in the first bucket...
|
|
||||||
|
|
||||||
recordid oldANext = A_contents->next;
|
assert(hashRid.size == sizeof(hashEntry) + keySize + valSize);
|
||||||
|
recordid ba = hashRid; ba.slot = next_split;
|
||||||
A_contents->next = NULLRID;
|
recordid bb = hashRid; bb.slot = next_split + twoToThe(i-1);
|
||||||
|
|
||||||
if(firstD) {
|
hashEntry * D_contents = calloc(1,sizeof(hashEntry) + keySize + valSize);
|
||||||
// assert(memcmp(&A_contents->next, &D_contents->next, sizeof(recordid)));
|
hashEntry * A_contents = calloc(1,sizeof(hashEntry) + keySize + valSize);
|
||||||
Tset(xid, D, A_contents);
|
hashEntry * B_contents = calloc(1,sizeof(hashEntry) + keySize + valSize);
|
||||||
firstD = 0;
|
|
||||||
} else {
|
Tread(xid, ba, A_contents);
|
||||||
/* D at end of list => can overwrite next. */
|
Tread(xid, bb, D_contents);
|
||||||
D_contents->next = Talloc(xid, sizeof(hashEntry) + keySize + valSize); /* @todo
|
recordid A = ba; //ba_contents;
|
||||||
unfortunate
|
recordid D = bb; //bb_contents;
|
||||||
to
|
recordid B = A_contents->next;
|
||||||
dealloc
|
recordid C;
|
||||||
A's
|
|
||||||
successor,
|
if(!A_contents->next.size) {
|
||||||
then
|
/* Bucket A is empty, so we're done. */
|
||||||
alloc.. */
|
|
||||||
// assert(memcmp(&A_contents->next, &D_contents->next, sizeof(recordid)));
|
|
||||||
Tset(xid, D_contents->next, A_contents);
|
|
||||||
// assert(memcmp(&D, &D_contents->next, sizeof(recordid)));
|
|
||||||
Tset(xid, D, D_contents);
|
|
||||||
D = A;
|
|
||||||
}
|
|
||||||
hashEntry * swap = D_contents;
|
|
||||||
D_contents = A_contents;
|
|
||||||
A_contents = swap;
|
|
||||||
|
|
||||||
/* A_contents is now garbage. */
|
|
||||||
|
|
||||||
assert(A.size == sizeof(hashEntry) + keySize + valSize);
|
|
||||||
if(oldANext.size == -1) {
|
|
||||||
memset(A_contents, 0, sizeof(hashEntry) + keySize + valSize);
|
|
||||||
// assert(memcmp(&A_contents->next, &A, sizeof(recordid)));
|
|
||||||
Tset(xid, A, A_contents);
|
|
||||||
free(D_contents);
|
free(D_contents);
|
||||||
free(A_contents);
|
free(A_contents);
|
||||||
free(B_contents);
|
free(B_contents);
|
||||||
/* printf("Loop 1 returning.\n");
|
/* printf("Expand was a noop.\n");
|
||||||
fflush(NULL); */
|
fflush(NULL); */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert(oldANext.size == sizeof(hashEntry) + keySize + valSize);
|
|
||||||
Tread(xid, oldANext, A_contents);
|
|
||||||
// assert(memcmp(&A_contents->next, &A, sizeof(recordid)));
|
|
||||||
Tset(xid, A, A_contents);
|
|
||||||
Tdealloc(xid, oldANext);
|
|
||||||
|
|
||||||
new_hash = hash(A_contents+1, keySize, i, ULONG_MAX) + 2;
|
int old_hash;
|
||||||
}
|
int new_hash = hash(A_contents+1, keySize, i, ULONG_MAX) + 2;
|
||||||
/* printf("Got past loop 1\n");
|
|
||||||
fflush(NULL); */
|
|
||||||
|
|
||||||
B = A_contents->next;
|
while(new_hash != next_split) {
|
||||||
|
// Need a record in A that belongs in the first bucket...
|
||||||
while(B.size != -1) {
|
|
||||||
assert(B.size == sizeof(hashEntry) + keySize + valSize);
|
recordid oldANext = A_contents->next;
|
||||||
Tread(xid, B, B_contents);
|
|
||||||
C = B_contents->next;
|
A_contents->next = NULLRID;
|
||||||
|
|
||||||
old_hash = hash(B_contents+1, keySize, i-1, ULONG_MAX) + 2;
|
if(firstD) {
|
||||||
new_hash = hash(B_contents+1, keySize, i, ULONG_MAX) + 2;
|
// assert(memcmp(&A_contents->next, &D_contents->next, sizeof(recordid)));
|
||||||
|
Tset(xid, D, A_contents);
|
||||||
assert(next_split == old_hash);
|
firstD = 0;
|
||||||
assert(new_hash == old_hash || new_hash == old_hash + twoToThe(i-1));
|
} else {
|
||||||
|
/* D at end of list => can overwrite next. */
|
||||||
if(new_hash == old_hash) {
|
D_contents->next = Talloc(xid, sizeof(hashEntry) + keySize + valSize); /* @todo
|
||||||
A = B;
|
unfortunate
|
||||||
B = C;
|
to
|
||||||
C.size = -1;
|
dealloc
|
||||||
firstA = 0;
|
A's
|
||||||
} else {
|
successor,
|
||||||
assert(D.size == sizeof(hashEntry) + keySize + valSize);
|
then
|
||||||
assert(B.size == -1 || B.size == sizeof(hashEntry) + keySize + valSize);
|
alloc.. */
|
||||||
Tread(xid, D, D_contents);
|
// assert(memcmp(&A_contents->next, &D_contents->next, sizeof(recordid)));
|
||||||
D_contents->next = B;
|
Tset(xid, D_contents->next, A_contents);
|
||||||
assert(B.size != 0);
|
// assert(memcmp(&D, &D_contents->next, sizeof(recordid)));
|
||||||
Tset(xid, D, D_contents);
|
Tset(xid, D, D_contents);
|
||||||
|
D = A;
|
||||||
// A is somewhere in the first list.
|
}
|
||||||
assert(A.size == sizeof(hashEntry) + keySize + valSize);
|
hashEntry * swap = D_contents;
|
||||||
assert(C.size == -1 || C.size == sizeof(hashEntry) + keySize + valSize);
|
D_contents = A_contents;
|
||||||
Tread(xid, A, A_contents);
|
A_contents = swap;
|
||||||
A_contents->next = C;
|
|
||||||
assert(C.size != 0);
|
/* A_contents is now garbage. */
|
||||||
|
|
||||||
Tset(xid, A, A_contents);
|
assert(A.size == sizeof(hashEntry) + keySize + valSize);
|
||||||
|
if(oldANext.size == -1) {
|
||||||
// B _can't_ be a bucket.
|
memset(A_contents, 0, sizeof(hashEntry) + keySize + valSize);
|
||||||
|
// assert(memcmp(&A_contents->next, &A, sizeof(recordid)));
|
||||||
|
Tset(xid, A, A_contents);
|
||||||
|
free(D_contents);
|
||||||
|
free(A_contents);
|
||||||
|
free(B_contents);
|
||||||
|
/* printf("Loop 1 returning.\n");
|
||||||
|
fflush(NULL); */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
assert(oldANext.size == sizeof(hashEntry) + keySize + valSize);
|
||||||
|
Tread(xid, oldANext, A_contents);
|
||||||
|
// assert(memcmp(&A_contents->next, &A, sizeof(recordid)));
|
||||||
|
Tset(xid, A, A_contents);
|
||||||
|
Tdealloc(xid, oldANext);
|
||||||
|
|
||||||
|
new_hash = hash(A_contents+1, keySize, i, ULONG_MAX) + 2;
|
||||||
|
}
|
||||||
|
/* printf("Got past loop 1\n");
|
||||||
|
fflush(NULL); */
|
||||||
|
|
||||||
|
B = A_contents->next;
|
||||||
|
|
||||||
|
while(B.size != -1) {
|
||||||
assert(B.size == sizeof(hashEntry) + keySize + valSize);
|
assert(B.size == sizeof(hashEntry) + keySize + valSize);
|
||||||
Tread(xid, B, B_contents);
|
Tread(xid, B, B_contents);
|
||||||
B_contents->next = NULLRID;
|
C = B_contents->next;
|
||||||
Tset(xid, B, B_contents);
|
|
||||||
|
old_hash = hash(B_contents+1, keySize, i-1, ULONG_MAX) + 2;
|
||||||
// Update Loop State
|
new_hash = hash(B_contents+1, keySize, i, ULONG_MAX) + 2;
|
||||||
D = B;
|
|
||||||
B = C;
|
assert(next_split == old_hash);
|
||||||
C.size = -1;
|
assert(new_hash == old_hash || new_hash == old_hash + twoToThe(i-1));
|
||||||
firstD = 0;
|
|
||||||
|
if(new_hash == old_hash) {
|
||||||
|
A = B;
|
||||||
|
B = C;
|
||||||
|
C.size = -1;
|
||||||
|
firstA = 0;
|
||||||
|
} else {
|
||||||
|
assert(D.size == sizeof(hashEntry) + keySize + valSize);
|
||||||
|
assert(B.size == -1 || B.size == sizeof(hashEntry) + keySize + valSize);
|
||||||
|
Tread(xid, D, D_contents);
|
||||||
|
D_contents->next = B;
|
||||||
|
assert(B.size != 0);
|
||||||
|
Tset(xid, D, D_contents);
|
||||||
|
|
||||||
|
// A is somewhere in the first list.
|
||||||
|
assert(A.size == sizeof(hashEntry) + keySize + valSize);
|
||||||
|
assert(C.size == -1 || C.size == sizeof(hashEntry) + keySize + valSize);
|
||||||
|
Tread(xid, A, A_contents);
|
||||||
|
A_contents->next = C;
|
||||||
|
assert(C.size != 0);
|
||||||
|
|
||||||
|
Tset(xid, A, A_contents);
|
||||||
|
|
||||||
|
// B _can't_ be a bucket.
|
||||||
|
assert(B.size == sizeof(hashEntry) + keySize + valSize);
|
||||||
|
Tread(xid, B, B_contents);
|
||||||
|
B_contents->next = NULLRID;
|
||||||
|
Tset(xid, B, B_contents);
|
||||||
|
|
||||||
|
// Update Loop State
|
||||||
|
D = B;
|
||||||
|
B = C;
|
||||||
|
C.size = -1;
|
||||||
|
firstD = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
free(D_contents);
|
||||||
free(D_contents);
|
free(A_contents);
|
||||||
free(A_contents);
|
free(B_contents);
|
||||||
free(B_contents);
|
} end;
|
||||||
|
|
||||||
}
|
}
|
||||||
void insertIntoBucket(int xid, recordid hashRid, int bucket_number, hashEntry * bucket_contents,
|
void insertIntoBucket(int xid, recordid hashRid, int bucket_number, hashEntry * bucket_contents,
|
||||||
hashEntry * e, int keySize, int valSize, int skipDelete) {
|
hashEntry * e, int keySize, int valSize, int skipDelete) {
|
||||||
|
@ -344,6 +354,7 @@ int deleteFromBucket(int xid, recordid hash, int bucket_number, hashEntry * buck
|
||||||
memcpy(B, bucket_contents, sizeof(hashEntry) + keySize + valSize);
|
memcpy(B, bucket_contents, sizeof(hashEntry) + keySize + valSize);
|
||||||
Baddr = this;
|
Baddr = this;
|
||||||
while(B->next.size != -1) {
|
while(B->next.size != -1) {
|
||||||
|
if(compensation_error()) { break; } // guard the asserts below.
|
||||||
hashEntry * tmp = A;
|
hashEntry * tmp = A;
|
||||||
A = B;
|
A = B;
|
||||||
Aaddr = Baddr;
|
Aaddr = Baddr;
|
||||||
|
|
|
@ -191,7 +191,7 @@ compensated_function void pageOperationsInit() {
|
||||||
commit / abort. If other transactions need to allocate when the
|
commit / abort. If other transactions need to allocate when the
|
||||||
lock is held, then they simply do not reuse pages. Since locking is
|
lock is held, then they simply do not reuse pages. Since locking is
|
||||||
not yet implemented, we require applications to manually serialize
|
not yet implemented, we require applications to manually serialize
|
||||||
transactions that call Talloc() or TdeAlloc
|
transactions that call Talloc or Tdealloc
|
||||||
|
|
||||||
A better solution: defer the addition of 100 to the freelist until
|
A better solution: defer the addition of 100 to the freelist until
|
||||||
commit, and use a 'real' data structure, like a concurrent B-Tree.
|
commit, and use a 'real' data structure, like a concurrent B-Tree.
|
||||||
|
|
|
@ -10,184 +10,196 @@ typedef struct {
|
||||||
short keySize;
|
short keySize;
|
||||||
} pagedListEntry;
|
} pagedListEntry;
|
||||||
|
|
||||||
recordid TpagedListAlloc(int xid) {
|
compensated_function recordid TpagedListAlloc(int xid) {
|
||||||
|
recordid ret;
|
||||||
recordid ret = Talloc(xid, sizeof(pagedListHeader));
|
try_ret(NULLRID) {
|
||||||
pagedListHeader header;
|
ret = Talloc(xid, sizeof(pagedListHeader));
|
||||||
header.thisPage = 0;
|
pagedListHeader header;
|
||||||
header.nextPage = NULLRID;
|
header.thisPage = 0;
|
||||||
Tset(xid, ret, &header);
|
header.nextPage = NULLRID;
|
||||||
|
Tset(xid, ret, &header);
|
||||||
|
} end_ret(NULLRID);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
compensated_function int TpagedListInsert(int xid, recordid list, const byte * key, int keySize, const byte * value, int valueSize) {
|
compensated_function int TpagedListInsert(int xid, recordid list, const byte * key, int keySize, const byte * value, int valueSize) {
|
||||||
pagedListHeader header;
|
int ret;
|
||||||
Tread(xid, list, &header);
|
try_ret(compensation_error()) {
|
||||||
recordid headerRid = list;
|
pagedListHeader header;
|
||||||
|
Tread(xid, list, &header);
|
||||||
|
recordid headerRid = list;
|
||||||
|
|
||||||
byte * garbage;
|
byte * garbage;
|
||||||
int ret = (TpagedListFind(xid, list, key, keySize, &garbage) != -1);
|
ret = (TpagedListFind(xid, list, key, keySize, &garbage) != -1);
|
||||||
if(ret) {
|
if(ret) {
|
||||||
TpagedListRemove(xid, list, key, keySize);
|
free(garbage);
|
||||||
free(garbage);
|
TpagedListRemove(xid, list, key, keySize);
|
||||||
}
|
|
||||||
int entrySize = sizeof(pagedListEntry) + keySize + valueSize;
|
|
||||||
|
|
||||||
recordid rid = TallocFromPage(xid, headerRid.page, entrySize);
|
|
||||||
DEBUG("Alloced rid: {%d %d %d}", rid.page, rid.slot, rid.size);
|
|
||||||
|
|
||||||
// When the loop completes, header will contain the contents of the page header the entry will be inserted into,
|
|
||||||
// headerrid will contain the rid of that header, and rid will contain the newly allocated recordid
|
|
||||||
while(rid.size == -1) {
|
|
||||||
if(header.nextPage.size == -1) {
|
|
||||||
header.nextPage = Talloc(xid, sizeof(pagedListHeader));
|
|
||||||
DEBUG("allocing on new page %d\n", header.nextPage.page);
|
|
||||||
Tset(xid, headerRid, &header);
|
|
||||||
pagedListHeader newHead;
|
|
||||||
newHead.thisPage = 0;
|
|
||||||
newHead.nextPage.page =0;
|
|
||||||
newHead.nextPage.slot =0;
|
|
||||||
newHead.nextPage.size =-1;
|
|
||||||
Tset(xid, header.nextPage, &newHead);
|
|
||||||
}
|
}
|
||||||
|
int entrySize = sizeof(pagedListEntry) + keySize + valueSize;
|
||||||
headerRid = header.nextPage;
|
|
||||||
Tread(xid, header.nextPage, &header);
|
recordid rid = TallocFromPage(xid, headerRid.page, entrySize);
|
||||||
rid = TallocFromPage(xid, headerRid.page, entrySize);
|
|
||||||
DEBUG("Alloced rid: {%d %d %d}", rid.page, rid.slot, rid.size);
|
DEBUG("Alloced rid: {%d %d %d}", rid.page, rid.slot, rid.size);
|
||||||
}
|
|
||||||
|
// When the loop completes, header will contain the contents of the page header the entry will be inserted into,
|
||||||
pagedListEntry * dat = malloc(entrySize);
|
// headerrid will contain the rid of that header, and rid will contain the newly allocated recordid
|
||||||
|
while(rid.size == -1) {
|
||||||
dat->keySize = keySize;
|
if(compensation_error()) { break; }
|
||||||
dat->nextEntry = header.thisPage;
|
if(header.nextPage.size == -1) {
|
||||||
memcpy(dat+1, key, keySize);
|
header.nextPage = Talloc(xid, sizeof(pagedListHeader));
|
||||||
memcpy(((byte*)(dat+1))+keySize, value, valueSize);
|
DEBUG("allocing on new page %d\n", header.nextPage.page);
|
||||||
Tset(xid, rid, dat);
|
Tset(xid, headerRid, &header);
|
||||||
|
pagedListHeader newHead;
|
||||||
header.thisPage = rid.slot;
|
newHead.thisPage = 0;
|
||||||
DEBUG("Header.thisPage = %d\n", rid.slot);
|
newHead.nextPage.page =0;
|
||||||
Tset(xid, headerRid, &header);
|
newHead.nextPage.slot =0;
|
||||||
free(dat);
|
newHead.nextPage.size =-1;
|
||||||
|
Tset(xid, header.nextPage, &newHead);
|
||||||
|
}
|
||||||
|
|
||||||
|
headerRid = header.nextPage;
|
||||||
|
Tread(xid, header.nextPage, &header);
|
||||||
|
rid = TallocFromPage(xid, headerRid.page, entrySize);
|
||||||
|
DEBUG("Alloced rid: {%d %d %d}", rid.page, rid.slot, rid.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
pagedListEntry * dat = malloc(entrySize);
|
||||||
|
|
||||||
|
dat->keySize = keySize;
|
||||||
|
dat->nextEntry = header.thisPage;
|
||||||
|
memcpy(dat+1, key, keySize);
|
||||||
|
memcpy(((byte*)(dat+1))+keySize, value, valueSize);
|
||||||
|
Tset(xid, rid, dat);
|
||||||
|
|
||||||
|
header.thisPage = rid.slot;
|
||||||
|
DEBUG("Header.thisPage = %d\n", rid.slot);
|
||||||
|
Tset(xid, headerRid, &header);
|
||||||
|
free(dat);
|
||||||
|
} end_ret(compensation_error());
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
compensated_function int TpagedListFind(int xid, recordid list, const byte * key, int keySize, byte ** value) {
|
compensated_function int TpagedListFind(int xid, recordid list, const byte * key, int keySize, byte ** value) {
|
||||||
pagedListHeader header;
|
try_ret(compensation_error()) {
|
||||||
Tread(xid, list, &header);
|
pagedListHeader header;
|
||||||
|
Tread(xid, list, &header);
|
||||||
recordid rid;
|
|
||||||
rid.page = list.page;
|
|
||||||
rid.slot = header.thisPage;
|
|
||||||
|
|
||||||
while(rid.slot || header.nextPage.size != -1) {
|
|
||||||
|
|
||||||
if(rid.slot) {
|
|
||||||
rid.size = TrecordSize(xid, rid);
|
|
||||||
pagedListEntry * dat;
|
|
||||||
dat = malloc(rid.size);
|
|
||||||
Tread(xid, rid, dat);
|
|
||||||
|
|
||||||
if(!memcmp(dat+1, key, keySize)) {
|
|
||||||
int valueSize = rid.size - keySize - sizeof(pagedListEntry);
|
|
||||||
*value = malloc(valueSize);
|
|
||||||
memcpy(*value, ((byte*)(dat+1))+keySize, valueSize);
|
|
||||||
free(dat);
|
|
||||||
return valueSize;
|
|
||||||
}
|
|
||||||
// if(dat->nextEntry) { // another entry on this page
|
|
||||||
rid.slot = dat->nextEntry;
|
|
||||||
free(dat); // }
|
|
||||||
} else if (header.nextPage.size != -1) { // another page
|
|
||||||
rid.page = header.nextPage.page;
|
|
||||||
Tread(xid, header.nextPage, &header);
|
|
||||||
rid.slot = header.thisPage;
|
|
||||||
} else { // we've reached the end of the last page
|
|
||||||
rid.slot = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
recordid rid;
|
||||||
|
rid.page = list.page;
|
||||||
|
rid.slot = header.thisPage;
|
||||||
|
|
||||||
}
|
while(rid.slot || header.nextPage.size != -1) {
|
||||||
|
if(compensation_error()) { break; }
|
||||||
|
|
||||||
|
if(rid.slot) {
|
||||||
|
rid.size = TrecordSize(xid, rid);
|
||||||
|
pagedListEntry * dat;
|
||||||
|
if(compensation_error()) { break; }
|
||||||
|
dat = malloc(rid.size);
|
||||||
|
Tread(xid, rid, dat);
|
||||||
|
|
||||||
|
if(!memcmp(dat+1, key, keySize)) {
|
||||||
|
int valueSize = rid.size - keySize - sizeof(pagedListEntry);
|
||||||
|
*value = malloc(valueSize);
|
||||||
|
memcpy(*value, ((byte*)(dat+1))+keySize, valueSize);
|
||||||
|
free(dat);
|
||||||
|
return valueSize;
|
||||||
|
}
|
||||||
|
// if(dat->nextEntry) { // another entry on this page
|
||||||
|
rid.slot = dat->nextEntry;
|
||||||
|
free(dat); // }
|
||||||
|
} else if (header.nextPage.size != -1) { // another page
|
||||||
|
rid.page = header.nextPage.page;
|
||||||
|
Tread(xid, header.nextPage, &header);
|
||||||
|
rid.slot = header.thisPage;
|
||||||
|
} else { // we've reached the end of the last page
|
||||||
|
rid.slot = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} end_ret(compensation_error());
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
compensated_function int TpagedListRemove(int xid, recordid list, const byte * key, int keySize) {
|
compensated_function int TpagedListRemove(int xid, recordid list, const byte * key, int keySize) {
|
||||||
pagedListHeader header;
|
pagedListHeader header;
|
||||||
Tread(xid, list, &header);
|
int ret = 0;
|
||||||
recordid headerRid;
|
try_ret(compensation_error()) {
|
||||||
recordid rid;
|
|
||||||
rid.page = list.page;
|
Tread(xid, list, &header);
|
||||||
rid.slot = header.thisPage;
|
recordid headerRid;
|
||||||
short lastSlot = -1;
|
recordid rid;
|
||||||
headerRid = list;
|
rid.page = list.page;
|
||||||
while(rid.slot || header.nextPage.size != -1) {
|
rid.slot = header.thisPage;
|
||||||
if(rid.slot) {
|
short lastSlot = -1;
|
||||||
rid.size = TrecordSize(xid, rid);
|
headerRid = list;
|
||||||
pagedListEntry * dat = malloc(rid.size);
|
while(rid.slot || header.nextPage.size != -1) {
|
||||||
Tread(xid, rid, dat);
|
if(compensation_error()) { break; }
|
||||||
|
if(rid.slot) {
|
||||||
if(!memcmp(dat+1, key, keySize)) {
|
rid.size = TrecordSize(xid, rid);
|
||||||
|
if(compensation_error()) { break; };
|
||||||
|
pagedListEntry * dat = malloc(rid.size);
|
||||||
|
Tread(xid, rid, dat);
|
||||||
|
|
||||||
if(lastSlot != -1) {
|
if(!memcmp(dat+1, key, keySize)) {
|
||||||
recordid lastRid = rid;
|
|
||||||
lastRid.slot = lastSlot;
|
if(lastSlot != -1) {
|
||||||
lastRid.size = TrecordSize(xid, lastRid);
|
recordid lastRid = rid;
|
||||||
pagedListEntry * lastRidBuf = malloc(lastRid.size);
|
lastRid.slot = lastSlot;
|
||||||
Tread(xid, lastRid, lastRidBuf);
|
lastRid.size = TrecordSize(xid, lastRid);
|
||||||
lastRidBuf->nextEntry = dat->nextEntry;
|
if(compensation_error()) { free(dat); break; }
|
||||||
Tset(xid, lastRid, lastRidBuf);
|
pagedListEntry * lastRidBuf = malloc(lastRid.size);
|
||||||
free(lastRidBuf);
|
Tread(xid, lastRid, lastRidBuf);
|
||||||
} else {
|
lastRidBuf->nextEntry = dat->nextEntry;
|
||||||
|
Tset(xid, lastRid, lastRidBuf);
|
||||||
|
free(lastRidBuf);
|
||||||
|
} else {
|
||||||
header.thisPage = dat->nextEntry;
|
header.thisPage = dat->nextEntry;
|
||||||
Tset(xid, headerRid, &header);
|
Tset(xid, headerRid, &header);
|
||||||
|
}
|
||||||
|
Tdealloc(xid, rid);
|
||||||
|
free(dat);
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
Tdealloc(xid, rid);
|
lastSlot = rid.slot;
|
||||||
|
rid.slot = dat->nextEntry;
|
||||||
free(dat);
|
free(dat);
|
||||||
return 1;
|
} else if (header.nextPage.size != -1) { // another page
|
||||||
|
lastSlot = -1;
|
||||||
|
rid.page = header.nextPage.page;
|
||||||
|
headerRid = header.nextPage;
|
||||||
|
Tread(xid, header.nextPage, &header);
|
||||||
|
rid.slot = header.thisPage;
|
||||||
}
|
}
|
||||||
lastSlot = rid.slot;
|
|
||||||
rid.slot = dat->nextEntry;
|
|
||||||
// }
|
|
||||||
// if(dat->nextEntry) { // another entry on this page
|
|
||||||
// lastSlot = rid.slot;
|
|
||||||
// rid.slot = dat->nextEntry;
|
|
||||||
free(dat);
|
|
||||||
} else if (header.nextPage.size != -1) { // another page
|
|
||||||
lastSlot = -1;
|
|
||||||
rid.page = header.nextPage.page;
|
|
||||||
headerRid = header.nextPage;
|
|
||||||
Tread(xid, header.nextPage, &header);
|
|
||||||
rid.slot = header.thisPage;
|
|
||||||
// } else { // we've reached the end of the last page
|
|
||||||
// rid.slot = 0;
|
|
||||||
}
|
}
|
||||||
|
} end_ret(compensation_error());
|
||||||
// free(dat);
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compensated_function int TpagedListMove(int xid, recordid start_list, recordid end_list, const byte * key, int keySize) {
|
compensated_function int TpagedListMove(int xid, recordid start_list, recordid end_list, const byte * key, int keySize) {
|
||||||
byte * value;
|
byte * value = NULL;
|
||||||
int valueSize = TpagedListFind(xid, start_list, key, keySize, &value);
|
int ret;
|
||||||
if(valueSize != -1) {
|
try_ret(compensation_error()) {
|
||||||
int ret = TpagedListRemove(xid, start_list, key, keySize);
|
int valueSize = TpagedListFind(xid, start_list, key, keySize, &value);
|
||||||
assert(ret);
|
if(valueSize != -1) {
|
||||||
ret = TpagedListInsert(xid, end_list, key, keySize, value, valueSize);
|
int ret = TpagedListRemove(xid, start_list, key, keySize);
|
||||||
assert(!ret);
|
assert(ret);
|
||||||
free(value);
|
ret = TpagedListInsert(xid, end_list, key, keySize, value, valueSize);
|
||||||
return 1;
|
assert(!ret);
|
||||||
} else {
|
if(value) { free(value); }
|
||||||
return 0;
|
ret = 1;
|
||||||
}
|
} else {
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
} end_ret(compensation_error());
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
lladd_pagedList_iterator * TpagedListIterator(int xid, recordid list) {
|
compensated_function lladd_pagedList_iterator * TpagedListIterator(int xid, recordid list) {
|
||||||
pagedListHeader header;
|
pagedListHeader header;
|
||||||
Tread(xid, list, &header);
|
try_ret(NULL) {
|
||||||
|
Tread(xid, list, &header);
|
||||||
|
} end_ret(NULL);
|
||||||
|
|
||||||
lladd_pagedList_iterator * it = malloc(sizeof(lladd_pagedList_iterator));
|
lladd_pagedList_iterator * it = malloc(sizeof(lladd_pagedList_iterator));
|
||||||
|
|
||||||
it->headerRid = header.nextPage;
|
it->headerRid = header.nextPage;
|
||||||
|
@ -198,17 +210,20 @@ lladd_pagedList_iterator * TpagedListIterator(int xid, recordid list) {
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TpagedListNext(int xid, lladd_pagedList_iterator * it,
|
compensated_function int TpagedListNext(int xid, lladd_pagedList_iterator * it,
|
||||||
byte ** key, int * keySize,
|
byte ** key, int * keySize,
|
||||||
byte ** value, int * valueSize) {
|
byte ** value, int * valueSize) {
|
||||||
while(it->entryRid.slot || it->headerRid.size != -1) {
|
while(it->entryRid.slot || it->headerRid.size != -1) {
|
||||||
if(it->entryRid.slot) {
|
if(it->entryRid.slot) {
|
||||||
it->entryRid.size = TrecordSize(xid, it->entryRid);
|
try_ret(compensation_error()) {
|
||||||
|
it->entryRid.size = TrecordSize(xid, it->entryRid);
|
||||||
|
} end_ret(compensation_error());
|
||||||
assert(it->entryRid.size != -1);
|
assert(it->entryRid.size != -1);
|
||||||
|
|
||||||
pagedListEntry * entry = malloc(it->entryRid.size);
|
pagedListEntry * entry = malloc(it->entryRid.size);
|
||||||
|
begin_action_ret(free, entry, compensation_error()) {
|
||||||
Tread(xid, it->entryRid, entry);
|
Tread(xid, it->entryRid, entry);
|
||||||
|
} end_action_ret(compensation_error());
|
||||||
|
|
||||||
*keySize = entry->keySize;
|
*keySize = entry->keySize;
|
||||||
*valueSize = it->entryRid.size - *keySize - sizeof(pagedListEntry);
|
*valueSize = it->entryRid.size - *keySize - sizeof(pagedListEntry);
|
||||||
|
@ -227,7 +242,9 @@ int TpagedListNext(int xid, lladd_pagedList_iterator * it,
|
||||||
|
|
||||||
} else { // move to next page.
|
} else { // move to next page.
|
||||||
pagedListHeader header;
|
pagedListHeader header;
|
||||||
Tread(xid, it->headerRid, &header);
|
try_ret(compensation_error()) {
|
||||||
|
Tread(xid, it->headerRid, &header);
|
||||||
|
} end_ret(compensation_error());
|
||||||
it->entryRid.page = it->headerRid.page;
|
it->entryRid.page = it->headerRid.page;
|
||||||
it->headerRid = header.nextPage;
|
it->headerRid = header.nextPage;
|
||||||
it->entryRid.slot = header.thisPage;
|
it->entryRid.slot = header.thisPage;
|
||||||
|
|
|
@ -216,7 +216,6 @@ compensated_function recordid slottedPreRalloc(int xid, long size, Page ** pp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
compensated_function recordid slottedPreRallocFromPage(int xid, long page, long size, Page **pp) {
|
compensated_function recordid slottedPreRallocFromPage(int xid, long page, long size, Page **pp) {
|
||||||
recordid ret;
|
|
||||||
int isBlob = 0;
|
int isBlob = 0;
|
||||||
if(size == BLOB_SLOT) {
|
if(size == BLOB_SLOT) {
|
||||||
isBlob = 1;
|
isBlob = 1;
|
||||||
|
@ -235,7 +234,8 @@ compensated_function recordid slottedPreRallocFromPage(int xid, long page, long
|
||||||
slottedPageInitialize(*pp);
|
slottedPageInitialize(*pp);
|
||||||
}
|
}
|
||||||
assert(*page_type_ptr(*pp) == SLOTTED_PAGE);
|
assert(*page_type_ptr(*pp) == SLOTTED_PAGE);
|
||||||
ret = slottedRawRalloc(*pp, size);
|
recordid ret = slottedRawRalloc(*pp, size);
|
||||||
|
assert(ret.size == size);
|
||||||
if(isBlob) {
|
if(isBlob) {
|
||||||
*slot_length_ptr(*pp, ret.slot) = BLOB_SLOT;
|
*slot_length_ptr(*pp, ret.slot) = BLOB_SLOT;
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,17 +92,19 @@ int Tinit() {
|
||||||
|
|
||||||
openLogWriter();
|
openLogWriter();
|
||||||
|
|
||||||
pageOperationsInit();
|
try_ret(compensation_error()) {
|
||||||
|
pageOperationsInit();
|
||||||
|
} end_ret(compensation_error());
|
||||||
initNestedTopActions();
|
initNestedTopActions();
|
||||||
ThashInit();
|
ThashInit();
|
||||||
|
|
||||||
compensations_init();
|
compensations_init();
|
||||||
|
|
||||||
setupLockManagerCallbacksNil();
|
setupLockManagerCallbacksNil();
|
||||||
//setupLockManagerCallbacksPage();
|
//setupLockManagerCallbacksPage();
|
||||||
|
|
||||||
InitiateRecovery();
|
InitiateRecovery();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,20 +146,43 @@ int Tbegin() {
|
||||||
return XactionTable[index].xid;
|
return XactionTable[index].xid;
|
||||||
}
|
}
|
||||||
|
|
||||||
compensated_function void Tupdate(int xid, recordid rid, const void *dat, int op) {
|
static compensated_function void TupdateHelper(int xid, recordid rid, const void * dat, int op, Page * p) {
|
||||||
LogEntry * e;
|
LogEntry * e;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if(globalLockManager.writeLockPage) {
|
||||||
|
globalLockManager.writeLockPage(xid, rid.page);
|
||||||
|
}
|
||||||
|
} end;
|
||||||
|
|
||||||
|
|
||||||
|
e = LogUpdate(&XactionTable[xid % MAX_TRANSACTIONS], p, rid, op, dat);
|
||||||
|
|
||||||
|
assert(XactionTable[xid % MAX_TRANSACTIONS].prevLSN == e->LSN);
|
||||||
|
|
||||||
|
DEBUG("T update() e->LSN: %ld\n", e->LSN);
|
||||||
|
|
||||||
|
doUpdate(e, p);
|
||||||
|
|
||||||
|
free(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
compensated_function void Tupdate(int xid, recordid rid, const void *dat, int op) {
|
||||||
Page * p;
|
Page * p;
|
||||||
#ifdef DEBUGGING
|
#ifdef DEBUGGING
|
||||||
pthread_mutex_lock(&transactional_2_mutex);
|
pthread_mutex_lock(&transactional_2_mutex);
|
||||||
assert(numActiveXactions <= MAX_TRANSACTIONS);
|
assert(numActiveXactions <= MAX_TRANSACTIONS);
|
||||||
pthread_mutex_unlock(&transactional_2_mutex);
|
pthread_mutex_unlock(&transactional_2_mutex);
|
||||||
#endif
|
#endif
|
||||||
p = loadPage(xid, rid.page);
|
try {
|
||||||
|
p = loadPage(xid, rid.page);
|
||||||
|
} end;
|
||||||
if(*page_type_ptr(p) == INDIRECT_PAGE) {
|
if(*page_type_ptr(p) == INDIRECT_PAGE) {
|
||||||
releasePage(p);
|
releasePage(p);
|
||||||
rid = dereferenceRID(xid, rid);
|
try {
|
||||||
p = loadPage(xid, rid.page);
|
rid = dereferenceRID(xid, rid);
|
||||||
|
p = loadPage(xid, rid.page);
|
||||||
|
} end;
|
||||||
/** @todo Kludge! Shouldn't special case operations in transactional2. */
|
/** @todo Kludge! Shouldn't special case operations in transactional2. */
|
||||||
} else if(*page_type_ptr(p) == ARRAY_LIST_PAGE &&
|
} else if(*page_type_ptr(p) == ARRAY_LIST_PAGE &&
|
||||||
op != OPERATION_LINEAR_INSERT &&
|
op != OPERATION_LINEAR_INSERT &&
|
||||||
|
@ -166,85 +191,66 @@ compensated_function void Tupdate(int xid, recordid rid, const void *dat, int op
|
||||||
op != OPERATION_UNDO_LINEAR_DELETE ) {
|
op != OPERATION_UNDO_LINEAR_DELETE ) {
|
||||||
rid = dereferenceArrayListRid(p, rid.slot);
|
rid = dereferenceArrayListRid(p, rid.slot);
|
||||||
releasePage(p);
|
releasePage(p);
|
||||||
p = loadPage(xid, rid.page);
|
try {
|
||||||
|
p = loadPage(xid, rid.page);
|
||||||
|
} end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @todo For logical undo logs, grabbing a lock makes no sense! */
|
/** @todo For logical undo logs, grabbing a lock makes no sense! */
|
||||||
try {
|
begin_action(releasePage, p) {
|
||||||
if(globalLockManager.writeLockPage) {
|
TupdateHelper(xid, rid, dat, op, p);
|
||||||
|
/* if(globalLockManager.writeLockPage) {
|
||||||
globalLockManager.writeLockPage(xid, rid.page);
|
globalLockManager.writeLockPage(xid, rid.page);
|
||||||
}
|
}
|
||||||
} end;
|
|
||||||
|
e = LogUpdate(&XactionTable[xid % MAX_TRANSACTIONS], p, rid, op, dat);
|
||||||
e = LogUpdate(&XactionTable[xid % MAX_TRANSACTIONS], p, rid, op, dat);
|
|
||||||
|
} en d_action;
|
||||||
assert(XactionTable[xid % MAX_TRANSACTIONS].prevLSN == e->LSN);
|
|
||||||
|
assert(XactionTable[xid % MAX_TRANSACTIONS].prevLSN == e->LSN);
|
||||||
DEBUG("Tupdate() e->LSN: %ld\n", e->LSN);
|
|
||||||
|
DEBUG("Tupdate() e->LSN: %ld\n", e->LSN);
|
||||||
doUpdate(e, p);
|
|
||||||
releasePage(p);
|
doUpdate(e, p);
|
||||||
|
releasePage(p);*/
|
||||||
free(e);
|
} compensate;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compensated_function void alTupdate(int xid, recordid rid, const void *dat, int op) {
|
compensated_function void alTupdate(int xid, recordid rid, const void *dat, int op) {
|
||||||
LogEntry * e;
|
Page * p ;
|
||||||
Page * p;
|
try {
|
||||||
|
|
||||||
try {
|
|
||||||
if(globalLockManager.writeLockPage) {
|
|
||||||
globalLockManager.writeLockPage(xid, rid.page);
|
|
||||||
}
|
|
||||||
} end;
|
|
||||||
|
|
||||||
p = loadPage(xid, rid.page);
|
p = loadPage(xid, rid.page);
|
||||||
|
} end;
|
||||||
|
|
||||||
|
begin_action(releasePage, p) {
|
||||||
|
TupdateHelper(xid, rid, dat, op, p);
|
||||||
|
} compensate;
|
||||||
|
|
||||||
|
|
||||||
/* if(*page_type_ptr(p) == INDIRECT_PAGE) {
|
|
||||||
releasePage(p);
|
|
||||||
rid = dereferenceRID(rid);
|
|
||||||
p = loadPage(rid.page);
|
|
||||||
/ ** @todo Kludge! Shouldn't special case operations in transactional2. * /
|
|
||||||
} else if(*page_type_ptr(p) == ARRAY_LIST_PAGE &&
|
|
||||||
op != OPERATION_LINEAR_INSERT &&
|
|
||||||
op != OPERATION_UNDO_LINEAR_INSERT &&
|
|
||||||
op != OPERATION_LINEAR_DELETE &&
|
|
||||||
op != OPERATION_UNDO_LINEAR_DELETE ) {
|
|
||||||
rid = dereferenceArrayListRid(p, rid.slot);
|
|
||||||
releasePage(p);
|
|
||||||
p = loadPage(rid.page);
|
|
||||||
} */
|
|
||||||
|
|
||||||
e = LogUpdate(&XactionTable[xid % MAX_TRANSACTIONS], p, rid, op, dat);
|
|
||||||
|
|
||||||
assert(XactionTable[xid % MAX_TRANSACTIONS].prevLSN == e->LSN);
|
|
||||||
|
|
||||||
DEBUG("Tupdate() e->LSN: %ld\n", e->LSN);
|
|
||||||
|
|
||||||
doUpdate(e, p);
|
|
||||||
releasePage(p);
|
|
||||||
/* end Tupdate() */
|
|
||||||
free(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TreadUnlocked(int xid, recordid rid, void * dat) {
|
void TreadUnlocked(int xid, recordid rid, void * dat) {
|
||||||
Page * p = loadPage(xid, rid.page);
|
Page * p;
|
||||||
|
try {
|
||||||
|
p = loadPage(xid, rid.page);
|
||||||
|
} end;
|
||||||
int page_type = *page_type_ptr(p);
|
int page_type = *page_type_ptr(p);
|
||||||
if(page_type == SLOTTED_PAGE || page_type == FIXED_PAGE || !page_type ) {
|
if(page_type == SLOTTED_PAGE || page_type == FIXED_PAGE || !page_type ) {
|
||||||
|
|
||||||
} else if(page_type == INDIRECT_PAGE) {
|
} else if(page_type == INDIRECT_PAGE) {
|
||||||
releasePage(p);
|
releasePage(p);
|
||||||
rid = dereferenceRIDUnlocked(xid, rid);
|
|
||||||
p = loadPage(xid, rid.page);
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
rid = dereferenceRIDUnlocked(xid, rid);
|
||||||
|
p = loadPage(xid, rid.page);
|
||||||
|
} end;
|
||||||
} else if(page_type == ARRAY_LIST_PAGE) {
|
} else if(page_type == ARRAY_LIST_PAGE) {
|
||||||
rid = dereferenceArrayListRidUnlocked(p, rid.slot);
|
rid = dereferenceArrayListRidUnlocked(p, rid.slot);
|
||||||
releasePage(p);
|
releasePage(p);
|
||||||
p = loadPage(xid, rid.page);
|
try {
|
||||||
|
p = loadPage(xid, rid.page);
|
||||||
|
} end;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
abort();
|
abort();
|
||||||
|
|
|
@ -19,17 +19,17 @@ int main(int argc, char ** argv) {
|
||||||
// cht_evals go here...
|
// cht_evals go here...
|
||||||
|
|
||||||
int xid = 1;//NULL_MACHINE; // What's the deal with this? ;)
|
int xid = 1;//NULL_MACHINE; // What's the deal with this? ;)
|
||||||
clusterHashTable_t * new_ht;
|
clusterHashTable_t new_ht;
|
||||||
cHtCreate(xid, cht_client, new_ht);
|
cHtCreate(xid, cht_client, &new_ht);
|
||||||
int i;
|
int i;
|
||||||
for(i = 0; i < 10000; i++) {
|
for(i = 0; i < 10000; i++) {
|
||||||
int one = i; int two = i+1;
|
int one = i; int two = i+1;
|
||||||
cHtInsert(xid, cht_client, new_ht, &one, sizeof(int), &two, sizeof(int));
|
cHtInsert(xid, cht_client, &new_ht, &one, sizeof(int), &two, sizeof(int));
|
||||||
int newOne, newTwo;
|
int newOne, newTwo;
|
||||||
newOne = i;
|
newOne = i;
|
||||||
newTwo = 0;
|
newTwo = 0;
|
||||||
unsigned int newLen = sizeof(int);
|
unsigned int newLen = sizeof(int);
|
||||||
int ret = cHtLookup(xid, cht_client, new_ht, &newOne, sizeof(int), &newTwo, &newLen);
|
int ret = cHtLookup(xid, cht_client, &new_ht, &newOne, sizeof(int), &newTwo, &newLen);
|
||||||
// xid++;
|
// xid++;
|
||||||
//printf("lookup returned %d (%d->%d)\n", ret, newOne, newTwo);
|
//printf("lookup returned %d (%d->%d)\n", ret, newOne, newTwo);
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
@ -42,19 +42,19 @@ int main(int argc, char ** argv) {
|
||||||
for(i = 0; i < 10000; i+=10) {
|
for(i = 0; i < 10000; i+=10) {
|
||||||
int one = i; int two = -1;
|
int one = i; int two = -1;
|
||||||
unsigned int size = sizeof(int);
|
unsigned int size = sizeof(int);
|
||||||
int removed = cHtRemove(xid, cht_client, new_ht, &one, sizeof(int), &two, &size);
|
int removed = cHtRemove(xid, cht_client, &new_ht, &one, sizeof(int), &two, &size);
|
||||||
assert(removed);
|
assert(removed);
|
||||||
|
|
||||||
size = sizeof(int);
|
size = sizeof(int);
|
||||||
two = -1;
|
two = -1;
|
||||||
removed = cHtRemove(xid, cht_client, new_ht, &one, sizeof(int), &two, &size);
|
removed = cHtRemove(xid, cht_client, &new_ht, &one, sizeof(int), &two, &size);
|
||||||
assert(!removed);
|
assert(!removed);
|
||||||
|
|
||||||
int newOne, newTwo;
|
int newOne, newTwo;
|
||||||
newOne = i;
|
newOne = i;
|
||||||
newTwo = 0;
|
newTwo = 0;
|
||||||
unsigned int newLen = sizeof(int);
|
unsigned int newLen = sizeof(int);
|
||||||
int ret = cHtLookup(xid, cht_client, new_ht, &newOne, sizeof(int), &newTwo, &newLen);
|
int ret = cHtLookup(xid, cht_client, &new_ht, &newOne, sizeof(int), &newTwo, &newLen);
|
||||||
|
|
||||||
assert(!ret);
|
assert(!ret);
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ int main(int argc, char ** argv) {
|
||||||
newOne = i;
|
newOne = i;
|
||||||
newTwo = 0;
|
newTwo = 0;
|
||||||
unsigned int newLen = sizeof(int);
|
unsigned int newLen = sizeof(int);
|
||||||
int ret = cHtLookup(xid, cht_client, new_ht, &newOne, sizeof(int), &newTwo, &newLen);
|
int ret = cHtLookup(xid, cht_client, &new_ht, &newOne, sizeof(int), &newTwo, &newLen);
|
||||||
// xid++;
|
// xid++;
|
||||||
//printf("lookup returned %d (%d->%d)\n", ret, newOne, newTwo);
|
//printf("lookup returned %d (%d->%d)\n", ret, newOne, newTwo);
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
|
Loading…
Reference in a new issue