IFSFileImplC.mesa
Levin - 14-Sep-81 13:51:35
Russ Atkinson, November 9, 1983 11:50 pm
DIRECTORY
PrincOpsUtils USING [ByteBlt],
IFSFile USING [Buffer, ByteCount, Completer, CompleterArg, Position, Problem],
IFSFilePrivate USING [
FileHandle, FileObject, FileState, FreeIORequestBlock, GetIORequestBlock, IORequest],
Leaf USING [
Answer, ByteCount, FileAddress, LeafType, OpSpecificFileAddress, readOp, readAns, Request, writeOp, WriteRequest],
PrincOps USING [zEXCH],
Sequin USING [Broken, Buffer, Get, GetEmptyBuffer, Put, ReleaseBuffer];
IFSFileImplC: MONITOR
LOCKS file.LOCK USING file: IFSFilePrivate.FileHandle
IMPORTS PrincOpsUtils, IFSFilePrivate, Sequin
EXPORTS IFSFile, IFSFilePrivate = {
OPEN IFSFilePrivate;
Miscellaneous --
InvalidFile: ERROR = CODE;
LeafGibberish: ERROR = CODE;
TheWellIsDry: ERROR = CODE;
Procedures, Types, and Signals Exported to IFSFile --
FileObject: PUBLIC TYPE = IFSFilePrivate.FileObject;
Error: PUBLIC ERROR [reason: IFSFile.Problem] = CODE;
StartRead: PUBLIC PROC
[file: FileHandle, pos: IFSFile.Position, bytes: IFSFile.ByteCount, buffer: IFSFile.Buffer, callback: IFSFile.Completer, arg: IFSFile.CompleterArg] = {
request: IORequest;
ValidateFile[file];
request ← GetIORequestBlock[];
request^ ← [link: , buffer: buffer, address: PositionToFileAddress[pos], op: read,
bytesToGo: bytes, proc: callback, arg: arg];
DoRead[file, request];
};
StartWrite: PUBLIC PROC [file: FileHandle, pos: IFSFile.Position, bytes: IFSFile.ByteCount,
buffer: IFSFile.Buffer, callback: IFSFile.Completer,
arg: IFSFile.CompleterArg] = {
request: IORequest;
ValidateFile[file];
request ← GetIORequestBlock[];
request^ ← [link: , buffer: buffer,address: PositionToFileAddress[pos], op: write,
bytesToGo: bytes, proc: callback, arg: arg];
DoWrite[file, request];
};
Procedures Exported to IFSFilePrivate --
DoRead: PUBLIC PROC [file: FileHandle, request: IORequest] = {
sequinBuffer: Sequin.Buffer ← Sequin.GetEmptyBuffer[];
sequinRequest: Leaf.Request ← LOOPHOLE[sequinBuffer.data];
AcquireIOLock[file];
EnqueueRequest[file, request];
sequinRequest^ ←
[op: Leaf.readOp,
opSpecific: read[handle: file.leafHandle, address: request.address,
length: request.bytesToGo]];
sequinBuffer.nBytes ← Leaf.readOp.length;
Sequin.Put[file.sequin, sequinBuffer ! Sequin.Broken => CONTINUE];
ReleaseIOLock[file];
};
DoWrite: PUBLIC PROC [file: FileHandle, request: IORequest] = {
OPEN IFSFile;
bytesSent: Leaf.ByteCount ← 0;
bytesToGo: Leaf.ByteCount = request.bytesToGo;
initialPosition: Position = FileAddressToPosition[request.address];
positionAfterWrite: Position = initialPosition + request.bytesToGo;
AcquireIOLock[file];
IF file.length < positionAfterWrite THEN file.length ← positionAfterWrite;
EnqueueRequest[file, request];
DO
positionThisTime: Position = initialPosition + bytesSent;
buffer: Sequin.Buffer ← Sequin.GetEmptyBuffer[];
sequinRequest: Leaf.WriteRequest ← LOOPHOLE[buffer.data];
bytesThisTime: Leaf.ByteCount;
buffer.nBytes ← Leaf.writeOp.length;
bytesThisTime ← MIN[buffer.maxBytes - buffer.nBytes, bytesToGo - bytesSent];
sequinRequest^ ←
[op: Leaf.writeOp, opSpecific: write[handle: file.leafHandle,
address: PositionToFileAddress[positionThisTime, request.address.opSpecific],
length: bytesThisTime, writeBody: ]];
[] ← PrincOpsUtils.ByteBlt[
to: [blockPointer: @sequinRequest.writeBytes,
startIndex: 0, stopIndexPlusOne: bytesThisTime],
from: [blockPointer: request.buffer,
startIndex: bytesSent, stopIndexPlusOne: bytesSent+bytesThisTime]];
sequinRequest.op.length ← buffer.nBytes ← buffer.nBytes + bytesThisTime;
Sequin.Put[file.sequin, buffer ! Sequin.Broken => EXIT];
bytesSent ← bytesSent + bytesThisTime;
IF bytesSent = bytesToGo THEN EXIT;
ENDLOOP;
ReleaseIOLock[file];
};
FileWatcher: PUBLIC PROC [file: FileHandle] RETURNS [BOOL] = {
current: IORequest ← NIL;
fatalError: BOOLFALSE;
offset: CARDINAL;
DO
buffer: Sequin.Buffer;
outcome: IFSFile.Problem;
CheckState: ENTRY PROC [file: FileHandle] RETURNS [FileState] = {
DO
SELECT file.state FROM
alive => IF fatalError THEN file.state ← flush;
flush => NULL;
closing => {
IF file.ioPending = NIL THEN {file.state ← closed; BROADCAST file.ioSynch};
EXIT
};
ENDCASE;
IF file.ioPending ~= NIL THEN EXIT;
WAIT file.ioSynch;
ENDLOOP;
RETURN[file.state]
};
CompleteRequest: PROC = {
request: IORequest = DequeueRequest[file];
request.proc[request.arg, outcome];
FreeIORequestBlock[request];
current ← NIL;
};
SELECT CheckState[file] FROM
alive => {
answer: Leaf.Answer;
position: IFSFile.Position;
EnsureCurrent: ENTRY PROC [file: FileHandle] = {
IF current ~= NIL THEN RETURN;
IF file.ioPending = NIL THEN ERROR TheWellIsDry;
current ← file.ioPending.link;
position ← FileAddressToPosition[current.address];
offset ← 0;
};
ValidateArrival: PROC [address: Leaf.FileAddress, bytes: Leaf.ByteCount] = {
pos: IFSFile.Position = FileAddressToPosition[address];
IF current.op = answer.op.type AND pos = position THEN {
outcome ← ok;
current.bytesToGo ← current.bytesToGo - bytes;
position ← position + bytes;
}
ELSE fatalError ← TRUE;
};
EnsureCurrent[file];
outcome ← other;
buffer ← Sequin.Get[file.sequin ! Sequin.Broken =>
{outcome ← io; fatalError ← TRUE; LOOP}];
answer ← LOOPHOLE[buffer.data];
IF answer.op.sense ~= reply THEN GO TO protocolViolation;
WITH ans: answer SELECT answer.op.type FROM
error => {
IF current.op ~= ans.errorOp.type THEN GO TO protocolViolation;
SELECT ans.error FROM
accessDenied, fileBusy,
IN [userName .. connectPassword] => outcome ← credentials;
allocExceeded, fileSystemFull => outcome ← resources;
brokenLeaf => {outcome ← io; GO TO broken};
illegalRead, illegalWrite => outcome ← illegalIO;
IN [unimplementedOp..illegalTruncate] => ERROR LeafGibberish;
ENDCASE;
current.bytesToGo ← 0;
};
read => {
dataBytes: Leaf.ByteCount = ans.op.length - Leaf.readAns.length;
ValidateArrival[ans.address, dataBytes];
[] ← PrincOpsUtils.ByteBlt[
to: [blockPointer: current.buffer,
startIndex: offset, stopIndexPlusOne: offset+dataBytes],
from: [blockPointer: @ans.readBytes,
startIndex: 0, stopIndexPlusOne: dataBytes]];
offset ← offset + dataBytes;
};
write => ValidateArrival[ans.address, ans.length];
ENDCASE => GO TO protocolViolation;
IF current.bytesToGo = 0 THEN CompleteRequest[];
Sequin.ReleaseBuffer[buffer];
EXITS
protocolViolation, broken =>
{fatalError ← TRUE; Sequin.ReleaseBuffer[buffer]};
};
flush => CompleteRequest[];
closing => {outcome ← other; CompleteRequest[]};
closed => EXIT;
ENDCASE;
ENDLOOP;
RETURN[~fatalError]
};
ValidateFile: PUBLIC PROC [file: FileHandle] =
{IF file.seal ~= openSeal THEN ERROR InvalidFile};
FileAddressToPosition: PUBLIC PROC [fa: Leaf.FileAddress]
RETURNS [pos: IFSFile.Position] = {
Note: regards 'fa' as a signed quantity (to allow leader page access).
Flip: PROC [Leaf.FileAddress] RETURNS [IFSFile.Position] =
MACHINE CODE { PrincOps.zEXCH; };
fa.opSpecific ← Leaf.OpSpecificFileAddress[open[fill: IF fa.high > 1777B THEN 37B ELSE 0]];
RETURN[Flip[fa]]
};
PositionToFileAddress: PUBLIC PROC [
pos: IFSFile.Position, opSpecific: Leaf.OpSpecificFileAddress ← [read[]]]
RETURNS [fa: Leaf.FileAddress] = {
Note: regards 'pos' as a signed quantity (to allow leader page access).
Flip: PROC [IFSFile.Position] RETURNS [Leaf.FileAddress] =
MACHINE CODE { PrincOps.zEXCH; };
fa ← Flip[pos];
fa.opSpecific ← opSpecific;
};
Internal Procedures --
AcquireIOLock: ENTRY PROC [file: FileHandle] = {
WHILE file.locked DO WAIT file.ioSynch ENDLOOP;
file.locked ← TRUE;
};
ReleaseIOLock: ENTRY PROC [file: FileHandle] = {
file.locked ← FALSE;
BROADCAST file.ioSynch;
};
EnqueueRequest: ENTRY PROC [file: FileHandle, request: IORequest] = {
IF file.ioPending = NIL THEN request.link ← request
ELSE {request.link ← file.ioPending.link; file.ioPending.link ← request};
file.ioPending ← request;
BROADCAST file.ioSynch;
};
DequeueRequest: ENTRY PROC [file: FileHandle] RETURNS [request: IORequest] = {
IF file.ioPending = NIL THEN ERROR TheWellIsDry;
request ← file.ioPending.link;
file.ioPending.link ← request.link;
IF request = file.ioPending THEN file.ioPending ← NIL;
};
}.