-- 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.