<> <> <> <> <> <> <<>> <> <<1. At present, writes which go directly to the base (because they are above highWaterMark) are not logged at all. This will need to be revised when duplicate logging is implemented, and also has some interactions with the backup system.>> DIRECTORY AlpineEnvironment, AlpineFile, AlpineInternal, FileInstance, FileLock, FileLog, FileMap, FilePageMgr, FilePrivate, LeaderPage, Log, LogMap, OpenFileMap, PrincOpsUtils USING [LongCopy], SkiPatrolHooks USING[FileInfoToTransID], SkiPatrolLog USING[notice, OpFailureInfo], TransactionMap; PageActionsImpl: PROGRAM IMPORTS AlpineFile, FileInstance, FileLock, FileLog, FileMap, FilePageMgr, FilePrivate, LeaderPage, LogMap, OpenFileMap, PrincOpsUtils, SkiPatrolHooks, SkiPatrolLog EXPORTS AlpineFile, FileLog, FilePrivate = BEGIN OPEN AlpineFile; VMPageSet: TYPE = FilePageMgr.VMPageSet; <> ReadPages: PUBLIC PROCEDURE [conversation: Conversation, openFileID: OpenFileID, pageRun: PageRun, pageBuffer: RESULTPageBuffer, lock: LockOption _ [read, wait]] = BEGIN Work: FilePrivate.OpenFileWork --[openFile, fileInstance, file, trans, pUpdateCost]-- = BEGIN referencePattern: ReferencePattern = OpenFileMap.GetReferencePattern[openFile]; 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 { logProc: PROC [SkiPatrolLog.OpFailureInfo]; IF (logProc _ SkiPatrolLog.notice.operationFailed) # NIL THEN logProc[[ what: inconsistentDescriptor, where: "PageActionsImpl.ReadPages", transID: SkiPatrolHooks.FileInfoToTransID[conversation, openFileID], message: "" ]]; ERROR OperationFailed[inconsistentDescriptor]; }; FileLock.AcquirePageLocks[fileInstance: fileInstance, pageRun: pageRun, requested: lock, minimum: read]; WHILE remainingPageRun.count#0 DO mappedRun: LogMap.MappedPageRun _ LogMap.LocatePages[file: file, trans: trans, pageRun: remainingPageRun ! LogMap.Error => GOTO nonexistentFilePage]; IF mappedRun.pageRun.firstPage#remainingPageRun.firstPage OR mappedRun.pageRun.count>remainingPageRun.count THEN ERROR; -- LogMap foulup WITH mpr: mappedRun SELECT FROM base => BEGIN <> BaseToClient: PageRunProc --[vmPageSet] RETURNS [release, keep]-- = BEGIN CopyPages[file: file, to: BASE[pageBuffer]+ WordsFromPages[vmPageSet.pageRun.firstPage-pageRun.firstPage], from: vmPageSet.pages, nPages: vmPageSet.pageRun.count]; RETURN [release: clean, keep: referencePattern=random]; END; -- BaseToClient ApplyToFilePages[file: file, pageRun: mpr.pageRun, proc: BaseToClient, fpmProc: FilePageMgr.ReadPages ! FilePageMgr.PageRunExtendsPastEof => GOTO nonexistentFilePage]; END; log => IF mpr.checkedOut THEN BEGIN <> LogToBaseToClient: PageRunProc --[vmPageSet] RETURNS [release, keep]-- = BEGIN OPEN vpr: vmPageSet.pageRun; beginIntersection, endIntersection: PageNumber; <> [] _ FileLog.LogReadPages[fileInstance: fileInstance, where: vmPageSet.pages, pageRun: vpr, recordID: mpr.logRecordID]; <> beginIntersection _ MAX[vpr.firstPage, mpr.pageRun.firstPage]; endIntersection _ MIN[vpr.firstPage+vpr.count, mpr.pageRun.firstPage+mpr.pageRun.count]; IF endIntersection>beginIntersection THEN CopyPages[file: file, to: BASE[pageBuffer] + WordsFromPages[beginIntersection-pageRun.firstPage], from: vmPageSet.pages + WordsFromPages[beginIntersection-vpr.firstPage], nPages: endIntersection-beginIntersection]; <> RETURN [release: IF referencePattern=random THEN writeIndividualNoWait ELSE writeBatchedNoWait, keep: referencePattern=random]; END; -- LogToBaseToClient ApplyToFilePages[file: file, pageRun: mpr.logMapPageRun, proc: LogToBaseToClient, fpmProc: FilePageMgr.UsePages]; LogMap.UnregisterPages[file: file, pageRun: mpr.logMapPageRun]; END ELSE <> [] _ FileLog.LogReadPages[fileInstance: fileInstance, where: BASE[pageBuffer] + WordsFromPages[mpr.pageRun.firstPage-pageRun.firstPage], pageRun: mpr.pageRun, recordID: mpr.logRecordID]; ENDCASE => ERROR; remainingPageRun _ IncrementPageRun[remainingPageRun, mappedRun.pageRun.count]; ENDLOOP; <> <> IF referencePattern=sequential THEN BEGIN ENABLE LogMap.Error, FilePageMgr.PageRunExtendsPastEof => CONTINUE; nextRun: PageRun = [firstPage: pageRun.firstPage+pageRun.count, count: pageRun.count]; <> mappedRun: LogMap.MappedPageRun _ LogMap.LocatePages[file: file, trans: trans, pageRun: nextRun, checkOut: none]; WITH mpr: mappedRun SELECT FROM base => FilePageMgr.ReadAheadPages[fileHandle: file, pageRun: mpr.pageRun]; ENDCASE; END; EXITS nonexistentFilePage => { logProc: PROC [SkiPatrolLog.OpFailureInfo]; IF (logProc _ SkiPatrolLog.notice.operationFailed) # NIL THEN logProc[[ what: nonexistentFilePage, where: "PageActionsImpl.ReadPages", transID: SkiPatrolHooks.FileInfoToTransID[conversation, openFileID], message: "tried to go past EOF" ]]; ERROR OperationFailed[nonexistentFilePage]; }; END; -- Work FilePrivate.EstablishOpenFileContext[conversation, openFileID, Work]; END; WritePages: PUBLIC PROCEDURE [conversation: Conversation, openFileID: OpenFileID, pageRun: PageRun, pageBuffer: VALUEPageBuffer, lock: LockOption _ [update, wait]] = BEGIN 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 { logProc: PROC [SkiPatrolLog.OpFailureInfo]; IF (logProc _ SkiPatrolLog.notice.operationFailed) # NIL THEN logProc[[ what: inconsistentDescriptor, where: "PageActionsImpl.WritePages", transID: SkiPatrolHooks.FileInfoToTransID[conversation, openFileID], message: "" ]]; ERROR OperationFailed[inconsistentDescriptor]; }; IF OpenFileMap.GetAccessRights[openFile] # readWrite THEN ERROR AccessFailed[handleReadWrite]; FileLock.AcquirePageLocks[fileInstance: fileInstance, pageRun: pageRun, requested: lock, minimum: update]; <> 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; <> 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]; <> IF FileInstance.GetLockMode[fileInstance] IN FileLock.AssertedFileLock AND pageRun.firstPage+pageRun.count > highWaterMark THEN BEGIN <> 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]; <> 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 <> 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; <> 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 => { logProc: PROC [SkiPatrolLog.OpFailureInfo]; IF (logProc _ SkiPatrolLog.notice.operationFailed) # NIL THEN logProc[[ what: nonexistentFilePage, where: "PageActionsImpl.WritePages", transID: SkiPatrolHooks.FileInfoToTransID[conversation, openFileID], message: "tried to write past EOF" ]]; ERROR OperationFailed[nonexistentFilePage]; }; END; -- Work FilePrivate.EstablishOpenFileContext[conversation, openFileID, Work]; END; LockPages: PUBLIC PROCEDURE [conversation: Conversation, openFileID: OpenFileID, pageRun: PageRun, lock: LockOption _ [read, wait]] = BEGIN Work: FilePrivate.OpenFileWork --[openFile, fileInstance, file, trans, pUpdateCost]-- = BEGIN IF pageRun.firstPage<0 OR pageRun.firstPage+pageRun.count>AlpineEnvironment.maxPagesPerFile THEN ERROR StaticallyInvalid; FileLock.AcquirePageLocks[fileInstance: fileInstance, pageRun: pageRun, requested: lock, minimum: read]; END; -- Work FilePrivate.EstablishOpenFileContext[conversation, openFileID, Work]; END; UnlockPages: PUBLIC PROCEDURE [conversation: Conversation, openFileID: OpenFileID, pageRun: PageRun] = BEGIN Work: FilePrivate.OpenFileWork --[openFile, fileInstance, file, trans, pUpdateCost]-- = BEGIN IF pageRun.firstPage<0 OR pageRun.firstPage+pageRun.count>AlpineEnvironment.maxPagesPerFile THEN ERROR StaticallyInvalid; FileLock.ReleasePageLocks[fileInstance: fileInstance, pageRun: pageRun]; END; -- Work FilePrivate.EstablishOpenFileContext[conversation, openFileID, Work]; END; <> PerformDeferredPageActions: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle] = BEGIN file: FileMap.Handle = FileInstance.GetFileHandle[fileInstance]; trans: TransactionMap.Handle = FileInstance.GetTransHandle[fileInstance]; actualSize: PageCount = FilePageMgr.GetSize[file]; -- This is the committed size for this transaction, except during recovery in which case it might be the committed size for some later transaction. DO LogToBase: PageRunProc --[vmPageSet] RETURNS [release, keep]-- = BEGIN referencePattern: ReferencePattern = FileLog.LogReadPages[fileInstance: fileInstance, where: vmPageSet.pages, pageRun: vmPageSet.pageRun, recordID: logRecordID]; RETURN [release: IF referencePattern=random THEN writeIndividualNoWait ELSE writeBatchedNoWait, keep: referencePattern=random]; END; -- LogToBase found: BOOLEAN; logRecordID: Log.RecordID; logMapPageRun: PageRun; [found: found, logRecordID: logRecordID, logMapPageRun: logMapPageRun] _ LogMap.LocateAnyPagesForTrans[file: file, trans: trans]; IF ~found THEN EXIT; <> IF logMapPageRun.firstPageactualSize THEN pageRun.count _ actualSize-pageRun.firstPage; -- cut it down to size ApplyToFilePages[file: file, pageRun: pageRun, proc: LogToBase, fpmProc: FilePageMgr.UsePages]; END; LogMap.UnregisterPages[file: file, pageRun: logMapPageRun]; ENDLOOP; END; PerformAnyCommittedIntentions: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle, pageRun: FilePrivate.LongPageRun] = BEGIN file: FileMap.Handle = FileInstance.GetFileHandle[fileInstance]; trans: TransactionMap.Handle = FileInstance.GetTransHandle[fileInstance]; WHILE pageRun.count#0 DO shortPageRun: PageRun = [firstPage: pageRun.firstPage, count: MIN[pageRun.count, LAST[CARDINAL]]]; mappedRun: LogMap.MappedPageRun _ LogMap.LocatePages[file: file, trans: trans, pageRun: shortPageRun]; IF mappedRun.pageRun.firstPage#shortPageRun.firstPage OR mappedRun.pageRun.count>shortPageRun.count THEN ERROR; -- LogMap foulup WITH mpr: mappedRun SELECT FROM base => NULL; <> log => IF mpr.checkedOut THEN BEGIN <> LogToBase: PageRunProc --[vmPageSet] RETURNS [release, keep]-- = BEGIN referencePattern: ReferencePattern = FileLog.LogReadPages[fileInstance: fileInstance, where: vmPageSet.pages, pageRun: vmPageSet.pageRun, recordID: mpr.logRecordID]; RETURN [release: IF referencePattern=random THEN writeIndividualNoWait ELSE writeBatchedNoWait, keep: referencePattern=random]; END; -- LogToBase ApplyToFilePages[file: file, pageRun: mpr.logMapPageRun, proc: LogToBase, fpmProc: FilePageMgr.UsePages]; LogMap.UnregisterPages[file: file, pageRun: mpr.logMapPageRun]; END; <> ENDCASE => ERROR; pageRun _ [firstPage: pageRun.firstPage+mappedRun.pageRun.count, count: pageRun.count-mappedRun.pageRun.count]; ENDLOOP; END; <> RecoverWritePages: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle, recordID: Log.RecordID, pageRun: PageRun, outcome: FileLog.TransState] = BEGIN file: FileMap.Handle = FileInstance.GetFileHandle[fileInstance]; trans: TransactionMap.Handle = FileInstance.GetTransHandle[fileInstance]; IF outcome=ready THEN FileLock.AcquirePageLocks[fileInstance: fileInstance, pageRun: pageRun, requested: [write, fail], minimum: write]; IF outcome#aborted THEN BEGIN <> actualSize: PageCount = FilePageMgr.GetSize[file]; IF pageRun.firstPageactualSize THEN pageRun.count _ actualSize-pageRun.firstPage; PerformAnyCommittedIntentions[fileInstance: fileInstance, pageRun: [firstPage: pageRun.firstPage, count: pageRun.count]]; LogMap.RegisterPages[file: file, trans: trans, pageRun: pageRun, logRecordID: recordID]; END; END; END; <> ApplyToFilePages: PROCEDURE [file: FileMap.Handle, pageRun: PageRun, proc: PageRunProc, fpmProc: FPMProc] = <> <> BEGIN WHILE pageRun.count#0 DO release: FilePageMgr.ReleaseState; keep: BOOLEAN; vmPageSet: VMPageSet _ fpmProc[fileHandle: file, pageRun: pageRun]; thisCount: CARDINAL _ vmPageSet.pageRun.count; IF thisCount>pageRun.count THEN ERROR; -- FilePageMgr foulup [release: release, keep: keep] _ proc[vmPageSet]; FilePageMgr.ReleaseVMPageSet[vMPageSet: vmPageSet, releaseState: release, keep: keep]; pageRun _ IncrementPageRun[pageRun, thisCount]; ENDLOOP; END; PageRunProc: TYPE = PROCEDURE [vmPageSet: VMPageSet] RETURNS [release: FilePageMgr.ReleaseState, keep: BOOLEAN]; FPMProc: TYPE = PROCEDURE [fileHandle: FileMap.Handle, pageRun: PageRun] RETURNS [vMPageSet: VMPageSet]; <> CopyPages: PROCEDURE [file: FileMap.Handle, to, from: LONG POINTER TO UNSPECIFIED, nPages: CARDINAL] = <> BEGIN DoCopy: SAFE PROC = TRUSTED { PrincOpsUtils.LongCopy[to: to, from: from, nwords: WordsFromPages[nPages]]}; FileMap.Enter[file, DoCopy]; END; WordsFromPages: PROCEDURE [pages: PageCount] RETURNS [words: CARDINAL] = INLINE <> BEGIN IF pages > LAST[CARDINAL]/AlpineEnvironment.wordsPerPage THEN ERROR; RETURN [CARDINAL[pages]*AlpineEnvironment.wordsPerPage]; END; IncrementPageRun: PROCEDURE [pageRun: PageRun, count: CARDINAL] RETURNS [PageRun] = BEGIN IF count>pageRun.count THEN ERROR; RETURN [[firstPage: pageRun.firstPage+count, count: pageRun.count-count]]; END; END. CHANGE LOG <> <> <> <> <> <> <<>>