Reimplemented allocationPolicy.c; updated API + added more cases to the unit tests.
This commit is contained in:
parent
beb14ec917
commit
5b10bcc63b
4 changed files with 527 additions and 509 deletions
|
@ -36,371 +36,456 @@
|
|||
#include <assert.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
|
||||
// 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*.
|
||||
// Views:
|
||||
|
||||
struct allocationPolicy {
|
||||
struct LH_ENTRY(table) * xidAlloced;
|
||||
struct LH_ENTRY(table) * xidDealloced;
|
||||
struct RB_ENTRY(tree) * availablePages;
|
||||
struct LH_ENTRY(table) * pageOwners;
|
||||
struct LH_ENTRY(table) * allPages;
|
||||
typedef struct {
|
||||
pageid_t pageid;
|
||||
size_t freespace;
|
||||
} availablePages_pageid_freespace;
|
||||
typedef struct {
|
||||
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) {
|
||||
const availablePage * a = (const availablePage *)ap;
|
||||
const availablePage * b = (const availablePage *)bp;
|
||||
// View maintenance functions. Called by the table insertion functions.
|
||||
|
||||
if(a->pageid < b->pageid) {
|
||||
return -1;
|
||||
} else if (a->pageid > b->pageid) {
|
||||
// ######## Helpers ###############
|
||||
|
||||
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;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int cmpFreespace(const void * ap, const void * bp, const void * param) {
|
||||
const availablePage * a = (const availablePage *) ap;
|
||||
const availablePage * b = (const availablePage *) bp;
|
||||
|
||||
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_pageid(const void *ap, const void *bp, const void* ign) {
|
||||
const pageOwners_xid_freespace_pageid *a = ap, *b = bp;
|
||||
return a->pageid < b->pageid ? -1 : a->pageid > b->pageid ? 1 : 0;
|
||||
}
|
||||
|
||||
inline static availablePage* getAvailablePage(stasis_allocation_policy_t * ap, pageid_t pageid) {
|
||||
return (availablePage*) LH_ENTRY(find)(ap->allPages, &pageid, sizeof(pageid));
|
||||
static int pageOwners_cmp_xid_freespace_pageid(const void *ap, const void *bp, const void* ign) {
|
||||
const pageOwners_xid_freespace_pageid *a = ap, *b = bp;
|
||||
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;
|
||||
}
|
||||
|
||||
inline static void insert_xidAlloced(stasis_allocation_policy_t * ap, int xid, availablePage * p) {
|
||||
|
||||
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 allPages_cmp_pageid(const void *ap, const void *bp, const void* ign) {
|
||||
const allPages_pageid_freespace *a = ap, *b = bp;
|
||||
return a->pageid < b->pageid ? -1 : a->pageid > b->pageid ? 1 : 0;
|
||||
}
|
||||
|
||||
inline static void remove_xidAlloced(stasis_allocation_policy_t * ap, int xid, availablePage * p) {
|
||||
assert(p->lockCount);
|
||||
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_pageid_xid(const void *ap, const void *bp, const void* ign) {
|
||||
const xidAllocedDealloced_xid_pageid *a = ap, *b = bp;
|
||||
return a->pageid < b->pageid ? -1 : a->pageid > b->pageid ? 1 : a->xid < b->xid ? -1 : a->xid > b->xid ? 1 : 0;
|
||||
}
|
||||
|
||||
inline static void insert_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) {
|
||||
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);
|
||||
}
|
||||
static int xidAllocedDealloced_cmp_xid_pageid(const void *ap, const void *bp, const void* ign) {
|
||||
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;
|
||||
}
|
||||
|
||||
stasis_allocation_policy_t * stasis_allocation_policy_init() {
|
||||
stasis_allocation_policy_t * ap = malloc(sizeof(stasis_allocation_policy_t));
|
||||
|
||||
ap->xidAlloced = LH_ENTRY(create)(10);
|
||||
ap->xidDealloced = LH_ENTRY(create)(10);
|
||||
ap->availablePages = RB_ENTRY(init)(cmpFreespace, 0);
|
||||
ap->pageOwners = LH_ENTRY(create)(10);
|
||||
ap->allPages = LH_ENTRY(create)(10);
|
||||
stasis_allocation_policy_t * ap = malloc(sizeof(*ap));
|
||||
ap->availablePages_key_pageid = rbinit(availablePages_cmp_pageid, 0);
|
||||
ap->availablePages_key_freespace_pageid = rbinit(availablePages_cmp_freespace_pageid, 0);
|
||||
ap->pageOwners_key_pageid = rbinit(pageOwners_cmp_pageid, 0);
|
||||
ap->pageOwners_key_xid_freespace_pageid = rbinit(pageOwners_cmp_xid_freespace_pageid, 0);
|
||||
ap->allPages_key_pageid = rbinit(allPages_cmp_pageid, 0);
|
||||
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;
|
||||
}
|
||||
|
||||
void stasis_allocation_policy_deinit(stasis_allocation_policy_t * ap) {
|
||||
|
||||
const availablePage * next;
|
||||
while(( next = RB_ENTRY(min)(ap->availablePages) )) {
|
||||
RB_ENTRY(delete)(next, ap->availablePages);
|
||||
free((void*)next);
|
||||
}
|
||||
LH_ENTRY(destroy)(ap->xidAlloced);
|
||||
LH_ENTRY(destroy)(ap->xidDealloced);
|
||||
RB_ENTRY(destroy)(ap->availablePages);
|
||||
LH_ENTRY(destroy)(ap->pageOwners);
|
||||
LH_ENTRY(destroy)(ap->allPages);
|
||||
|
||||
allPages_removeAll(ap); // frees entries in availablePages, asserts that all pages are available.
|
||||
rbdestroy(ap->availablePages_key_pageid);
|
||||
rbdestroy(ap->availablePages_key_freespace_pageid);
|
||||
rbdestroy(ap->pageOwners_key_pageid);
|
||||
rbdestroy(ap->pageOwners_key_xid_freespace_pageid);
|
||||
rbdestroy(ap->allPages_key_pageid);
|
||||
rbdestroy(ap->xidAlloced_key_pageid_xid);
|
||||
rbdestroy(ap->xidAlloced_key_xid_pageid);
|
||||
rbdestroy(ap->xidDealloced_key_pageid_xid);
|
||||
rbdestroy(ap->xidDealloced_key_xid_pageid);
|
||||
free(ap);
|
||||
}
|
||||
|
||||
void stasis_allocation_policy_register_new_pages(stasis_allocation_policy_t * ap, availablePage** newPages) {
|
||||
|
||||
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]);
|
||||
|
||||
}
|
||||
|
||||
void stasis_allocation_policy_register_new_page(stasis_allocation_policy_t * ap, pageid_t pageid, size_t freespace) {
|
||||
int existed = allPages_add(ap,pageid,freespace);
|
||||
assert(!existed);
|
||||
}
|
||||
|
||||
/// XXX need updateAlloced, updateFree, which remove a page, change
|
||||
/// its freespace / lockCount, and then re-insert them into the tree.
|
||||
|
||||
availablePage * stasis_allocation_policy_pick_suitable_page(stasis_allocation_policy_t * ap, int xid, int freespace) {
|
||||
// For the best fit amongst the pages in availablePages, call:
|
||||
//
|
||||
// rblookup(RB_LUGREAT, key, availablePages)
|
||||
//
|
||||
// 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
|
||||
pageid_t stasis_allocation_policy_pick_suitable_page(stasis_allocation_policy_t * ap, int xid, size_t freespace) {
|
||||
// does the xid have a suitable page?
|
||||
pageid_t pageid;
|
||||
int found = pageOwners_lookup_by_xid_freespace(ap, xid, freespace, &pageid);
|
||||
if(found) { return pageid; }
|
||||
// pick one from global pool.
|
||||
found = availablePages_lookup_by_freespace(ap, freespace, &pageid);
|
||||
if(found) {
|
||||
return pageid;
|
||||
} else {
|
||||
// someone owns the page. Is it this xid?
|
||||
return (*xidp == xid);
|
||||
return INVALID_PAGE;
|
||||
}
|
||||
}
|
||||
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) {
|
||||
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 is not owned.
|
||||
// this can happen if more than one transaction deallocs from the same page
|
||||
assert(p->lockCount);
|
||||
printf("Two transactions concurrently dealloced from page; now a "
|
||||
"transaction has alloced from it. This leads to unrecoverable "
|
||||
"schedules. Refusing to continue.");
|
||||
fflush(stdout);
|
||||
if(alloced) {
|
||||
for(size_t i = 0; i < allocCount; i++) {
|
||||
xidAlloced_remove(ap, xid, allocPages[i]);
|
||||
}
|
||||
free(allocPages);
|
||||
}
|
||||
if(dealloced) {
|
||||
for(size_t i = 0; i < deallocCount; i++) {
|
||||
xidDealloced_remove(ap, xid, deallocPages[i]);
|
||||
}
|
||||
free(deallocPages);
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
if(check1) {
|
||||
assert(p->lockCount == 0);
|
||||
lockAlloced(ap, xid, (availablePage*)p);
|
||||
} else {
|
||||
// new: this xact is allocing on a page that an active xact alloced on.
|
||||
// perhaps this is safe, but it's spooky.
|
||||
assert(*xidp == xid);
|
||||
}
|
||||
xidAlloced_add(ap, xid, pageid);
|
||||
}
|
||||
|
||||
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.)
|
||||
int stasis_allocation_policy_can_xid_alloc_from_page(stasis_allocation_policy_t * ap, int xid, pageid_t pageid) {
|
||||
int *xids;
|
||||
size_t count;
|
||||
int ret = 1;
|
||||
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; }
|
||||
}
|
||||
|
||||
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);
|
||||
if(xids) { free(xids); }
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -222,16 +222,11 @@ static void stasis_alloc_register_old_regions() {
|
|||
do {
|
||||
DEBUG("boundary tag %lld type %d\n", boundary, t.allocation_manager);
|
||||
if(t.allocation_manager == STORAGE_MANAGER_TALLOC) {
|
||||
availablePage ** newPages = malloc(sizeof(availablePage*)*(t.size+1));
|
||||
for(pageid_t i = 0; i < t.size; i++) {
|
||||
Page * p = loadPage(-1, boundary + i);
|
||||
readlock(p->rwlatch,0);
|
||||
if(p->pageType == SLOTTED_PAGE) {
|
||||
availablePage * next = malloc(sizeof(availablePage));
|
||||
next->pageid = boundary+i;
|
||||
next->freespace = stasis_record_freespace(-1, p);
|
||||
next->lockCount = 0;
|
||||
newPages[i] = next;
|
||||
stasis_allocation_policy_register_new_page(allocPolicy, p->id, stasis_record_freespace(-1, p));
|
||||
DEBUG("registered page %lld\n", boundary+i);
|
||||
} else {
|
||||
abort();
|
||||
|
@ -239,9 +234,6 @@ static void stasis_alloc_register_old_regions() {
|
|||
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)) {
|
||||
}
|
||||
|
@ -253,11 +245,7 @@ static void stasis_alloc_reserve_new_region(int xid) {
|
|||
pageid_t firstPage = TregionAlloc(xid, TALLOC_REGION_SIZE, STORAGE_MANAGER_TALLOC);
|
||||
int initialFreespace = -1;
|
||||
|
||||
availablePage ** newPages = malloc(sizeof(availablePage*)*(TALLOC_REGION_SIZE+1));
|
||||
|
||||
for(pageid_t i = 0; i < TALLOC_REGION_SIZE; i++) {
|
||||
availablePage * next = malloc(sizeof(availablePage)); // * TALLOC_REGION_SIZE);
|
||||
|
||||
TinitializeSlottedPage(xid, firstPage + i);
|
||||
if(initialFreespace == -1) {
|
||||
Page * p = loadPage(xid, firstPage);
|
||||
|
@ -266,17 +254,10 @@ static void stasis_alloc_reserve_new_region(int xid) {
|
|||
unlock(p->rwlatch);
|
||||
releasePage(p);
|
||||
}
|
||||
next->pageid = firstPage + i;
|
||||
next->freespace = initialFreespace;
|
||||
next->lockCount = 0;
|
||||
newPages[i] = next;
|
||||
stasis_allocation_policy_register_new_page(allocPolicy, firstPage + i, initialFreespace);
|
||||
}
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
pthread_mutex_lock(&talloc_mutex);
|
||||
|
||||
availablePage * ap =
|
||||
pageid_t pageid =
|
||||
stasis_allocation_policy_pick_suitable_page(allocPolicy, xid,
|
||||
stasis_record_type_to_size(type));
|
||||
|
||||
if(!ap) {
|
||||
if(pageid == INVALID_PAGE) {
|
||||
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));
|
||||
}
|
||||
lastFreepage = ap->pageid;
|
||||
lastFreepage = pageid;
|
||||
|
||||
Page * p = loadPage(xid, lastFreepage);
|
||||
|
||||
|
@ -316,25 +297,19 @@ compensated_function recordid Talloc(int xid, unsigned long size) {
|
|||
}
|
||||
|
||||
unlock(p->rwlatch);
|
||||
if(!ap->lockCount) {
|
||||
stasis_allocation_policy_update_freespace_unlocked_page(allocPolicy, ap,
|
||||
newFreespace);
|
||||
} else {
|
||||
stasis_allocation_policy_update_freespace_locked_page(allocPolicy, xid, ap,
|
||||
newFreespace);
|
||||
}
|
||||
stasis_allocation_policy_update_freespace(allocPolicy, pageid, newFreespace);
|
||||
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));
|
||||
|
||||
if(!ap) {
|
||||
if(pageid == INVALID_PAGE) {
|
||||
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));
|
||||
}
|
||||
|
||||
lastFreepage = ap->pageid;
|
||||
lastFreepage = pageid;
|
||||
|
||||
p = loadPage(xid, lastFreepage);
|
||||
writelock(p->rwlatch, 0);
|
||||
|
@ -346,8 +321,8 @@ compensated_function recordid Talloc(int xid, unsigned long size) {
|
|||
|
||||
stasis_record_alloc_done(xid, p, rid);
|
||||
int newFreespace = stasis_record_freespace(xid, p);
|
||||
stasis_allocation_policy_alloced_from_page(allocPolicy, xid, ap->pageid);
|
||||
stasis_allocation_policy_update_freespace_locked_page(allocPolicy, xid, ap, newFreespace);
|
||||
stasis_allocation_policy_alloced_from_page(allocPolicy, xid, pageid);
|
||||
stasis_allocation_policy_update_freespace(allocPolicy, pageid, newFreespace);
|
||||
unlock(p->rwlatch);
|
||||
|
||||
alloc_arg a = { rid.slot, type };
|
||||
|
@ -431,7 +406,7 @@ compensated_function void Tdealloc(int xid, recordid rid) {
|
|||
readlock(p->rwlatch,0);
|
||||
|
||||
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 type = stasis_record_type_read(xid,p,rid);
|
||||
|
|
|
@ -3,23 +3,16 @@
|
|||
|
||||
#include <stasis/common.h>
|
||||
|
||||
struct allocationPolicy;
|
||||
typedef struct allocationPolicy 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;
|
||||
struct stasis_allocation_policy_t;
|
||||
typedef struct stasis_allocation_policy_t stasis_allocation_policy_t;
|
||||
|
||||
stasis_allocation_policy_t * stasis_allocation_policy_init();
|
||||
void stasis_allocation_policy_deinit(stasis_allocation_policy_t * ap);
|
||||
void stasis_allocation_policy_register_new_pages(stasis_allocation_policy_t * ap, availablePage** newPages);
|
||||
availablePage * stasis_allocation_policy_pick_suitable_page(stasis_allocation_policy_t * ap, int xid, int freespace);
|
||||
void stasis_allocation_policy_register_new_page(stasis_allocation_policy_t * ap, pageid_t page, size_t freespace); //availablePage** newPages);
|
||||
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_update_freespace_unlocked_page(stasis_allocation_policy_t * ap, availablePage * key, int newFree);
|
||||
void stasis_allocation_policy_update_freespace_locked_page(stasis_allocation_policy_t * ap, int xid, availablePage * key, int newFree);
|
||||
void stasis_allocation_policy_lock_page(stasis_allocation_policy_t * ap, int xid, pageid_t page);
|
||||
void stasis_allocation_policy_update_freespace(stasis_allocation_policy_t * ap, pageid_t pageid, size_t freespace);
|
||||
void stasis_allocation_policy_dealloced_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.
|
||||
|
|
|
@ -44,6 +44,7 @@ terms specified in this license.
|
|||
|
||||
#include <stasis/allocationPolicy.h>
|
||||
#include <stasis/common.h>
|
||||
#include <stasis/constants.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
@ -58,89 +59,61 @@ terms specified in this license.
|
|||
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_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);
|
||||
stasis_allocation_policy_alloced_from_page(ap, 1, p1->pageid);
|
||||
pageid_t pageid1 = stasis_allocation_policy_pick_suitable_page(ap, 1, 51);
|
||||
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);
|
||||
assert(p1->pageid == 0 || p1->pageid == 1);
|
||||
stasis_allocation_policy_update_freespace_locked_page(ap, 1, p1, 0);
|
||||
pageid_t pageid2 = stasis_allocation_policy_pick_suitable_page(ap, 1, 21);
|
||||
assert(pageid2 != INVALID_PAGE);
|
||||
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);
|
||||
stasis_allocation_policy_alloced_from_page(ap, 1, p2->pageid);
|
||||
pageid_t pageid3 = stasis_allocation_policy_pick_suitable_page(ap, 2, 51);
|
||||
stasis_allocation_policy_alloced_from_page(ap, 2, pageid3);
|
||||
assert(pageid1 != pageid3);
|
||||
stasis_allocation_policy_update_freespace(ap, pageid3, 0);
|
||||
|
||||
assert(p2->pageid == 3);
|
||||
stasis_allocation_policy_update_freespace_locked_page(ap, 1, p2, 0);
|
||||
pageid_t pageid4 = stasis_allocation_policy_pick_suitable_page(ap, 2, 51);
|
||||
assert(pageid4 == INVALID_PAGE);
|
||||
|
||||
availablePage * p3 = stasis_allocation_policy_pick_suitable_page(ap, 2, 51);
|
||||
stasis_allocation_policy_alloced_from_page(ap, 2, p3->pageid);
|
||||
assert(p1->pageid != p3->pageid);
|
||||
stasis_allocation_policy_update_freespace_locked_page(ap, 2, p3, 0);
|
||||
pageid_t pageid5 = stasis_allocation_policy_pick_suitable_page(ap, 2, 50);
|
||||
stasis_allocation_policy_alloced_from_page(ap, 2, pageid5);
|
||||
assert(pageid5== 2);
|
||||
stasis_allocation_policy_update_freespace(ap, pageid5, 0);
|
||||
|
||||
availablePage * p4 = stasis_allocation_policy_pick_suitable_page(ap, 2, 51);
|
||||
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_update_freespace(ap, pageid1, 100);
|
||||
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);
|
||||
stasis_allocation_policy_alloced_from_page(ap, 2, p6->pageid);
|
||||
assert(p6->pageid == 1 || p6->pageid == 0);
|
||||
stasis_allocation_policy_update_freespace_locked_page(ap, 2, p6, 0);
|
||||
pageid_t pageid6 = stasis_allocation_policy_pick_suitable_page(ap, 2, 50);
|
||||
stasis_allocation_policy_alloced_from_page(ap, 2, pageid6);
|
||||
assert(pageid6 == 1 || pageid6 == 0);
|
||||
stasis_allocation_policy_update_freespace(ap, pageid6, 0);
|
||||
|
||||
availablePage * p7 = stasis_allocation_policy_pick_suitable_page(ap, 2, 50);
|
||||
assert(!p7);
|
||||
pageid_t pageid7 = stasis_allocation_policy_pick_suitable_page(ap, 2, 50);
|
||||
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);
|
||||
assert(p8->pageid == 3);
|
||||
stasis_allocation_policy_alloced_from_page(ap, 2, p8->pageid);
|
||||
pageid_t pageid8 =stasis_allocation_policy_pick_suitable_page(ap, 2, 51);
|
||||
assert(pageid8 == 3);
|
||||
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_deinit(ap);
|
||||
|
||||
free(pages);
|
||||
|
||||
|
||||
} END_TEST
|
||||
|
||||
#define AVAILABLE_PAGE_COUNT_A 1000
|
||||
|
@ -155,8 +128,8 @@ static const int MAX_DESIRED_FREESPACE =
|
|||
static int nextxid = 0;
|
||||
int activexidcount = 0;
|
||||
static void takeRandomAction(stasis_allocation_policy_t * ap, int * xids,
|
||||
availablePage ** pages1, availablePage ** pages2) {
|
||||
switch(myrandom(6)) {
|
||||
pageid_t * pages1, pageid_t * pages2) {
|
||||
switch(myrandom(5)) {
|
||||
case 0 : { // find page
|
||||
int thexid = myrandom(XACT_COUNT);
|
||||
if(xids[thexid] == -1) {
|
||||
|
@ -166,9 +139,9 @@ static void takeRandomAction(stasis_allocation_policy_t * ap, int * xids,
|
|||
DEBUG("xid begins\n");
|
||||
}
|
||||
int thefreespace = myrandom(MAX_DESIRED_FREESPACE);
|
||||
availablePage * p =
|
||||
pageid_t p =
|
||||
stasis_allocation_policy_pick_suitable_page(ap, xids[thexid], thefreespace);
|
||||
if(p) {
|
||||
if(p != INVALID_PAGE) {
|
||||
DEBUG("alloc succeeds\n");
|
||||
// xxx validate returned value...
|
||||
} else {
|
||||
|
@ -185,7 +158,7 @@ static void takeRandomAction(stasis_allocation_policy_t * ap, int * xids,
|
|||
DEBUG("complete");
|
||||
} break;
|
||||
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;
|
||||
if(!activexidcount) { break; }
|
||||
while(xids[thexid = myrandom(XACT_COUNT)] == -1) { }
|
||||
|
@ -195,33 +168,32 @@ static void takeRandomAction(stasis_allocation_policy_t * ap, int * xids,
|
|||
} else {
|
||||
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);
|
||||
if(p && p->lockCount == 0) {
|
||||
int thenewfreespace = p->freespace+thespacediff;
|
||||
stasis_allocation_policy_update_freespace_unlocked_page(ap, p, thenewfreespace);
|
||||
if(p != INVALID_PAGE) {
|
||||
int thenewfreespace = myrandom(MAX_DESIRED_FREESPACE/2)+thespacediff;
|
||||
stasis_allocation_policy_update_freespace(ap, p, thenewfreespace);
|
||||
// printf("updated freespace unlocked");
|
||||
}
|
||||
} break;
|
||||
case 3 : { // update freespace locked
|
||||
} break;
|
||||
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;
|
||||
case 3 : { // dealloc from page
|
||||
int thexid;
|
||||
if(!activexidcount) { break; }
|
||||
while(xids[thexid = myrandom(XACT_COUNT)] == -1) { }
|
||||
pageid_t thepage=myrandom(AVAILABLE_PAGE_COUNT_A + pages2?AVAILABLE_PAGE_COUNT_B:0);
|
||||
stasis_allocation_policy_alloced_from_page(ap,thexid,thepage); */
|
||||
|
||||
while(xids[thexid = myrandom(XACT_COUNT)] == -1) {}
|
||||
pageid_t p = pages1[myrandom(AVAILABLE_PAGE_COUNT_A)];
|
||||
stasis_allocation_policy_dealloced_from_page(ap, xids[thexid], p);
|
||||
} 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) {
|
||||
|
@ -232,10 +204,8 @@ START_TEST(allocationPolicy_randomTest) {
|
|||
printf("\nSeed = %ld\n", seed);
|
||||
srandom(seed);
|
||||
|
||||
availablePage ** pages1 =
|
||||
malloc((1+AVAILABLE_PAGE_COUNT_A) * sizeof(availablePage *));
|
||||
availablePage ** pages2 =
|
||||
malloc((1+AVAILABLE_PAGE_COUNT_B) * sizeof(availablePage *));
|
||||
pageid_t * pages1 = malloc(AVAILABLE_PAGE_COUNT_A * sizeof(pageid_t));
|
||||
pageid_t * pages2 = malloc(AVAILABLE_PAGE_COUNT_B * sizeof(pageid_t));
|
||||
|
||||
int * xids = malloc(sizeof(int) * XACT_COUNT);
|
||||
|
||||
|
@ -243,35 +213,30 @@ START_TEST(allocationPolicy_randomTest) {
|
|||
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_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++) {
|
||||
// Don't pass in pages2; ap doesn't know about them yet!
|
||||
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++) {
|
||||
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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue