2004-10-06 06:08:09 +00:00
|
|
|
#include <config.h>
|
|
|
|
|
2007-06-11 21:36:57 +00:00
|
|
|
#include <stasis/operations/arrayList.h>
|
2004-10-06 06:08:09 +00:00
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <math.h>
|
|
|
|
|
2009-06-02 18:25:35 +00:00
|
|
|
#define MAX_OFFSET_POSITION 3
|
|
|
|
#define FIRST_DATA_PAGE_OFFSET 4
|
|
|
|
|
2004-10-06 06:08:09 +00:00
|
|
|
typedef struct {
|
2008-10-03 02:42:25 +00:00
|
|
|
pageid_t firstPage;
|
|
|
|
pageid_t initialSize;
|
|
|
|
pageid_t multiplier;//XXX rest are not page numbers or offsets, but must all be same length
|
2009-06-02 18:25:35 +00:00
|
|
|
pageid_t size; // *has* to be smaller than a page; passed into TinitializeFixedPage()
|
2008-10-03 02:42:25 +00:00
|
|
|
pageid_t maxOffset;
|
2009-06-02 18:25:35 +00:00
|
|
|
} array_list_parameter_t;
|
2007-06-07 21:53:09 +00:00
|
|
|
|
2004-10-06 06:08:09 +00:00
|
|
|
|
2009-06-02 18:25:35 +00:00
|
|
|
static array_list_parameter_t array_list_read_parameter(int xid, Page * p) {
|
2004-10-06 06:08:09 +00:00
|
|
|
|
2009-06-02 18:25:35 +00:00
|
|
|
array_list_parameter_t alp;
|
|
|
|
alp.firstPage = p->id;
|
|
|
|
/* tlp.maxOffset = *(int*)fixed_record_ptr(p, 3); */
|
|
|
|
recordid rid = { p->id, 0, sizeof(pageid_t) };
|
|
|
|
alp.initialSize = *(pageid_t*)stasis_record_read_begin(xid, p, rid);
|
|
|
|
rid.slot = 1;
|
|
|
|
alp.multiplier = *(pageid_t*)stasis_record_read_begin(xid, p, rid);
|
|
|
|
rid.slot = 2;
|
|
|
|
alp.size = *(pageid_t*)stasis_record_read_begin(xid, p, rid);
|
|
|
|
rid.slot = 3;
|
|
|
|
alp.maxOffset = *(pageid_t*)stasis_record_read_begin(xid, p, rid);
|
2004-10-06 06:08:09 +00:00
|
|
|
|
2009-06-02 18:25:35 +00:00
|
|
|
return alp;
|
2004-10-06 06:08:09 +00:00
|
|
|
}
|
|
|
|
|
2009-06-02 18:25:35 +00:00
|
|
|
static int array_list_get_block_containing_offset(array_list_parameter_t alp, int offset, pageid_t * firstSlotInBlock) {
|
|
|
|
int rec_per_page = stasis_fixed_records_per_page((size_t)alp.size);
|
|
|
|
long thisHigh = rec_per_page * alp.initialSize;
|
|
|
|
int lastHigh = 0;
|
|
|
|
int pageRidSlot = 0;
|
|
|
|
int currentPageLength = alp.initialSize;
|
2004-10-06 06:08:09 +00:00
|
|
|
|
2009-06-02 18:25:35 +00:00
|
|
|
while(((pageid_t)offset) >= thisHigh) {
|
|
|
|
pageRidSlot ++;
|
|
|
|
lastHigh = thisHigh;
|
|
|
|
currentPageLength *= alp.multiplier;
|
|
|
|
thisHigh += rec_per_page * currentPageLength;
|
|
|
|
}
|
|
|
|
if(firstSlotInBlock) {
|
|
|
|
*firstSlotInBlock = lastHigh;
|
|
|
|
}
|
|
|
|
return pageRidSlot;
|
|
|
|
}
|
2004-10-06 06:08:09 +00:00
|
|
|
|
2009-06-02 18:25:35 +00:00
|
|
|
static int array_list_op_init_header(const LogEntry* e, Page* p) {
|
2008-09-28 03:11:24 +00:00
|
|
|
|
2009-06-02 18:25:35 +00:00
|
|
|
assert(e->update.arg_size == sizeof(array_list_parameter_t));
|
2004-10-06 06:08:09 +00:00
|
|
|
|
2009-06-02 18:25:35 +00:00
|
|
|
const array_list_parameter_t * alp
|
|
|
|
= (const array_list_parameter_t*)getUpdateArgs(e);
|
2004-10-06 06:08:09 +00:00
|
|
|
|
2009-03-31 05:02:54 +00:00
|
|
|
stasis_fixed_initialize_page(p, sizeof(pageid_t),
|
|
|
|
stasis_fixed_records_per_page(sizeof(pageid_t)));
|
2004-10-06 06:08:09 +00:00
|
|
|
|
2009-06-02 18:25:35 +00:00
|
|
|
recordid initialSizeRid, multiplierRid, slotSizeRid, maxOffsetRid, firstDataPageRid;
|
|
|
|
initialSizeRid.page
|
|
|
|
= multiplierRid.page = slotSizeRid.page
|
|
|
|
= maxOffsetRid.page = firstDataPageRid.page = p->id;
|
2009-03-31 05:02:54 +00:00
|
|
|
|
2009-06-02 18:25:35 +00:00
|
|
|
initialSizeRid.size
|
2009-03-31 05:02:54 +00:00
|
|
|
= multiplierRid.size = slotSizeRid.size
|
2009-06-02 18:25:35 +00:00
|
|
|
= maxOffsetRid.size = firstDataPageRid.size = sizeof(pageid_t);
|
2004-10-06 06:08:09 +00:00
|
|
|
|
2009-06-02 18:25:35 +00:00
|
|
|
initialSizeRid.slot = 0;
|
2004-10-06 06:08:09 +00:00
|
|
|
multiplierRid.slot = 1;
|
|
|
|
slotSizeRid.slot = 2;
|
2009-06-02 18:25:35 +00:00
|
|
|
maxOffsetRid.slot = 3;
|
|
|
|
// Note that firstDataPageRid is not part of the header page's header..
|
|
|
|
// Instead, it is the value stored on the first slot of the header page.
|
2004-10-06 06:08:09 +00:00
|
|
|
firstDataPageRid.slot = 4;
|
|
|
|
|
2009-06-02 18:25:35 +00:00
|
|
|
// Write header.
|
|
|
|
stasis_record_write(e->xid, p, initialSizeRid, (const byte*)&(alp->initialSize));
|
|
|
|
stasis_record_write(e->xid, p, multiplierRid, (const byte*)&(alp->multiplier));
|
|
|
|
stasis_record_write(e->xid, p, slotSizeRid, (const byte*)&(alp->size));
|
|
|
|
stasis_record_write(e->xid, p, maxOffsetRid, (const byte*)&(alp->maxOffset));
|
2004-10-06 06:08:09 +00:00
|
|
|
|
2009-06-02 18:25:35 +00:00
|
|
|
// Write first slot. The page after this one stores data
|
|
|
|
pageid_t firstDataPage = alp->firstPage + 1;
|
|
|
|
stasis_record_write(e->xid, p, firstDataPageRid, (const byte*)&firstDataPage);
|
2004-10-06 06:08:09 +00:00
|
|
|
|
2009-06-02 18:25:35 +00:00
|
|
|
|
|
|
|
*stasis_page_type_ptr(p) = ARRAY_LIST_PAGE;
|
2007-06-01 21:32:33 +00:00
|
|
|
|
2004-10-06 06:08:09 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-06-02 18:25:35 +00:00
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
2009-03-31 05:02:54 +00:00
|
|
|
stasis_operation_impl stasis_op_impl_array_list_header_init() {
|
|
|
|
stasis_operation_impl o = {
|
|
|
|
OPERATION_ARRAY_LIST_HEADER_INIT,
|
|
|
|
OPERATION_ARRAY_LIST_HEADER_INIT,
|
|
|
|
/* Do not need to roll back this page, since it will be deallocated. */
|
|
|
|
OPERATION_NOOP,
|
2009-06-02 18:25:35 +00:00
|
|
|
&array_list_op_init_header
|
2004-10-06 06:08:09 +00:00
|
|
|
};
|
|
|
|
return o;
|
|
|
|
}
|
2009-06-02 18:25:35 +00:00
|
|
|
|
|
|
|
recordid stasis_array_list_dereference_recordid(int xid, Page * p, int offset) {
|
|
|
|
readlock(p->rwlatch,0);
|
|
|
|
array_list_parameter_t tlp = array_list_read_parameter(xid, p);
|
|
|
|
|
|
|
|
int rec_per_page = stasis_fixed_records_per_page((size_t)tlp.size);
|
|
|
|
pageid_t lastHigh = 0;
|
|
|
|
int pageRidSlot = 0; /* The slot on the root arrayList page that contains the first page of the block of interest */
|
|
|
|
|
|
|
|
assert(tlp.maxOffset >= offset);
|
|
|
|
|
|
|
|
pageRidSlot = array_list_get_block_containing_offset(tlp, offset, &lastHigh);
|
|
|
|
|
|
|
|
int dataSlot = offset - lastHigh; /* The offset in the block of interest of the slot we want. */
|
|
|
|
pageid_t blockPage = dataSlot / rec_per_page; /* The page in the block of interest that contains the slot we want */
|
|
|
|
int blockSlot = dataSlot - blockPage * rec_per_page;
|
|
|
|
|
|
|
|
pageid_t thePage;
|
|
|
|
|
|
|
|
recordid rid = { p->id, pageRidSlot + FIRST_DATA_PAGE_OFFSET, sizeof(pageid_t) };
|
|
|
|
thePage = *(int*)stasis_record_read_begin(xid,p,rid);
|
|
|
|
unlock(p->rwlatch);
|
|
|
|
|
|
|
|
rid.page = thePage + blockPage;
|
|
|
|
rid.slot = blockSlot;
|
|
|
|
rid.size = tlp.size;
|
|
|
|
|
|
|
|
return rid;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2004-10-06 06:08:09 +00:00
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
2009-06-02 18:25:35 +00:00
|
|
|
compensated_function recordid TarrayListAlloc(int xid, pageid_t count, int multiplier, int recordSize) {
|
|
|
|
|
|
|
|
pageid_t firstPage;
|
|
|
|
try_ret(NULLRID) {
|
|
|
|
firstPage = TpageAllocMany(xid, count+1);
|
|
|
|
} end_ret(NULLRID);
|
|
|
|
array_list_parameter_t alp;
|
|
|
|
|
|
|
|
alp.firstPage = firstPage;
|
|
|
|
alp.initialSize = count;
|
|
|
|
alp.multiplier = multiplier;
|
|
|
|
alp.size = recordSize;
|
|
|
|
alp.maxOffset = -1;
|
|
|
|
|
|
|
|
recordid rid;
|
|
|
|
|
|
|
|
rid.page = firstPage;
|
|
|
|
rid.slot = 0; /* number of slots in array (maxOffset + 1) */
|
|
|
|
rid.size = recordSize;
|
|
|
|
try_ret(NULLRID) {
|
|
|
|
Tupdate(xid, firstPage, &alp, sizeof(alp), OPERATION_ARRAY_LIST_HEADER_INIT);
|
|
|
|
} end_ret(NULLRID);
|
|
|
|
|
|
|
|
return rid;
|
|
|
|
}
|
|
|
|
|
2005-02-24 21:12:36 +00:00
|
|
|
/** @todo locking for arrayList... this isn't pressing since currently
|
|
|
|
the only thing that calls arraylist (the hashtable
|
2009-06-02 18:25:35 +00:00
|
|
|
implementations) serialize bucket list operations anyway...
|
2005-03-02 05:47:38 +00:00
|
|
|
|
2009-06-02 18:25:35 +00:00
|
|
|
@todo this function calls pow(), which is horribly inefficient.
|
2005-03-02 05:47:38 +00:00
|
|
|
*/
|
2005-02-24 21:12:36 +00:00
|
|
|
|
2008-09-28 03:11:24 +00:00
|
|
|
compensated_function int TarrayListExtend(int xid, recordid rid, int slots) {
|
2005-02-22 03:10:54 +00:00
|
|
|
Page * p;
|
|
|
|
try_ret(compensation_error()) {
|
|
|
|
p = loadPage(xid, rid.page);
|
|
|
|
} end_ret(compensation_error());
|
2007-06-07 21:53:09 +00:00
|
|
|
readlock(p->rwlatch, 0);
|
2009-06-02 18:25:35 +00:00
|
|
|
array_list_parameter_t alp
|
|
|
|
= array_list_read_parameter(xid, p);
|
2007-06-07 21:53:09 +00:00
|
|
|
unlock(p->rwlatch);;
|
2005-02-24 21:12:36 +00:00
|
|
|
releasePage(p);
|
|
|
|
p = NULL;
|
2004-10-06 06:08:09 +00:00
|
|
|
|
2008-10-03 02:42:25 +00:00
|
|
|
int lastCurrentBlock; // just a slot on a page
|
2009-06-02 18:25:35 +00:00
|
|
|
if(alp.maxOffset == -1) {
|
2004-10-06 06:08:09 +00:00
|
|
|
lastCurrentBlock = -1;
|
|
|
|
} else{
|
2009-06-02 18:25:35 +00:00
|
|
|
lastCurrentBlock = array_list_get_block_containing_offset(alp, alp.maxOffset, NULL);
|
|
|
|
}
|
|
|
|
int lastNewBlock = array_list_get_block_containing_offset(alp, alp.maxOffset+slots, NULL);
|
2004-10-06 06:08:09 +00:00
|
|
|
|
|
|
|
DEBUG("lastCurrentBlock = %d, lastNewBlock = %d\n", lastCurrentBlock, lastNewBlock);
|
|
|
|
|
|
|
|
recordid tmp; /* recordid of slot in base page that holds new block. */
|
|
|
|
tmp.page = rid.page;
|
2008-10-03 02:42:25 +00:00
|
|
|
tmp.size = sizeof(pageid_t);
|
2009-06-02 18:25:35 +00:00
|
|
|
|
2004-10-06 06:08:09 +00:00
|
|
|
recordid tmp2; /* recordid of newly created pages. */
|
|
|
|
tmp2.slot = 0;
|
2009-06-02 18:25:35 +00:00
|
|
|
tmp2.size = alp.size;
|
2004-10-18 18:24:54 +00:00
|
|
|
/* Iterate over the (small number) of indirection blocks that need to be updated */
|
2005-02-24 21:12:36 +00:00
|
|
|
try_ret(compensation_error()) {
|
2008-10-03 02:42:25 +00:00
|
|
|
for(pageid_t i = lastCurrentBlock+1; i <= lastNewBlock; i++) {
|
2005-02-24 21:12:36 +00:00
|
|
|
/* Alloc block i */
|
2009-06-02 18:25:35 +00:00
|
|
|
pageid_t blockSize = alp.initialSize * powl(alp.multiplier, i);
|
2008-10-03 02:42:25 +00:00
|
|
|
pageid_t newFirstPage = TpageAllocMany(xid, blockSize);
|
2005-02-24 21:12:36 +00:00
|
|
|
DEBUG("block %d\n", i);
|
|
|
|
tmp.slot = i + FIRST_DATA_PAGE_OFFSET;
|
2007-11-11 17:18:57 +00:00
|
|
|
/* Iterate over the (large number) of new blocks, clearing their contents */
|
|
|
|
/* @todo XXX arraylist generates N log entries initing pages.
|
2009-06-02 18:25:35 +00:00
|
|
|
It should generate 1 entry. (Need better LSN handling first.)*/
|
2007-11-11 17:18:57 +00:00
|
|
|
{
|
2009-06-02 18:25:35 +00:00
|
|
|
for(pageid_t i = newFirstPage; i < newFirstPage + blockSize; i++) {
|
|
|
|
TinitializeFixedPage(xid, i, alp.size);
|
|
|
|
}
|
2007-11-11 17:18:57 +00:00
|
|
|
}
|
2008-09-28 03:11:24 +00:00
|
|
|
TsetRaw(xid,tmp,&newFirstPage);
|
|
|
|
|
2005-02-24 21:12:36 +00:00
|
|
|
DEBUG("Tset: {%d, %d, %d} = %d\n", tmp.page, tmp.slot, tmp.size, newFirstPage);
|
|
|
|
}
|
2009-06-02 18:25:35 +00:00
|
|
|
|
2005-02-24 21:12:36 +00:00
|
|
|
tmp.slot = MAX_OFFSET_POSITION;
|
2009-06-02 18:25:35 +00:00
|
|
|
|
|
|
|
pageid_t newMaxOffset = alp.maxOffset+slots;
|
2008-09-28 03:11:24 +00:00
|
|
|
TsetRaw(xid, tmp, &newMaxOffset);
|
2005-02-24 21:12:36 +00:00
|
|
|
} end_ret(compensation_error());
|
2004-10-06 06:08:09 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2009-06-02 18:25:35 +00:00
|
|
|
compensated_function int TarrayListLength(int xid, recordid rid) {
|
2006-07-28 00:00:32 +00:00
|
|
|
Page * p = loadPage(xid, rid.page);
|
2007-06-07 21:53:09 +00:00
|
|
|
readlock(p->rwlatch, 0);
|
2009-06-02 18:25:35 +00:00
|
|
|
array_list_parameter_t alp
|
|
|
|
= array_list_read_parameter(xid, p);
|
2007-06-07 21:53:09 +00:00
|
|
|
unlock(p->rwlatch);
|
2006-07-28 00:00:32 +00:00
|
|
|
releasePage(p);
|
2009-06-02 18:25:35 +00:00
|
|
|
return alp.maxOffset+1;
|
2004-10-06 06:08:09 +00:00
|
|
|
}
|