2006-06-22 19:10:02 +00:00
# include "../page.h"
# include <lladd/operations.h>
2006-07-18 23:59:00 +00:00
# include "../page/slotted.h"
# include <assert.h>
2006-06-22 19:10:02 +00:00
2006-07-18 23:59:00 +00:00
# define INVALID_XID (-1)
2006-07-25 00:56:50 +00:00
typedef struct regionAllocLogArg {
int startPage ;
unsigned int pageCount ;
int allocationManager ;
} regionAllocArg ;
2006-06-22 19:10:02 +00:00
# define boundary_tag_ptr(p) (((byte*)end_of_usable_space_ptr((p)))-sizeof(boundary_tag_t))
2006-07-25 00:56:50 +00:00
pthread_mutex_t region_mutex = PTHREAD_MUTEX_INITIALIZER ;
static void TregionAllocHelper ( int xid , unsigned int pageid , unsigned int pageCount , int allocationManager ) ;
2006-07-18 23:59:00 +00:00
static int operate_alloc_boundary_tag ( int xid , Page * p , lsn_t lsn , recordid rid , const void * dat ) {
slottedPageInitialize ( p ) ;
* page_type_ptr ( p ) = BOUNDARY_TAG_PAGE ;
slottedPostRalloc ( xid , p , lsn , rid ) ;
slottedWrite ( xid , p , lsn , rid , dat ) ;
return 0 ;
}
2006-06-22 19:10:02 +00:00
2006-07-25 00:56:50 +00:00
static int operate_alloc_region ( int xid , Page * p , lsn_t lsn , recordid rid , const void * datP ) {
pthread_mutex_lock ( & region_mutex ) ;
regionAllocArg * dat = ( regionAllocArg * ) datP ;
TregionAllocHelper ( xid , dat - > startPage , dat - > pageCount , dat - > allocationManager ) ;
pthread_mutex_unlock ( & region_mutex ) ;
return 0 ;
}
static int operate_dealloc_region ( int xid , Page * p , lsn_t lsn , recordid rid , const void * datP ) {
regionAllocArg * dat = ( regionAllocArg * ) datP ;
TregionDealloc ( xid , dat - > startPage + 1 ) ;
return 0 ;
}
2006-07-18 23:59:00 +00:00
// TODO: Implement these four functions.
2006-07-20 01:29:39 +00:00
static void TallocBoundaryTag ( int xid , unsigned int page , boundary_tag * tag ) {
// printf("Alloc boundary tag at %d\n", page);
2006-07-18 23:59:00 +00:00
recordid rid = { page , 0 , sizeof ( boundary_tag ) } ;
Tupdate ( xid , rid , tag , OPERATION_ALLOC_BOUNDARY_TAG ) ;
}
2006-07-20 01:29:39 +00:00
static void TdeallocBoundaryTag ( int xid , unsigned int page ) {
//no-op
2006-07-18 23:59:00 +00:00
}
2006-07-20 01:29:39 +00:00
static void TreadBoundaryTag ( int xid , unsigned int page , boundary_tag * tag ) {
2006-07-18 23:59:00 +00:00
recordid rid = { page , 0 , sizeof ( boundary_tag ) } ;
Tread ( xid , rid , tag ) ;
}
2006-07-20 01:29:39 +00:00
static void TsetBoundaryTag ( int xid , unsigned int page , boundary_tag * tag ) {
// printf("Writing boundary tag at %d\n", page);
2006-07-18 23:59:00 +00:00
recordid rid = { page , 0 , sizeof ( boundary_tag ) } ;
Tset ( xid , rid , tag ) ;
2006-06-22 19:10:02 +00:00
}
2006-07-18 23:59:00 +00:00
void regionsInit ( ) {
Page * p = loadPage ( - 1 , 0 ) ;
int pageType = * page_type_ptr ( p ) ;
if ( pageType ! = BOUNDARY_TAG_PAGE ) {
boundary_tag t ;
2006-07-20 01:29:39 +00:00
t . size = UINT32_MAX ;
t . prev_size = UINT32_MAX ;
2006-07-18 23:59:00 +00:00
t . status = REGION_VACANT ;
t . region_xid = INVALID_XID ;
t . allocation_manager = 0 ;
2006-07-20 01:29:39 +00:00
// This does what TallocBoundaryTag(-1, 0, &t); would do, but it
// doesn't produce a log entry. The log entry would be invalid
// since we haven't initialized everything yet. We don't need to
// flush the page, since this code is deterministic, and will be
// re-run before recovery if this update doesn't make it to disk
// after a crash.
recordid rid = { 0 , 0 , sizeof ( boundary_tag ) } ;
operate_alloc_boundary_tag ( 0 , p , 0 , rid , & t ) ;
2006-07-18 23:59:00 +00:00
}
2006-07-21 01:07:09 +00:00
releasePage ( p ) ;
2006-06-22 19:10:02 +00:00
}
2006-07-25 00:56:50 +00:00
static void TregionAllocHelper ( int xid , unsigned int pageid , unsigned int pageCount , int allocationManager ) {
2006-07-18 23:59:00 +00:00
boundary_tag t ;
2006-07-25 00:56:50 +00:00
TreadBoundaryTag ( xid , pageid , & t ) ;
2006-07-18 23:59:00 +00:00
if ( t . size ! = pageCount ) {
2006-06-22 19:10:02 +00:00
// need to split region
2006-07-18 23:59:00 +00:00
// allocate new boundary tag.
2006-07-20 01:29:39 +00:00
unsigned int newPageid = pageid + pageCount + 1 ;
2006-07-18 23:59:00 +00:00
boundary_tag new_tag ;
2006-07-20 01:29:39 +00:00
if ( t . size ! = UINT32_MAX ) {
2006-07-18 23:59:00 +00:00
new_tag . size = t . size - pageCount - 1 ; // pageCount must be strictly less than t->size, so this is non-negative.
boundary_tag succ_tag ;
TreadBoundaryTag ( xid , pageid + t . size + 1 , & succ_tag ) ;
2006-07-20 01:29:39 +00:00
succ_tag . prev_size = new_tag . size ;
2006-07-18 23:59:00 +00:00
TsetBoundaryTag ( xid , pageid + t . size + 1 , & succ_tag ) ;
2006-06-22 19:10:02 +00:00
} else {
2006-07-18 23:59:00 +00:00
2006-07-20 01:29:39 +00:00
new_tag . size = UINT32_MAX ;
2006-07-18 23:59:00 +00:00
2006-06-22 19:10:02 +00:00
}
2006-07-18 23:59:00 +00:00
new_tag . prev_size = pageCount ;
// Create the new region, and disassociate it from this transaction immediately.
// This has two implications:
// - It could cause some fragmentation if interleaved transactions are allocating, and some abort.
// - Multiple transactions can allocate space at the end of the page file without blocking each other.
new_tag . status = REGION_VACANT ;
new_tag . region_xid = INVALID_XID ;
new_tag . allocation_manager = 0 ;
TallocBoundaryTag ( xid , newPageid , & new_tag ) ;
2006-06-22 19:10:02 +00:00
}
2006-07-18 23:59:00 +00:00
2006-07-20 01:29:39 +00:00
t . status = REGION_ZONED ;
t . region_xid = xid ;
t . allocation_manager = allocationManager ;
t . size = pageCount ;
2006-07-18 23:59:00 +00:00
TsetBoundaryTag ( xid , pageid , & t ) ;
2006-06-22 19:10:02 +00:00
2006-07-25 00:56:50 +00:00
}
unsigned int TregionAlloc ( int xid , unsigned int pageCount , int allocationManager ) {
// Initial implementation. Naive first fit.
pthread_mutex_lock ( & region_mutex ) ;
unsigned int pageid = 0 ;
boundary_tag t ;
unsigned int prev_size = UINT32_MAX ;
TreadBoundaryTag ( xid , pageid , & t ) ; // XXX need to check if there is a boundary tag there or not!
while ( t . status ! = REGION_VACANT | | t . size < pageCount ) { // TODO: This while loop and the boundary tag manipulation below should be factored into two submodules.
// printf("t.status = %d, REGION_VACANT = %d, t.size = %d, pageCount = %d\n", t.status, REGION_VACANT, t.size, pageCount);
assert ( t . prev_size = = prev_size ) ;
prev_size = t . size ;
pageid + = ( t . size + 1 ) ;
TreadBoundaryTag ( xid , pageid , & t ) ;
}
// printf("page = %d, t.status = %d, REGION_VACANT = %d, t.size = %d, pageCount = %d (alloced)\n", pageid, t.status, REGION_VACANT, t.size, pageCount);
assert ( t . prev_size = = prev_size ) ;
regionAllocArg arg = { pageid , pageCount , allocationManager } ;
void * ntaHandle = TbeginNestedTopAction ( xid , OPERATION_ALLOC_REGION , ( const byte * ) & arg , sizeof ( regionAllocArg ) ) ;
TregionAllocHelper ( xid , pageid , pageCount , allocationManager ) ;
TendNestedTopAction ( xid , ntaHandle ) ;
2006-06-22 19:10:02 +00:00
pthread_mutex_unlock ( & region_mutex ) ;
2006-07-18 23:59:00 +00:00
2006-07-20 01:29:39 +00:00
return pageid + 1 ;
2006-06-22 19:10:02 +00:00
}
2006-07-20 01:29:39 +00:00
void TregionDealloc ( int xid , unsigned int firstPage ) {
2006-06-22 19:10:02 +00:00
2006-07-18 23:59:00 +00:00
// Note that firstPage is the first *caller visible* page in the
// region. The boundary tag is stored on firstPage - 1. Also, note
// that a region of size N takes up N+1 pages on disk.
// Deferred coalescing would probably make sense...
pthread_mutex_lock ( & region_mutex ) ;
boundary_tag t ;
TreadBoundaryTag ( xid , firstPage - 1 , & t ) ;
2006-07-20 01:29:39 +00:00
assert ( t . status ! = REGION_VACANT ) ;
t . status = REGION_VACANT ;
2006-07-18 23:59:00 +00:00
// If successor is vacant, merge.
2006-07-20 01:29:39 +00:00
if ( t . size ! = UINT32_MAX ) { // is there a successor?
unsigned int succ_page = firstPage + t . size ;
2006-07-18 23:59:00 +00:00
boundary_tag succ_tag ;
TreadBoundaryTag ( xid , succ_page , & succ_tag ) ;
// TODO: Check page_type_ptr()...
2006-07-20 01:29:39 +00:00
if ( succ_tag . size = = UINT32_MAX ) {
t . size = UINT32_MAX ;
2006-07-18 23:59:00 +00:00
// TODO: Truncate page file.
TdeallocBoundaryTag ( xid , succ_page ) ;
} else if ( succ_tag . status = = REGION_VACANT ) {
2006-06-22 19:10:02 +00:00
2006-07-18 23:59:00 +00:00
t . size = t . size + succ_tag . size + 1 ;
2006-07-20 01:29:39 +00:00
unsigned int succ_succ_page = succ_page + succ_tag . size + 1 ;
2006-06-22 19:10:02 +00:00
2006-07-18 23:59:00 +00:00
boundary_tag succ_succ_tag ;
TreadBoundaryTag ( xid , succ_succ_page , & succ_succ_tag ) ;
succ_succ_tag . prev_size = t . size ;
TsetBoundaryTag ( xid , succ_succ_page , & succ_succ_tag ) ;
TsetBoundaryTag ( xid , succ_page , & succ_tag ) ;
}
}
// If predecessor is vacant, merge. (Doing this after the successor
// is merged makes life easier, since merging with the predecessor
// creates a situation where the current page is not a boundary
// tag...)
2006-07-20 01:29:39 +00:00
if ( t . prev_size ! = UINT32_MAX ) {
2006-07-18 23:59:00 +00:00
2006-07-20 01:29:39 +00:00
unsigned int pred_page = ( firstPage - 2 ) - t . prev_size ; // If the predecessor is length zero, then it's boundary tag is two pages before this region's tag.
2006-07-18 23:59:00 +00:00
boundary_tag pred_tag ;
TreadBoundaryTag ( xid , pred_page , & pred_tag ) ;
if ( pred_tag . status = = REGION_VACANT ) {
2006-07-20 01:29:39 +00:00
if ( t . size = = UINT32_MAX ) {
pred_tag . size = UINT32_MAX ;
2006-07-18 23:59:00 +00:00
// TODO: truncate region
} else {
pred_tag . size + = ( t . size + 1 ) ;
2006-07-20 01:29:39 +00:00
unsigned int succ_page = firstPage + t . size ;
assert ( pred_page + pred_tag . size + 1 = = succ_page ) ;
2006-07-18 23:59:00 +00:00
boundary_tag succ_tag ;
TreadBoundaryTag ( xid , succ_page , & succ_tag ) ;
succ_tag . prev_size = pred_tag . size ;
TsetBoundaryTag ( xid , succ_page , & succ_tag ) ;
assert ( succ_tag . status ! = REGION_VACANT ) ;
2006-07-20 01:29:39 +00:00
assert ( succ_page - pred_page - 1 = = pred_tag . size ) ;
2006-07-18 23:59:00 +00:00
}
TsetBoundaryTag ( xid , pred_page , & pred_tag ) ;
TdeallocBoundaryTag ( xid , firstPage - 1 ) ;
} else {
TsetBoundaryTag ( xid , firstPage - 1 , & t ) ;
}
} else {
TsetBoundaryTag ( xid , firstPage - 1 , & t ) ;
}
pthread_mutex_unlock ( & region_mutex ) ;
2006-06-22 19:10:02 +00:00
}
2006-07-18 23:59:00 +00:00
Operation getAllocBoundaryTag ( ) {
Operation o = {
OPERATION_ALLOC_BOUNDARY_TAG ,
2006-07-20 01:29:39 +00:00
sizeof ( boundary_tag ) ,
2006-07-18 23:59:00 +00:00
OPERATION_NOOP ,
& operate_alloc_boundary_tag
} ;
return o ;
2006-06-22 19:10:02 +00:00
}
2006-07-25 00:56:50 +00:00
Operation getAllocRegion ( ) {
Operation o = {
OPERATION_ALLOC_REGION ,
sizeof ( regionAllocArg ) ,
OPERATION_DEALLOC_REGION ,
& operate_alloc_region
} ;
return o ;
}
Operation getDeallocRegion ( ) {
Operation o = {
OPERATION_DEALLOC_REGION ,
sizeof ( regionAllocArg ) ,
OPERATION_ALLOC_REGION ,
& operate_dealloc_region
} ;
return o ;
}
2006-07-20 01:29:39 +00:00
void TregionFindNthActive ( int xid , unsigned int regionNumber , unsigned int * firstPage , unsigned int * size ) {
boundary_tag t ;
recordid rid = { 0 , 0 , sizeof ( boundary_tag ) } ;
Tread ( xid , rid , & t ) ;
unsigned int prevSize = 0 ;
while ( t . status = = REGION_VACANT ) {
rid . page + = ( t . size + 1 ) ;
Tread ( xid , rid , & t ) ;
assert ( t . size ! = UINT_MAX ) ;
assert ( t . prev_size ! = UINT_MAX ) ;
assert ( prevSize = = t . prev_size | | ! prevSize ) ;
prevSize = t . size ;
}
for ( int i = 0 ; i < regionNumber ; i + + ) {
rid . page + = ( t . size + 1 ) ;
Tread ( xid , rid , & t ) ;
if ( t . status = = REGION_VACANT ) { i - - ; }
assert ( t . size ! = UINT_MAX ) ;
assert ( t . prev_size ! = UINT_MAX | | i = = 0 ) ;
assert ( prevSize = = t . prev_size | | ! prevSize ) ;
prevSize = t . size ;
}
* firstPage = rid . page + 1 ;
* size = t . size ;
}
2006-07-18 23:59:00 +00:00
/*Operation getAllocRegion() {
2006-06-22 19:10:02 +00:00
}
2006-07-18 23:59:00 +00:00
Operation getFreeRegion ( ) {
} */