long lastPage; // last allocated page
long lastPreallocPage; // last pre-allcated page
long pageNumber =
ContainerHandle.INVALID_PAGE_NUMBER; // init to appease compiler
// the page number of the new page
PageKey pkey; // the identity of the new page
boolean reuse; // if true, we are trying to reuse a page
/* in case the page recommeded by allocPage is not committed yet, may
/* need to retry a couple of times */
boolean retry;
int numtries = 0;
int maxTries = MAX_INTERRUPT_RETRIES;
long startSearch = lastAllocatedPage;
AllocPage allocPage = null; // the alloc page
BasePage page = null; // the new page
try
{
do
{
retry = false; // we don't expect we need to retry
synchronized(allocCache)
{
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(
ntt.getId().equals(
allocHandle.getTransaction().getId()));
if (useNTT)
SanityManager.ASSERT(
!ntt.getId().equals(
userHandle.getTransaction().getId()));
}
/* find an allocation page that can handle adding a new
* page.
*
* allocPage is unlatched when the ntt commits. The new
* page is initialized by the ntt but the latch is
* transfered to the user transaction before the allocPage
* is unlatched. The allocPage latch prevents almost any
* other reader or writer from finding the new page until
* the ntt is committed and the new page is latched by the
* user transaction.
*
* (If the page is being reused, it is possible for another
* xact which kept a handle on the reused page to find the
* page during the transfer UT -> NTT. If this unlikely
* even occurs and the transfer fails [see code relating
* to transfer below], we retry from the beginning.)
*
* After the NTT commits a reader (getNextPageNumber) may
* get the page number of the newly allocated page and it
* will wait for the new page and latch it when the user
* transaction commits, aborts or unlatches the new page.
* Whether the user transaction commits or aborts, the new
* page stay allocated.
*
* RESOLVE: before NTT rolls back (or commits) the latch is
* released. To repopulate the allocation cache, need to
* get either the container lock on add page, or get a per
* allocation page lock.
*
* This blocks all page read (getPage) from accessing this
* alloc page in this container until the alloc page is
* unlatched. Those who already have a page handle into
* this container are unaffected.
*
* In other words, allocation blocks out reader (of any
* page that is managed by this alloc page) by the latch
* on the allocation page.
*
* Note that write page can proceed as usual.
*/
try {
allocPage =
findAllocPageForAdd(allocHandle, ntt, startSearch);
} catch (InterruptDetectedException e) {
// Retry. We needed to back all the way up here in the
// case of the container having been closed due to an
// interrupt on another thread, since that thread's
// recovery needs the monitor to allocCache which we
// hold. We release it when we do "continue" below.
if (--maxTries > 0) {
// Clear firstAllocPageNumber, i.e. undo side
// effect of makeAllocPage, so retry will work
firstAllocPageNumber =
ContainerHandle.INVALID_PAGE_NUMBER;
retry = true;
// Wait a bit so recovery can take place before
// we re-grab monitor on "this" (which recovery
// needs) and retry writeRAFHeader.
try {
Thread.sleep(INTERRUPT_RETRY_SLEEP);
} catch (InterruptedException ee) {
// This thread received an interrupt as
// well, make a note.
InterruptStatus.setInterrupted();
}
continue;
} else {
throw StandardException.newException(
SQLState.FILE_IO_INTERRUPTED, e);
}
}
allocCache.invalidate(allocPage, allocPage.getPageNumber());
}
if (SanityManager.DEBUG)
{
if (allocPage == null)
allocCache.dumpAllocationCache();
SanityManager.ASSERT(allocPage != null,
"findAllocPageForAdd returned a null alloc page");
}
//
// get the next free page's number.
// for case 1, page number > lastPreallocPage
// for case 2, page number <= lastPage
// for case 3, lastPage < page number <= lastPreallocPage
//
pageNumber = allocPage.nextFreePageNumber(startSearch);
// need to distinguish between the following 3 cases:
// 1) the page has not been allocate or initalized.
// Create it in the page cache and sync it to disk.
// 2) the page is being re-allocated.
// We need to read it in to re-initialize it
// 3) the page has been preallocated.
// Create it in the page cache and don't sync it to disk
//
// first find out the current last initialized page and
// preallocated page before the new page is added
lastPage = allocPage.getLastPagenum();
lastPreallocPage = allocPage.getLastPreallocPagenum();
reuse = pageNumber <= lastPage;
// no address translation necessary
pkey = new PageKey(identity, pageNumber);
if (reuse)
{
// if re-useing a page, make sure the deallocLock on the new