<> <> <> <> DIRECTORY IFSFile USING [AccessFailure, CantOpen, Error], IFSFilePrivate USING [FileHandle, FileObject, FSInstance], Rope USING [Equal, ROPE]; IFSFileImplE: MONITOR LOCKS fs.LOCK USING fs: IFSFilePrivate.FSInstance IMPORTS IFSFile, Rope EXPORTS IFSFilePrivate = { OPEN IFSFilePrivate; ROPE: TYPE = Rope.ROPE; <> FileListSmashed: ERROR = CODE; FileNotInList: ERROR = CODE; IllegalDestroy: ERROR = CODE; InvalidFile: ERROR = CODE; <> InsertFile: PUBLIC PROC [fs: FSInstance, name: ROPE, openProc: PROC [FileHandle] RETURNS [IFSFile.AccessFailure]] RETURNS [file: FileHandle] = { NewlyOpened: ENTRY PROC [fs: FSInstance] RETURNS [BOOL] = { <> file _ fs.fileList; UNTIL file = NIL DO IF Rope.Equal[file.name, name, FALSE] THEN { SELECT file.seal FROM openSeal => {file.openCount _ file.openCount + 1; RETURN[FALSE]}; underwaySeal => {WAIT fs.changeInOpenState; file _ fs.fileList}; ENDCASE => GO TO bogusList } ELSE -- ensure pointer validity SELECT file.seal FROM openSeal, underwaySeal => file _ file.link; ENDCASE => GO TO bogusList; REPEAT bogusList => ERROR FileListSmashed; ENDLOOP; file _ NEW[FileObject _ FileObject[link: fs.fileList, name: name, fs: fs]]; fs.fileList _ file; RETURN[TRUE] }; AnnounceOutcome: ENTRY PROC [fs: FSInstance, outcome: IFSFile.AccessFailure] = { IF outcome = ok THEN file.seal _ openSeal ELSE RemoveFile[fs, file]; BROADCAST fs.changeInOpenState; SELECT outcome FROM ok => RETURN; io => RETURN WITH ERROR IFSFile.Error[io]; ENDCASE => RETURN WITH ERROR IFSFile.CantOpen[outcome]; }; IF NewlyOpened[fs] THEN AnnounceOutcome[fs, openProc[file]]; }; ReleaseFile: PUBLIC PROC [file: FileHandle, closeProc: PROC [FileHandle]] = { IF LastReference[file.fs, file] THEN {closeProc[file]; FlushFile[file]}; }; PurgeFile: PUBLIC PROC [file: FileHandle, destroyProc: PROC [FileHandle]] = { IF LastReference[file.fs, file] THEN {destroyProc[file]; FlushFile[file]} ELSE ERROR IllegalDestroy; }; <> LastReference: ENTRY PROC [fs: FSInstance, file: FileHandle] RETURNS [last: BOOL] = { IF file.seal ~= openSeal OR file.openCount = 0 THEN ERROR InvalidFile; IF (last _ file.openCount = 1) THEN file.seal _ underwaySeal ELSE file.openCount _ file.openCount - 1; RETURN[last] }; FlushFile: PROC [file: FileHandle] = { DoRemoveFile: ENTRY PROC [fs: FSInstance] = {RemoveFile[fs, file]; BROADCAST fs.changeInOpenState}; DoRemoveFile[file.fs]; }; RemoveFile: INTERNAL PROC [fs: FSInstance, file: FileHandle] = { <> IF file = fs.fileList THEN fs.fileList _ file.link ELSE { IF fs.fileList = NIL THEN GO TO Trouble; FOR prev: FileHandle _ fs.fileList, prev.link UNTIL prev.link = NIL DO IF prev.link = file THEN {prev.link _ file.link; EXIT}; REPEAT FINISHED => GO TO Trouble; ENDLOOP; EXITS Trouble => ERROR FileNotInList; }; file.seal _ closedSeal; -- try to catch dangling references }; }.