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