PROCEDURE [conversation: Conversation, openFileID: OpenFileID, pageRun: PageRun, pageBuffer: VALUEPageBuffer, lock: LockOption ← [update, wait]] =
Work: FilePrivate.OpenFileWork
--[openFile, fileInstance, file, trans, pUpdateCost]-- =
BEGIN
referencePattern: ReferencePattern = OpenFileMap.GetReferencePattern[openFile];
highWaterMark, previousHighWaterMark: PageCount;
remainingPageRun: PageRun ← pageRun;
IF pageRun.firstPage<0
OR pageRun.firstPage+pageRun.count>AlpineEnvironment.maxPagesPerFile
OR pageRun.count
NOT
IN [1..maxPagesPerRun]
OR lock.mode
NOT
IN AlpineEnvironment.LockMode
THEN
ERROR StaticallyInvalid;
IF
LENGTH[pageBuffer] # pageRun.count*AlpineEnvironment.wordsPerPage
THEN
ERROR OperationFailed[inconsistentDescriptor];
IF OpenFileMap.GetAccessRights[openFile] # readWrite
THEN
ERROR AccessFailed[handleReadWrite];
FileLock.AcquirePageLocks[fileInstance: fileInstance, pageRun: pageRun, requested: lock, minimum: update];
Ensure that the committed file size and highWaterMark are known to the LogMap, and establish them if not. Note that if an uncommitted size or highWaterMark exists for this transaction then the corresponding committed value must also be known to the LogMap. Conversely, if the LogMap does not know the committed value then there cannot be an uncommitted value (for ANY transaction); consequently it is correct simply to read the base to find out the committed value.
IF LogMap.GetCommittedSize[file: file]=
LAST[PageCount]
THEN
BEGIN
size: PageCount = FilePageMgr.GetSize[file];
IF LogMap.GetCommittedSize[file: file]=
LAST[PageCount]
THEN
LogMap.SetCommittedSize[file: file, size: size];
END;
highWaterMark ← LogMap.GetCommittedHighWaterMark[file];
IF highWaterMark=
LAST[PageCount]
THEN
BEGIN
highWaterMark ← NARROW[LeaderPage.GetProperty[fileInstance, highWaterMark], PropertyValuePair[highWaterMark]].highWaterMark;
IF LogMap.GetCommittedHighWaterMark[file]=
LAST[PageCount]
THEN
LogMap.SetCommittedHighWaterMark[file, highWaterMark]
ELSE highWaterMark ← LogMap.GetCommittedHighWaterMark[file];
END;
Carry out any committed intentions in the specified interval. Note that since the LogMap now knows the committed file size (and the uncommitted size also, if it has been changed by this transaction), a pageRun extending past end-of-file is guaranteed to be detected as an error at this point.
PerformAnyCommittedIntentions[fileInstance: fileInstance, pageRun: [firstPage: pageRun.firstPage, count: pageRun.count] !
LogMap.Error => GOTO nonexistentFilePage];
pUpdateCost^ ← INT[pageRun.count]; -- Cost is proportional to size of run
FileInstance.SetMaxDeltaVersion[fileInstance, 1];
Decide what writes to do to the base and what to the log. If this transaction holds a whole-file lock (of any kind) then writes above the committed high water mark can go directly to the base. All others must go to the log.
IF FileInstance.GetLockMode[fileInstance]
IN FileLock.AssertedFileLock
AND pageRun.firstPage+pageRun.count > highWaterMark
THEN
BEGIN
Writes above highWaterMark go to the base.
thisRun: PageRun = IF pageRun.firstPage>=highWaterMark THEN pageRun ELSE [firstPage: highWaterMark, count: pageRun.count - CARDINAL[highWaterMark-pageRun.firstPage]];
ClientToBase: PageRunProc
--[vmPageSet]
RETURNS [release, keep]-- =
BEGIN
CopyPages[file: file, to: vmPageSet.pages, from: BASE[pageBuffer] + WordsFromPages[vmPageSet.pageRun.firstPage-pageRun.firstPage], nPages: vmPageSet.pageRun.count];
Note: release vmPageSet according to referencePattern being used now, not the one in use at the time the intention was recorded.
RETURN [release: IF referencePattern=random THEN writeIndividualNoWait ELSE writeBatchedNoWait, keep: referencePattern=random];
END; -- ClientToBase
ApplyToFilePages[file: file, pageRun: thisRun, proc: ClientToBase, fpmProc: FilePageMgr.UsePages];
remainingPageRun.count ← remainingPageRun.count-thisRun.count;
END;
IF remainingPageRun.count#0
THEN
BEGIN
Writes below highWaterMark (or to a file not locked in a whole-file lock mode) go to the log.
logRecordID: Log.RecordID = FileLog.LogWritePages[fileInstance: fileInstance, where: BASE[pageBuffer], pageRun: remainingPageRun, referencePattern: referencePattern];
LogMap.RegisterPages[file: file, trans: trans, pageRun: remainingPageRun, logRecordID: logRecordID];
END;
Advance uncommitted high water mark. This is tricky, because we want to scrupulously maintain the MAX high water mark even in the face of concurrent writers.
highWaterMark ← pageRun.firstPage+pageRun.count;
previousHighWaterMark ← 0;
DO
previousHighWaterMark ← FileInstance.SetHighWaterMark[fileInstance, MAX[highWaterMark, previousHighWaterMark]];
IF previousHighWaterMark=LAST[PageCount] OR previousHighWaterMark<=highWaterMark THEN EXIT;
ENDLOOP;
EXITS
nonexistentFilePage => ERROR OperationFailed[nonexistentFilePage];
END; -- Work