-- File: DBFileAlpineImpl.mesa -- Contents: Alpine implementation of file level of Cypress -- Last edited by: -- MBrown on June 7, 1983 4:40 pm -- Kolling on May 12, 1983 3:35 pm -- Cattell on May 10, 1984 7:13:38 pm PDT -- Last Edited by: Wert, August 9, 1984 2:18:42 pm PDT DIRECTORY AlpineEnvironment USING[bytesPerPage, Conversation, OpenFileID, Outcome, PageCount, PageNumber, TransID, UniversalFile, wordsPerPage, PropertyValuePair], AlpFile USING[GetSize, Handle, Open, ReadPages, SetSize, PropertySet, WritePages, ReadProperties, WriteProperties], AlpineInterimDirectory USING[Error, Open, CreateOptions], AlpInstance USING[AccessFailed, Create, Failed, Handle, Unknown], AlpTransaction USING[Create, Handle, Finish, OperationFailed], DBEnvironment USING[Aborted, Failure, Error], DBCommon USING[VersionOptions], DBFileAlpine, DBStats USING[Starting, Stopping], RPC USING[CallFailed], Rope USING[ROPE, Text]; DBFileAlpineImpl: PROGRAM IMPORTS AlpFile, AlpineInterimDirectory, AlpInstance, AlpTransaction, DBEnvironment, DBStats, RPC EXPORTS DBFileAlpine = BEGIN OPEN AE: AlpineEnvironment; ROPE: TYPE = Rope.ROPE; VersionOptions: TYPE = DBCommon.VersionOptions; bytesPerPage: INT = AE.bytesPerPage; Conversation: TYPE = AE.Conversation; OpenFileID: TYPE = AE.OpenFileID; PageCount: TYPE = AE.PageCount; PageNumber: TYPE = AE.PageNumber; TransID: TYPE = AE.TransID; AlpineTrans: TYPE = REF ANY; -- must narrow to AlpTransaction.Handle AlpineOpenFileHandle: TYPE = REF ANY; -- must narrow to AlpFile.Handle CreateTransaction: PUBLIC PROC [server: ROPE] RETURNS [t: AlpineTrans] = { needRetry: BOOL _ FALSE; haveRetried: BOOL _ FALSE; transHandle: AlpTransaction.Handle; DBStats.Starting[AlpineFileCreateTransaction]; DO instance: AlpInstance.Handle _ AlpInstance.Create[fileStore: server ! AlpInstance.Failed => IF why = authenticateFailed THEN ERROR DBEnvironment.Error[BadUserPassword] ELSE ERROR DBEnvironment.Failure[$communication, server] ]; transHandle _ AlpTransaction.Create[instance ! AlpTransaction.OperationFailed => IF why = busy THEN ERROR DBEnvironment.Failure[ $serverBusy, server]; RPC.CallFailed => TRUSTED { IF why = unbound THEN {needRetry _ TRUE; CONTINUE} -- a moderately likely failure, due to the instance cache ELSE IF why IN [timeout .. busy] THEN ERROR DBEnvironment.Failure[ $communication, server]} ]; IF NOT needRetry THEN EXIT; IF haveRetried THEN ERROR DBEnvironment.Failure[ $communication, transHandle.inst.fileStore]; needRetry _ FALSE; haveRetried _ TRUE; ENDLOOP; DBStats.Stopping[AlpineFileCreateTransaction]; RETURN[transHandle] }; FinishTransaction: PUBLIC PROC [t: AlpineTrans, abort: BOOL, continue: BOOL] = { outcome: AE.Outcome; transHandle: AlpTransaction.Handle = NARROW[t]; DBStats.Starting[AlpineFileFinishTransaction]; outcome _ transHandle.Finish[ requestedOutcome: IF abort THEN abort ELSE commit, continue: continue ! RPC.CallFailed => TRUSTED { IF why IN [timeout .. busy] THEN IF abort THEN {outcome_ abort; CONTINUE} -- so can escape when no communication! ELSE ERROR DBEnvironment.Failure[$communication, transHandle.inst.fileStore]} ]; IF NOT abort AND outcome = abort THEN ERROR DBEnvironment.Aborted[t]; DBStats.Stopping[AlpineFileFinishTransaction]; }; CreateOptionsFromVersionOptions: ARRAY VersionOptions OF AlpineInterimDirectory.CreateOptions = [NewFileOnly: newOnly, OldFileOnly: oldOnly, None: none]; VersionNumberFromOpenFile: PUBLIC PROC [f: AlpineOpenFileHandle] RETURNS [versionNumber: INT] = { fileHandle: AlpFile.Handle = NARROW[f]; BEGIN ENABLE BEGIN AlpInstance.Unknown => SELECT what FROM transID, openFileID => ERROR DBEnvironment.Aborted[fileHandle.trans]; ENDCASE => REJECT; RPC.CallFailed => TRUSTED { IF why IN [timeout .. busy] THEN ERROR DBEnvironment.Failure[ $communications, fileHandle.trans.inst.fileStore]}; END; versionList: LIST OF AlpineEnvironment.PropertyValuePair; versionProp: AlpFile.PropertySet; versionProp[version] _ TRUE; IF fileHandle = NIL THEN RETURN[versionNumber: 0]; versionList _ fileHandle.ReadProperties[versionProp]; versionNumber _ NARROW[versionList.first, AlpineEnvironment.PropertyValuePair[version]].version; END; }; OpenFile: PUBLIC PROC [t: AlpineTrans, file: Rope.Text, version: VersionOptions, discardFileContents: BOOL, nPagesInitial: INT, readOnly: BOOL, noLog: BOOL] RETURNS [f: AlpineOpenFileHandle, createdFile: BOOL, versionNumber: INT] = { ENABLE AlpInstance.Unknown => SELECT what FROM transID, openFileID => ERROR DBEnvironment.Aborted[t]; ENDCASE => REJECT; transHandle: AlpTransaction.Handle = NARROW[t]; fileHandle: AlpFile.Handle; refUniversalFile: REF AE.UniversalFile _ NIL; needRetry: BOOL _ FALSE; haveRetried: BOOL _ FALSE; DBStats.Starting[AlpineFileOpen]; DO [, refUniversalFile, createdFile] _ AlpineInterimDirectory.Open[ file, CreateOptionsFromVersionOptions[version], nPagesInitial*bytesPerPage ! AlpineInterimDirectory.Error => SELECT why FROM authenticateFailed => ERROR DBEnvironment.Error[BadUserPassword]; damaged, ownerRecordFull => REJECT; fileAlreadyExists => ERROR DBEnvironment.Error[AlreadyExists]; fileNotFound, ownerNotFound => ERROR DBEnvironment.Error[FileNotFound]; illegalFileName => ERROR DBEnvironment.Error[IllegalFileName]; insufficientPermission => ERROR DBEnvironment.Error[ProtectionViolation]; lockFailed, transAborted => {needRetry _ TRUE; CONTINUE}; quota => ERROR DBEnvironment.Error[QuotaExceeded]; remoteCallFailed, regServersUnavailable, serverNotFound => ERROR DBEnvironment.Failure[$communication, transHandle.inst.fileStore]; serverBusy => ERROR DBEnvironment.Failure[ $serverBusy, transHandle.inst.fileStore]; ENDCASE => REJECT]; -- DirectoryInconsistent {ownerRootFileNotFound} IF NOT needRetry THEN EXIT; IF haveRetried THEN ERROR DBEnvironment.Failure[ $lockConflict, transHandle.inst.fileStore]; needRetry _ FALSE; haveRetried _ TRUE; ENDLOOP; TRUSTED { ENABLE BEGIN AlpInstance.Unknown => SELECT what FROM transID, openFileID => ERROR DBEnvironment.Aborted[t]; ENDCASE => REJECT; RPC.CallFailed => IF why IN [timeout .. busy] THEN ERROR DBEnvironment.Failure[ $communications, transHandle.inst.fileStore]; END; versionList: LIST OF AlpineEnvironment.PropertyValuePair; versionProp: AlpFile.PropertySet; versionProp[version]_ TRUE; fileHandle _ AlpFile.Open[ transHandle: transHandle, universalFile: refUniversalFile^, access: IF readOnly THEN readOnly ELSE readWrite, lock: [$write, $wait], recoveryOption: IF noLog THEN $noLog ELSE $log, referencePattern: $random ! AlpInstance.AccessFailed => DBEnvironment.Error[ProtectionViolation] ].handle; IF NOT createdFile AND discardFileContents THEN fileHandle.WriteProperties[properties: LIST[[highWaterMark[highWaterMark: 0]]]]; versionList_ fileHandle.ReadProperties[versionProp]; versionNumber_ NARROW[versionList.first, AlpineEnvironment.PropertyValuePair[version]].version; }; DBStats.Stopping[AlpineFileOpen]; RETURN [fileHandle, createdFile, versionNumber]; }; ReadFilePage: PUBLIC PROC [ f: AlpineOpenFileHandle, p: CARDINAL, corePage: LONG POINTER] = { fileHandle: AlpFile.Handle = NARROW[f]; DBStats.Starting[AlpineFileReadPage]; { ENABLE BEGIN AlpInstance.Unknown => SELECT what FROM transID, openFileID => ERROR DBEnvironment.Aborted[fileHandle.trans]; ENDCASE => REJECT; RPC.CallFailed => TRUSTED { IF why IN [timeout .. busy] THEN ERROR DBEnvironment.Failure[ $communications, fileHandle.trans.inst.fileStore]}; END; fileHandle.ReadPages[ pageRun: [firstPage: p], pageBuffer: DESCRIPTOR [corePage, AE.wordsPerPage]]; }; DBStats.Stopping[AlpineFileReadPage]; }; WriteFilePage: PUBLIC PROC [ f: AlpineOpenFileHandle, p: CARDINAL, corePage: LONG POINTER] = { fileHandle: AlpFile.Handle = NARROW[f]; DBStats.Starting[AlpineFileWritePage]; { ENABLE BEGIN AlpInstance.Unknown => SELECT what FROM transID, openFileID => TRUSTED {ERROR DBEnvironment.Aborted[fileHandle.trans]}; ENDCASE => REJECT; RPC.CallFailed => TRUSTED {IF why IN [timeout .. busy] THEN ERROR DBEnvironment.Failure[$communications, fileHandle.trans.inst.fileStore]}; END; fileHandle.WritePages[ pageRun: [firstPage: p], pageBuffer: DESCRIPTOR [corePage, AE.wordsPerPage]]; }; DBStats.Stopping[AlpineFileWritePage]; }; GetSize: PUBLIC PROC [f: AlpineOpenFileHandle] RETURNS [nPages: CARDINAL] = { size: INT; fileHandle: AlpFile.Handle = NARROW[f]; DBStats.Starting[AlpineFileGetSize]; { ENABLE BEGIN AlpInstance.Unknown => SELECT what FROM transID, openFileID => ERROR DBEnvironment.Aborted[fileHandle.trans]; ENDCASE => REJECT; RPC.CallFailed => TRUSTED { IF why IN [timeout .. busy] THEN ERROR DBEnvironment.Failure[$communications, fileHandle.trans.inst.fileStore]}; END; size _ fileHandle.GetSize[]; }; DBStats.Stopping[AlpineFileGetSize]; RETURN [size]; }; SetSize: PUBLIC PROC [f: AlpineOpenFileHandle, nPages: CARDINAL] = { fileHandle: AlpFile.Handle = NARROW[f]; DBStats.Starting[AlpineFileSetSize]; { ENABLE BEGIN AlpInstance.Unknown => SELECT what FROM transID, openFileID => TRUSTED { ERROR DBEnvironment.Aborted[fileHandle.trans]}; ENDCASE => REJECT; RPC.CallFailed => TRUSTED { IF why IN [timeout .. busy] THEN ERROR DBEnvironment.Failure[$communications, fileHandle.trans.inst.fileStore]}; END; fileHandle.SetSize[size: nPages]; fileHandle.WriteProperties[properties: LIST[[byteLength[byteLength: nPages*bytesPerPage]]]]; }; DBStats.Stopping[AlpineFileSetSize]; }; END. Changed by wert on July 26, 1984 4:35:07 pm PDT -- added VersionNumberFromOpenFile[] Changed by wert on August 9, 1984 2:16:26 pm PDT -- hacked VersionNumberFromOpenFile[] to check for a NIL fileHandle