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: ROPE ← NIL
];
GetServer:
PUBLIC
PROC [server:
ROPE]
RETURNS [h: ServerHandle] ~ {
Raises Error if server is down.
obsolete: BOOL;
downMsg: ROPE;
kids: LIST OF PROCESS ← NIL;
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: ROPE ← NIL;
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:
ROPE ←
NIL] ~ {
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[] ] };
}...