XNSCHPrivateImpl.mesa
Demers, January 1, 1987 9:31:32 pm PST
To Do: Check whether CrRPCRuntime support catches errors in bulk data i/o procs.
To Do: Consider a cached serverName -> address map for hint processing?
DIRECTORY
BasicTime USING [earliestGMT, GMT, latestGMT, Now, Period, Update],
CHEntriesP0V0 USING [addressList, members],
CHOpsP2V3 USING [ArgumentError, AuthenticationError, Authenticator, CallError, ListDomainsServed, PropertyError, RetrieveItem, RetrieveMembers, UpdateError, WrongServer],
CrRPC USING [BulkDataCheckAbortProc, BulkDataValueProc, BulkDataXferProc, CreateClientHandle, DestroyClientHandle, Error, GetRope, Handle, ReadBulkDataStream],
Process USING [Detach, EnableAborts, SecondsToTicks, SetTimeout],
Random USING [ChooseInt],
Rope USING [Cat, Index, IsEmpty, ROPE, Substr],
SymTab USING [Create, EachPairAction, Fetch, Pairs, Ref, Store],
XNS USING [Address, Host, unknownAddress, unknownHost, unknownSocket],
XNSAuth USING [Conversation, GetCredentials, GetNextVerifier, GetNullIdentity, Identity, Initiate, SetRecipientHostNumber, Terminate],
XNSCH USING [AddressesFromItem, BestAddressInList, Element, Error, ErrorCode, Item, Name, Pattern, Properties, PropertyID, Which],
XNSCHPrivate USING [GetElement, RemoteProc],
XNSRouter USING [GetHops, unreachable],
XNSServerLocation USING [EachAddressProc, LocateServers, StopBroadcast],
XNSWKS USING [clearinghouse]
;
XNSCHPrivateImpl: CEDAR MONITOR
IMPORTS BasicTime, CHOpsP2V3, CrRPC, Process, Random, Rope, SymTab, XNSAuth, XNSCH, XNSCHPrivate, XNSRouter, XNSServerLocation
EXPORTS XNSCH, XNSCHPrivate
~ {
OPEN CHEntries: CHEntriesP0V0, CHOps: CHOpsP2V3;
chPgmNum: CARD ~ 2;
chVersionNum: CARDINAL ~ 3;
CARD16: TYPE ~ CARDINAL; -- temporary until Mimosa ????
ROPE: TYPE ~ Rope.ROPE;
Name: TYPE ~ XNSCH.Name;
Pattern: TYPE ~ XNSCH.Pattern;
Element: TYPE ~ XNSCH.Element;
PropertyID: TYPE ~ XNSCH.PropertyID;
Properties: TYPE ~ XNSCH.Properties;
Item: TYPE ~ XNSCH.Item;
WrongServerError: PUBLIC ERROR [hint: Name] ← CHOps.WrongServer;
shiftInName: ROPE ~ "CHServers";
chServiceName: Name ~
[organization~shiftInName, domain~shiftInName, object~"Clearinghouse Service"];
Conversations
Conversation: TYPE ~ REF ConversationObject;
ConversationObject: PUBLIC TYPE ~ RECORD [
conversation: XNSAuth.Conversation,
host: XNS.Host ← XNS.unknownHost,
handle: CrRPC.Handle ← NIL
];
InitiateConversation: PUBLIC PROC [identity: XNSAuth.Identity, server: XNS.Address]
RETURNS [c: Conversation] ~ {
c ← NEW[ ConversationObject ← [] ];
IF identity = NIL THEN identity ← XNSAuth.GetNullIdentity[];
c.conversation ← XNSAuth.Initiate[identity, chServiceName];
IF server # XNS.unknownAddress THEN {
c.handle ← CrRPC.CreateClientHandle[$SPP, NEW[XNS.Address ← server]];
c.host ← server.host };
};
TerminateConversation: PUBLIC PROC [c: Conversation] ~ {
IF c.handle # NIL THEN CrRPC.DestroyClientHandle[c.handle];
XNSAuth.Terminate[c.conversation] };
GetAuthenticator: PUBLIC PROC [c: Conversation, host: XNS.Host]
RETURNS [CHOps.Authenticator] ~ {
XNSAuth.SetRecipientHostNumber[c.conversation, host];
RETURN [ [XNSAuth.GetCredentials[c.conversation],
XNSAuth.GetNextVerifier[c.conversation]] ] };
IsGeneric: PUBLIC PROC [c: Conversation] RETURNS [BOOL] ~ {
RETURN [c.handle = NIL] };
Server Objects
CachedServer: TYPE ~ REF CachedServerObject;
CachedServerObject: TYPE ~ RECORD [
next: CachedServer ← NIL, -- successor in serversByAddress. ML
address: REF XNS.Address, -- readonly, REF to conform to CrRPC.RefAddress
knowDomainsServed: BOOLFALSE,  -- hint, no ML
referenced: BasicTime.GMT, -- ML
inactiveUntil: BasicTime.GMT-- ML
];
cachedServerTimeout: INT ← 8*LONG[3600]; -- seconds
infiniteTimeout: INT ← 07FFFFFFFH;
numHeaders: CARDINAL ~ 101;
HeaderIndex: TYPE ~ [0..numHeaders);
Headers: TYPE ~ REF HeadersObject;
HeadersObject: TYPE ~ ARRAY HeaderIndex OF CachedServer;
serversByAddress: Headers ~ NEW[HeadersObject ← ALL[NIL]];
HashHost: PROC [host: XNS.Host] RETURNS [HeaderIndex] ~ {
acc: CARD ← ((((host.a*5+host.b)*5+host.c)*5+host.d)*5+host.e)*5+host.f;
RETURN [acc MOD numHeaders] };
GetServerByAddress: ENTRY PROC [addr: XNS.Address, makeActive: BOOL]
RETURNS [s: CachedServer] ~ {
Net and host are significant, socket is set to XNS.unknownSocket
ENABLE UNWIND => NULL;
index: HeaderIndex ~ HashHost[addr.host];
addr.socket ← XNS.unknownSocket;
FOR s ← serversByAddress^[index], s.next WHILE s # NIL DO
IF s.address^ = addr THEN {
IF makeActive THEN s.inactiveUntil ← BasicTime.earliestGMT;
RETURN };
ENDLOOP;
s ← serversByAddress^[index] ← NEW[CachedServerObject ←
[next~serversByAddress^[index], address~NEW[XNS.Address ← addr], referenced~BasicTime.Now[], inactiveUntil~BasicTime.earliestGMT]];
};
ApplyToAllServers: INTERNAL PROC [proc: PROC[CachedServer]] ~ {
FOR index: HeaderIndex IN [HeaderIndex.FIRST .. HeaderIndex.LAST] DO
FOR s: CachedServer ← serversByAddress^[index], s.next WHILE s # NIL DO
proc[s];
ENDLOOP;
ENDLOOP;
};
secondsBusy: INT ← 10;
secondsDown: INT ← 600;
MarkServerUsable: ENTRY PROC [s: CachedServer] ~ {
ENABLE UNWIND => NULL;
s.inactiveUntil ← BasicTime.earliestGMT };
MarkServerBusy: ENTRY PROC [s: CachedServer] ~ {
ENABLE UNWIND => NULL;
s.inactiveUntil ← BasicTime.Update[BasicTime.Now[], secondsBusy] };
MarkServerDown: ENTRY PROC [s: CachedServer] ~ {
ENABLE UNWIND => NULL;
s.inactiveUntil ← BasicTime.Update[BasicTime.Now[], secondsDown];
s.knowDomainsServed ← FALSE };
MarkServerNonexistent: ENTRY PROC [s: CachedServer] ~ {
ENABLE UNWIND => NULL;
s.inactiveUntil ← BasicTime.latestGMT;
s.knowDomainsServed ← FALSE };
ServerIsDead: INTERNAL PROC [s: CachedServer, timeout: INT] RETURNS [BOOL] ~ {
IF s.inactiveUntil = BasicTime.latestGMT THEN RETURN [TRUE];
IF BasicTime.Period[from~s.referenced, to~BasicTime.Now[]] > timeout THEN RETURN [TRUE];
RETURN [FALSE] };
SweepAllServers: ENTRY PROC ~ {
p, prev: CachedServer;
FOR i: HeaderIndex IN [HeaderIndex.FIRST .. HeaderIndex.LAST] DO
p ← serversByAddress^[i]; prev ← NIL;
WHILE p # NIL DO
IF ServerIsDead[p, infiniteTimeout] -- infinite => everybody believes it's dead
THEN {
p ← p.next; IF prev = NIL THEN serversByAddress^[i] ← p ELSE prev.next ← p }
ELSE {
prev ← p; p ← p.next };
ENDLOOP;
ENDLOOP;
};
Lists of Servers
CachedServerListHead: TYPE ~ REF CachedServerList;
CachedServerList: TYPE ~ REF CachedServerListElement;
CachedServerListElement: TYPE ~ RECORD [
next: CachedServerList, -- ML
server: CachedServer -- readonly
];
ListIsEmpty: PROC [listHead: CachedServerListHead] RETURNS [isEmpty: BOOL] ~ {
RETURN [listHead^ = NIL] };
ListContains: ENTRY PROC [listHead: CachedServerListHead, server: CachedServer]
RETURNS [isOnList: BOOL] ~ {
IF listHead = NIL THEN RETURN [FALSE];
FOR s: CachedServerList ← listHead^, s.next DO
IF s = NIL THEN RETURN [FALSE];
IF s.server = server THEN RETURN [TRUE]
ENDLOOP;
};
AddServerToList: ENTRY PROC [listHead: CachedServerListHead,
server: CachedServer] RETURNS [new: BOOL] ~ {
ENABLE UNWIND => NULL;
new ← AddServerToListInternal[listHead, server] };
AddServerToListInternal: INTERNAL PROC [listHead: CachedServerListHead,
server: CachedServer] RETURNS [new: BOOL] ~ {
FOR p: CachedServerList ← listHead^, p.next WHILE p # NIL DO
IF p.server = server THEN RETURN [FALSE];
ENDLOOP;
listHead^ ← NEW[CachedServerListElement ← [next~listHead^, server~server]];
RETURN [TRUE] };
AddNewServerToList: ENTRY PROC [listHead: CachedServerListHead, server: CachedServer] ~ {
Precondition: server is not already on list.
ENABLE UNWIND => NULL;
listHead^ ← NEW[CachedServerListElement ← [next~listHead^, server~server]];
};
DeleteServerFromListInternal: INTERNAL PROC [listHead: CachedServerListHead,
server: CachedServer] ~ {
p, prev: CachedServerList ← NIL;
IF listHead = NIL THEN RETURN;
p ← listHead^;
WHILE (p # NIL) AND (p.server # server) DO prev ← p; p ← p.next ENDLOOP;
IF p = NIL THEN RETURN;
IF prev = NIL THEN listHead^ ← p ELSE prev.next ← p;
};
ServerFilterProc: TYPE ~ PROC [CachedServer] RETURNS [ok: BOOL];
GetBestServerFromList: ENTRY PROC [listHead: CachedServerListHead,
filter: ServerFilterProc ← NIL]
RETURNS [bestServer: CachedServer ← NIL] ~ {
ENABLE UNWIND => NULL;
bestHops: CARDINALLAST[CARDINAL];
IF listHead = NIL THEN RETURN [NIL];
FOR p: CachedServerList ← listHead^, p.next WHILE p # NIL DO
server: CachedServer ~ p.server;
hops: CARDINAL;
IF BasicTime.Period[from: BasicTime.Now[], to: server.inactiveUntil] > 0 THEN LOOP;
hops ← XNSRouter.GetHops[server.address.net];
IF hops >= bestHops THEN LOOP;
IF hops >= XNSRouter.unreachable THEN LOOP;
IF filter # NIL THEN IF NOT filter[server].ok THEN LOOP;
bestServer ← server;
bestHops ← hops;
ENDLOOP;
IF bestServer # NIL THEN bestServer.referenced ← BasicTime.Now[];
};
SweepList: INTERNAL PROC [listHead: CachedServerListHead, timeout: INT] ~ {
Called by Daemon.
p, prev: CachedServerList ← NIL;
IF listHead = NIL THEN RETURN;
p ← listHead^;
WHILE p # NIL DO
IF ServerIsDead[p.server, timeout]
THEN { p ← p.next; IF prev = NIL THEN listHead^ ← p ELSE prev.next ← p }
ELSE { prev ← p; p ← p.next };
ENDLOOP;
};
Cache of Servers by Domain
domainServers: SymTab.Ref ~ SymTab.Create[mod~numHeaders, case~FALSE];
GetServersForDomain: PROC [domain: ROPE] RETURNS [CachedServerListHead] ~ {
RETURN [NARROW[SymTab.Fetch[domainServers, domain].val]] };
AddServerForDomain: ENTRY PROC [domain: ROPE, server: CachedServer] ~ {
ENABLE UNWIND => NULL;
listHead: CachedServerListHead;
listHead ← NARROW[SymTab.Fetch[domainServers, domain].val];
IF listHead = NIL THEN {
listHead ← NEW[CachedServerList ← NIL];
[] ← SymTab.Store[domainServers, domain, listHead] };
[] ← AddServerToListInternal[listHead, server] };
DeleteServerForDomain: ENTRY PROC [domain: ROPE, server: CachedServer] ~ {
ENABLE UNWIND => NULL;
listHead: CachedServerListHead ~ NARROW[SymTab.Fetch[domainServers, domain].val];
DeleteServerFromListInternal[listHead, server] };
SweepDomainCache: ENTRY PROC ~ {
Called by Daemon.
EachPair: INTERNAL SymTab.EachPairAction -- [key, val] RETURNS[BOOL] -- ~ {
IF val # NIL THEN SweepList[NARROW[val], cachedServerTimeout];
RETURN[FALSE] };
[] ← SymTab.Pairs[domainServers, EachPair];
};
ListDomainsIKnow: ENTRY PROC RETURNS [domains: LIST OF ROPE] ~ {
For debugging.
EachPair: INTERNAL SymTab.EachPairAction -- [key, val] RETURNS[BOOL] -- ~ {
domains ← CONS[key, domains]; RETURN[FALSE] };
[] ← SymTab.Pairs[domainServers, EachPair];
};
LearnDomainsServed: PROC [h: CrRPC.Handle, theServer: CachedServer] ~ {
c: Conversation;
ReadStreamOfDomainNames: CrRPC.BulkDataXferProc
[h: Handle, s: IO.STREAM, checkAbort: BulkDataCheckAbortProc] RETURNS [abort: BOOL];
~ {
EachDomainNameInStream: CrRPC.BulkDataValueProc
[s: IO.STREAM] RETURNS [abort: BOOLFALSE]
~ {
domPart, orgPart, key: ROPE;
orgPart ← CrRPC.GetRope[s];
domPart ← CrRPC.GetRope[s];
key ← Rope.Cat[orgPart, ":", domPart];
AddServerForDomain[key, theServer];
AddServerForDomain[OrganizationFromDomain[key], theServer]; -- <- BOGUS ???
};
RETURN [CrRPC.ReadBulkDataStream[h, s, checkAbort, EachDomainNameInStream]];
};
IF theServer.knowDomainsServed THEN RETURN;
c ← InitiateConversation[NIL, XNS.unknownAddress];
CHOps.ListDomainsServed[h, ReadStreamOfDomainNames,
GetAuthenticator[c, theServer.address.host]];
theServer.knowDomainsServed ← TRUE;
TerminateConversation[c];
};
OrganizationFromDomain: PROC [domain: ROPE] RETURNS [organization: ROPE] ~ {
e.g. "Xerox:PARC" -> "CHServers:Xerox"
len: INT ~ Rope.Index[s1~domain, s2~":"];
organization ← Rope.Cat[shiftInName, ":", Rope.Substr[domain, 0, len]] };
Cache of Nearby Servers
nearbyServers: CachedServerListHead ~ NEW[CachedServerList ← NIL];
defaultMaxHops: CARDINAL ← 3;
desperationContactMaxHops: CARDINAL ← 6;
desperationBroadcastMaxHops: CARDINAL ← 5;
desperationBroadcastTryLimit: CARDINAL ← 2;
sweepBroadcastTryLimit: CARDINAL ← 1;
maxHopsUsedForNearby: CARDINAL ← 0;
RememberHopsUsedForNearby: ENTRY PROC [hops: CARDINAL] ~ INLINE {
maxHopsUsedForNearby ← MAX[maxHopsUsedForNearby, hops] };
GetHopsUsedForNearby: ENTRY PROC RETURNS [hops: CARDINAL] ~ INLINE {
hops ← maxHopsUsedForNearby; maxHopsUsedForNearby ← 0 };
BroadcastForNearbyServers: PROC [
maxHops: CARDINAL, nWanted: CARDINALCARDINAL.LAST, tryLimit: CARDINAL ← 0] ~ {
nGot: CARDINAL ← 0;
EachAddress: XNSServerLocation.EachAddressProc -- [addr: XNS.Address] -- ~ {
server: CachedServer;
server ← GetServerByAddress[addr~addr, makeActive~TRUE];
IF AddServerToList[nearbyServers, server].new THEN nGot ← nGot.SUCC;
IF nGot = nWanted THEN ERROR XNSServerLocation.StopBroadcast[] };
XNSServerLocation.LocateServers[socket~XNSWKS.clearinghouse, remotePgm~chPgmNum, remotePgmVersion~chVersionNum, eachAddress~EachAddress, maxHops~maxHops, tryLimit~tryLimit];
};
StopSearch: ERROR ~ CODE;
NoticeKnownNearbyServers: ENTRY PROC[
maxHops: CARDINAL, nWanted: CARDINALCARDINAL.LAST] ~ {
nGot: CARDINAL ← 0;
EachServer: INTERNAL PROC [s: CachedServer] ~ {
IF XNSRouter.GetHops[s.address.net] > maxHops THEN RETURN;
IF AddServerToListInternal[nearbyServers, s].new THEN nGot ← nGot.SUCC;
IF nGot = nWanted THEN ERROR StopSearch[] };
ApplyToAllServers[proc~EachServer ! StopSearch => CONTINUE];
};
GetBestKnownServer: ENTRY PROC [maxHops: CARDINAL, filter: ServerFilterProc]
RETURNS [bestServer: CachedServer ← NIL] ~ {
EachServer: INTERNAL PROC [s: CachedServer] ~ {
hops: CARDINAL;
IF BasicTime.Period[from: BasicTime.Now[], to: s.inactiveUntil] > 0 THEN RETURN;
hops ← XNSRouter.GetHops[s.address.net];
IF hops > maxHops THEN RETURN;
IF (filter # NIL) AND (NOT filter[s]) THEN RETURN;
maxHops ← hops;
bestServer ← s };
ApplyToAllServers[proc~EachServer];
};
GetBestServer: PROC [filter: ServerFilterProc ← NIL, okToBroadcast: BOOL]
RETURNS [bestServer: CachedServer, didBroadcast: BOOLFALSE] ~ {
bestServer ← GetBestServerFromList[nearbyServers, filter];
IF bestServer = NIL THEN {
NoticeKnownNearbyServers[maxHops~defaultMaxHops];
bestServer ← GetBestServerFromList[nearbyServers, filter] };
IF bestServer = NIL THEN {
bestServer ← GetBestKnownServer[desperationContactMaxHops, filter] };
IF (bestServer = NIL) AND okToBroadcast THEN {
BroadcastForNearbyServers[maxHops~desperationBroadcastMaxHops, nWanted~1, tryLimit~desperationBroadcastTryLimit];
didBroadcast ← TRUE;
bestServer ← GetBestServerFromList[nearbyServers, filter] };
IF bestServer # NIL
THEN RememberHopsUsedForNearby[XNSRouter.GetHops[bestServer.address.net]]
ELSE RememberHopsUsedForNearby[CARDINAL.LAST];
};
SweepNearbyServers: PROC ~ {
Called by Daemon.
maxHopsUsed: CARDINAL;
EntrySweepNearbyServers[];
NoticeKnownNearbyServers[maxHops~defaultMaxHops];
IF (maxHopsUsed ← GetHopsUsedForNearby[]) > 0 THEN TRUSTED {
Process.Detach[FORK BroadcastForNearbyServers[maxHops~MIN[maxHopsUsed, defaultMaxHops], tryLimit~sweepBroadcastTryLimit] ];
};
};
EntrySweepNearbyServers: ENTRY PROC ~ {
SweepList[nearbyServers, infiniteTimeout];
};
Hint Processing
maxHints: CARDINAL ~ 25;
ProcessHint: PROC [h: CrRPC.Handle, theServer: CachedServer, hint: Name]
RETURNS [servers: CachedServerListHead] ~ {
c: Conversation;
nRead: CARDINAL;
hints: ARRAY [1..maxHints] OF ROPEALL[NIL];
ReadStreamOfNames: CrRPC.BulkDataXferProc
[h: Handle, s: IO.STREAM, checkAbort: BulkDataCheckAbortProc] RETURNS [abort: BOOL];
~ {
EachNameInStream: CrRPC.BulkDataValueProc
[s: IO.STREAM] RETURNS [abort: BOOLFALSE]
~ {
serverName: Name ~ XNSCHPrivate.GetElement[s];
nRead ← nRead + 1;
IF nRead <= maxHints THEN { hints[nRead] ← serverName.object; RETURN };
IF Random.ChooseInt[NIL, 1, nRead] > maxHints THEN RETURN;
hints[Random.ChooseInt[NIL, 1, maxHints]] ← serverName.object;
};
RETURN [CrRPC.ReadBulkDataStream[h, s, checkAbort, EachNameInStream]];
};
c ← InitiateConversation[NIL, XNS.unknownAddress];
nRead ← 0;
[] ← CHOps.RetrieveMembers[h, hint, CHEntries.members, ReadStreamOfNames, GetAuthenticator[c, theServer.address.host]
! CHOps.ArgumentError, CHOps.PropertyError, CHOps.UpdateError => CONTINUE];
servers ← NEW[CachedServerList ← NIL];
FOR i: CARDINAL IN [1 .. MIN[nRead, maxHints]] DO
{
ENABLE CHOps.ArgumentError, CHOps.PropertyError, CHOps.UpdateError =>
CONTINUE;
item: Item;
addresses: LIST OF XNS.Address;
[value~item] ← CHOps.RetrieveItem[h,
[organization~shiftInName, domain~shiftInName, object~hints[i]], CHEntries.addressList, GetAuthenticator[c, theServer.address.host]];
addresses ← XNSCH.AddressesFromItem[item];
IF addresses # NIL THEN {
server: CachedServer ~ GetServerByAddress[
addr~XNSCH.BestAddressInList[addresses], makeActive~FALSE];
[] ← AddServerToList[servers, server] };
};
ENDLOOP;
TerminateConversation[c];
};
Invoking a remote procedure
maxTries: CARDINAL ~ 15;
triesToUse: CARDINAL ← 7; -- Bounds Fault if this exceeds maxTries
CallRemote: PUBLIC PROC [c: Conversation, proc: XNSCHPrivate.RemoteProc,
domain: ROPENIL, idempotent: BOOLTRUE] ~ {
State: TYPE ~ { dom, hint, org, nearby, none };
state: State ← none;
organization: ROPENIL;
theServer: CachedServer ← NIL;
theHandle: CrRPC.Handle ← NIL;
gotHint: BOOL;
theHint: Name;
hintServers: CachedServerListHead ← NIL;
specific: BOOL ~ NOT IsGeneric[c];
inClientProc: BOOLFALSE;
didBroadcast: BOOLFALSE;
nAlreadyUsed: CARDINAL ← 0;
alreadyUsedCache: ARRAY [0..maxTries) OF CachedServer;
NoteServerAlreadyUsed: PROC [server: CachedServer] ~ {
Precondition: NOT ServerAlreadyUsed[server]
alreadyUsedCache[nAlreadyUsed] ← server;
nAlreadyUsed ← nAlreadyUsed + 1;
};
ServerUsable: PROC [server: CachedServer] RETURNS [BOOL] ~ {
FOR i: CARDINAL IN [0..nAlreadyUsed) DO
IF alreadyUsedCache[i] = server THEN RETURN [FALSE];
ENDLOOP;
RETURN [TRUE] };
CleanUp: PROC ~ {
IF theHandle # NIL THEN CrRPC.DestroyClientHandle[theHandle];
theHandle ← NIL };
GetServerCandidate: PROC ~ {
state ← none;
theServer ← NIL;
IF theHandle # NIL THEN { CrRPC.DestroyClientHandle[theHandle]; theHandle ← NIL };
IF NOT Rope.IsEmpty[domain] THEN {
state ← dom;
theServer ← GetBestServerFromList[GetServersForDomain[domain], ServerUsable];
IF theServer = NIL THEN {
state ← hint;
theServer ← GetBestServerFromList[hintServers, ServerUsable] };
IF theServer = NIL THEN {
state ← org;
IF organization = NIL THEN organization ← OrganizationFromDomain[domain];
theServer ← GetBestServerFromList[GetServersForDomain[organization], ServerUsable];
};
};
IF theServer = NIL THEN {
state ← nearby;
[theServer, didBroadcast] ← GetBestServer[ServerUsable, (NOT didBroadcast)] };
IF theServer = NIL THEN {
state ← none;
RETURN };
theHandle ← CrRPC.CreateClientHandle[$SPP, theServer.address];
};
Body of CallRemote ...
FOR tryNo: CARDINAL IN [1..triesToUse] DO
ENABLE {
UNWIND => CleanUp[];
CHOps.AuthenticationError => {
ERROR XNSCH.Error[credentialsInvalid, first]
};
CHOps.CallError => {
SELECT problem FROM
accessRightsInsufficient => {
ERROR XNSCH.Error[credentialsTooWeak, first] };
tooBusy => {
IF specific THEN ERROR XNSCH.Error[serverTooBusy, first];
MarkServerBusy[theServer];
LOOP };
serverDown => {
IF specific THEN ERROR XNSCH.Error[allDown, first];
If this server serves the right domain and can't answer, assume no other server can answer either. This is overly pessimistic. Doing better would be at least a little bit hard.
EXIT };
useCourier, other,
ENDCASE => -- can't happen -- {
IF specific THEN ERROR XNSCH.Error[unknown, first];
MarkServerNonexistent[theServer];
LOOP };
};
CHOps.ArgumentError => {
code: XNSCH.ErrorCode ~ (SELECT problem FROM
illegalProperty => illegalPropertyID,
illegalOrganizationName => illegalOrganizationName,
illegalDomainName => illegalDomainName,
illegalObjectName => illegalObjectName,
noSuchOrganization => noSuchOrganization,
noSuchDomain => noSuchDomain,
noSuchObject => noSuchObject
ENDCASE => unknown);
ERROR XNSCH.Error[code, IF which = first THEN first ELSE second] };
CHOps.PropertyError => {
code: XNSCH.ErrorCode ~ (SELECT problem FROM
missing => propertyIDNotFound,
wrongType => wrongPropertyType
ENDCASE => unknown);
ERROR XNSCH.Error[code, first] };
CHOps.UpdateError => {
code: XNSCH.ErrorCode ~ (SELECT problem FROM
noChange => noChange,
outOfDate => outOfDate,
objectOverflow => overflowOfName,
databaseOverflow => overflowOfDataBase
ENDCASE => unknown);
ERROR XNSCH.Error[code, IF which = first THEN first ELSE second] };
CrRPC.Error => {
SELECT errorReason FROM
courierVersionMismatch => {
IF specific THEN ERROR XNSCH.Error[serviceNotExported, first];
MarkServerNonexistent[theServer];
LOOP };
rejectedNoSuchProgram, rejectedNoSuchVersion, rejectedNoSuchProcedure, rejectedUnspecified => {
IF specific THEN ERROR XNSCH.Error[serviceNotExported, first];
MarkServerDown[theServer];
LOOP };
rejectedInvalidArgument => {
IF specific THEN ERROR XNSCH.Error[protocolError, first];
MarkServerDown[theServer];
LOOP };
remoteError, argsError, resultsError, bulkDataError, protocolError, remoteClose, communicationFailure, cantConnectToRemote => -- can't retry, progress made -- {
IF specific THEN ERROR XNSCH.Error[
(SELECT errorReason FROM
communicationFailure => communicationFailure,
cantConnectToRemote => communicationFailure,
ENDCASE => protocolError),
first];
MarkServerDown[theServer];
IF (NOT idempotent) AND (inClientProc) THEN
ERROR XNSCH.Error[wasUpNowDown, first];
LOOP };
unknown, unknownClass, notImplemented, unknownOperation, notServerHandle, notClientHandle, addressInappropriateForClass -- can't happen -- =>
ERROR;
ENDCASE => ERROR;
};
CHOps.WrongServer => {
IF specific THEN REJECT;
MarkServerNonexistent[theServer];
LOOP };
};
IF specific THEN {
inClientProc ← TRUE;
proc[c.handle, c.host];
inClientProc ← FALSE;
RETURN };
GetServerCandidate[]; -- sets [theServer, theHandle, state]
IF state = none THEN EXIT;
IF NOT theServer.knowDomainsServed THEN
LearnDomainsServed[theHandle, theServer];
gotHint ← FALSE;
inClientProc ← TRUE;
proc[theHandle, theServer.address.host
! CHOps.WrongServer => { theHint ← hint; gotHint ← TRUE; CONTINUE } ];
inClientProc ← FALSE;
IF gotHint THEN {
DeleteServerForDomain[domain, theServer]; -- correct the cache
IF tryNo >= triesToUse THEN EXIT;
hintServers ← ProcessHint[theHandle, theServer, theHint];
NoteServerAlreadyUsed[theServer];
LOOP };
NoteServerAlreadyUsed[theServer];
AddServerForDomain[domain, theServer];
CleanUp[];
RETURN;
ENDLOOP;
CleanUp[];
ERROR XNSCH.Error[allDown, first];
};
Daemon
daemonWakeup: CONDITION;
secondsBetweenSweeps: CARDINAL ← 900;
Daemon: PROC ~ {
DO
WaitDaemonWakeup[];
SweepDomainCache[];
SweepNearbyServers[];
SweepAllServers[];
ENDLOOP;
};
WaitDaemonWakeup: ENTRY PROC ~ {
TRUSTED {
Process.EnableAborts[@daemonWakeup];
Process.SetTimeout[@daemonWakeup, Process.SecondsToTicks[secondsBetweenSweeps]] };
WAIT daemonWakeup;
};
Mainline Code
nNearbyServersToStart: CARDINAL ← 2;
TRUSTED { Process.Detach[FORK Daemon[]] };
TRUSTED { Process.Detach[FORK BroadcastForNearbyServers[maxHops~defaultMaxHops, nWanted~nNearbyServersToStart]] };
}.