-- File: AltoFileOpsC.mesa -- Last edited by Levin: 30-Apr-81 16:22:32 DIRECTORY AltoFilePrivate USING [ FileHandle, EnterPageAndvDA, FindLastKnownPage, LastPageBytes, minUsefulRuns], DiskIODefs USING [ CompletionStatus, DiskError, DiskRequest, eofvDA, fillInvDA, InitiateDiskIO, PageCount, RequestID, vDA, VerboseCompletionProcedure, XferSpec], VMStorage USING [AllocatePage, FreePage]; AltoFileOpsC: MONITOR LOCKS synch.LOCK USING synch: IOSynch IMPORTS AltoFilePrivate, DiskIODefs, VMStorage EXPORTS AltoFilePrivate = BEGIN OPEN AltoFilePrivate, DiskIODefs; -- Types -- IOSynch: TYPE = POINTER TO SynchRecord; SynchRecord: TYPE = MONITORED RECORD [status: CompletionStatus, ioDone: CONDITION]; -- Miscellaneous Declarations -- notLastXfer: RequestID = 776B; lastXfer: RequestID = 777B; -- Procedures Exported to AltoFilePrivate -- DoDiskRequest: PUBLIC PROCEDURE [ req: POINTER TO DiskRequest, file: FileHandle ← NIL, signalError: BOOLEAN ← TRUE] RETURNS [CompletionStatus, vDA, LastPageBytes] = BEGIN ioSynch: SynchRecord; finalXfer: CARDINAL = LENGTH[req.xfers] - 1; tempStatus: CompletionStatus; lastBytes: LastPageBytes; next: vDA; IssueRequestAndWait: ENTRY PROCEDURE [synch: IOSynch] = INLINE -- issues disk request and waits for completion. BEGIN DO synch.status ← noStatus; tempStatus ← ok; InitiateDiskIO[req]; WHILE synch.status = noStatus DO WAIT synch.ioDone; ENDLOOP; IF synch.status ~= neverStarted THEN EXIT; ENDLOOP; END; DoNotify: ENTRY PROCEDURE [synch: IOSynch] = INLINE -- issues disk request and waits for completion. {synch.status ← tempStatus; NOTIFY synch.ioDone}; ProcessCompletion: VerboseCompletionProcedure = -- invoked by completion of operation to post result. BEGIN IF tempStatus = ok THEN IF (tempStatus ← status) = ok THEN BEGIN next ← label.next; lastBytes ← label.bytes; IF file ~= NIL AND file.nRuns >= minUsefulRuns THEN -- hack, but safe outside file monitor BEGIN EnterPageAndvDA[file, label.page, header.diskAddress]; EnterPageAndvDA[file, label.page - 1, label.prev]; EnterPageAndvDA[file, label.page + 1, label.next]; END; END; IF id = lastXfer THEN DoNotify[@ioSynch]; END; req.proc ← [verbose[ProcessCompletion]]; IF file ~= NIL THEN req.fileID ← file.fileID; req.nonXferID ← notLastXfer; FOR i: CARDINAL IN [0..finalXfer) DO req.xfers[i].id ← notLastXfer; ENDLOOP; req.xfers[finalXfer].id ← lastXfer; InitSynch[@ioSynch]; IssueRequestAndWait[@ioSynch]; IF ioSynch.status ~= ok AND signalError THEN ERROR DiskError[ioSynch.status]; RETURN[ioSynch.status, next, lastBytes] END; FindEOF: PUBLIC PROCEDURE [file: FileHandle] = BEGIN buffer: POINTER ← VMStorage.AllocatePage[]; -- needed only if file is huge xferSpec: ARRAY [0..1) OF XferSpec ← [[buffer, fillInvDA, lastXfer]]; state, tempState: {none, eofFound, needsRestart, fatalError}; request: DiskRequest; ioSynch: SynchRecord; prevNext: vDA; finalStatus: CompletionStatus; IssueRequestAndWait: ENTRY PROCEDURE [synch: IOSynch] = INLINE -- initiates disk request and waits for completion. BEGIN state ← tempState ← none; InitiateDiskIO[@request]; WHILE state = none DO WAIT synch.ioDone; ENDLOOP; END; DoNotify: ENTRY PROCEDURE [synch: IOSynch] = INLINE -- initiates disk request and waits for completion. {state ← tempState; NOTIFY synch.ioDone}; CheckCompletion: VerboseCompletionProcedure = -- processes completions, and notifies the main body of FindEOF when all has -- finished. BEGIN IF tempState = none THEN SELECT (finalStatus ← status) FROM ok => BEGIN file.lastPage ← label.page; file.bytes ← label.bytes; prevNext ← label.next; EnterPageAndvDA[file, label.page, header.diskAddress]; IF id = lastXfer THEN tempState ← needsRestart; -- huge file! END; checkError => tempState ← IF prevNext = eofvDA THEN eofFound ELSE fatalError; neverStarted => tempState ← needsRestart; ENDCASE => tempState ← fatalError; IF id = lastXfer THEN DoNotify[@ioSynch]; END; InitSynch[@ioSynch]; DO lastvDA: vDA; [file.lastPage, lastvDA] ← FindLastKnownPage[file]; request ← [firstPage: file.lastPage, fileID: file.fileID, firstPagevDA: lastvDA, pagesToSkip: LAST[PageCount] - file.lastPage, nonXferID: notLastXfer, proc: [verbose[CheckCompletion]], xfers: DESCRIPTOR[@xferSpec, 1], noRestore: TRUE, -- expect check error at EOF command: ReadD[]]; IssueRequestAndWait[@ioSynch]; SELECT state FROM eofFound => {file.lengthKnown ← TRUE; EXIT}; needsRestart => NULL; fatalError => {VMStorage.FreePage[buffer]; ERROR DiskError[finalStatus]}; ENDCASE; ENDLOOP; VMStorage.FreePage[buffer]; END; -- Internal Procedures -- InitSynch: PROCEDURE [synch: IOSynch] = INLINE {synch.ioDone.timeout ← 0}; END.