Reimplemented allocationPolicy.c; updated API + added more cases to the unit tests.

This commit is contained in:
Sears Russell 2009-08-05 06:35:11 +00:00
parent beb14ec917
commit 5b10bcc63b
4 changed files with 527 additions and 509 deletions

View file

@ -36,371 +36,456 @@
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>
#define ALLOCATION_POLICY_SANITY_CHECKS /**
* Allocation Policy maintains the following tables:
*
* AllPages: _pageid_, freespace, key1: pageid
*
* XidAlloced: _xid, pageid_ key1: xid,pageid, key2: pageid,xid
* XidDealloced: _xid, pageid_: key1: xid,pageid, key2: pageid,xid
*
* And the following views:
*
* AvailablePages: (_pageid_, freespace) The rows of AllPages with no entries in xidAllloced or xidDealloced. key1: pageid, key2: freespace, pageid
* PageOwners: (xid, freespace, _pageid_) The rows of AllPages with an entry in xidAlloced, and at most one entry in xidDealloced, key1: pageid, key2 = xid, freespace, pageid
*/
// Tables:
typedef struct {
pageid_t pageid;
size_t freespace;
} allPages_pageid_freespace;
typedef struct {
int xid;
pageid_t pageid;
} xidAllocedDealloced_xid_pageid;
// Each availablePage should either be in availablePages, or in // Views:
// xidAlloced and pageOwners. If a transaction allocs and
// deallocs from the same page, then it only has an entry for that
// page in xidAlloced.
//
// xidAlloced is an lhtable of type (int xid) -> (rbtree of availablePage*)
// xidDealloced is an lhtable of type (int xid) -> (lhtable of int pageid -> availablePage *)
// pageOwners is an lhtable of type (int pageid) -> (int xid)
// availablePages is a rbtree of availablePage*.
struct allocationPolicy { typedef struct {
struct LH_ENTRY(table) * xidAlloced; pageid_t pageid;
struct LH_ENTRY(table) * xidDealloced; size_t freespace;
struct RB_ENTRY(tree) * availablePages; } availablePages_pageid_freespace;
struct LH_ENTRY(table) * pageOwners; typedef struct {
struct LH_ENTRY(table) * allPages; int xid;
size_t freespace;
pageid_t pageid;
} pageOwners_xid_freespace_pageid;
struct stasis_allocation_policy_t {
// views
struct rbtree * availablePages_key_pageid;
struct rbtree * availablePages_key_freespace_pageid;
struct rbtree * pageOwners_key_pageid;
struct rbtree * pageOwners_key_xid_freespace_pageid;
// tables
struct rbtree * allPages_key_pageid;
struct rbtree * xidAlloced_key_xid_pageid;
struct rbtree * xidAlloced_key_pageid_xid;
struct rbtree * xidDealloced_key_xid_pageid;
struct rbtree * xidDealloced_key_pageid_xid;
// flags
char reuseWithinXact;
}; };
inline static int cmpPageid(const void * ap, const void * bp, const void * param) { // View maintenance functions. Called by the table insertion functions.
const availablePage * a = (const availablePage *)ap;
const availablePage * b = (const availablePage *)bp;
if(a->pageid < b->pageid) { // ######## Helpers ###############
return -1;
} else if (a->pageid > b->pageid) { static int void_single_add(void * val, struct rbtree * a) {
const void * old = rbdelete(val, a);
rbsearch(val, a);
int found = (old != 0);
if(found) { free((void*)old); }
return found;
}
static int void_single_remove(void * val, struct rbtree * a) {
const void * old = rbdelete(val, a);
int found = (old != 0);
if(found) { free((void*)old); }
return found;
}
static int void_double_add(void * val, struct rbtree * a, struct rbtree * b) {
const void *old1, *old2 ;
old1 = rbdelete(val, a);
rbsearch(val, a);
int found1 = (old1 != 0);
old2 = rbdelete(val, b);
rbsearch(val,b);
assert(old1 == old2);
if(found1) { free((void*)old1); }
return found1;
}
static int void_double_remove(const void * val, struct rbtree * primary, struct rbtree * secondary) {
const void * fullTuple= rbdelete(val, primary);
int found1 = (fullTuple != 0);
if(found1) {
const void * old = rbdelete(fullTuple, secondary);
assert(old == fullTuple);
free((void*)fullTuple);
return 1;
} else {
return 0;
}
}
// ######## AvailablePages ###########
static int availablePages_remove(stasis_allocation_policy_t *ap, pageid_t pageid);
static int availablePages_add(stasis_allocation_policy_t *ap, pageid_t pageid, size_t freespace) {
int ret = availablePages_remove(ap, pageid);
availablePages_pageid_freespace* tup= malloc(sizeof(*tup));
tup->pageid = pageid;
tup->freespace = freespace;
void_double_add(tup, ap->availablePages_key_pageid, ap->availablePages_key_freespace_pageid);
return ret;
}
static int availablePages_remove(stasis_allocation_policy_t *ap, pageid_t pageid) {
availablePages_pageid_freespace tup = {pageid, -1};
return void_double_remove(&tup, ap->availablePages_key_pageid, ap->availablePages_key_freespace_pageid);
}
// ######## PageOwners ###########
static int pageOwners_remove(stasis_allocation_policy_t *ap, pageid_t pageid);
static int pageOwners_add(stasis_allocation_policy_t *ap, int xid, size_t freespace, pageid_t pageid) {
int ret = pageOwners_remove(ap, pageid);
pageOwners_xid_freespace_pageid * tup = malloc(sizeof(*tup));
tup->xid = xid;
tup->freespace = freespace;
tup->pageid = pageid;
void_double_add(tup, ap->pageOwners_key_pageid, ap->pageOwners_key_xid_freespace_pageid);
return ret;
}
static int pageOwners_remove(stasis_allocation_policy_t *ap, pageid_t pageid) {
pageOwners_xid_freespace_pageid tup = { INVALID_XID, -1, pageid };
return void_double_remove(&tup, ap->pageOwners_key_pageid, ap->pageOwners_key_xid_freespace_pageid);
}
int pageOwners_lookup_by_xid_freespace(stasis_allocation_policy_t *ap, int xid, size_t freespace, pageid_t* pageid) {
pageOwners_xid_freespace_pageid query = { xid, freespace, 0 };
// find lowest numbered page w/ enough freespace.
const pageOwners_xid_freespace_pageid *tup = rblookup(RB_LUGTEQ, &query, ap->pageOwners_key_xid_freespace_pageid);
if(tup && tup->xid == xid) {
assert(tup->freespace >= freespace);
*pageid = tup->pageid;
return 1;
} else {
return 0;
}
}
int pageOwners_lookup_by_pageid(stasis_allocation_policy_t* ap, pageid_t pageid, int *xid, size_t *freespace) {
const pageOwners_xid_freespace_pageid query = { 0, 0, pageid };
const pageOwners_xid_freespace_pageid *tup = rbfind(&query, ap->pageOwners_key_pageid);
if(tup) {
*xid = tup->xid;
*freespace = tup->freespace;
return 1;
} else {
return 0;
}
}
/// TABLE METHODS FOLLOW. These functions perform view maintenance.
// ######## AllPages #############
static int allPages_lookup_by_pageid(stasis_allocation_policy_t *ap, pageid_t pageid, size_t *freespace) {
allPages_pageid_freespace query = {pageid, 0};
const allPages_pageid_freespace * tup = rbfind(&query, ap->allPages_key_pageid);
if(tup) {
assert(tup->pageid == pageid);
*freespace = tup->freespace;
return 1;
} else {
return 0;
}
}
static int allPages_add(stasis_allocation_policy_t *ap, pageid_t pageid, size_t freespace) {
allPages_pageid_freespace * tup = malloc(sizeof(*tup));
tup->pageid = pageid;
tup->freespace = freespace;
int ret = void_single_add(tup, ap->allPages_key_pageid);
if(!ret) {
int ret2 = availablePages_add(ap, pageid, freespace);
assert(!ret2);
} else {
// page may or may not be in availablePages...
}
return ret;
}
/** Assumes that the page is not in use by an outstanding xact */
static int allPages_remove(stasis_allocation_policy_t *ap, pageid_t pageid) {
allPages_pageid_freespace tup = { pageid, -1 };
int found = void_single_remove(&tup, ap->allPages_key_pageid);
int found2 = availablePages_remove(ap, pageid);
assert(found == found2);
return found;
}
static void allPages_removeAll(stasis_allocation_policy_t *ap) {
const allPages_pageid_freespace * tup;
while((tup = rbmin(ap->allPages_key_pageid))) {
allPages_remove(ap, tup->pageid);
}
}
static void allPages_set_freespace(stasis_allocation_policy_t *ap, pageid_t pageid, size_t freespace) {
allPages_pageid_freespace * tup = malloc(sizeof(*tup));
tup->pageid = pageid;
tup->freespace = freespace;
int existed = void_single_add(tup, ap->allPages_key_pageid);
assert(existed);
int availableExisted = availablePages_remove(ap, pageid);
if(availableExisted) {
availablePages_add(ap, pageid, freespace);
}
int xid;
size_t oldfreespace;
int ownerExisted = pageOwners_lookup_by_pageid(ap, pageid, &xid, &oldfreespace);
if(ownerExisted) {
pageOwners_add(ap, xid, freespace, pageid);
}
assert(!(ownerExisted && availableExisted));
}
static int xidAllocedDealloced_helper_lookup_by_xid(struct rbtree *t, int xid, pageid_t **pages, size_t*count) {
xidAllocedDealloced_xid_pageid query = {xid, -1};
const xidAllocedDealloced_xid_pageid *tup = rblookup(RB_LUGTEQ, &query, t);
int ret = 0;
*pages = 0;
*count = 0;
while(tup && tup->xid == xid) {
ret = 1;
// add pageid to ret value
(*count)++;
*pages = realloc(*pages, *count * sizeof(*pages[0]));
// printf("pages %x count %x *pages %x len %lld \n", pages, count, *pages, *count * sizeof(*pages[0]));
fflush(stdout);
(*pages)[(*count) - 1] = tup->pageid;
tup = rblookup(RB_LUGREAT, tup, t);
}
return ret;
}
static int xidAllocedDealloced_helper_lookup_by_pageid(struct rbtree *t, pageid_t pageid, int ** xids, size_t * count) {
xidAllocedDealloced_xid_pageid query = {-1, pageid};
const xidAllocedDealloced_xid_pageid *tup = rblookup(RB_LUGTEQ, &query, t);
int ret = 0;
*xids = 0;
*count = 0;
while (tup && tup->pageid == pageid) {
ret = 1;
// add xid to ret value.
(*count)++;
*xids = realloc(*xids, *count * sizeof(*xids[0]));
(*xids)[(*count) - 1] = tup->xid;
tup = rblookup(RB_LUGREAT, tup, t);
}
return ret;
}
static int xidAlloced_lookup_by_pageid(stasis_allocation_policy_t *ap, pageid_t pageid, int **xids, size_t * count);
static int xidDealloced_lookup_by_pageid(stasis_allocation_policy_t *ap, pageid_t pageid, int **xids, size_t * count);
static int update_views_for_page(stasis_allocation_policy_t *ap, pageid_t pageid) {
size_t xidAllocCount;
size_t xidDeallocCount;
int ret = 0;
int * allocXids;
int * deallocXids;
size_t freespace;
int inAllPages = allPages_lookup_by_pageid(ap, pageid, &freespace);
assert(inAllPages);
int inXidAlloced = xidAlloced_lookup_by_pageid(ap, pageid, &allocXids, &xidAllocCount);
int inXidDealloced = xidDealloced_lookup_by_pageid(ap, pageid, &deallocXids, &xidDeallocCount);
if(! inXidAlloced) { xidAllocCount = 0; allocXids = 0;}
if(! inXidDealloced) { xidDeallocCount = 0; deallocXids = 0; }
if(xidAllocCount == 0 && xidDeallocCount == 0) {
pageOwners_remove(ap, pageid);
availablePages_add(ap, pageid, freespace);
} else if ((xidAllocCount == 1 && xidDeallocCount == 0)
|| (xidAllocCount == 1 && xidDeallocCount == 1 && ap->reuseWithinXact && (allocXids[0] == deallocXids[0]))) {
pageOwners_add(ap, allocXids[0], freespace, pageid);
availablePages_remove(ap, pageid);
} else {
pageOwners_remove(ap, pageid);
availablePages_remove(ap, pageid);
}
if(allocXids) { free(allocXids); }
if(deallocXids) { free(deallocXids); }
return ret;
}
static int xidAllocedDealloced_helper_add(stasis_allocation_policy_t *ap, struct rbtree *first, struct rbtree* second, int xid, pageid_t pageid) {
xidAllocedDealloced_xid_pageid * tup = malloc(sizeof(*tup));
tup->xid = xid;
tup->pageid = pageid;
int existed = void_double_add(tup, first, second);
if(!existed) {
update_views_for_page(ap, pageid);
}
return existed;
}
static int xidAllocedDealloced_helper_remove(stasis_allocation_policy_t *ap, struct rbtree *first, struct rbtree*second, int xid, pageid_t pageid) {
xidAllocedDealloced_xid_pageid query = { xid, pageid };
int existed = void_double_remove(&query, first, second);
if(existed) {
update_views_for_page(ap, pageid);
}
return existed;
}
static int xidAlloced_lookup_by_xid(stasis_allocation_policy_t *ap, int xid, pageid_t ** pages, size_t * count) {
return xidAllocedDealloced_helper_lookup_by_xid(ap->xidAlloced_key_xid_pageid, xid, pages, count);
}
static int xidAlloced_lookup_by_pageid(stasis_allocation_policy_t *ap, pageid_t pageid, int **xids, size_t * count) {
return xidAllocedDealloced_helper_lookup_by_pageid(ap->xidAlloced_key_pageid_xid, pageid, xids, count);
}
static int xidAlloced_add(stasis_allocation_policy_t * ap, int xid, pageid_t pageid) {
return xidAllocedDealloced_helper_add(ap, ap->xidAlloced_key_pageid_xid, ap->xidAlloced_key_xid_pageid, xid, pageid);
}
static int xidAlloced_remove(stasis_allocation_policy_t * ap, int xid, pageid_t pageid) {
return xidAllocedDealloced_helper_remove(ap, ap->xidAlloced_key_pageid_xid, ap->xidAlloced_key_xid_pageid, xid, pageid);
}
static int xidDealloced_lookup_by_xid(stasis_allocation_policy_t *ap, int xid, pageid_t ** pages, size_t * count) {
return xidAllocedDealloced_helper_lookup_by_xid(ap->xidDealloced_key_xid_pageid, xid, pages, count);
}
static int xidDealloced_lookup_by_pageid(stasis_allocation_policy_t *ap, pageid_t pageid, int **xids, size_t * count) {
return xidAllocedDealloced_helper_lookup_by_pageid(ap->xidDealloced_key_pageid_xid, pageid, xids, count);
}
static int xidDealloced_add(stasis_allocation_policy_t * ap, int xid, pageid_t pageid) {
return xidAllocedDealloced_helper_add(ap, ap->xidDealloced_key_pageid_xid, ap->xidDealloced_key_xid_pageid, xid, pageid);
}
static int xidDealloced_remove(stasis_allocation_policy_t * ap, int xid, pageid_t pageid) {
return xidAllocedDealloced_helper_remove(ap, ap->xidDealloced_key_pageid_xid, ap->xidDealloced_key_xid_pageid, xid, pageid);
}
static int availablePages_cmp_pageid(const void *ap, const void *bp, const void* ign) {
const availablePages_pageid_freespace *a = ap, *b = bp;
return a->pageid < b->pageid ? -1 : a->pageid > b->pageid ? 1 : 0;
}
static int availablePages_cmp_freespace_pageid(const void *ap, const void *bp, const void* ign) {
const availablePages_pageid_freespace *a = ap, *b = bp;
return a->freespace < b->freespace ? -1 : a->freespace > b->freespace ? 1 : a->pageid < b->pageid ? -1 : a->pageid > b->pageid ? 1 : 0;
}
int availablePages_lookup_by_freespace(stasis_allocation_policy_t *ap, size_t freespace, pageid_t *pageid) {
const availablePages_pageid_freespace query = { 0, freespace };
const availablePages_pageid_freespace *tup = rblookup(RB_LUGTEQ, &query, ap->availablePages_key_freespace_pageid);
if(tup && tup->freespace >= freespace ) {
*pageid = tup->pageid;
return 1; return 1;
} else { } else {
return 0; return 0;
} }
} }
static int cmpFreespace(const void * ap, const void * bp, const void * param) { static int pageOwners_cmp_pageid(const void *ap, const void *bp, const void* ign) {
const availablePage * a = (const availablePage *) ap; const pageOwners_xid_freespace_pageid *a = ap, *b = bp;
const availablePage * b = (const availablePage *) bp; return a->pageid < b->pageid ? -1 : a->pageid > b->pageid ? 1 : 0;
if(a->freespace < b->freespace) {
return -1;
} else if (a->freespace > b->freespace) {
return 1;
} else {
return cmpPageid(ap,bp,param);
}
} }
static int pageOwners_cmp_xid_freespace_pageid(const void *ap, const void *bp, const void* ign) {
inline static availablePage* getAvailablePage(stasis_allocation_policy_t * ap, pageid_t pageid) { const pageOwners_xid_freespace_pageid *a = ap, *b = bp;
return (availablePage*) LH_ENTRY(find)(ap->allPages, &pageid, sizeof(pageid)); return a->xid < b->xid ? -1 : a->xid > b->xid ? 1 : a->freespace < b->freespace ? -1 : a->freespace > b->freespace ? 1 : a->pageid < b->pageid ? -1 : a->pageid > b->pageid ? 1 : 0;
} }
static int allPages_cmp_pageid(const void *ap, const void *bp, const void* ign) {
inline static void insert_xidAlloced(stasis_allocation_policy_t * ap, int xid, availablePage * p) { const allPages_pageid_freespace *a = ap, *b = bp;
return a->pageid < b->pageid ? -1 : a->pageid > b->pageid ? 1 : 0;
struct RB_ENTRY(tree) * pages = LH_ENTRY(find)(ap->xidAlloced, &xid, sizeof(xid));
if(!pages) {
pages = RB_ENTRY(init)(cmpFreespace, 0);
LH_ENTRY(insert)(ap->xidAlloced, &xid, sizeof(xid), pages);
}
const availablePage * check = RB_ENTRY(search)(p, pages);
assert(check == p);
} }
static int xidAllocedDealloced_cmp_pageid_xid(const void *ap, const void *bp, const void* ign) {
inline static void remove_xidAlloced(stasis_allocation_policy_t * ap, int xid, availablePage * p) { const xidAllocedDealloced_xid_pageid *a = ap, *b = bp;
assert(p->lockCount); return a->pageid < b->pageid ? -1 : a->pageid > b->pageid ? 1 : a->xid < b->xid ? -1 : a->xid > b->xid ? 1 : 0;
struct RB_ENTRY(tree) * pages = LH_ENTRY(find)(ap->xidAlloced, &xid, sizeof(xid));
assert(pages);
const availablePage * check = RB_ENTRY(delete)(p, pages);
assert(check == p); // sometimes fails
} }
static int xidAllocedDealloced_cmp_xid_pageid(const void *ap, const void *bp, const void* ign) {
inline static void insert_xidDealloced(stasis_allocation_policy_t * ap, int xid, availablePage * p) { const xidAllocedDealloced_xid_pageid *a = ap, *b = bp;
return a->xid < b->xid ? -1 : a->xid > b->xid ? 1 : a->pageid < b->pageid ? -1 : a->pageid > b->pageid ? 1 : 0;
struct RB_ENTRY(tree) * pages = LH_ENTRY(find)(ap->xidDealloced, &xid, sizeof(xid));
if(!pages) {
pages = RB_ENTRY(init)(cmpPageid, 0);
LH_ENTRY(insert)(ap->xidDealloced, &xid, sizeof(xid), pages);
}
assert(pages);
const availablePage * check = RB_ENTRY(search)(p, pages);
// XXX Assert that the page wasn't already there?
assert(check);
}
inline static void remove_xidDealloced(stasis_allocation_policy_t * ap, int xid, availablePage * p) {
struct RB_ENTRY(tree) * pages = LH_ENTRY(find)(ap->xidDealloced, &xid, sizeof(xid));
assert(pages);
const availablePage * check = RB_ENTRY(delete)(p, pages);
assert(check == p);
}
inline static int find_xidDealloced(stasis_allocation_policy_t * ap, int xid, availablePage * p) {
struct RB_ENTRY(tree) * pages = LH_ENTRY(find)(ap->xidDealloced, &xid, sizeof(xid));
if(!pages) { return 0; }
const availablePage * check = RB_ENTRY(find)(p, pages);
if(check) {
return 1;
} else {
return 0;
}
}
inline static void lockAlloced(stasis_allocation_policy_t * ap, int xid, availablePage * p) {
const availablePage * check = RB_ENTRY(delete)(p, ap->availablePages);
assert(check == p);
assert(p->lockCount == 0);
p->lockCount = 1;
int * xidp = malloc(sizeof(int));
*xidp = xid;
LH_ENTRY(insert)(ap->pageOwners, &(p->pageid), sizeof(p->pageid), xidp);
insert_xidAlloced(ap, xid, p);
}
inline static void unlockAlloced(stasis_allocation_policy_t * ap, int xid, availablePage * p) {
remove_xidAlloced(ap, xid, p);
p->lockCount--;
// assert(p->lockCount == 1);
// p->lockCount = 0;
if(!p->lockCount) {
const availablePage * check = RB_ENTRY(search)(p, ap->availablePages);
assert(check == p);
}
int * xidp = LH_ENTRY(remove)(ap->pageOwners, &(p->pageid), sizeof(p->pageid));
//assert(*xidp == xid);
if(xidp) { free(xidp); }
}
inline static void lockDealloced(stasis_allocation_policy_t * ap, int xid, availablePage * p) {
if(p->lockCount == 0) {
// xid should own it
lockAlloced(ap, xid, p);
} else if(p->lockCount == 1) {
int * xidp = LH_ENTRY(find)(ap->pageOwners, &(p->pageid), sizeof(p->pageid));
if(!xidp) {
// The only active transaction that touched this page deallocated from it,
// so just add the page to our dealloced table.
p->lockCount++;
insert_xidDealloced(ap, xid, p);
} else if(*xidp != xid) {
// Remove from the other transaction's "alloced" table.
remove_xidAlloced(ap, *xidp, p);
assert(p->lockCount == 1);
// Place in other transaction's "dealloced" table.
insert_xidDealloced(ap, *xidp, p);
// This page no longer has an owner
LH_ENTRY(remove)(ap->pageOwners, &(p->pageid), sizeof(p->pageid));
free(xidp);
// Add to our "dealloced" table, increment lockCount.
p->lockCount++;
insert_xidDealloced(ap, xid, p);
}
} else {
// not owned by anyone... is it already in this xid's Dealloced table?
if(!find_xidDealloced(ap, xid, p)) {
p->lockCount++;
insert_xidDealloced(ap, xid, p);
}
}
}
inline static void unlockDealloced(stasis_allocation_policy_t * ap, int xid, availablePage * p) {
assert(p->lockCount > 0);
p->lockCount--;
remove_xidDealloced(ap, xid, p);
if(!p->lockCount) {
// put it back into available pages.
LH_ENTRY(remove)(ap->pageOwners, &(p->pageid), sizeof(p->pageid)); // XXX new feb-29
const availablePage * check = RB_ENTRY(search)(p, ap->availablePages);
assert(check == p);
}
} }
stasis_allocation_policy_t * stasis_allocation_policy_init() { stasis_allocation_policy_t * stasis_allocation_policy_init() {
stasis_allocation_policy_t * ap = malloc(sizeof(stasis_allocation_policy_t)); stasis_allocation_policy_t * ap = malloc(sizeof(*ap));
ap->availablePages_key_pageid = rbinit(availablePages_cmp_pageid, 0);
ap->xidAlloced = LH_ENTRY(create)(10); ap->availablePages_key_freespace_pageid = rbinit(availablePages_cmp_freespace_pageid, 0);
ap->xidDealloced = LH_ENTRY(create)(10); ap->pageOwners_key_pageid = rbinit(pageOwners_cmp_pageid, 0);
ap->availablePages = RB_ENTRY(init)(cmpFreespace, 0); ap->pageOwners_key_xid_freespace_pageid = rbinit(pageOwners_cmp_xid_freespace_pageid, 0);
ap->pageOwners = LH_ENTRY(create)(10); ap->allPages_key_pageid = rbinit(allPages_cmp_pageid, 0);
ap->allPages = LH_ENTRY(create)(10); ap->xidAlloced_key_pageid_xid = rbinit(xidAllocedDealloced_cmp_pageid_xid, 0);
ap->xidAlloced_key_xid_pageid = rbinit(xidAllocedDealloced_cmp_xid_pageid, 0);
ap->xidDealloced_key_pageid_xid = rbinit(xidAllocedDealloced_cmp_pageid_xid, 0);
ap->xidDealloced_key_xid_pageid = rbinit(xidAllocedDealloced_cmp_xid_pageid, 0);
ap->reuseWithinXact = 0;
return ap; return ap;
} }
void stasis_allocation_policy_deinit(stasis_allocation_policy_t * ap) { void stasis_allocation_policy_deinit(stasis_allocation_policy_t * ap) {
allPages_removeAll(ap); // frees entries in availablePages, asserts that all pages are available.
const availablePage * next; rbdestroy(ap->availablePages_key_pageid);
while(( next = RB_ENTRY(min)(ap->availablePages) )) { rbdestroy(ap->availablePages_key_freespace_pageid);
RB_ENTRY(delete)(next, ap->availablePages); rbdestroy(ap->pageOwners_key_pageid);
free((void*)next); rbdestroy(ap->pageOwners_key_xid_freespace_pageid);
} rbdestroy(ap->allPages_key_pageid);
LH_ENTRY(destroy)(ap->xidAlloced); rbdestroy(ap->xidAlloced_key_pageid_xid);
LH_ENTRY(destroy)(ap->xidDealloced); rbdestroy(ap->xidAlloced_key_xid_pageid);
RB_ENTRY(destroy)(ap->availablePages); rbdestroy(ap->xidDealloced_key_pageid_xid);
LH_ENTRY(destroy)(ap->pageOwners); rbdestroy(ap->xidDealloced_key_xid_pageid);
LH_ENTRY(destroy)(ap->allPages);
free(ap); free(ap);
} }
void stasis_allocation_policy_register_new_page(stasis_allocation_policy_t * ap, pageid_t pageid, size_t freespace) {
void stasis_allocation_policy_register_new_pages(stasis_allocation_policy_t * ap, availablePage** newPages) { int existed = allPages_add(ap,pageid,freespace);
assert(!existed);
for(int i = 0; newPages[i] != 0; i++) {
const availablePage * ret = RB_ENTRY(search)(newPages[i], ap->availablePages);
assert(ret == newPages[i]);
LH_ENTRY(insert)(ap->allPages, &(newPages[i]->pageid), sizeof(newPages[i]->pageid), newPages[i]);
}
} }
pageid_t stasis_allocation_policy_pick_suitable_page(stasis_allocation_policy_t * ap, int xid, size_t freespace) {
/// XXX need updateAlloced, updateFree, which remove a page, change // does the xid have a suitable page?
/// its freespace / lockCount, and then re-insert them into the tree. pageid_t pageid;
int found = pageOwners_lookup_by_xid_freespace(ap, xid, freespace, &pageid);
availablePage * stasis_allocation_policy_pick_suitable_page(stasis_allocation_policy_t * ap, int xid, int freespace) { if(found) { return pageid; }
// For the best fit amongst the pages in availablePages, call: // pick one from global pool.
// found = availablePages_lookup_by_freespace(ap, freespace, &pageid);
// rblookup(RB_LUGREAT, key, availablePages) if(found) {
// return pageid;
// For the page with the most freespace, call:
//
// rbmax(availablePages);
//
availablePage tmp = { .freespace = freespace, .pageid = 0, .lockCount = 0 };
struct RB_ENTRY(tree) * locks;
// If we haven't heard of this transaction yet, then create an entry
// for it.
if(0 == (locks = LH_ENTRY(find)(ap->xidAlloced, &xid, sizeof(xid)))) {
// Since this is cmpPageid, we can change the amount of freespace
// without modifying the tree.
locks = RB_ENTRY(init)(cmpFreespace, 0);
LH_ENTRY(insert)(ap->xidAlloced, &xid, sizeof(xid), locks);
}
const availablePage *ret;
// Does this transaction already have an appropriate page?
if(!(ret = RB_ENTRY(lookup)(RB_LUGREAT, &tmp, locks))) {
// No; get a page from the availablePages.
ret = RB_ENTRY(lookup)(RB_LUGREAT, &tmp, ap->availablePages);
/*if(ret) {
assert(ret->lockCount == 0);
lockAlloced(ap, xid, (availablePage*) ret);
} */
}
// Done. (If ret is null, then it's the caller's problem.)
return (availablePage*) ret;
}
int stasis_allocation_policy_can_xid_alloc_from_page(stasis_allocation_policy_t *ap, int xid, pageid_t page) {
availablePage * p = getAvailablePage(ap, page);
const availablePage * check1 = RB_ENTRY(find)(p, ap->availablePages);
int * xidp = LH_ENTRY(find)(ap->pageOwners, &(page), sizeof(page));
if(!(xidp || check1)) {
// the page is not available, and not owned.
return 0; // can't safely alloc from page
}
if(check1) {
// the page is available and unlocked
return 1; // can safely alloc from page
} else { } else {
// someone owns the page. Is it this xid? return INVALID_PAGE;
return (*xidp == xid);
} }
} }
void stasis_allocation_policy_transaction_completed(stasis_allocation_policy_t * ap, int xid) {
pageid_t *allocPages;
pageid_t *deallocPages;
size_t allocCount;
size_t deallocCount;
int alloced = xidAlloced_lookup_by_xid(ap, xid, &allocPages, &allocCount);
int dealloced = xidDealloced_lookup_by_xid(ap, xid, &deallocPages, &deallocCount);
void stasis_allocation_policy_alloced_from_page(stasis_allocation_policy_t *ap, int xid, pageid_t page) { if(alloced) {
availablePage * p = getAvailablePage(ap, page); for(size_t i = 0; i < allocCount; i++) {
const availablePage * check1 = RB_ENTRY(find)(p, ap->availablePages); xidAlloced_remove(ap, xid, allocPages[i]);
int * xidp = LH_ENTRY(find)(ap->pageOwners, &(page), sizeof(page)); }
if(!(xidp || check1)) { free(allocPages);
// the page is not available, and is not owned. }
// this can happen if more than one transaction deallocs from the same page if(dealloced) {
assert(p->lockCount); for(size_t i = 0; i < deallocCount; i++) {
printf("Two transactions concurrently dealloced from page; now a " xidDealloced_remove(ap, xid, deallocPages[i]);
"transaction has alloced from it. This leads to unrecoverable " }
"schedules. Refusing to continue."); free(deallocPages);
fflush(stdout); }
}
void stasis_allocation_policy_update_freespace(stasis_allocation_policy_t *ap, pageid_t pageid, size_t freespace) {
allPages_set_freespace(ap, pageid, freespace);
}
void stasis_allocation_policy_dealloced_from_page(stasis_allocation_policy_t *ap, int xid, pageid_t pageid) {
xidDealloced_add(ap, xid, pageid);
}
void stasis_allocation_policy_alloced_from_page(stasis_allocation_policy_t * ap, int xid, pageid_t pageid) {
if(!stasis_allocation_policy_can_xid_alloc_from_page(ap, xid, pageid)) {
fprintf(stderr, "A transaction allocated space from a page that contains records deallocated by another active transaction. "
"This leads to unrecoverable schedules. Refusing to continue.");
fflush(stderr);
abort(); abort();
} }
if(check1) { xidAlloced_add(ap, xid, pageid);
assert(p->lockCount == 0); }
lockAlloced(ap, xid, (availablePage*)p); int stasis_allocation_policy_can_xid_alloc_from_page(stasis_allocation_policy_t * ap, int xid, pageid_t pageid) {
} else { int *xids;
// new: this xact is allocing on a page that an active xact alloced on. size_t count;
// perhaps this is safe, but it's spooky. int ret = 1;
assert(*xidp == xid); int existed = xidDealloced_lookup_by_pageid(ap, pageid, &xids, &count);
if(!existed) { count = 0; xids = 0;}
for(size_t i = 0; i < count ; i++) {
if(xids[i] != xid) { ret = 0; }
} }
} if(xids) { free(xids); }
return ret;
void stasis_allocation_policy_lock_page(stasis_allocation_policy_t *ap, int xid, pageid_t page) {
availablePage * p = getAvailablePage(ap, page);
lockDealloced(ap, xid, p);
}
void stasis_allocation_policy_transaction_completed(stasis_allocation_policy_t * ap, int xid) {
struct RB_ENTRY(tree) * locks = LH_ENTRY(find)(ap->xidAlloced, &xid, sizeof(xid));
if(locks) {
availablePage * next;
while(( next = (void*)RB_ENTRY(min)(locks) )) {
unlockAlloced(ap, xid, (next)); // This is really inefficient. (We're wasting hashtable lookups. Also, an iterator would be faster.)
}
LH_ENTRY(remove)(ap->xidAlloced, &xid, sizeof(xid));
RB_ENTRY(destroy)(locks);
}
locks = LH_ENTRY(find)(ap->xidDealloced, &xid, sizeof(xid));
if(locks) {
availablePage * next;
while(( next = (void*)RB_ENTRY(min)(locks) )) {
unlockDealloced(ap, xid, (availablePage*)next); // This is really inefficient. (We're wasting hashtable lookups. Also, an iterator would be faster.)
}
LH_ENTRY(remove)(ap->xidDealloced, &xid, sizeof(xid));
RB_ENTRY(destroy)(locks);
}
}
void stasis_allocation_policy_update_freespace_unlocked_page(stasis_allocation_policy_t * ap, availablePage * key, int newFree) {
availablePage * p = (availablePage*) RB_ENTRY(delete)(key, ap->availablePages);
assert(key == p);
p->freespace = newFree;
const availablePage * ret = RB_ENTRY(search)(p, ap->availablePages);
assert(ret == p);
}
void stasis_allocation_policy_update_freespace_locked_page(stasis_allocation_policy_t * ap, int xid, availablePage * key, int newFree) {
struct RB_ENTRY(tree) * locks = LH_ENTRY(find)(ap->xidAlloced, &xid, sizeof(xid));
assert(key);
availablePage * p = (availablePage*) RB_ENTRY(delete)(key, locks);
assert(p); // sometimes fails
p->freespace = newFree;
key->freespace = newFree;
const availablePage * ret = RB_ENTRY(search)(p, locks);
assert(ret == p);
assert(p->lockCount == 1);
} }

