RemoteFileImpl.mesa
Copyright Ó 1987 by Xerox Corporation. All rights reserved.
Demers, November 9, 1987 10:37:44 am PST
DIRECTORY
Atom USING [GetPName],
BasicTime USING [earliestGMT, GMT, Now, Period],
Booting USING [RegisterProcs, RollbackProc],
Process USING [Abort, Detach, EnableAborts, MsecToTicks, SetTimeout],
RemoteFile USING [GetProcsProc, GetServerProc, ServerHandle, ServerObject, ServerProcs, ServerProcsObject, SweepProc, ValidateProc],
Rope USING [Concat, EqualSubstrs, Length, ROPE, Substr],
SymTab USING [Create, Delete, EachPairAction, Erase, Fetch, Pairs, Ref, Store],
UserCredentials USING [CredentialsChangeProc, RegisterForChange]
;
RemoteFileImpl: CEDAR MONITOR
IMPORTS Atom, BasicTime, Booting, Process, Rope, SymTab, UserCredentials
EXPORTS RemoteFile
~{
Types
GetServerProc: TYPE ~ RemoteFile.GetServerProc;
GMT: TYPE ~ BasicTime.GMT;
ROPE: TYPE ~ Rope.ROPE;
ServerHandle: TYPE ~ RemoteFile.ServerHandle;
Parameters
secsBetweenSweeps: INT ← 6;
downServerTTL: CARD ← 60; -- seconds
waitForServerTimeout: INT ← 13; -- seconds
Registration (Exported to RemoteFile)
Registration: TYPE ~ REF RegistrationObject;
RegistrationObject: TYPE ~ RECORD [
next: Registration,
flavor: ATOM,
nameSuffix: ROPE, -- constructed from flavor
getServer: GetServerProc
];
registrations: Registration ← NIL;
Register: PUBLIC ENTRY PROC [flavor: ATOM, getServer: GetServerProc] ~ {
no ENABLE UNWIND
p, prev: Registration;
nameSuffix: ROPE ← Rope.Concat["-", Atom.GetPName[flavor]];
FOR p ← registrations, p.next WHILE p # NIL DO
IF p.flavor = flavor THEN {
IF prev = NIL THEN registrations ← p.next ELSE prev.next ← p.next;
EXIT;
};
prev ← p;
ENDLOOP;
IF getServer # NIL
THEN registrations ← NEW[RegistrationObject ← [registrations, flavor, nameSuffix, getServer]];
};
Server Handle Cache
serverTab: SymTab.Ref ← SymTab.Create[case~FALSE];
lastSweepTime: BasicTime.GMT ← BasicTime.earliestGMT;
readyToSweep: CONDITION;
Daemon: PROC ~ {
seconds: INT;
CallSweep: SymTab.EachPairAction -- [key, val] RETURNS [quit] -- ~ {
h: ServerHandle ~ NARROW[val];
h.procs.sweep[h, CARD[seconds]];
RETURN [FALSE];
};
WaitReadyToSweep: ENTRY PROC ~ INLINE {
ENABLE UNWIND => NULL;
DO
thisSweepTime: BasicTime.GMT ~ BasicTime.Now[];
seconds ← BasicTime.Period[from~lastSweepTime, to~thisSweepTime];
IF seconds >= secsBetweenSweeps THEN {
lastSweepTime ← thisSweepTime;
EXIT;
};
WAIT readyToSweep;
ENDLOOP;
};
TRUSTED {
Process.EnableAborts[@readyToSweep];
Process.SetTimeout[@readyToSweep, Process.MsecToTicks[INT[1000]*secsBetweenSweeps]];
};
DO
WaitReadyToSweep[];
[] ← SymTab.Pairs[serverTab, CallSweep];
ENDLOOP;
};
Rollback: ENTRY Booting.RollbackProc ~ {
ENABLE UNWIND => NULL;
SymTab.Erase[serverTab];
lastSweepTime ← BasicTime.Now[];
};
CredentialsChange: ENTRY UserCredentials.CredentialsChangeProc ~ {
ENABLE UNWIND => NULL;
SymTab.Erase[serverTab];
lastSweepTime ← BasicTime.Now[];
};
Finding a server (exported to RemoteFile)
GetServerResponse: TYPE ~ REF GetServerResponseObject;
GetServerResponseObject: TYPE ~ RECORD [
nRunning: CARDINAL ← 0,
wakeup: CONDITION,
handle: ServerHandle ← NIL,
downMsg: ROPENIL
];
GetServer: PUBLIC PROC [server: ROPE] RETURNS [h: ServerHandle] ~ {
Raises Error if server is down.
obsolete: BOOL;
downMsg: ROPE;
kids: LIST OF PROCESSNIL;
response: GetServerResponse;
theRegistration: Registration;
started: GMT;
MakeChild: ENTRY PROC [r: Registration] RETURNS [p: PROCESS] ~ --INLINE-- {
ENABLE UNWIND => NULL;
p ← FORK DoGetServer[r, server, response];
response.nRunning ← response.nRunning + 1;
};
WaitForResponse: ENTRY PROC ~ --INLINE-- {
ENABLE UNWIND => NULL;
set [h, downMsg] ...
WHILE (response.nRunning > 0) AND (response.handle = NIL) AND (response.downMsg = NIL) DO
sinceStarted: INT ~ BasicTime.Period[from~started, to~BasicTime.Now[]];
IF sinceStarted >= waitForServerTimeout THEN EXIT;
TRUSTED { Process.SetTimeout[@response.wakeup, Process.MsecToTicks[1000*(waitForServerTimeout-sinceStarted)+500]] };
WAIT response.wakeup;
ENDLOOP;
h ← response.handle;
downMsg ← response.downMsg;
};
h ← NARROW[SymTab.Fetch[serverTab, server].val];
IF h # NIL THEN {
[obsolete, downMsg] ← h.procs.validate[h];
IF NOT obsolete THEN {
IF downMsg # NIL THEN ErrorDownServer[h, downMsg];
RETURN;
};
};
response ← NEW [GetServerResponseObject];
FOR theRegistration ← registrations, theRegistration.next WHILE theRegistration # NIL DO
suffixLen, pos: INT;
suffixLen ← Rope.Length[theRegistration.nameSuffix];
pos ← Rope.Length[server] - suffixLen;
IF (pos >= 0) AND Rope.EqualSubstrs[server, pos, suffixLen, theRegistration.nameSuffix, 0, suffixLen, FALSE]
THEN { server ← Rope.Substr[server, 0, pos]; EXIT };
ENDLOOP;
IF theRegistration # NIL
THEN {
DoGetServer[theRegistration, server, response];
h ← response.handle;
IF h # NIL THEN h.name ← Rope.Concat[h.name, theRegistration.nameSuffix];
downMsg ← response.downMsg;
}
ELSE {
started ← BasicTime.Now[];
TRUSTED { Process.EnableAborts[@response.wakeup] };
FOR each: Registration ← registrations, each.next WHILE each # NIL DO
kids ← CONS [MakeChild[each], kids];
ENDLOOP;
WaitForResponse[];
FOR kid: LIST OF PROCESS ← kids, kid.rest WHILE kid # NIL DO
TRUSTED { Process.Abort[kid.first] };
TRUSTED { Process.Detach[kid.first] };
ENDLOOP;
};
IF h = NIL THEN {
IF downMsg = NIL
THEN {
downMsg ← "unknown server";
h ← MakeDownServer[server, $serverNotKnown, downMsg];
}
ELSE {
h ← MakeDownServer[server, $serverNotAvailable, downMsg];
};
};
[] ← SymTab.Store[serverTab, h.name, h];
IF downMsg # NIL THEN ErrorDownServer[h, downMsg];
};
DoGetServer: PROC [r: Registration, server: ROPE, response: GetServerResponse] ~ {
h: ServerHandle ← NIL;
downMsg: ROPENIL;
NotifyDone: ENTRY PROC ~ --INLINE-- {
ENABLE UNWIND => NULL;
response.nRunning ← response.nRunning - 1;
IF (response.handle # NIL) OR (response.downMsg # NIL) THEN RETURN;
SELECT TRUE FROM
(h # NIL) OR (downMsg # NIL) => -- found it -- {
response.handle ← h;
response.downMsg ← downMsg;
NOTIFY response.wakeup;
};
(response.nRunning = 0) => -- nobody will find it -- {
NOTIFY response.wakeup;
};
ENDCASE;
};
[h, downMsg] ← r.getServer[server ! Error -- can't happen--, ABORTED => CONTINUE];
NotifyDone[];
};
Error
Error: PUBLIC ERROR [code: ATOM, msg: ROPE] ~ CODE;
ErrorDownServer: PROC [h: ServerHandle, downMsg: ROPENIL] ~ {
code: ATOM;
WITH h.data SELECT FROM
d: DownServerData => {
code ← d.errorCode;
IF downMsg = NIL THEN downMsg ← d.downMsg;
};
ENDCASE => {
code$serverNotAvailable;
};
ERROR Error[code, downMsg];
};
Controlling the flavor of a cached server (Exported to RemoteFile)
SetCachedServer: PUBLIC PROC [server: ROPE, flavor: ATOM] ~ {
r: Registration;
h: ServerHandle;
downMsg: ROPE;
FOR r ← registrations, r.next WHILE (r # NIL) AND (r.flavor # flavor) DO NULL ENDLOOP;
IF r = NIL THEN RETURN;
[h, downMsg] ← r.getServer[server];
IF h = NIL THEN {
code: ATOM;
IF downMsg = NIL
THEN { downMsg ← "unknown server"; code ← $serverNotKnown }
ELSE { code ← $serverNotAvailable };
h ← MakeDownServer[server, code, downMsg];
};
[] ← SymTab.Store[serverTab, server, h];
};
ClearCachedServer: PUBLIC PROC [server: ROPE] ~ {
[] ← SymTab.Delete[serverTab, server];
};
Down server implementation
DownServerData: TYPE ~ REF DownServerDataObject;
DownServerDataObject: TYPE ~ RECORD [
ttl: CARD,
errorCode: ATOM,
downMsg: ROPE
];
downServerProcs: RemoteFile.ServerProcs ←
NEW[RemoteFile.ServerProcsObject ← [
sweep~SweepDownServer,
validate~ValidateDownServer,
getProcs~GetProcsDownServer
]
];
MakeDownServer: PROC [name: ROPE, errorCode: ATOM, downMsg: ROPE] RETURNS [h: ServerHandle] ~ {
d: DownServerData ← NEW[DownServerDataObject ← [downServerTTL, errorCode, downMsg]];
h ← NEW[RemoteFile.ServerObject ← [$DOWN, name, downServerProcs, d]];
};
SweepDownServer: RemoteFile.SweepProc -- [h: ServerHandle, seconds: CARD] -- ~ {
d: DownServerData ← NARROW[h.data];
IF d.ttl > seconds THEN d.ttl ← d.ttl - seconds ELSE d.ttl ← 0;
};
ValidateDownServer: RemoteFile.ValidateProc -- [h: ServerHandle] RETURNS [obsolete: BOOL, down: BOOL] -- ~ {
d: DownServerData ← NARROW[h.data];
RETURN[(d.ttl = 0), d.downMsg];
};
GetProcsDownServer: RemoteFile.GetProcsProc -- [h: ServerHandle, view: ATOM] RETURNS [procs: REF] -- ~ {
ErrorDownServer[h];
RETURN [NIL];
};
Initialization
Booting.RegisterProcs[r~Rollback];
UserCredentials.RegisterForChange[proc~CredentialsChange];
TRUSTED { Process.Detach[ FORK Daemon[] ] };
}...