<> <> <<>> <> < 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, BulkDataXferProc, CreateClientHandle, DestroyClientHandle, Error, Handle], 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 [BulkDataValueProc, ReadBulkDataStream, ReadElement, ReadRope, 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"]; <> 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, 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] }; <> CachedServer: TYPE ~ REF CachedServerObject; CachedServerObject: TYPE ~ RECORD [ next: CachedServer _ NIL, -- successor in serversByAddress. ML address: XNS.Address, -- readonly knowDomainsServed: BOOL _ FALSE, -- 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] ~ { <> 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~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; }; <> CachedServerListHead: TYPE ~ REF CachedServerList; CachedServerList: TYPE ~ REF CachedServerListElement; CachedServerListElement: TYPE ~ RECORD [ next: CachedServerList, -- ML server: CachedServer -- readonly ]; <> <> <<>> <> <> <> <> <> <> <<};>> <<>> 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] }; <> <> < NULL;>> <> <<};>> <<>> 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: CARDINAL _ LAST[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] ~ { <> 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; }; <> 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 ~ { <> 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] ~ { <> 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, stream: IO.STREAM, checkAbort: BulkDataCheckAbortProc] RETURNS [abort: BOOL];>> ~ { EachDomainNameInStream: XNSCHPrivate.BulkDataValueProc <<[stream: IO.STREAM] RETURNS [abort: BOOL _ FALSE]>> ~ { domPart, orgPart, key: ROPE; orgPart _ XNSCHPrivate.ReadRope[stream]; domPart _ XNSCHPrivate.ReadRope[stream]; key _ Rope.Cat[orgPart, ":", domPart]; AddServerForDomain[key, theServer]; AddServerForDomain[OrganizationFromDomain[key], theServer]; -- <- BOGUS ??? }; RETURN [XNSCHPrivate.ReadBulkDataStream[h, stream, 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] ~ { < "CHServers:Xerox">> len: INT ~ Rope.Index[s1~domain, s2~":"]; organization _ Rope.Cat[shiftInName, ":", Rope.Substr[domain, 0, len]] }; <> 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: CARDINAL _ CARDINAL.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: CARDINAL _ CARDINAL.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: BOOL _ FALSE] ~ { 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 ~ { <> 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]; }; <> maxHints: CARDINAL ~ 25; ProcessHint: PROC [h: CrRPC.Handle, theServer: CachedServer, hint: Name] RETURNS [servers: CachedServerListHead] ~ { c: Conversation; nRead: CARDINAL; hints: ARRAY [1..maxHints] OF ROPE _ ALL[NIL]; ReadStreamOfNames: CrRPC.BulkDataXferProc <<[h: Handle, stream: IO.STREAM, checkAbort: BulkDataCheckAbortProc] RETURNS [abort: BOOL];>> ~ { EachNameInStream: XNSCHPrivate.BulkDataValueProc <<[stream: IO.STREAM] RETURNS [abort: BOOL _ FALSE]>> ~ { serverName: Name ~ XNSCHPrivate.ReadElement[stream]; 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 [XNSCHPrivate.ReadBulkDataStream[h, stream, 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]; }; <> maxTries: CARDINAL ~ 15; triesToUse: CARDINAL _ 7; -- Bounds Fault if this exceeds maxTries CallRemote: PUBLIC PROC [c: Conversation, proc: XNSCHPrivate.RemoteProc, domain: ROPE _ NIL, idempotent: BOOL _ TRUE] ~ { State: TYPE ~ { dom, hint, org, nearby, none }; state: State _ none; organization: ROPE _ NIL; theServer: CachedServer _ NIL; theHandle: CrRPC.Handle _ NIL; gotHint: BOOL; theHint: Name; hintServers: CachedServerListHead _ NIL; specific: BOOL ~ NOT IsGeneric[c]; inClientProc: BOOL _ FALSE; didBroadcast: BOOL _ FALSE; nAlreadyUsed: CARDINAL _ 0; alreadyUsedCache: ARRAY [0..maxTries) OF CachedServer; NoteServerAlreadyUsed: PROC [server: CachedServer] ~ { <> 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, 90000]; -- BOGUS TIMEOUT ???? }; <> 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]; <> EXIT }; <> 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 }; resultsTooShort, timeout, protocolError, remoteClose, communicationFailure, remoteError => -- can't retry, progress made -- { IF specific THEN ERROR XNSCH.Error[ (SELECT errorReason FROM communicationFailure => communicationFailure, ENDCASE => protocolError), first]; MarkServerDown[theServer]; IF (NOT idempotent) AND (inClientProc) THEN ERROR XNSCH.Error[wasUpNowDown, first]; LOOP }; unknown, unknownClass, argsTooLong, notImplemented, notServerHandle, notClientHandle, notBroadcastHandle -- 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]; }; <> 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; }; <> nNearbyServersToStart: CARDINAL _ 2; TRUSTED { Process.Detach[FORK Daemon[]] }; TRUSTED { Process.Detach[FORK BroadcastForNearbyServers[maxHops~defaultMaxHops, nWanted~nNearbyServersToStart]] }; }.