EstablishOpenFileContext:
PUBLIC
PROCEDURE [conversation: AlpineEnvironment.Conversation, openFileID: AlpineEnvironment.OpenFileID, work: OpenFileWork, workLevel: AlpineInternal.WorkLevel ← normal, concurrency: Concurrency ← normal] =
BEGIN
openFile: OpenFileMap.Handle = OpenFileMap.GetAndCheckHandle[conversation: conversation, openFileID: openFileID !
OpenFileMap.OpenFileIDNotFound, OpenFileMap.BadConversation => GOTO unknownOpenFileID];
fileInstance: FileInstance.Handle = OpenFileMap.GetFileInstanceHandle[openFile];
file: FileMap.Handle = FileInstance.GetFileHandle[fileInstance];
trans: TransactionMap.Handle = FileInstance.GetTransHandle[fileInstance];
updateCost: INT;
lockFailure: AlpineEnvironment.LockFailure;
operationFailure: AlpineEnvironment.OperationFailure;
unknownType: AlpineEnvironment.UnknownType;
DO
IF concurrency=normal
THEN
BEGIN
IF ~TransactionMap.StartWork[trans, workLevel]
THEN
ERROR AlpineFile.Unknown[transID]; -- trans must already have finished
END
ELSE StartExclusiveWork[file, trans, workLevel];
BEGIN
ENABLE {
Informational signal from AcquireFileInterlock:
AcquireInterlockAttempted => {
IF ~failed THEN {concurrency ← exclusive; RESUME}
ELSE GOTO acquireInterlockFailed};
Public error. Clean up local state and then allow signal to propagate. n.b. this does the right thing only for ERRORs, not SIGNALs.
AlpineFile.AccessFailed, AlpineFile.LockFailed, AlpineFile.OperationFailed, AlpineFile.StaticallyInvalid, AlpineFile.Unknown => {
IF concurrency=normal THEN TransactionMap.StopWork[trans, updateCost]
ELSE StopExclusiveWork[file, trans, updateCost]}};
description: LogMap.FileDescription = LogMap.DescribeFile[file, trans];
IF description.registered
AND ~description.exists
THEN
ERROR AlpineFile.Unknown[openFileID]; -- most likely the file has been deleted by this transaction
updateCost ← 0;
BEGIN
work[openFile: openFile, fileInstance: fileInstance, file: file, trans: trans, pUpdateCost: @updateCost !
Lock.Failed => {lockFailure ← why; GOTO lockFailed};
AccessControl.LockFailed => {lockFailure ← why; GOTO ACLockFailed};
AccessControl.OperationFailed => {operationFailure ← why; GOTO operationFailed};
AccessControl.StaticallyInvalid => GOTO staticallyInvalid;
AccessControl.Unknown => {unknownType ← why; GOTO unknown};
Lock.TransAborting => {unknownType ← transID; GOTO unknown}];
EXITS
lockFailed => {
logProc: PROC [SkiPatrolLog.LockConflictInfo];
IF (logProc ← SkiPatrolLog.notice.lockConflict) #
NIL
THEN
logProc[[
what: lockFailure,
where: "FilePrivateImpl.EstablishOpenFileContext",
transID: SkiPatrolHooks.FileInfoToTransID[conversation, openFileID],
mode: none,
specifics: entireFile[FileMap.GetName[FileInstance.GetFileHandle[fileInstance]]],
message: "(sigh) attempted locking mode unknown"
]];
ERROR AlpineFile.LockFailed[lockFailure];
};
(SkiPatrolLog logging is done elsewhere for the rest of the errors)
ACLockFailed => ERROR AlpineFile.LockFailed[lockFailure];
operationFailed => ERROR AlpineFile.OperationFailed[operationFailure];
staticallyInvalid => ERROR AlpineFile.StaticallyInvalid;
unknown => ERROR AlpineFile.Unknown[unknownType];
END;
GOTO finished;
EXITS
acquireInterlockFailed =>
BEGIN
TransactionMap.StopWork[trans, updateCost];
AwaitExclusiveWorkCompleted[];
now loop back to "BEGIN ENABLE ..."
END;
END;
ENDLOOP;
IF concurrency=normal THEN TransactionMap.StopWork[trans, updateCost]
ELSE StopExclusiveWork[file, trans, updateCost];
EXITS
unknownOpenFileID => ERROR AlpineFile.Unknown[openFileID];
END;
AcquireFileInterlock:
PUBLIC
PROCEDURE [file: FileMap.Handle] =
BEGIN
SIGNAL AcquireInterlockAttempted[failed: FileMap.SetInterlock[file, TRUE]]; -- RESUMEd only if failed=FALSE
END;
EstablishTransactionContext:
PUBLIC
PROCEDURE [conversation: AlpineEnvironment.Conversation, transID: AlpineEnvironment.TransID, work: TransactionWork, workLevel: AlpineInternal.WorkLevel ← normal] =
BEGIN
trans: TransactionMap.Handle = TransactionMap.GetHandle[transID];
updateCost: INT;
lockFailure: AlpineEnvironment.LockFailure;
operationFailure: AlpineEnvironment.OperationFailure;
unknownType: AlpineEnvironment.UnknownType;
IF trans=TransactionMap.nullHandle
OR ~TransactionMap.StartWork[trans, workLevel]
THEN
ERROR AlpineFile.Unknown[transID];
BEGIN
ENABLE
Public error. Clean up local state and then allow signal to propagate. n.b. this does the right thing only for ERRORs, not SIGNALs.
AlpineFile.AccessFailed, AlpineFile.LockFailed, AlpineFile.OperationFailed, AlpineFile.StaticallyInvalid, AlpineFile.Unknown =>
TransactionMap.StopWork[trans, updateCost];
updateCost ← 0;
BEGIN -- extra nesting so errors raised from EXITS clause are caught by the ENABLE
work[trans: trans, pUpdateCost: @updateCost !
Lock.Failed => {lockFailure ← why; GOTO lockFailed};
AccessControl.LockFailed => {lockFailure ← why; GOTO ACLockFailed};
AccessControl.OperationFailed => {operationFailure ← why; GOTO operationFailed};
AccessControl.StaticallyInvalid => GOTO staticallyInvalid;
AccessControl.Unknown => {unknownType ← why; GOTO unknown};
Lock.TransAborting => {unknownType ← transID; GOTO unknown}];
EXITS
lockFailed => {
logProc: PROC [SkiPatrolLog.LockConflictInfo];
IF (logProc ← SkiPatrolLog.notice.lockConflict) #
NIL
THEN
logProc[[
what: lockFailure,
where: "FilePrivateImpl.EstablishTransactionContext",
transID: transID,
mode: none,
specifics: unknown[],
message: "(sigh) attempted locking mode unknown"
]];
ERROR AlpineFile.LockFailed[lockFailure];
};
(SkiPatrolLog logging is done elsewhere for the rest of the errors)
ACLockFailed => ERROR AlpineFile.LockFailed[lockFailure];
operationFailed => ERROR AlpineFile.OperationFailed[operationFailure];
staticallyInvalid => ERROR AlpineFile.StaticallyInvalid;
unknown => ERROR AlpineFile.Unknown[unknownType];
END;
END;
TransactionMap.StopWork[trans, updateCost];
END;