FSFileLookupImpl.mesa
Copyright © 1984, 1985 by Xerox Corporation. All rights reserved.
Schroeder, November 8, 1983 1:53 pm
Levin, June 24, 1983 5:38 pm
Russ Atkinson, March 18, 1985 10:41:54 pm PST
Doug Wyatt (DKW), November 27, 1984 2:18:36 pm PST -- changed SetState
Bob Hagmann April 30, 1985 1:05:25 pm PDT
DIRECTORY
BasicTime USING [FromPupTime, GMT, Now, nullGMT, Period],
FSBackdoor USING [Version],
FSRemoteFile USING [GetServerPupName, LookupResult],
PrincOps USING [zEXCH],
Process USING [Detach, Ticks, SecondsToTicks],
PupDefs USING [GetFreePupBuffer, GetHopsToNetwork, GetPupAddress, MsToTocks, PupAddress, PupNameTrouble, PupBuffer, PupSocket, PupSocketDestroy, PupSocketMake, ReturnFreePupBuffer, SetPupContentsBytes],
PupTypes USING [fillInSocketID, Pair, PupSocketID, PupType],
Rope USING [Equal, Fetch, Length, ROPE],
RuntimeError USING [UNCAUGHT];
FSFileLookupImpl: CEDAR MONITOR
IMPORTS BasicTime, FSRemoteFile, Process, PupDefs, Rope, RuntimeError
EXPORTS FSRemoteFile
= {
CARD: TYPE = LONG CARDINAL;
GMT: TYPE = BasicTime.GMT;
ROPE: TYPE = Rope.ROPE;
Exported to FSRemoteFile
Lookup: PUBLIC PROC [server, file: ROPE] RETURNS [result: FSRemoteFile.LookupResult, version: FSBackdoor.Version, create: GMT, count: INT] = {
sI: REF ServerInfo;
[result, sI] ← FindServer[server];
IF result = ok THEN {
[result, version, create, count] ← DoFileLookup[sI.addr, file];
SetState[sI, result];
};
};
FileLookup protocol implementation
serverList: REF ServerInfo ← NIL; -- list of known servers;
keepingList: BOOLTRUE;
keepingList => cache responses to try to reduce name lookup traffic and useless traffic when a server is obviously down
packetCount: CARD ← 0; -- outgoing; also used as pupID
ReverseCardinal: TYPE = MACHINE DEPENDENT RECORD [high, low: CARDINAL];
ServerInfo: TYPE = RECORD [
name: ROPE,
addr: PupDefs.PupAddress,
state: FSRemoteFile.LookupResult, -- can be noResponse, noSuchPort, or ok
timeOK: GMT, -- GMT of last ok
timeErr: GMT, -- GMT of last error
next: REF ServerInfo];
FindServer: ENTRY PROC [server: ROPE] RETURNS [result: FSRemoteFile.LookupResult, info: REF ServerInfo] = TRUSTED {
ENABLE UNWIND => NULL;
result ← ok;
FOR info ← serverList, info.next UNTIL info = NIL DO
IF Rope.Equal[info.name, server, FALSE] THEN {result ← info.state; RETURN};
ENDLOOP;
Didn't find an entry, so make one
{
LookupFileSocket: PupTypes.PupSocketID = [0, 61B];
addr: PupDefs.PupAddress;
addr ← PupDefs.GetPupAddress[LookupFileSocket, FSRemoteFile.GetServerPupName[server]
! PupDefs.PupNameTrouble => {
SELECT code FROM
errorFromServer => result ← noSuchServer;
ENDCASE => result ← noResponse;
CONTINUE } ];
IF result # noSuchServer THEN {
time: GMT ← BasicTime.Now[];
info ← NEW [ServerInfo ← [
name: server, addr: addr, state: result,
timeOK: IF result = ok THEN time ELSE BasicTime.nullGMT,
timeErr: IF result # ok THEN time ELSE BasicTime.nullGMT,
next: serverList]];
IF serverList = NIL THEN Process.Detach[FORK ServerCollector[]];
IF keepingList THEN serverList ← info;
};
};
};
DoFileLookup: PROC [addr: PupDefs.PupAddress, file: ROPE] RETURNS [result: FSRemoteFile.LookupResult, version: FSBackdoor.Version, create: GMT, count: INT] = TRUSTED {
LookupFileType: PupTypes.PupType = LOOPHOLE[200B];
LookupFileReplyType: PupTypes.PupType = LOOPHOLE[201B];
LookupFileErrorType: PupTypes.PupType = LOOPHOLE[202B];
tries: CARDINAL = 4;
msWait: CARDINAL = 2000+500*MIN[8, PupDefs.GetHopsToNetwork[addr.net]];
s: PupDefs.PupSocket;
fileNameChars: CARDINAL = Rope.Length[file];
result ← noResponse; -- assume result
s ← PupDefs.PupSocketMake [local: PupTypes.fillInSocketID, remote: addr, ticks: PupDefs.MsToTocks[msWait] ];
THROUGH [1..tries] DO
b: PupDefs.PupBuffer = PupDefs.GetFreePupBuffer[];
b.pupType ← LookupFileType;
b.pupID ← PupIDFromLongCardinal[packetCount ← packetCount + 1];
FOR i: CARDINAL IN [0 .. fileNameChars) DO
b.pupChars[i] ← Rope.Fetch[file, i];
ENDLOOP;
PupDefs.SetPupContentsBytes[b, fileNameChars];
s.put[b];
{
r: PupDefs.PupBuffer = s.get[];
IF r = NIL THEN LOOP; -- no response yet
SELECT r.pupType FROM
LookupFileReplyType => {
rPtr: LONG POINTER TO MACHINE DEPENDENT RECORD[v: CARDINAL, c, l: ReverseCardinal] = LOOPHOLE[@r.pupBody];
result ← ok;
version ← [rPtr.v];
create ← BasicTime.FromPupTime[
LongCardFromReverseCardinal[rPtr.c]
! RuntimeError.UNCAUGHT => {create ← BasicTime.nullGMT; CONTINUE}];
count ← LongCardFromReverseCardinal[rPtr.l];
};
LookupFileErrorType => result ← noSuchFile;
error => SELECT r.errorCode FROM
noProcessPupErrorCode => result ← noSuchPort;
cantGetTherePupErrorCode => NULL;
ENDCASE => {PupDefs.ReturnFreePupBuffer[r]; LOOP};
ENDCASE => {PupDefs.ReturnFreePupBuffer[r]; LOOP};
PupDefs.ReturnFreePupBuffer[r];
EXIT;
};
ENDLOOP;
PupDefs.PupSocketDestroy[s];
};
PupIDFromLongCardinal: PROC [s: CARD] RETURNS [PupTypes.Pair] = TRUSTED
MACHINE CODE {PrincOps.zEXCH};
LongCardFromReverseCardinal: PROC [r: ReverseCardinal] RETURNS [CARD] = TRUSTED
MACHINE CODE {PrincOps.zEXCH};
SetState: ENTRY PROC [info: REF ServerInfo, state: FSRemoteFile.LookupResult] = {
ENABLE UNWIND => NULL;
time: GMT ← BasicTime.Now[];
IF state = ok THEN info.timeOK ← time ELSE info.timeErr ← time;
SELECT state FROM
noResponse, noSuchPort => info.state ← state;
ENDCASE;
DKW: formerly IF state = noSuchPort THEN info.state ← noSuchPort;
};
pollingInterval: NAT ← 5;
seconds to wait before polling
okTimeout: NAT ← 1800;
seconds to wait before having a cached OK server evaporate
noResponseTimeout: NAT ← 30;
seconds to wait before having a noResponse evaporate
noSuchPortTimeout: NAT ← 30;
seconds to wait before having a noSuchPort evaporate
errorTimeout: NAT ← 30;
seconds to wait before having any other error evaporate
ServerCollector: ENTRY PROC = {
ENABLE UNWIND => NULL;
UNTIL serverList = NIL DO
last: REF ServerInfo ← NIL;
forPolling: CONDITION ← [Process.SecondsToTicks[pollingInterval]];
WAIT forPolling;
FOR info: REF ServerInfo ← serverList, info.next UNTIL info = NIL DO
time: GMT = BasicTime.Now[];
ageOK: INT = BasicTime.Period[info.timeOK, time];
ageErr: INT = BasicTime.Period[info.timeErr, time];
{
SELECT info.state FROM
ok => IF ageOK > okTimeout THEN GO TO splice;
noResponse => IF ageErr > noResponseTimeout THEN GO TO splice;
noSuchPort => IF ageErr > noSuchPortTimeout THEN GO TO splice;
ENDCASE => IF ageErr > errorTimeout THEN GO TO splice;
last ← info;
EXITS splice =>
IF last=NIL THEN serverList ← info.next ELSE last.next ← info.next;
};
ENDLOOP;
ENDLOOP;
};
}.
Bob Hagmann April 29, 1985 10:25:49 am PDT
changes to: DIRECTORY, FindServer