-- File: AltoFileList.mesa
-- Last edited by Levin: 30-Apr-81 16:23:53
DIRECTORY
AltoFile USING [FP],
AltoFilePrivate USING [
closedSeal, FileHandle, FileObject, openSeal, underwaySeal],
DiskIODefs USING [DiskError, FID],
VMStorage USING [shortTerm];
AltoFileList: MONITOR LOCKS lock↑
IMPORTS DiskIODefs, VMStorage
EXPORTS AltoFile, AltoFilePrivate =
BEGIN OPEN AltoFilePrivate;
-- Global Variables --
fileList: FileHandle;
lock: POINTER TO MONITORLOCK;
LOCK: MONITORLOCK;
ChangeInOpenState: CONDITION;
-- Miscellaneous Declarations --
DuplicateSerialNumbers: ERROR = CODE;
FileInUse: ERROR = CODE;
FileListSmashed: ERROR = CODE;
FileNotInList: ERROR = CODE;
IllegalDestroy: ERROR = CODE;
InvalidFile: ERROR = CODE;
-- Procedures and Signals Exported to AltoFilePrivate --
InitializeFileList: PUBLIC PROCEDURE =
{lock ← @LOCK; fileList ← NIL};
FinalizeFileList: PUBLIC PROCEDURE =
{IF fileList ~= NIL THEN ERROR FileInUse};
InsertFile: PUBLIC PROCEDURE [
fp: AltoFile.FP, openProc: PROCEDURE [FileHandle, BOOLEAN] RETURNS [BOOLEAN]]
RETURNS [FileHandle] =
BEGIN
OpenState: TYPE = {newlyOpened, wasOpen};
file: FileHandle;
CheckOpenState: ENTRY PROCEDURE RETURNS [OpenState] = INLINE
-- checks to see if the file identified by 'fp' has already been entered in the
-- file list. If not, it enters it and indicates that the open operation is
-- underway. If so, it waits for any previous open attempt to be resolved, then
-- reports the outcome.
BEGIN
fID: DiskIODefs.FID = [version: 1, serial: fp.serial];
file ← fileList;
UNTIL file = NIL DO
IF file.fileID = fID THEN
BEGIN
IF file.leadervDA ~= fp.leaderDA THEN ERROR DuplicateSerialNumbers;
SELECT file.seal FROM
openSeal => {file.openCount ← file.openCount + 1; RETURN[wasOpen]};
underwaySeal => {WAIT ChangeInOpenState; file ← fileList};
ENDCASE => GO TO BogusList
END
ELSE -- ensure pointer validity
SELECT file.seal FROM
openSeal, underwaySeal => file ← file.link;
ENDCASE => GO TO BogusList;
REPEAT BogusList => ERROR FileListSmashed;
ENDLOOP;
file ← VMStorage.shortTerm.NEW[FileObject ← FileObject[
fileID: fID, leadervDA: fp.leaderDA, lengthKnown: FALSE, lastPage:, bytes:,
lengthChanged:, openCount: 1, link: fileList, seal: underwaySeal, nRuns:,
runTable:]];
fileList ← file;
RETURN[newlyOpened]
END;
AnnounceOutcome: ENTRY PROCEDURE [success: BOOLEAN] = INLINE
BEGIN
IF success THEN file.seal ← openSeal ELSE RemoveFile[file];
BROADCAST ChangeInOpenState;
IF ~success THEN RETURN WITH ERROR InvalidFP[fp];
END;
SELECT CheckOpenState[] FROM
newlyOpened => AnnounceOutcome[openProc[file, TRUE]];
wasOpen => [] ← openProc[file, FALSE];
ENDCASE;
RETURN[file]
END;
InvalidFP: PUBLIC ERROR [fp: AltoFile.FP] = CODE;
ReleaseFile: PUBLIC PROCEDURE [file: FileHandle, closeProc: PROCEDURE [FileHandle]] =
BEGIN
IF LastReference[file] THEN
BEGIN
closeProc[file ! DiskIODefs.DiskError => CONTINUE];
FlushFile[file];
END;
END;
PurgeFile: PUBLIC PROCEDURE [
file: FileHandle, destroyProc: PROCEDURE [FileHandle]] =
BEGIN
IF LastReference[file] THEN
BEGIN
destroyProc[file ! DiskIODefs.DiskError => CONTINUE];
FlushFile[file];
END
ELSE ERROR IllegalDestroy;
END;
-- Internal Procedures --
LastReference: ENTRY PROCEDURE [file: FileHandle] RETURNS [last: BOOLEAN] =
BEGIN
IF file.seal ~= openSeal OR file.openCount = 0 THEN
RETURN WITH ERROR InvalidFile;
IF (last ← file.openCount = 1) THEN file.seal ← underwaySeal
ELSE file.openCount ← file.openCount - 1;
RETURN[last]
END;
FlushFile: PROCEDURE [file: FileHandle] =
BEGIN
DoRemoveFile: ENTRY PROCEDURE = INLINE
{RemoveFile[file]; BROADCAST ChangeInOpenState};
DoRemoveFile[];
VMStorage.shortTerm.FREE[@file];
END;
RemoveFile: INTERNAL PROCEDURE [file: FileHandle] =
-- removes the file from the list.
BEGIN
IF file = fileList THEN fileList ← file.link
ELSE
BEGIN
IF fileList = NIL THEN GO TO Trouble;
FOR prev: FileHandle ← fileList, prev.link UNTIL prev.link = NIL DO
IF prev.link = file THEN BEGIN prev.link ← file.link; EXIT END;
REPEAT FINISHED => GO TO Trouble;
ENDLOOP;
EXITS Trouble => ERROR FileNotInList;
END;
file.seal ← closedSeal; -- try to catch dangling references
END;
END.