Server Objects
CachedServer: TYPE ~ REF CachedServerObject;
CachedServerObject:
TYPE ~
RECORD [
address: REF XNS.Address, -- readonly, REF to conform to CrRPC.RefAddress
knowDomainsServed: BOOL ← FALSE, -- hint, no ML
inactiveUntil: BasicTime.GMT ← BasicTime.earliestGMT -- ML
];
numHeaders: CARDINAL ~ 101;
serversByAddress: RefTab.Ref ~ RefTab.Create[mod~numHeaders, equal~EqualAddressesIgnoringSocket, hash~HashAddress];
HashAddress: RefTab.HashProc ~ {
host: XNS.Host ← NARROW[key, REF XNS.Address].host;
acc: CARDINAL ← ((((host.a*5+host.b)*5+host.c)*5+host.d)*5+host.e)*5+host.f;
RETURN [acc];
};
EqualAddressesIgnoringSocket: RefTab.EqualProc ~ {
ra1: REF XNS.Address ← NARROW[key1];
ra2: REF XNS.Address ← NARROW[key2];
RETURN [(ra1.net = ra2.net) AND (ra1.host = ra2.host)];
};
GetServerByAddress:
PROC [ra:
REF
XNS.Address, makeActive:
BOOL]
RETURNS [s: CachedServer] ~ {
Net and host are significant, socket is set to XNS.unknownSocket
ENABLE UNWIND => NULL;
val: RefTab.Val;
found: BOOL;
[found, val] ← RefTab.Fetch[x~serversByAddress, key~ra];
IF
NOT found
THEN {
val ← NEW[CachedServerObject ← [address~ra]];
[] ← RefTab.Insert[x~serversByAddress, key~ra, val~val] };
s ← NARROW[val];
IF makeActive THEN MarkServerUsable[s];
};
ServerIsUsableInternal:
INTERNAL
PROC [s: CachedServer]
RETURNS [usable:
BOOL] ~ {
usable ← ( BasicTime.Period[from: s.inactiveUntil, to: BasicTime.Now[]] >= 0 );
};
MarkServerUsable:
ENTRY
PROC [s: CachedServer] ~ {
ENABLE UNWIND => NULL;
s.inactiveUntil ← BasicTime.earliestGMT;
};
MarkServerBusy:
ENTRY
PROC [s: CachedServer, secondsBusy:
INT ← 10] ~ {
ENABLE UNWIND => NULL;
s.inactiveUntil ← BasicTime.Update[BasicTime.Now[], secondsBusy];
};
MarkServerDown:
ENTRY
PROC [s: CachedServer, secondsDown:
INT ← 600] ~ {
ENABLE UNWIND => NULL;
s.inactiveUntil ← BasicTime.Update[BasicTime.Now[], secondsDown];
s.knowDomainsServed ← FALSE;
};
MarkServerDead:
ENTRY
PROC [s: CachedServer] ~ {
ENABLE UNWIND => NULL;
s.inactiveUntil ← BasicTime.latestGMT;
s.knowDomainsServed ← FALSE;
};
Lists of Servers
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];
};
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 NOT ServerIsUsableInternal[server] 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;
};
}...