DBFileAlpineImpl.mesa - Alpine implementation of file level of Cypress
Copyright © 1985 by Xerox Corporation. All rights reserved.
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
Wert, August 9, 1984 2:18:42 pm PDT
Willie-Sue, April 25, 1985 12:52:32 pm PST
Russ Atkinson (RRA) March 20, 1985 9:35:05 pm PST
Carl Hauser, May 14, 1985 1:50:01 pm PDT
Widom, July 22, 1985 2:29:32 pm PDT
Donahue, March 21, 1986 8:17:00 am PST
DIRECTORY
AlpineEnvironment USING[bytesPerPage, Conversation, LockOption, OpenFileID, Outcome, PageCount, PageNumber, TransID, UniversalFile, wordsPerPage, PropertyValuePair],
AlpFile USING[Close, GetSize, Handle, ReadPages, SetSize, PropertySet, WritePages, ReadProperties, WriteProperties, Unknown],
AlpineDirectory USING[Error, OpenFile, CreateOptions],
AlpInstance USING[AccessFailed, Create, Failed, Handle, Unknown],
AlpTransaction USING[Create, Handle, Finish, OperationFailed],
DB USING[Aborted, Failure, Error],
DBCommon USING[VersionOptions],
DBFileAlpine,
DBStats USING[Starting, Stopping],
RPC USING[CallFailed],
DBFileAlpineImpl:
CEDAR
PROGRAM
IMPORTS AlpFile, AlpineDirectory, AlpInstance, AlpTransaction, DB, 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 DB.Error[BadUserPassword]
ELSE ERROR DB.Failure[$communication, server]
];
transHandle ← AlpTransaction.Create[instance !
AlpTransaction.OperationFailed =>
IF why = busy
THEN
ERROR DB.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 DB.Failure[$communication, server]}
];
IF NOT needRetry THEN EXIT;
IF haveRetried
THEN
ERROR DB.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
DB.Failure[$communication, transHandle.inst.fileStore]}
];
DBStats.Stopping[AlpineFileFinishTransaction];
IF NOT abort AND outcome = abort THEN ERROR DB.Aborted;
};
CreateOptionsFromVersionOptions:
ARRAY VersionOptions
OF AlpineDirectory.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 DB.Aborted;
ENDCASE => REJECT;
RPC.CallFailed =>
TRUSTED {
IF why
IN [timeout .. busy]
THEN
ERROR DB.Failure[$communications, fileHandle.trans.inst.fileStore]};
END;
versionList: LIST OF AlpineEnvironment.PropertyValuePair;
versionProp: AlpFile.PropertySet;
IF fileHandle = NIL THEN RETURN[versionNumber: 0];
versionProp[version] ← TRUE;
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, lock: AlpineEnvironment.LockOption, readOnly:
BOOL]
RETURNS [f: AlpineOpenFileHandle, createdFile:
BOOL] = {
ENABLE
AlpInstance.Unknown =>
SELECT what
FROM
transID, openFileID => ERROR DB.Aborted;
ENDCASE => REJECT;
transHandle: AlpTransaction.Handle = NARROW[t];
fileHandle: AlpFile.Handle;
refUniversalFile: REF AE.UniversalFile ← NIL;
DBStats.Starting[AlpineFileOpen];
TRUSTED BEGIN
ENABLE {
AlpineDirectory.Error =>
SELECT type
FROM
authenticateFailed => ERROR DB.Error[BadUserPassword];
damaged, ownerRecordFull => REJECT;
fileAlreadyExists => ERROR DB.Error[AlreadyExists];
fileNotFound, ownerNotFound => ERROR DB.Error[FileNotFound];
illegalFileName => ERROR DB.Error[IllegalFileName];
insufficientPermission => ERROR DB.Error[ProtectionViolation];
lockFailed, transAborted =>
ERROR DB.Failure[$lockConflict, transHandle.inst.fileStore];
quota => ERROR DB.Error[QuotaExceeded];
remoteCallFailed, regServersUnavailable, serverNotFound =>
ERROR DB.Failure[$communication, transHandle.inst.fileStore];
serverBusy =>
ERROR DB.Failure[$serverBusy, transHandle.inst.fileStore];
ENDCASE => REJECT; -- DirectoryInconsistent {ownerRootFileNotFound}
RPC.CallFailed =>
IF why
IN [timeout .. busy]
THEN
ERROR
DB.Failure[$communications, transHandle.inst.fileStore];
};
[fileHandle, createdFile, ] ← AlpineDirectory.OpenFile[trans: transHandle, name: file, access: IF readOnly THEN readOnly ELSE readWrite, lock: lock, recoveryOption: $log, createOptions: CreateOptionsFromVersionOptions[version], referencePattern: $random ! AlpInstance.AccessFailed => DB.Error[ProtectionViolation] ]
END;
IF
NOT createdFile
AND discardFileContents
THEN
fileHandle.WriteProperties[properties: LIST[[highWaterMark[highWaterMark: 0]]]];
DBStats.Stopping[AlpineFileOpen];
RETURN [fileHandle, createdFile];
};
Close:
PUBLIC PROC [f: AlpineOpenFileHandle] ~ {
fileHandle: AlpFile.Handle = NARROW[f];
AlpFile.Close[fileHandle ! AlpFile.Unknown => CONTINUE]
};
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 DB.Aborted;
ENDCASE => REJECT;
RPC.CallFailed =>
TRUSTED {
IF why
IN [timeout .. busy]
THEN
ERROR DB.Failure[$communications, fileHandle.trans.inst.fileStore]};
END;
TRUSTED {
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 DB.Aborted};
ENDCASE => REJECT;
RPC.CallFailed =>
TRUSTED {
IF why
IN [timeout .. busy]
THEN
ERROR DB.Failure[$communications, fileHandle.trans.inst.fileStore]};
END;
TRUSTED {
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 DB.Aborted;
ENDCASE => REJECT;
RPC.CallFailed =>
TRUSTED {
IF why
IN [timeout .. busy]
THEN
ERROR DB.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 DB.Aborted};
ENDCASE => REJECT;
RPC.CallFailed =>
TRUSTED {
IF why
IN [timeout .. busy]
THEN
ERROR DB.Failure[$communications, fileHandle.trans.inst.fileStore]};
END;
fileHandle.SetSize[size: nPages];
fileHandle.WriteProperties[properties: LIST[[byteLength[byteLength: nPages*bytesPerPage]]]];
};
DBStats.Stopping[AlpineFileSetSize];
};
END.