Fixed regressions, major update of page / slotted interfaces in anticipation of moving LLADD towards a generic transactional page system.

This commit is contained in:
Sears Russell 2004-08-17 01:46:17 +00:00
parent 82e3fdb53a
commit b4d7883f66
20 changed files with 359 additions and 304 deletions

View file

@ -65,14 +65,24 @@ WARN_LOGFILE =
# configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = lladd \
libdfa \
pbl \
src \
test
# libdfa \
# pbl \
#
FILE_PATTERNS = *.c \
*.h
RECURSIVE = YES
EXCLUDE =
EXCLUDE = src/2pc \
src/libdfa \
src/pbl \
test/2pc \
test/cht \
test/dfa \
test/lladd-old \
test/messages \
test/monotree \
src/apps/cht
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXAMPLE_PATH =

View file

@ -159,6 +159,7 @@ void doUpdate(const LogEntry * e, Page * p);
extra CLRs being generated during recovery.
@param e The log entry containing the operation to be undone.
@param p A pointer to the memory resident copy of the page that is being managed by bufferManager.
@param clr_lsn The lsn of the clr that corresponds to this undo operation.
*/
void undoUpdate(const LogEntry * e, Page * p, lsn_t clr_lsn);

View file

@ -14,7 +14,7 @@
#include "io.h"
#include <pbl/pbl.h>
#include "page.h"
#include "page/slotted.h"
#include <stdio.h>
pthread_mutex_t blob_hash_mutex;
@ -231,7 +231,7 @@ void allocBlob(int xid, Page * p, lsn_t lsn, recordid rid) {
fileSize = myFseek(blobf1, 0, SEEK_END);
blob_rec.offset = fileSize;
pageSetSlotType(p, rid.slot, BLOB_SLOT);
slottedSetType(p, rid.slot, BLOB_SLOT);
rid.size = BLOB_SLOT;
/* Tset() needs to know to 'do the right thing' here, since we've

View file

@ -76,10 +76,10 @@ int bufInit() {
activePages = pblHtCreate();
dummy_page = pageAlloc(-1);
dummy_page = pageMalloc();
pageRealloc(dummy_page, -1);
Page *first;
first = pageAlloc(0);
first = pageMalloc();
pageRealloc(first, 0);
pblHtInsert(activePages, &first->id, sizeof(int), first);
@ -218,7 +218,7 @@ Page * getPage(int pageid, int locktype) {
} else {
ret = pageAlloc(-1);
ret = pageMalloc();
ret->id = -1;
ret->inCache = 0;
}

View file

@ -51,11 +51,6 @@ terms specified in this license.
#include <stdio.h>
#ifndef NULL
#define NULL 0
#endif
void printList(LinkedList *l) {
LinkedListPtr tmp = l;
printf ("List is ");

View file

@ -160,7 +160,7 @@ void deleteLogWriter();
/**
Read a log entry at a particular LSN.
@param the LSN of the entry that will be read.
@param LSN the LSN of the entry that will be read.
*/
LogEntry * readLSNEntry(lsn_t LSN);

View file

@ -2,11 +2,13 @@
#include <lladd/common.h>
#include <lladd/operations/alloc.h>
#include <lladd/transactional.h>
#include <lladd/bufferManager.h>
#include "../blobManager.h"
#include "../page.h"
#include "../page/slotted.h"
#include <assert.h>
/**
@file
@ -31,14 +33,17 @@
*/
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? */
/* if(*page_type_ptr(p) == UNINITIALIZED_PAGE) {
*page_type_ptr(p) = SLOTTED_PAGE;
} */
assert(*page_type_ptr(p) == SLOTTED_PAGE);
if(rid.size >= BLOB_THRESHOLD_SIZE) {
allocBlob(xid, p, lsn, rid);
} else {
/* Page * loadedPage = loadPage(rid.page); */
/* pageSlotRalloc(loadedPage, lsn, rid); */
/** Has no effect during normal operation. */
pageSlotRalloc(p, lsn, rid);
slottedPostRalloc(p, lsn, rid);
}
return 0;
@ -48,8 +53,7 @@ static int operate(int xid, Page * p, lsn_t lsn, recordid rid, const void * dat)
static int deoperate(int xid, Page * p, lsn_t lsn, recordid rid, const void * dat) {
/* Page * loadedPage = loadPage(rid.page); */
/** Has no effect during normal operation, other than updating the LSN. */
/* pageSlotRalloc(loadedPage, lsn, rid); */
pageSlotRalloc(p, lsn, rid);
slottedPostRalloc(p, lsn, rid);
return 0;
}
@ -70,9 +74,7 @@ recordid Talloc(int xid, long size) {
if(size >= BLOB_THRESHOLD_SIZE) {
rid = preAllocBlob(xid, size);
} else {
rid = ralloc(xid, size);
rid = slottedPreRalloc(xid, size);
}
Tupdate(xid,rid, NULL, OPERATION_ALLOC);

View file

@ -1,10 +1,15 @@
#include "../page.h"
#include <lladd/operations/pageOperations.h>
#include <assert.h>
#include "../page/slotted.h"
int __pageAlloc(int xid, Page * p, lsn_t lsn, recordid r, const void * d) {
int type = *(int*)d;
*page_type_ptr(p) = type;
/** @todo this sort of thing should be done in a centralized way. */
if(type == SLOTTED_PAGE) {
slottedPageInitialize(p);
}
return 0;
@ -32,7 +37,7 @@ int TpageSet(int xid, int pageid, Page* p) {
/** @todo Need to re-think TpageDealloc/TpageAlloc's logging
strategies when we implement page re-use. Currently, TpageDealloc can
use logical logging. Perhaps TpageDealloc should use physical
logging, and wipe the page to zero, while pageAlloc should continue to
logging, and wipe the page to zero, while TpageAlloc should continue to
use logical logging. (Have we ever had operation's whose inverses
took differnt types of log entries? Do such operations work?) */
@ -53,7 +58,7 @@ int TpageDealloc(int xid, int pageid) {
int TpageAlloc(int xid, int type) {
recordid rid;
int pageid = pageAllocMultiple(1);
int pageid = pageAlloc();
rid.page = pageid;
rid.slot = 0;
@ -68,14 +73,14 @@ int TpageAlloc(int xid, int type) {
implemented yet...
*/
int TpageAllocMany(int xid, int count, int type) {
int firstPage;
int firstPage = -1;
int lastPage = -1;
for(int i = 0 ; i < count; i++) {
int thisPage = TpageAlloc(xid, type);
if(lastPage == -1) {
firstPage = lastPage = thisPage;
} else {
assert(lastPage +1 == thisPage);
assert((lastPage +1) == thisPage);
lastPage = thisPage;
}
}

View file

@ -90,34 +90,17 @@ terms specified in this license.
/* TODO: Combine with buffer size... */
static int nextPage = 0;
/**
Invariant: This lock should be held while updating lastFreepage, or
while performing any operation that may decrease the amount of
freespace in the page that lastFreepage refers to.
Since pageCompact and pageDeRalloc may only increase this value,
they do not need to hold this lock. Since bufferManager is the
only place where pageRalloc is called, pageRalloc does not obtain
this lock.
*/
pthread_mutex_t lastFreepage_mutex;
unsigned int lastFreepage = 0;
static int lastAllocedPage;
static pthread_mutex_t lastAllocedPage_mutex;
/* ------ */
static pthread_mutex_t pageAllocMutex;
static pthread_mutex_t pageMallocMutex;
/** We need one dummy page for locking purposes, so this array has one extra page in it. */
Page pool[MAX_BUFFER_SIZE+1];
/**
* pageWriteLSN() assumes that the page is already loaded in memory. It takes
* as a parameter a Page. The Page struct contains the new LSN and the page
* number to which the new LSN must be written to.
*
* @param page You must have a writelock on page before calling this function.
*/
void pageWriteLSN(Page * page, lsn_t lsn) {
/* unlocked since we're only called by a function that holds the writelock. */
/* *(long *)(page->memAddr + START_OF_LSN) = page->LSN; */
@ -127,11 +110,6 @@ void pageWriteLSN(Page * page, lsn_t lsn) {
}
}
/**
* pageReadLSN() assumes that the page is already loaded in memory. It takes
* as a parameter a Page and returns the LSN that is currently written on that
* page in memory.
*/
lsn_t pageReadLSN(const Page * page) {
lsn_t ret;
@ -149,9 +127,6 @@ static void pageReallocNoLock(Page *p, int id) {
p->id = id;
p->LSN = 0;
p->dirty = 0;
/* assert(p->pending == 0);
assert(p->waiting == 1);
p->waiting = 0;*/
}
/* ----- end static functions ----- */
@ -165,54 +140,29 @@ static void pageReallocNoLock(Page *p, int id) {
void pageInit() {
nextPage = 0;
/**
* For now, we will assume that slots are 4 bytes long, and that the
* first two bytes are the offset, and the second two bytes are the
* the length. There are some functions at the bottom of this file
* that may be useful later if we decide to dynamically choose
* sizes for offset and length.
*/
/**
* the largest a slot length can be is the size of the page,
* and the greatest offset at which a record could possibly
* start is at the end of the page
*/
/* SLOT_LENGTH_SIZE = SLOT_OFFSET_SIZE = 2; / * in bytes * /
SLOT_SIZE = SLOT_OFFSET_SIZE + SLOT_LENGTH_SIZE;
LSN_SIZE = sizeof(long);
FREE_SPACE_SIZE = NUMSLOTS_SIZE = 2;
/ * START_OF_LSN is the offset in the page to the lsn * /
START_OF_LSN = PAGE_SIZE - LSN_SIZE;
START_OF_FREE_SPACE = START_OF_LSN - FREE_SPACE_SIZE;
START_OF_NUMSLOTS = START_OF_FREE_SPACE - NUMSLOTS_SIZE;
MASK_0000FFFF = (1 << (2*BITS_PER_BYTE)) - 1;
MASK_FFFF0000 = ~MASK_0000FFFF;
*/
pthread_mutex_init(&pageAllocMutex, NULL);
for(int i = 0; i < MAX_BUFFER_SIZE+1; i++) {
pool[i].rwlatch = initlock();
pool[i].loadlatch = initlock();
assert(!posix_memalign((void*)(&(pool[i].memAddr)), PAGE_SIZE, PAGE_SIZE));
}
pthread_mutex_init(&pageMallocMutex, NULL);
pthread_mutex_init(&lastFreepage_mutex , NULL);
lastFreepage = 0;
for(int i = 0; i < MAX_BUFFER_SIZE+1; i++) {
pool[i].rwlatch = initlock();
pool[i].loadlatch = initlock();
assert(!posix_memalign((void*)(&(pool[i].memAddr)), PAGE_SIZE, PAGE_SIZE));
}
pthread_mutex_init(&lastAllocedPage_mutex , NULL);
lastAllocedPage = 0;
slottedPageInit();
}
void pageDeInit() {
for(int i = 0; i < MAX_BUFFER_SIZE+1; i++) {
deletelock(pool[i].rwlatch);
deletelock(pool[i].loadlatch);
free(pool[i].memAddr);
}
pthread_mutex_destroy(&lastAllocedPage_mutex);
}
void pageCommit(int xid) {
@ -221,6 +171,24 @@ void pageCommit(int xid) {
void pageAbort(int xid) {
}
int pageAllocUnlocked() {
int ret = lastAllocedPage;
Page * p;
lastAllocedPage += 1;
p = loadPage(lastAllocedPage);
/** TODO Incorrect, but this kludge tricks the tests (for now) */
while(*page_type_ptr(p) != UNINITIALIZED_PAGE) {
releasePage(p);
lastAllocedPage++;
p = loadPage(lastAllocedPage);
}
releasePage(p);
return ret;
}
/**
@todo DATA CORRUPTION BUG pageAllocMultiple needs to scan forward in the store file until
it finds page(s) with type = UNINITIALIZED_PAGE. Otherwise, after recovery, it will trash the storefile.
@ -229,45 +197,10 @@ void pageAbort(int xid) {
slot of the first page in the storefile for metadata, and to keep
lastFreepage there, instead of in RAM.
*/
int pageAllocMultiple(int newPageCount) {
pthread_mutex_lock(&lastFreepage_mutex);
int ret = lastFreepage+1; /* Currently, just discard the current page. */
lastFreepage += (newPageCount + 1);
pthread_mutex_unlock(&lastFreepage_mutex);
return ret;
}
/** @todo ralloc ignores it's xid parameter; change the interface?
@todo ralloc doesn't set the page type, and interacts poorly with other methods that allocate pages.
*/
recordid ralloc(int xid, long size) {
recordid ret;
Page * p;
/* DEBUG("Rallocing record of size %ld\n", (long int)size); */
assert(size < BLOB_THRESHOLD_SIZE);
pthread_mutex_lock(&lastFreepage_mutex);
p = loadPage(lastFreepage);
*page_type_ptr(p) = SLOTTED_PAGE;
while(freespace(p) < size ) {
releasePage(p);
lastFreepage++;
p = loadPage(lastFreepage);
*page_type_ptr(p) = SLOTTED_PAGE;
}
ret = pageRalloc(p, size);
releasePage(p);
pthread_mutex_unlock(&lastFreepage_mutex);
/* DEBUG("alloced rid = {%d, %d, %ld}\n", ret.page, ret.slot, ret.size); */
int pageAlloc() {
pthread_mutex_lock(&lastAllocedPage_mutex);
int ret = pageAllocUnlocked();
pthread_mutex_unlock(&lastAllocedPage_mutex);
return ret;
}
@ -284,14 +217,14 @@ void pageRealloc(Page *p, int id) {
/**
Allocate a new page.
@param id The id of the new page.
@return A pointer to the new page. This memory is part of a pool,
and should never be freed manually.
and should never be freed manually. Instead, you can
reclaim it with pageRealloc()
*/
Page *pageAlloc(int id) {
Page *pageMalloc() {
Page *page;
pthread_mutex_lock(&pageAllocMutex);
pthread_mutex_lock(&pageMallocMutex);
page = &(pool[nextPage]);
@ -299,49 +232,35 @@ Page *pageAlloc(int id) {
/* There's a dummy page that we need to keep around, thus the +1 */
assert(nextPage <= MAX_BUFFER_SIZE + 1);
pthread_mutex_unlock(&pageAllocMutex);
pthread_mutex_unlock(&pageMallocMutex);
return page;
}
void writeRecord(int xid, Page * p, lsn_t lsn, recordid rid, const void *dat) {
assert( (p->id == rid.page) && (p->memAddr != NULL) );
if(rid.size > BLOB_THRESHOLD_SIZE) {
/* DEBUG("Writing blob.\n"); */
writeBlob(xid, p, lsn, rid, dat);
} else {
/* DEBUG("Writing record.\n"); */
assert( (p->id == rid.page) && (p->memAddr != NULL) );
pageWriteRecord(xid, p, lsn, rid, dat);
assert( (p->id == rid.page) && (p->memAddr != NULL) );
slottedWrite(xid, p, lsn, rid, dat);
}
assert( (p->id == rid.page) && (p->memAddr != NULL) );
writelock(p->rwlatch, 225); /* Need a writelock so that we can update the lsn. */
pageWriteLSN(p, lsn);
unlock(p->rwlatch);
}
void readRecord(int xid, Page * p, recordid rid, void *buf) {
assert(rid.page == p->id);
if(rid.size > BLOB_THRESHOLD_SIZE) {
/* DEBUG("Reading blob. xid = %d rid = { %d %d %ld } buf = %x\n",
xid, rid.page, rid.slot, rid.size, (unsigned int)buf); */
/* @todo should readblob take a page pointer? */
readBlob(xid, p, rid, buf);
} else {
assert(rid.page == p->id);
/* DEBUG("Reading record xid = %d rid = { %d %d %ld } buf = %x\n",
xid, rid.page, rid.slot, rid.size, (unsigned int)buf); */
pageReadRecord(xid, p, rid, buf);
assert(rid.page == p->id);
slottedRead(xid, p, rid, buf);
}
assert(rid.page == p->id);
}

View file

@ -153,7 +153,7 @@ struct Page_s {
Any circumstance where one these locks are held during an I/O
operation is a bug.
For the 'no lock' cases, see @loadlatch
For the 'no lock' cases, @see loadlatch
*/
@ -193,16 +193,22 @@ struct Page_s {
};
/**
* initializes all the important variables needed in all the
* functions dealing with pages.
* initializes all the global variables needed by the functions
* dealing with pages.
*/
void pageInit();
/**
* releases all resources held by the page sub-system.
*/
void pageDeInit();
/**
* assumes that the page is already loaded in memory. It takes
* as a parameter a Page. The Page struct contains the new LSN and the page
* number to which the new LSN must be written to.
*
* @param page You must have a writelock on page before calling this function.
* @param lsn The new lsn of the page. If the new lsn is less than the page's current lsn, then the page's lsn will not be changed.
*/
void pageWriteLSN(Page * page, lsn_t lsn);
@ -226,65 +232,23 @@ void writeRecord(int xid, Page * page, lsn_t lsn, recordid rid, const void *dat)
/**
* @param xid transaction ID
* @param rid
* @param dat buffer for data
*/
void readRecord(int xid, Page * page, recordid rid, void *dat);
/**
* allocate a record. This must be done in two phases. The first
* phase reserves a slot, and produces a log entry. The second phase
* sets up the slot according to the contents of the log entry.
*
* Ralloc implements the first phase.
*
* @param xid The active transaction.
* @param size The size of the new record
* @return allocated record
*
* @see slotRalloc the implementation of the second phase.
*/
recordid ralloc(int xid, long size);
/**
* assumes that the page is already loaded in memory. It takes as
* parameters a Page and the size in bytes of the new record. pageRalloc()
* returns a recordid representing the newly allocated record.
*
* If you call this function, you probably need to be holding lastFreepage_mutex.
*
* @see lastFreepage_mutex
*
* NOTE: might want to pad records to be multiple of words in length, or, simply
* make sure all records start word aligned, but not necessarily having
* a length that is a multiple of words. (Since Tread(), Twrite() ultimately
* call memcpy(), this shouldn't be an issue)
*
* NOTE: pageRalloc() assumes that the caller already made sure that sufficient
* amount of freespace exists in this page. (@see freespace())
*
* @todo Makes no attempt to reuse old recordid's.
*/
recordid pageRalloc(Page * page, int size);
recordid pageSlotRalloc(Page * page, lsn_t lsn, recordid rid);
void pageDeRalloc(Page * page, recordid rid);
void pageCommit(int xid);
void pageAbort(int xid);
Page* pageAlloc(int id);
Page* pageMalloc();
void pageRealloc(Page * p, int id);
/** Allocates a set of contiguous pages on disk. Has nothing to do with pageAlloc.
@todo need a better naming convention for pageAlloc (alloc's memory) and pageAllocMultiple (alloc's disk)
/**
Allocates a single page on disk. Has nothing to do with pageMalloc.
@todo is there any case where this function can safely be called with newPageCount > 1?
@return the pageid of the newly allocated page, which is the
offset of the page in the file, divided by the page size.
*/
int pageAllocMultiple(int newPageCount) ;
int pageGetSlotType(Page * p, int slot, int type);
void pageSetSlotType(Page * p, int slot, int type);
int pageAlloc() ;
END_C_DECLS

View file

@ -142,14 +142,16 @@ recordid __rallocMany(int xid, int parentPage, int recordSize, int recordCount)
} else {
DEBUG("recordsize = %d, recordCount = %d, level = 0 (don't need indirect pages)\n", recordSize, recordCount);
/* Initialize leaves. (As SLOTTED_PAGE's) */
pageInitialize(&p);
slottedPageInitialize(&p);
p.id = parentPage;
for(int i = 0; i < recordCount; i++) {
/* Normally, we would worry that the page id isn't set, but
we're discarding the recordid returned by page ralloc
anyway. */
pageRalloc(&p, recordSize);
slottedRawRalloc(&p, recordSize);
}
}

View file

@ -18,7 +18,7 @@ increase the available free space.
The caller of this function must have a writelock on the page.
*/
static void pageCompact(Page * page) {
static void slottedCompact(Page * page) {
int i;
Page bufPage;
@ -40,7 +40,7 @@ static void pageCompact(Page * page) {
memcpy(buffer + PAGE_SIZE - meta_size, page->memAddr + PAGE_SIZE - meta_size, meta_size);
pageInitialize(&bufPage);
slottedPageInitialize(&bufPage);
numSlots = *numslots_ptr(page);
for (i = 0; i < numSlots; i++) {
@ -96,7 +96,37 @@ static void pageCompact(Page * page) {
}
void pageInitialize(Page * page) {
/**
Invariant: This lock should be held while updating lastFreepage, or
while performing any operation that may decrease the amount of
freespace in the page that lastFreepage refers to.
Since pageCompact and slottedDeRalloc may only increase this value,
they do not need to hold this lock. Since bufferManager is the
only place where rawPageRallocSlot is called, rawPageRallocSlot does not obtain
this lock.
If you are calling rawPageRallocSlot on a page that may be the page
lastFreepage refers to, then you will need to acquire
lastFreepage_mutex. (Doing so from outside of slotted.c is almost
certainly asking for trouble, so lastFreepage_mutex is static.)
*/
static pthread_mutex_t lastFreepage_mutex;
static unsigned int lastFreepage = -1;
void slottedPageInit() {
pthread_mutex_init(&lastFreepage_mutex , NULL);
lastFreepage = -1;
}
void slottedPageDeinit() {
pthread_mutex_destroy(&lastFreepage_mutex);
}
void slottedPageInitialize(Page * page) {
/* printf("Initializing page %d\n", page->id);
fflush(NULL); */
memset(page->memAddr, 0, PAGE_SIZE);
@ -104,23 +134,15 @@ void pageInitialize(Page * page) {
*freespace_ptr(page) = 0;
*numslots_ptr(page) = 0;
*freelist_ptr(page) = INVALID_SLOT;
}
int unlocked_freespace(Page * page) {
static int unlocked_freespace(Page * page) {
return (int)slot_length_ptr(page, *numslots_ptr(page)) - (int)(page->memAddr + *freespace_ptr(page));
}
/**
* freeSpace() assumes that the page is already loaded in memory. It takes
* as a parameter a Page, and returns an estimate of the amount of free space
* available to a new slot on this page. (This is the amount of unused space
* in the page, minus the size of a new slot entry.) This is either exact,
* or an underestimate.
*
* @todo is it ever safe to call freespace without a lock on the page?
*
*/
int freespace(Page * page) {
int slottedFreespace(Page * page) {
int ret;
readlock(page->rwlatch, 292);
ret = unlocked_freespace(page);
@ -129,11 +151,49 @@ int freespace(Page * page) {
}
/**
@todo pageRalloc's algorithm for reusing slot id's reclaims the
highest numbered slots first, which encourages fragmentation.
/** @todo slottedPreRalloc ignores it's xid parameter; change the
interface? (The xid is there for now, in case it allows some
optimizations later. Perhaps it's better to cluster allocations
from the same xid on the same page, or something...)
*/
recordid pageRalloc(Page * page, int size) {
recordid slottedPreRalloc(int xid, long size) {
recordid ret;
Page * p;
/* DEBUG("Rallocing record of size %ld\n", (long int)size); */
assert(size < BLOB_THRESHOLD_SIZE);
pthread_mutex_lock(&lastFreepage_mutex);
/** @todo is ((unsigned int) foo) == -1 portable? Gotta love C.*/
if(lastFreepage == -1) {
lastFreepage = TpageAlloc(xid, SLOTTED_PAGE);
p = loadPage(lastFreepage);
} else {
p = loadPage(lastFreepage);
}
if(slottedFreespace(p) < size ) {
releasePage(p);
lastFreepage = TpageAlloc(xid, SLOTTED_PAGE);
p = loadPage(lastFreepage);
}
ret = slottedRawRalloc(p, size);
releasePage(p);
pthread_mutex_unlock(&lastFreepage_mutex);
DEBUG("alloced rid = {%d, %d, %ld}\n", ret.page, ret.slot, ret.size);
return ret;
}
recordid slottedRawRalloc(Page * page, int size) {
writelock(page->rwlatch, 342);
@ -166,7 +226,7 @@ static void __really_do_ralloc(Page * page, recordid rid) {
assert(rid.size > 0);
if(unlocked_freespace(page) < rid.size) {
pageCompact(page);
slottedCompact(page);
/* Make sure there's enough free space... */
assert (unlocked_freespace(page) >= rid.size);
@ -190,9 +250,7 @@ static void __really_do_ralloc(Page * page, recordid rid) {
}
/** Only used for recovery, to make sure that consistent RID's are created
* on log playback. */
recordid pageSlotRalloc(Page * page, lsn_t lsn, recordid rid) {
recordid slottedPostRalloc(Page * page, lsn_t lsn, recordid rid) {
writelock(page->rwlatch, 376);
@ -215,7 +273,7 @@ recordid pageSlotRalloc(Page * page, lsn_t lsn, recordid rid) {
}
void pageDeRalloc(Page * page, recordid rid) {
void slottedDeRalloc(Page * page, recordid rid) {
readlock(page->rwlatch, 443);
@ -233,7 +291,7 @@ void pageDeRalloc(Page * page, recordid rid) {
@todo If the rid size has been overridden, we should check to make
sure that this really is a special record.
*/
void pageReadRecord(int xid, Page * page, recordid rid, byte *buff) {
void slottedRead(int xid, Page * page, recordid rid, byte *buff) {
int slot_length;
readlock(page->rwlatch, 519);
@ -251,7 +309,7 @@ void pageReadRecord(int xid, Page * page, recordid rid, byte *buff) {
}
void pageWriteRecord(int xid, Page * page, lsn_t lsn, recordid rid, const byte *data) {
void slottedWrite(int xid, Page * page, lsn_t lsn, recordid rid, const byte *data) {
int slot_length;
readlock(page->rwlatch, 529);
@ -275,16 +333,14 @@ void pageWriteRecord(int xid, Page * page, lsn_t lsn, recordid rid, const byte *
}
/** @todo: Should the caller need to obtain the writelock when calling pageSetSlotType? */
void pageSetSlotType(Page * p, int slot, int type) {
void slottedSetType(Page * p, int slot, int type) {
assert(type > PAGE_SIZE);
writelock(p->rwlatch, 686);
*slot_length_ptr(p, slot) = type;
unlock(p->rwlatch);
}
int pageGetSlotType(Page * p, int slot, int type) {
int slottedGetType(Page * p, int slot) {
int ret;
readlock(p->rwlatch, 693);
ret = *slot_length_ptr(p, slot);

View file

@ -59,17 +59,10 @@ Slotted page layout:
#define SLOTTED_PAGE_OVERHEAD_PER_RECORD 4
#define SLOTTED_PAGE_HEADER_OVERHEAD 6
void pageWriteRecord(int xid, Page * page, lsn_t lsn, recordid rid, const byte *data);
void pageReadRecord(int xid, Page * page, recordid rid, byte *buff);
void slottedWrite(int xid, Page * page, lsn_t lsn, recordid rid, const byte *data);
void slottedRead(int xid, Page * page, recordid rid, byte *buff);
/**
* assumes that the page is already loaded in memory. It takes as a
* parameter a Page, and returns an estimate of the amount of free space on this
* page. This is either exact, or an underestimate.
* @todo how should this be handled? */
int freespace(Page * p);
void pageInitialize(Page * p);
void slottedPageInitialize(Page * p);
#define freespace_ptr(page) shorts_from_end((page), 1)
#define numslots_ptr(page) shorts_from_end((page), 2)
@ -79,3 +72,129 @@ void pageInitialize(Page * p);
#define record_ptr(page, n) bytes_from_start((page), *slot_ptr((page), (n)))
#define isValidSlot(page, n) ((*slot_ptr((page), (n)) == INVALID_SLOT) ? 0 : 1)
/**
* allocate a record. This must be done in two phases. The first
* phase reserves a slot, and produces a log entry. The second phase
* sets up the slot according to the contents of the log entry.
*
* Essentially, the implementation of this function chooses a page
* with enough space for the allocation, then calls slottedRawRalloc.
*
* Ralloc implements the first phase.
*
* @param xid The active transaction.
* @param size The size of the new record
* @return allocated record
*
* @see postRallocSlot the implementation of the second phase.
*
*/
recordid slottedPreRalloc(int xid, long size);
/**
* The second phase of slot allocation. Called after the log entry
* has been produced, and during recovery.
*
* @param page The page that should contain the new record.
*
* @param lsn The lsn of the corresponding log entry (the page's LSN
* is updated to reflect this value.)
*
* @param rid A recordid that should exist on this page. If it does
* not exist, then it is created. Because slottedPreRalloc never
* 'overbooks' pages, we are guaranteed to have enough space on the
* page for this record (though it is possible that we need to compact
* the page)
*/
recordid slottedPostRalloc(Page * page, lsn_t lsn, recordid rid);
/**
* Mark the space used by a record for reclaimation.
*
* @param rid the recordid to be freed.
*/
void slottedDeRalloc(Page * page, recordid rid);
void slottedPageInit();
void slottedPageDeinit();
/**
*
* Bypass logging and allocate a record. It should only be used
* when the recordid returned is deterministic, and will be available
* during recovery, as it bypassses the normal two-phase alloc / log
* procedure for record allocation. This usually means that this
* function must be called by an Operation implementation that
* alocates entire pages, logs the page ids that were allocated,
* initializes the pages and finally calls this function in a
* deterministic fashion.
*
* @see indirect.c for an example of how to use this function
* correctly.
*
* This function assumes that the page is already loaded in memory.
* It takes as parameters a Page and the size in bytes of the new
* record.
*
* If you call this function, you probably need to be holding
* lastFreepage_mutex.
*
* @see lastFreepage_mutex
*
* @return a recordid representing the newly allocated record.
*
* NOTE: might want to pad records to be multiple of words in length, or, simply
* make sure all records start word aligned, but not necessarily having
* a length that is a multiple of words. (Since Tread(), Twrite() ultimately
* call memcpy(), this shouldn't be an issue)
*
* NOTE: pageRalloc() assumes that the caller already made sure that sufficient
* amount of freespace exists in this page.
* @see slottedFreespace()
*
* @todo pageRalloc's algorithm for reusing slot id's reclaims the
* highest numbered slots first, which encourages fragmentation.
*/
recordid slottedRawRalloc(Page * page, int size);
/**
* Obtain an estimate of the amount of free space on this page.
* Assumes that the page is already loaded in memory.
*
* (This function is particularly useful if you need to use
* slottedRawRalloc for something...)
*
* @param p the page whose freespace will be estimated.
*
* @return an exact measurment of the freespace, or, in the case of
* fragmentation, an underestimate.
*/
int slottedFreespace(Page * p);
/**
* Check to see if a slot is a normal slot, or something else, such
* as a blob. This is stored in the size field in the slotted page
* structure. If the size field is greater than PAGE_SIZE, then the
* slot contains a special value, and the size field indicates the
* type. (The type is looked up in a table to determine the amount
* of the page's physical space used by the slot.)
*
* @param p the page of interest
* @param slot the slot in p that we're checking.
* @param The type of this slot.
*/
int slottedGetType(Page * p, int slot);
/**
* Set the type of a slot to a special type, such as BLOB_SLOT. This
* function does not update any other information in the page
* structure, and just writes the raw value of type into the slot's
* size field. In particular, setting the type to NORMAL_SLOT will
* result in undefined behavior. (Such a call would destroy all
* record of the slot's true physical size)
*
* @param p the page containing the slot to be updated.
* @param slot the slot whose type will be changed.
* @param type the new type of slot. Must be greater than PAGE_SIZE.
*
*/
void slottedSetType(Page * p, int slot, int type);

View file

@ -1,13 +1,11 @@
/**
@file
This file handles all of the file I/O for pages.
*/
#include "page.h"
#include "page/slotted.h"
#include <lladd/bufferManager.h>
#include "pageFile.h"
#include <assert.h>
#include "logger/logWriter.h"
@ -15,7 +13,8 @@
#include <sys/types.h>
#include <sys/stat.h>
#define __USE_GNU /* For O_DIRECT.. */
/** For O_DIRECT. It's unclear that this is the correct thing to #define, but it works under linux. */
#define __USE_GNU
#include <fcntl.h>
#include <unistd.h>
@ -45,8 +44,8 @@ void pageRead(Page *ret) {
offset = myLseekNoLock(stable, pageoffset, SEEK_SET);
assert(offset == pageoffset);
if(fileSize <= pageoffset) {
pageInitialize(ret);
write(stable, ret->memAddr, PAGE_SIZE);
memset(ret->memAddr, 0, PAGE_SIZE);
write(stable, ret->memAddr, PAGE_SIZE); /* all this does is extend the file.. */
}
} else if(read_size == -1) {
perror("pageFile.c couldn't read");

View file

@ -132,9 +132,10 @@ void Tupdate(int xid, recordid rid, const void *dat, int op) {
void Tread(int xid, recordid rid, void * dat) {
Page * p = loadPage(rid.page);
if(*page_type_ptr(p) == SLOTTED_PAGE) {
int page_type = *page_type_ptr(p);
if(page_type == SLOTTED_PAGE) {
readRecord(xid, p, rid, dat);
} else if(*page_type_ptr(p) == INDIRECT_PAGE) {
} else if(page_type == INDIRECT_PAGE) {
releasePage(p);
rid = dereferenceRID(rid);
p = loadPage(rid.page);

View file

@ -9,6 +9,7 @@
#include "../../src/lladd/logger/logWriter.h"
#include "../../src/lladd/latches.h"
#include "../../src/lladd/page.h"
#include "../../src/lladd/page/slotted.h"
#include <lladd/bufferManager.h>
#include <sched.h>
#include <assert.h>
@ -35,15 +36,11 @@ void initializePages() {
rid.slot = 0;
rid.size = sizeof(int);
p = loadPage(rid.page);
/* p = loadPage(i); */
assert(p->id != -1);
pageSlotRalloc(p, 0, rid);
/* rid = pageRalloc(p, sizeof(int)); */
/* addPendingEvent(rid.page); */
assert(p->id != -1);
slottedPostRalloc(p, 0, rid);
writeRecord(1, p, 1, rid, &i);
/* removePendingEvent(rid.page); */
/* assert(p->pending == 0); */
releasePage(p);
}
@ -90,7 +87,7 @@ void * workerThreadWriting(void * q) {
recordid rids[RECORDS_PER_THREAD];
for(int i = 0 ; i < RECORDS_PER_THREAD; i++) {
rids[i] = ralloc(1, sizeof(int));
rids[i] = slottedPreRalloc(1, sizeof(int));
/* printf("\nRID:\t%d,%d\n", rids[i].page, rids[i].slot); */
/* fflush(NULL); */

View file

@ -47,6 +47,7 @@ terms specified in this license.
#include "../check_includes.h"
#include "../../src/lladd/page.h"
#include "../../src/lladd/page/slotted.h"
#define LOG_NAME "check_operations.log"
@ -66,7 +67,7 @@ START_TEST(operation_physical_do_undo) {
Tinit();
rid = ralloc(xid, sizeof(int));
rid = slottedPreRalloc(xid, sizeof(int));
buf = 1;
arg = 2;
@ -75,7 +76,6 @@ START_TEST(operation_physical_do_undo) {
/* Do, undo and redo operation without updating the LSN field of the page. */
/* writeLSN(lsn, rid.page); */
DEBUG("B\n");
p = loadPage(rid.page);
@ -84,7 +84,6 @@ START_TEST(operation_physical_do_undo) {
setToTwo->LSN = 10;
DEBUG("C\n");
/* addPendingEvent(rid.page); */
p = loadPage(rid.page);
doUpdate(setToTwo, p); /* PAGE LSN= 10, value = 2. */
releasePage(p);
@ -105,7 +104,6 @@ START_TEST(operation_physical_do_undo) {
setToTwo->LSN = 5;
/* addPendingEvent(rid.page); */
undoUpdate(setToTwo, p, 8); /* Should succeed, CLR LSN is too low, but undoUpdate only checks the log entry. */
releasePage(p);
@ -116,7 +114,6 @@ START_TEST(operation_physical_do_undo) {
fail_unless(buf == 1, NULL);
DEBUG("E\n");
/* addPendingEvent(rid.page); */
redoUpdate(setToTwo);
@ -126,8 +123,6 @@ START_TEST(operation_physical_do_undo) {
fail_unless(buf == 1, NULL);
/* writeLSN(3,rid.page); */
/* Now, simulate scenarios from normal operation:
do the operation, and update the LSN, (update happens)
then undo, and update the LSN again. (undo happens)
@ -141,7 +136,6 @@ START_TEST(operation_physical_do_undo) {
lsn = 0;
buf = 1;
/* writeLSN(lsn, rid.page); */
p = loadPage(rid.page);
writeRecord(xid, p, lsn, rid, &buf);
releasePage(p);
@ -164,9 +158,7 @@ START_TEST(operation_physical_do_undo) {
setToTwo->LSN = 10;
DEBUG("F\n");
/* addPendingEvent(rid.page); */
redoUpdate(setToTwo);
/* writeLSN(setToTwo->LSN, rid.page); */
p = loadPage(rid.page);
readRecord(xid, p, rid, &buf);
@ -174,7 +166,6 @@ START_TEST(operation_physical_do_undo) {
fail_unless(buf == 2, NULL);
DEBUG("G undo set to 2\n");
/* addPendingEvent(rid.page); */
undoUpdate(setToTwo, p, 20); /* Succeeds -- 20 is the 'CLR' entry's lsn.*/
readRecord(xid, p, rid, &buf);
@ -183,7 +174,6 @@ START_TEST(operation_physical_do_undo) {
releasePage(p);
DEBUG("H don't redo set to 2\n");
/* addPendingEvent(rid.page); */
redoUpdate(setToTwo); /* Fails */
p = loadPage(rid.page);
@ -193,10 +183,8 @@ START_TEST(operation_physical_do_undo) {
fail_unless(buf == 1, NULL);
writeRecord(xid, p, 0, rid, &buf); /* reset the page's LSN. */
/* writeLSN(0,rid.page); */
DEBUG("I redo set to 2\n");
/* addPendingEvent(rid.page); */
releasePage(p);
redoUpdate(setToTwo); /* Succeeds */

View file

@ -72,11 +72,11 @@ static void * multiple_simultaneous_pages ( void * arg_ptr) {
Page * p = (Page*)arg_ptr;
int i;
lsn_t this_lsn;
int j;
short j;
int first = 1;
int k;
recordid rid[100];
for(i = 0; i < 10000; i++) {
pthread_mutex_lock(&lsn_mutex);
this_lsn = lsn;
@ -84,13 +84,11 @@ static void * multiple_simultaneous_pages ( void * arg_ptr) {
pthread_mutex_unlock(&lsn_mutex);
if(! first ) {
/* addPendingEvent(p); */
/*pageReadRecord(1, p, rid, (byte*)&j);*/
for(k = 0; k < 100; k++) {
readRecord(1, p, rid[k], (byte*)&j);
assert((j + 1) == i + k);
pageDeRalloc(p, rid[k]);
slottedDeRalloc(p, rid[k]);
sched_yield();
}
}
@ -99,15 +97,12 @@ static void * multiple_simultaneous_pages ( void * arg_ptr) {
for(k = 0; k < 100; k++) {
rid[k] = pageRalloc(p, sizeof(short));
rid[k] = slottedRawRalloc(p, sizeof(short));
i +=k;
/* printf("Slot = %d\n", rid[k].slot); */
/* printf("Slot %d = %d\n", rid[k].slot, i); */
writeRecord(1, p, lsn, rid[k], (byte*)&i);
i -=k;
sched_yield();
}
assert(pageReadLSN(p) <= lsn);
@ -131,17 +126,15 @@ static void* worker_thread(void * arg_ptr) {
pthread_mutex_unlock(&lsn_mutex);
if(! first ) {
/* addPendingEvent(p); */
/*pageReadRecord(1, p, rid, (byte*)&j);*/
readRecord(1, p, rid, (byte*)&j);
assert((j + 1) == i);
pageDeRalloc(p, rid);
slottedDeRalloc(p, rid);
sched_yield();
}
first = 0;
rid = pageRalloc(p, sizeof(int));
rid = slottedRawRalloc(p, sizeof(int));
writeRecord(1, p, lsn, rid, (byte*)&i);
sched_yield();
@ -175,7 +168,7 @@ START_TEST(pageNoThreadTest)
Tinit();
p = loadPage(0);
slottedPageInitialize(p);
worker_thread(p);
unlock(p->loadlatch);
@ -264,10 +257,10 @@ START_TEST(pageNoThreadMultPageTest)
Tinit();
p = loadPage(1);
slottedPageInitialize(p);
multiple_simultaneous_pages(p);
unlock(p->loadlatch);
releasePage(p);
/* unlock(p->loadlatch); */
Tdeinit();
@ -292,6 +285,7 @@ START_TEST(pageThreadTest) {
fail_unless(1, NULL);
Page * p = loadPage(2);
slottedPageInitialize(p);
fail_unless(1, NULL);
for(i = 0; i < THREAD_COUNT; i++) {

View file

@ -285,6 +285,9 @@ START_TEST(recovery_clr) {
Tread(xid, rid, &j);
Tcommit(xid);
xid = Tbegin();
Tincrement(xid, rid);
Tabort(xid);

View file

@ -73,7 +73,7 @@ void * writingAbortingBlobWorkerThread ( void * v ) {
recordid * rids = malloc(RECORDS_PER_THREAD * sizeof(recordid));
int xid = Tbegin();
for(int i = 0; i < RECORDS_PER_THREAD; i++) {
rids[i] = /* ralloc(xid, sizeof(int)); */ Talloc(xid, 1024 * sizeof(int));
rids[i] = Talloc(xid, 1024 * sizeof(int));
if(! (i %100)) {
printf("A%d", i/100);fflush(NULL);
}
@ -134,7 +134,7 @@ void * writingAbortingWorkerThread ( void * v ) {
recordid * rids = malloc(RECORDS_PER_THREAD * sizeof(recordid));
int xid = Tbegin();
for(int i = 0; i < RECORDS_PER_THREAD; i++) {
rids[i] = /* ralloc(xid, sizeof(int)); */ Talloc(xid, sizeof(int));
rids[i] = Talloc(xid, sizeof(int));
if(! (i %100)) {
printf("A%d", i/100);fflush(NULL);
}
@ -192,7 +192,7 @@ void * writingWorkerThread ( void * v ) {
recordid * rids = malloc(RECORDS_PER_THREAD * sizeof(recordid));
int xid = Tbegin();
for(int i = 0; i < RECORDS_PER_THREAD; i++) {
rids[i] = /* ralloc(xid, sizeof(int)); */ Talloc(xid, sizeof(int));
rids[i] = Talloc(xid, sizeof(int));
if(! (i %100)) {
printf("A%d", i/100);fflush(NULL);
}
@ -450,8 +450,8 @@ Suite * check_suite(void) {
tcase_add_test(tc, transactional_threads_commit);
tcase_add_test(tc, transactional_nothreads_abort);
tcase_add_test(tc, transactional_threads_abort);
tcase_add_test(tc, transactional_blobs_nothreads_abort);
/* tcase_add_test(tc, transactional_blobs_threads_abort); */
tcase_add_test(tc, transactional_blobs_nothreads_abort);
/* tcase_add_test(tc, transactional_blobs_threads_abort); */
/* --------------------------------------------- */
tcase_add_checked_fixture(tc, setup, teardown);
suite_add_tcase(s, tc);