View file

@ -222,26 +222,18 @@ static void stasis_alloc_register_old_regions() {
do { do {
DEBUG("boundary tag %lld type %d\n", boundary, t.allocation_manager); DEBUG("boundary tag %lld type %d\n", boundary, t.allocation_manager);
if(t.allocation_manager == STORAGE_MANAGER_TALLOC) { if(t.allocation_manager == STORAGE_MANAGER_TALLOC) {
availablePage ** newPages = malloc(sizeof(availablePage*)*(t.size+1)); for(pageid_t i = 0; i < t.size; i++) {
for(pageid_t i = 0; i < t.size; i++) { Page * p = loadPage(-1, boundary + i);
Page * p = loadPage(-1, boundary + i); readlock(p->rwlatch,0);
readlock(p->rwlatch,0); if(p->pageType == SLOTTED_PAGE) {
if(p->pageType == SLOTTED_PAGE) { stasis_allocation_policy_register_new_page(allocPolicy, p->id, stasis_record_freespace(-1, p));
availablePage * next = malloc(sizeof(availablePage)); DEBUG("registered page %lld\n", boundary+i);
next->pageid = boundary+i; } else {
next->freespace = stasis_record_freespace(-1, p); abort();
next->lockCount = 0; }
newPages[i] = next; unlock(p->rwlatch);
DEBUG("registered page %lld\n", boundary+i); releasePage(p);
} else { }
abort();
}
unlock(p->rwlatch);
releasePage(p);
}
newPages[t.size]=0;
stasis_allocation_policy_register_new_pages(allocPolicy, newPages);
free(newPages);
} }
} while(TregionNextBoundaryTag(-1, &boundary, &t, 0)); //STORAGE_MANAGER_TALLOC)) { } while(TregionNextBoundaryTag(-1, &boundary, &t, 0)); //STORAGE_MANAGER_TALLOC)) {
} }
@ -253,11 +245,7 @@ static void stasis_alloc_reserve_new_region(int xid) {
pageid_t firstPage = TregionAlloc(xid, TALLOC_REGION_SIZE, STORAGE_MANAGER_TALLOC); pageid_t firstPage = TregionAlloc(xid, TALLOC_REGION_SIZE, STORAGE_MANAGER_TALLOC);
int initialFreespace = -1; int initialFreespace = -1;
availablePage ** newPages = malloc(sizeof(availablePage*)*(TALLOC_REGION_SIZE+1));
for(pageid_t i = 0; i < TALLOC_REGION_SIZE; i++) { for(pageid_t i = 0; i < TALLOC_REGION_SIZE; i++) {
availablePage * next = malloc(sizeof(availablePage)); // * TALLOC_REGION_SIZE);
TinitializeSlottedPage(xid, firstPage + i); TinitializeSlottedPage(xid, firstPage + i);
if(initialFreespace == -1) { if(initialFreespace == -1) {
Page * p = loadPage(xid, firstPage); Page * p = loadPage(xid, firstPage);
@ -266,17 +254,10 @@ static void stasis_alloc_reserve_new_region(int xid) {
unlock(p->rwlatch); unlock(p->rwlatch);
releasePage(p); releasePage(p);
} }
next->pageid = firstPage + i; stasis_allocation_policy_register_new_page(allocPolicy, firstPage + i, initialFreespace);
next->freespace = initialFreespace;
next->lockCount = 0;
newPages[i] = next;
} }
newPages[TALLOC_REGION_SIZE]= 0;
stasis_allocation_policy_register_new_pages(allocPolicy, newPages);
free(newPages); // Don't free the structs it points to; they are in use by the allocation policy.
TendNestedTopAction(xid, nta); TendNestedTopAction(xid, nta);
} }
compensated_function recordid Talloc(int xid, unsigned long size) { compensated_function recordid Talloc(int xid, unsigned long size) {
@ -293,16 +274,16 @@ compensated_function recordid Talloc(int xid, unsigned long size) {
begin_action_ret(pthread_mutex_unlock, &talloc_mutex, NULLRID) { begin_action_ret(pthread_mutex_unlock, &talloc_mutex, NULLRID) {
pthread_mutex_lock(&talloc_mutex); pthread_mutex_lock(&talloc_mutex);
availablePage * ap = pageid_t pageid =
stasis_allocation_policy_pick_suitable_page(allocPolicy, xid, stasis_allocation_policy_pick_suitable_page(allocPolicy, xid,
stasis_record_type_to_size(type)); stasis_record_type_to_size(type));
if(!ap) { if(pageid == INVALID_PAGE) {
stasis_alloc_reserve_new_region(xid); stasis_alloc_reserve_new_region(xid);
ap = stasis_allocation_policy_pick_suitable_page(allocPolicy, xid, pageid = stasis_allocation_policy_pick_suitable_page(allocPolicy, xid,
stasis_record_type_to_size(type)); stasis_record_type_to_size(type));
} }
lastFreepage = ap->pageid; lastFreepage = pageid;
Page * p = loadPage(xid, lastFreepage); Page * p = loadPage(xid, lastFreepage);
@ -316,25 +297,19 @@ compensated_function recordid Talloc(int xid, unsigned long size) {
} }
unlock(p->rwlatch); unlock(p->rwlatch);
if(!ap->lockCount) { stasis_allocation_policy_update_freespace(allocPolicy, pageid, newFreespace);
stasis_allocation_policy_update_freespace_unlocked_page(allocPolicy, ap,
newFreespace);
} else {
stasis_allocation_policy_update_freespace_locked_page(allocPolicy, xid, ap,
newFreespace);
}
releasePage(p); releasePage(p);
ap = stasis_allocation_policy_pick_suitable_page(allocPolicy, xid, pageid = stasis_allocation_policy_pick_suitable_page(allocPolicy, xid,
stasis_record_type_to_size(type)); stasis_record_type_to_size(type));
if(!ap) { if(pageid == INVALID_PAGE) {
stasis_alloc_reserve_new_region(xid); stasis_alloc_reserve_new_region(xid);
ap = stasis_allocation_policy_pick_suitable_page(allocPolicy, xid, pageid = stasis_allocation_policy_pick_suitable_page(allocPolicy, xid,
stasis_record_type_to_size(type)); stasis_record_type_to_size(type));
} }
lastFreepage = ap->pageid; lastFreepage = pageid;
p = loadPage(xid, lastFreepage); p = loadPage(xid, lastFreepage);
writelock(p->rwlatch, 0); writelock(p->rwlatch, 0);
@ -346,8 +321,8 @@ compensated_function recordid Talloc(int xid, unsigned long size) {
stasis_record_alloc_done(xid, p, rid); stasis_record_alloc_done(xid, p, rid);
int newFreespace = stasis_record_freespace(xid, p); int newFreespace = stasis_record_freespace(xid, p);
stasis_allocation_policy_alloced_from_page(allocPolicy, xid, ap->pageid); stasis_allocation_policy_alloced_from_page(allocPolicy, xid, pageid);
stasis_allocation_policy_update_freespace_locked_page(allocPolicy, xid, ap, newFreespace); stasis_allocation_policy_update_freespace(allocPolicy, pageid, newFreespace);
unlock(p->rwlatch); unlock(p->rwlatch);
alloc_arg a = { rid.slot, type }; alloc_arg a = { rid.slot, type };
@ -431,7 +406,7 @@ compensated_function void Tdealloc(int xid, recordid rid) {
readlock(p->rwlatch,0); readlock(p->rwlatch,0);
recordid newrid = stasis_record_dereference(xid, p, rid); recordid newrid = stasis_record_dereference(xid, p, rid);
stasis_allocation_policy_lock_page(allocPolicy, xid, newrid.page); stasis_allocation_policy_dealloced_from_page(allocPolicy, xid, newrid.page);
int64_t size = stasis_record_length_read(xid,p,rid); int64_t size = stasis_record_length_read(xid,p,rid);
int64_t type = stasis_record_type_read(xid,p,rid); int64_t type = stasis_record_type_read(xid,p,rid);

View file

@ -3,23 +3,16 @@
#include <stasis/common.h> #include <stasis/common.h>
struct allocationPolicy; struct stasis_allocation_policy_t;
typedef struct allocationPolicy stasis_allocation_policy_t; typedef struct stasis_allocation_policy_t stasis_allocation_policy_t;
typedef struct availablePage {
int freespace;
pageid_t pageid;
int lockCount; // Number of active transactions that have alloced or dealloced from this page.
} availablePage;
stasis_allocation_policy_t * stasis_allocation_policy_init(); stasis_allocation_policy_t * stasis_allocation_policy_init();
void stasis_allocation_policy_deinit(stasis_allocation_policy_t * ap); void stasis_allocation_policy_deinit(stasis_allocation_policy_t * ap);
void stasis_allocation_policy_register_new_pages(stasis_allocation_policy_t * ap, availablePage** newPages); void stasis_allocation_policy_register_new_page(stasis_allocation_policy_t * ap, pageid_t page, size_t freespace); //availablePage** newPages);
availablePage * stasis_allocation_policy_pick_suitable_page(stasis_allocation_policy_t * ap, int xid, int freespace); pageid_t stasis_allocation_policy_pick_suitable_page(stasis_allocation_policy_t * ap, int xid, size_t freespace);
void stasis_allocation_policy_transaction_completed(stasis_allocation_policy_t * ap, int xid); void stasis_allocation_policy_transaction_completed(stasis_allocation_policy_t * ap, int xid);
void stasis_allocation_policy_update_freespace_unlocked_page(stasis_allocation_policy_t * ap, availablePage * key, int newFree); void stasis_allocation_policy_update_freespace(stasis_allocation_policy_t * ap, pageid_t pageid, size_t freespace);
void stasis_allocation_policy_update_freespace_locked_page(stasis_allocation_policy_t * ap, int xid, availablePage * key, int newFree); void stasis_allocation_policy_dealloced_from_page(stasis_allocation_policy_t * ap, int xid, pageid_t page);
void stasis_allocation_policy_lock_page(stasis_allocation_policy_t * ap, int xid, pageid_t page);
void stasis_allocation_policy_alloced_from_page(stasis_allocation_policy_t * ap, int xid, pageid_t page); void stasis_allocation_policy_alloced_from_page(stasis_allocation_policy_t * ap, int xid, pageid_t page);
/** /**
Check to see if it is safe to allocate from a particular page. Check to see if it is safe to allocate from a particular page.

View file

@ -44,6 +44,7 @@ terms specified in this license.
#include <stasis/allocationPolicy.h> #include <stasis/allocationPolicy.h>
#include <stasis/common.h> #include <stasis/common.h>
#include <stasis/constants.h>
#include <sys/time.h> #include <sys/time.h>
#include <time.h> #include <time.h>
@ -58,89 +59,61 @@ terms specified in this license.
START_TEST(allocationPolicy_smokeTest) START_TEST(allocationPolicy_smokeTest)
{ {
availablePage ** pages = malloc(5 * sizeof(availablePage*));
for(int i = 0; i < 4; i++) {
pages[i] = malloc(sizeof(availablePage));
}
availablePage p;
p.pageid = 0;
p.freespace = 100;
p.lockCount = 0;
(*pages[0]) = p;
p.pageid = 1;
p.freespace = 100;
(*pages[1]) = p;
p.pageid = 2;
p.freespace = 50;
(*pages[2]) = p;
p.pageid = 3;
p.freespace = 25;
(*pages[3]) = p;
pages[4] = 0;
stasis_allocation_policy_t * ap = stasis_allocation_policy_init(); stasis_allocation_policy_t * ap = stasis_allocation_policy_init();
stasis_allocation_policy_register_new_pages(ap, pages); stasis_allocation_policy_register_new_page(ap, 0, 100);
stasis_allocation_policy_register_new_page(ap, 1, 100);
stasis_allocation_policy_register_new_page(ap, 2, 50);
stasis_allocation_policy_register_new_page(ap, 3, 25);
availablePage * p1 = stasis_allocation_policy_pick_suitable_page(ap, 1, 51); pageid_t pageid1 = stasis_allocation_policy_pick_suitable_page(ap, 1, 51);
stasis_allocation_policy_alloced_from_page(ap, 1, p1->pageid); assert(pageid1 != INVALID_PAGE);
stasis_allocation_policy_alloced_from_page(ap, 1, pageid1);
assert(pageid1 == 0 || pageid1 == 1);
stasis_allocation_policy_update_freespace(ap, pageid1, 0);
assert(p1); pageid_t pageid2 = stasis_allocation_policy_pick_suitable_page(ap, 1, 21);
assert(p1->pageid == 0 || p1->pageid == 1); assert(pageid2 != INVALID_PAGE);
stasis_allocation_policy_update_freespace_locked_page(ap, 1, p1, 0); stasis_allocation_policy_alloced_from_page(ap, 1, pageid2);
assert(pageid2 == 3);
stasis_allocation_policy_update_freespace(ap, pageid2, 0);
availablePage * p2 = stasis_allocation_policy_pick_suitable_page(ap, 1, 21); pageid_t pageid3 = stasis_allocation_policy_pick_suitable_page(ap, 2, 51);
stasis_allocation_policy_alloced_from_page(ap, 1, p2->pageid); stasis_allocation_policy_alloced_from_page(ap, 2, pageid3);
assert(pageid1 != pageid3);
stasis_allocation_policy_update_freespace(ap, pageid3, 0);
assert(p2->pageid == 3); pageid_t pageid4 = stasis_allocation_policy_pick_suitable_page(ap, 2, 51);
stasis_allocation_policy_update_freespace_locked_page(ap, 1, p2, 0); assert(pageid4 == INVALID_PAGE);
availablePage * p3 = stasis_allocation_policy_pick_suitable_page(ap, 2, 51); pageid_t pageid5 = stasis_allocation_policy_pick_suitable_page(ap, 2, 50);
stasis_allocation_policy_alloced_from_page(ap, 2, p3->pageid); stasis_allocation_policy_alloced_from_page(ap, 2, pageid5);
assert(p1->pageid != p3->pageid); assert(pageid5== 2);
stasis_allocation_policy_update_freespace_locked_page(ap, 2, p3, 0); stasis_allocation_policy_update_freespace(ap, pageid5, 0);
availablePage * p4 = stasis_allocation_policy_pick_suitable_page(ap, 2, 51); stasis_allocation_policy_update_freespace(ap, pageid1, 100);
assert(!p4);
availablePage * p5 = stasis_allocation_policy_pick_suitable_page(ap, 2, 50);
stasis_allocation_policy_alloced_from_page(ap, 2, p5->pageid);
assert(p5 && p5->pageid == 2);
stasis_allocation_policy_update_freespace_locked_page(ap, 2, p5, 0);
stasis_allocation_policy_update_freespace_locked_page(ap, 1, p1, 100);
stasis_allocation_policy_transaction_completed(ap, 1); stasis_allocation_policy_transaction_completed(ap, 1);
stasis_allocation_policy_update_freespace_unlocked_page(ap, p2, 25); stasis_allocation_policy_update_freespace(ap, pageid2, 25);
availablePage * p6 = stasis_allocation_policy_pick_suitable_page(ap, 2, 50); pageid_t pageid6 = stasis_allocation_policy_pick_suitable_page(ap, 2, 50);
stasis_allocation_policy_alloced_from_page(ap, 2, p6->pageid); stasis_allocation_policy_alloced_from_page(ap, 2, pageid6);
assert(p6->pageid == 1 || p6->pageid == 0); assert(pageid6 == 1 || pageid6 == 0);
stasis_allocation_policy_update_freespace_locked_page(ap, 2, p6, 0); stasis_allocation_policy_update_freespace(ap, pageid6, 0);
availablePage * p7 = stasis_allocation_policy_pick_suitable_page(ap, 2, 50); pageid_t pageid7 = stasis_allocation_policy_pick_suitable_page(ap, 2, 50);
assert(!p7); assert(pageid7 == INVALID_PAGE);
stasis_allocation_policy_update_freespace_unlocked_page(ap, pages[3], 51); stasis_allocation_policy_update_freespace(ap, 3, 51);
availablePage * p8 =stasis_allocation_policy_pick_suitable_page(ap, 2, 51); pageid_t pageid8 =stasis_allocation_policy_pick_suitable_page(ap, 2, 51);
assert(p8->pageid == 3); assert(pageid8 == 3);
stasis_allocation_policy_alloced_from_page(ap, 2, p8->pageid); stasis_allocation_policy_alloced_from_page(ap, 2, pageid8);
stasis_allocation_policy_update_freespace_locked_page(ap, 2, p8, 0); stasis_allocation_policy_update_freespace(ap, pageid8, 0);
stasis_allocation_policy_transaction_completed(ap, 2); stasis_allocation_policy_transaction_completed(ap, 2);
stasis_allocation_policy_deinit(ap); stasis_allocation_policy_deinit(ap);
free(pages);
} END_TEST } END_TEST
#define AVAILABLE_PAGE_COUNT_A 1000 #define AVAILABLE_PAGE_COUNT_A 1000
@ -155,8 +128,8 @@ static const int MAX_DESIRED_FREESPACE =
static int nextxid = 0; static int nextxid = 0;
int activexidcount = 0; int activexidcount = 0;
static void takeRandomAction(stasis_allocation_policy_t * ap, int * xids, static void takeRandomAction(stasis_allocation_policy_t * ap, int * xids,
availablePage ** pages1, availablePage ** pages2) { pageid_t * pages1, pageid_t * pages2) {
switch(myrandom(6)) { switch(myrandom(5)) {
case 0 : { // find page case 0 : { // find page
int thexid = myrandom(XACT_COUNT); int thexid = myrandom(XACT_COUNT);
if(xids[thexid] == -1) { if(xids[thexid] == -1) {
@ -166,9 +139,9 @@ static void takeRandomAction(stasis_allocation_policy_t * ap, int * xids,
DEBUG("xid begins\n"); DEBUG("xid begins\n");
} }
int thefreespace = myrandom(MAX_DESIRED_FREESPACE); int thefreespace = myrandom(MAX_DESIRED_FREESPACE);
availablePage * p = pageid_t p =
stasis_allocation_policy_pick_suitable_page(ap, xids[thexid], thefreespace); stasis_allocation_policy_pick_suitable_page(ap, xids[thexid], thefreespace);
if(p) { if(p != INVALID_PAGE) {
DEBUG("alloc succeeds\n"); DEBUG("alloc succeeds\n");
// xxx validate returned value... // xxx validate returned value...
} else { } else {
@ -185,7 +158,7 @@ static void takeRandomAction(stasis_allocation_policy_t * ap, int * xids,
DEBUG("complete"); DEBUG("complete");
} break; } break;
case 2 : { // update freespace unlocked case 2 : { // update freespace unlocked
int thespacediff = myrandom(MAX_DESIRED_FREESPACE/2) - (MAX_DESIRED_FREESPACE/2); int thespacediff = myrandom(MAX_DESIRED_FREESPACE) - (MAX_DESIRED_FREESPACE/2);
int thexid; int thexid;
if(!activexidcount) { break; } if(!activexidcount) { break; }
while(xids[thexid = myrandom(XACT_COUNT)] == -1) { } while(xids[thexid = myrandom(XACT_COUNT)] == -1) { }
@ -195,33 +168,32 @@ static void takeRandomAction(stasis_allocation_policy_t * ap, int * xids,
} else { } else {
minfreespace = 0; minfreespace = 0;
} }
availablePage * p = stasis_allocation_policy_pick_suitable_page(ap, xids[thexid], pageid_t p = stasis_allocation_policy_pick_suitable_page(ap, xids[thexid],
minfreespace); minfreespace);
if(p && p->lockCount == 0) { if(p != INVALID_PAGE) {
int thenewfreespace = p->freespace+thespacediff; int thenewfreespace = myrandom(MAX_DESIRED_FREESPACE/2)+thespacediff;
stasis_allocation_policy_update_freespace_unlocked_page(ap, p, thenewfreespace); stasis_allocation_policy_update_freespace(ap, p, thenewfreespace);
// printf("updated freespace unlocked"); // printf("updated freespace unlocked");
} }
} break; } break;
case 3 : { // update freespace locked case 3 : { // dealloc from page
} break; int thexid;
case 4 : { // lock page
} break;
case 5 : { // alloced from page
// xxx this case is nonsense. we pick a random page, alloc from it, and allocation policy correctly sees that we're violating protocol, and cores...
// xxx write better one..
/* int thexid;
if(!activexidcount) { break; } if(!activexidcount) { break; }
while(xids[thexid = myrandom(XACT_COUNT)] == -1) { } while(xids[thexid = myrandom(XACT_COUNT)] == -1) {}
pageid_t thepage=myrandom(AVAILABLE_PAGE_COUNT_A + pages2?AVAILABLE_PAGE_COUNT_B:0); pageid_t p = pages1[myrandom(AVAILABLE_PAGE_COUNT_A)];
stasis_allocation_policy_alloced_from_page(ap,thexid,thepage); */ stasis_allocation_policy_dealloced_from_page(ap, xids[thexid], p);
} break; } break;
case 4 : { // alloced from page
int thexid;
if(!activexidcount) { break; }
while(xids[thexid = myrandom(XACT_COUNT)] == -1) {}
pageid_t p = pages1[myrandom(AVAILABLE_PAGE_COUNT_A)];
if(stasis_allocation_policy_can_xid_alloc_from_page(ap, xids[thexid], p)) {
stasis_allocation_policy_alloced_from_page(ap, xids[thexid], p);
}
} break;
default: abort();
} }
} }
START_TEST(allocationPolicy_randomTest) { START_TEST(allocationPolicy_randomTest) {
@ -232,10 +204,8 @@ START_TEST(allocationPolicy_randomTest) {
printf("\nSeed = %ld\n", seed); printf("\nSeed = %ld\n", seed);
srandom(seed); srandom(seed);
availablePage ** pages1 = pageid_t * pages1 = malloc(AVAILABLE_PAGE_COUNT_A * sizeof(pageid_t));
malloc((1+AVAILABLE_PAGE_COUNT_A) * sizeof(availablePage *)); pageid_t * pages2 = malloc(AVAILABLE_PAGE_COUNT_B * sizeof(pageid_t));
availablePage ** pages2 =
malloc((1+AVAILABLE_PAGE_COUNT_B) * sizeof(availablePage *));
int * xids = malloc(sizeof(int) * XACT_COUNT); int * xids = malloc(sizeof(int) * XACT_COUNT);
@ -243,35 +213,30 @@ START_TEST(allocationPolicy_randomTest) {
xids[i] = -1; xids[i] = -1;
} }
for(int i = 0; i < AVAILABLE_PAGE_COUNT_A; i++) {
pages1[i] = malloc(sizeof(availablePage));
pages1[i]->pageid = i;
pages1[i]->freespace = i * FREE_MUL;
pages1[i]->lockCount = 0;
}
pages1[AVAILABLE_PAGE_COUNT_A] = 0;
for(int i = 0 ; i < AVAILABLE_PAGE_COUNT_B; i++) {
pages2[i] = malloc(sizeof(availablePage));
pages2[i]->pageid = AVAILABLE_PAGE_COUNT_A + i;
pages2[i]->freespace = (AVAILABLE_PAGE_COUNT_A + i) * FREE_MUL;
pages2[i]->lockCount = 0;
}
pages2[AVAILABLE_PAGE_COUNT_B] = 0;
stasis_allocation_policy_t * ap = stasis_allocation_policy_init(); stasis_allocation_policy_t * ap = stasis_allocation_policy_init();
stasis_allocation_policy_register_new_pages(ap, pages1); for(int i = 0; i < AVAILABLE_PAGE_COUNT_A; i++) {
pages1[i] = i;
stasis_allocation_policy_register_new_page(ap, i, i * FREE_MUL);
}
for(int k = 0; k < PHASE_ONE_COUNT; k++) { for(int k = 0; k < PHASE_ONE_COUNT; k++) {
// Don't pass in pages2; ap doesn't know about them yet! // Don't pass in pages2; ap doesn't know about them yet!
takeRandomAction(ap, xids, pages1, 0); takeRandomAction(ap, xids, pages1, 0);
} }
stasis_allocation_policy_register_new_pages(ap, pages2); for(int i = 0 ; i < AVAILABLE_PAGE_COUNT_B; i++) {
pages2[i] = AVAILABLE_PAGE_COUNT_A + i;
stasis_allocation_policy_register_new_page(ap, pages2[i], (AVAILABLE_PAGE_COUNT_A + i) * FREE_MUL);
}
for(int k = 0; k < PHASE_TWO_COUNT; k++) { for(int k = 0; k < PHASE_TWO_COUNT; k++) {
takeRandomAction(ap, xids, pages1, pages2); takeRandomAction(ap, xids, pages1, pages2);
} }
for(int i = 0; i < XACT_COUNT; i++) {
if(xids[i] != INVALID_XID) { stasis_allocation_policy_transaction_completed(ap, xids[i]); }
}
stasis_allocation_policy_deinit(ap); stasis_allocation_policy_deinit(ap);