-- File: IFSFileOpsB.mesa
-- Last edited by Levin: 27-Feb-81 11:30:04
-- edit by Randy: 27-Jul-81 19:58:12
-- edit by Andrew: 11-Feb-81 14:51:07
DIRECTORY
ByteBlt USING [ByteBlt],
Environment USING [Block],
FileDefs USING [
Buffer, bytesPerPage, ComparePositions, Completer, CompleterArg,
IncrementPosition, PageNumber, Position, Problem],
IFSFilePrivate USING [
FileHandle, FileState, FreeIORequestBlock, GetIORequestBlock, IORequest,
openSeal],
Leaf USING [
Answer, ByteCount, FileAddress, LeafType, OpSpecificFileAddress, readOp,
readAns, Request, writeOp, WriteRequest],
Sequin USING [Broken, Buffer, Get, GetEmptyBuffer, Put, ReleaseBuffer];
IFSFileOpsB: MONITOR LOCKS file.LOCK USING file: IFSFilePrivate.FileHandle
IMPORTS ByteBlt, FileDefs, IFSFilePrivate, Sequin EXPORTS IFSFilePrivate =
BEGIN OPEN IFSFilePrivate;
-- Types --
FAPieces: TYPE = MACHINE DEPENDENT RECORD [
ugh(0): SELECT OVERLAID * FROM
fa => [fa(0): Leaf.FileAddress],
faPieces => [
opSpecific(0:0..4): Leaf.OpSpecificFileAddress,
faHigh(0:5..15): [0..3777B],
faMiddle(1:0..6): [0..177B],
faLow(1:7..15): [0..777B]],
posPieces => [
posHigh(0:0..8): [0..777B],
posMiddle(0:9..15): [0..177B],
posLow(1:0..15): [0..777B]],
position => [pos(0): FileDefs.Position],
ENDCASE];
-- Miscellaneous --
bytesPerPage: CARDINAL = FileDefs.bytesPerPage;
FileTooBig: ERROR = CODE;
InvalidFile: ERROR = CODE;
LeafGibberish: ERROR = CODE;
TheWellIsDry: ERROR = CODE;
MoveBytes: PROCEDURE [
to, from: LONG POINTER, toByte, fromByte, nBytes: CARDINAL] = INLINE
BEGIN
toBlock, fromBlock: Environment.Block;
toBlock ← [
blockPointer: to, startIndex: toByte, stopIndexPlusOne: toByte + nBytes];
fromBlock ← [
blockPointer: from, startIndex: fromByte,
stopIndexPlusOne: fromByte + nBytes];
[] ← ByteBlt.ByteBlt[toBlock, fromBlock];
END;
-- Operations (exported to IFSFilePrivate on behalf of FileDefs) --
StartRead: PUBLIC PROCEDURE [
file: FileHandle, page: FileDefs.PageNumber, buffer: FileDefs.Buffer,
callback: FileDefs.Completer, arg: FileDefs.CompleterArg] =
BEGIN
request: IORequest;
bytesToRead: CARDINAL;
ValidateFile[file];
bytesToRead ←
IF file.length.page = page THEN file.length.byte ELSE bytesPerPage;
request ← GetIORequestBlock[];
request↑ ← [
link:, buffer: buffer,
address: PositionToFileAddress[[page: page, byte: 0]], op: read,
bytesToGo: bytesToRead, proc: callback, arg: arg];
DoRead[file, request];
END;
StartWrite: PUBLIC PROCEDURE [
file: FileHandle, page: FileDefs.PageNumber, buffer: FileDefs.Buffer,
callback: FileDefs.Completer, arg: FileDefs.CompleterArg] =
BEGIN
request: IORequest;
ValidateFile[file];
request ← GetIORequestBlock[];
request↑ ← [
link:, buffer: buffer,
address: PositionToFileAddress[[page: page, byte: 0]], op: write,
bytesToGo: bytesPerPage, proc: callback, arg: arg];
DoWrite[file, request];
END;
-- Other Procedures exported to IFSFilePrivate --
DoRead: PUBLIC PROCEDURE [file: FileHandle, request: IORequest] =
BEGIN
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];
END;
DoWrite: PUBLIC PROCEDURE [file: FileHandle, request: IORequest] =
BEGIN OPEN FileDefs;
bytesSent: Leaf.ByteCount ← 0;
bytesToGo: Leaf.ByteCount = request.bytesToGo;
initialPosition: Position = FileAddressToPosition[request.address];
positionAfterWrite: Position = IncrementPosition[
initialPosition, request.bytesToGo];
AcquireIOLock[file];
IF ComparePositions[file.length, positionAfterWrite] = less THEN
file.length ← positionAfterWrite;
EnqueueRequest[file, request];
DO
positionThisTime: Position = IncrementPosition[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:]];
MoveBytes[
to: @sequinRequest.writeBytes, from: request.buffer, toByte: 0,
fromByte: bytesSent, nBytes: 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];
END;
FileWatcher: PUBLIC PROCEDURE [file: FileHandle] RETURNS [BOOLEAN] =
BEGIN
current: IORequest ← NIL;
fatalError: BOOLEAN ← FALSE;
offset: CARDINAL;
DO
buffer: Sequin.Buffer;
outcome: FileDefs.Problem;
CheckState: ENTRY PROCEDURE [file: FileHandle] RETURNS [FileState] = -- INLINE --
BEGIN
DO
SELECT file.state FROM
alive => IF fatalError THEN file.state ← flush;
flush => NULL;
closing =>
BEGIN
IF file.ioPending = NIL THEN {
file.state ← closed; BROADCAST file.ioSynch};
EXIT
END;
ENDCASE;
IF file.ioPending ~= NIL THEN EXIT;
WAIT file.ioSynch;
ENDLOOP;
RETURN[file.state]
END;
CompleteRequest: PROCEDURE =
BEGIN
request: IORequest = DequeueRequest[file];
request.proc[request.arg, outcome];
FreeIORequestBlock[request];
current ← NIL;
END;
SELECT CheckState[file] FROM
alive =>
BEGIN
answer: Leaf.Answer;
position: FileDefs.Position;
EnsureCurrent: ENTRY PROCEDURE [file: FileHandle] = -- INLINE --
BEGIN
IF current ~= NIL THEN RETURN;
IF file.ioPending = NIL THEN ERROR TheWellIsDry;
current ← file.ioPending.link;
position ← FileAddressToPosition[current.address];
offset ← 0;
END;
ValidateArrival: PROCEDURE [
address: Leaf.FileAddress, bytes: Leaf.ByteCount] =
BEGIN
pos: FileDefs.Position = FileAddressToPosition[address];
IF current.op = answer.op.type AND pos = position THEN
BEGIN
outcome ← ok;
current.bytesToGo ← current.bytesToGo - bytes;
position ← FileDefs.IncrementPosition[position, bytes];
END
ELSE fatalError ← TRUE;
END;
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
ELSE
WITH ans: answer SELECT answer.op.type FROM
error =>
BEGIN
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};
IN [unimplementedOp..illegalWrite] => ERROR LeafGibberish;
ENDCASE;
current.bytesToGo ← 0;
END;
read =>
BEGIN
dataBytes: Leaf.ByteCount = ans.op.length - Leaf.readAns.length;
ValidateArrival[ans.address, dataBytes];
MoveBytes[
to: current.buffer, from: @ans.readBytes, toByte: offset,
fromByte: 0, nBytes: dataBytes];
offset ← offset + dataBytes;
END;
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]};
END;
flush => CompleteRequest[];
closing => {outcome ← other; CompleteRequest[]};
closed => EXIT;
ENDCASE;
ENDLOOP;
RETURN[~fatalError]
END;
ValidateFile: PUBLIC PROCEDURE [file: FileHandle] = {
IF file.seal ~= openSeal THEN ERROR InvalidFile};
FileAddressToPosition: PUBLIC PROCEDURE [fa: Leaf.FileAddress]
RETURNS [FileDefs.Position] =
-- Note: regards 'fa' as a signed quantity (to allow leader page access).
BEGIN
p1, p2: FAPieces;
p1.fa ← fa;
IF ~(p1.faHigh <= 777B OR p1.faHigh IN [3400B..3777B]) THEN ERROR FileTooBig;
p2 ← [
posPieces[posHigh: p1.faHigh, posMiddle: p1.faMiddle, posLow: p1.faLow]];
RETURN[p2.pos]
END;
PositionToFileAddress: PUBLIC PROCEDURE [
pos: FileDefs.Position, opSpecific: Leaf.OpSpecificFileAddress ← [read[]]]
RETURNS [Leaf.FileAddress] =
-- Note: regards 'pos' as a signed quantity (to allow leader page access).
BEGIN
p1, p2: FAPieces;
p1.pos ← pos;
p2 ← [
faPieces[
opSpecific: opSpecific, faHigh: p1.posHigh, faMiddle: p1.posMiddle,
faLow: p1.posLow]];
IF p1.posHigh >= 400B THEN p2.faHigh ← p1.posHigh + 3000B;
RETURN[p2.fa]
END;
-- Internal Procedures --
AcquireIOLock: ENTRY PROCEDURE [file: FileHandle] =
BEGIN WHILE file.locked DO WAIT file.ioSynch ENDLOOP; file.locked ← TRUE; END;
ReleaseIOLock: ENTRY PROCEDURE [file: FileHandle] =
BEGIN file.locked ← FALSE; BROADCAST file.ioSynch; END;
EnqueueRequest: ENTRY PROCEDURE [file: FileHandle, request: IORequest] =
BEGIN
IF file.ioPending = NIL THEN request.link ← request
ELSE {request.link ← file.ioPending.link; file.ioPending.link ← request};
file.ioPending ← request;
BROADCAST file.ioSynch;
END;
DequeueRequest: ENTRY PROCEDURE [file: FileHandle]
RETURNS [request: IORequest] =
BEGIN
IF file.ioPending = NIL THEN ERROR TheWellIsDry;
request ← file.ioPending.link;
file.ioPending.link ← request.link;
IF request = file.ioPending THEN file.ioPending ← NIL;
END;
END.