GrapevineUser (Cedar) - Location of servers
GVLocateImpl.mesa
Andrew Birrell May 13, 1983 1:32 pm
Mike Schroeder March 21, 1981 10:59 AM
DIRECTORY
GVBasics USING [Connect, RName],
GVLocate USING [FoundServerInfo, FoundState],
GVNames USING [GetConnect, GetMembers, MemberInfo, NameType, RListHandle],
GVProtocol USING [GetSocket, IsLocal],
Process USING [MsecToTicks, SetTimeout ],
PupDefs USING [EnumeratePupAddresses, GetFreePupBuffer, GetHopsToNetwork, GetPupAddress, PupAddress, PupBuffer, PupNameTrouble, PupRouterSendThis, PupSocket, PupSocketDestroy, PupSocketID, PupSocketKick, PupSocketMake, ReturnFreePupBuffer, SetPupContentsWords, veryLongWait],
PupTypes USING [fillInSocketID, PupSocketID],
Rope USING [Cat, Equal, Fetch, Length, Substr];
GVLocateImpl: CEDAR MONITOR
IMPORTS GVNames, GVProtocol, Process, PupDefs, Rope
EXPORTS GVLocate =
BEGIN
AddressInfo: TYPE = REF AddressInfoObject;
AddressInfoObject: TYPE = RECORD[
preSorted: BOOLEAN,
found: CARDINAL,
addresses: SEQUENCE possible: CARDINAL OF AddressRec];
AddressRec: TYPE = RECORD[
addr: PupDefs.PupAddress,
hops: HopCount,
reply: BOOLEAN];
HopCount: TYPE = [0..37777B];
ReplyInfo: TYPE = RECORD[ found: BOOLEAN, where: PupDefs.PupAddress ];
Prod: PROCEDURE[ socket: PupDefs.PupSocket, addrInfo: AddressInfo, accept: PROC[PupDefs.PupAddress]RETURNS[BOOLEAN] ] RETURNS[ info: ReplyInfo ] =
TRUSTED BEGIN
This is complicated! The parameters are a table of candidate addresses, and a socket. The prodder process sends out "echoMe" pups to the addresses; the slurper process accepts "iAmEcho" replies, and marks the table. The original process reads the marks from the table, and calls the client's "accept" procedure to see if this address is ok. This satisfies various constraints about not hogging pup buffers. The "echoMe" packets are sent out to closer addresses first; they're not sent after the client has accepted an address. Everywhere, the Monitor is not locked while doing long waits.
from: PupDefs.PupAddress = socket.getLocalAddress[];
replyRecvd: BOOLEANFALSE;-- "reply-waiting" flag --
cond: CONDITION; -- notified when reply comes in --
pleaseDie: BOOLEANFALSE; -- for killing auxiliary processes --
DeathWish: ENTRY PROC RETURNS[BOOLEAN] = CHECKED INLINE
{ RETURN[pleaseDie] };
KillSiblings: ENTRY PROC = CHECKED INLINE
{ pleaseDie ← TRUE; PupDefs.PupSocketKick[socket] };
NoteReply: ENTRY PROC[i: CARDINAL, addr: PupDefs.PupAddress] =
CHECKED BEGIN
-- caller promises that "i" is in range --
IF addrInfo.addresses[i].addr.host = 0
THEN { addrInfo.addresses[i].addr.host ← addr.host;
addrInfo.addresses[i].addr.net ← addr.net };
addrInfo.addresses[i].reply ← TRUE;
IF NOT replyRecvd THEN NOTIFY cond;
replyRecvd ← TRUE;
END;
TestReply: ENTRY PROC[i: CARDINAL] RETURNS[yes: BOOLEAN] = CHECKED INLINE
{ yes ← addrInfo.addresses[i].reply; addrInfo.addresses[i].reply ← FALSE };
More: ENTRY PROC RETURNS[yes: BOOLEAN] =
CHECKED { IF NOT replyRecvd THEN WAIT cond; yes ← replyRecvd; replyRecvd ← FALSE };
SendProd: PROCEDURE =
TRUSTED BEGIN
sent: CARDINAL ← 0;
lastHops: CARDINAL = 3; --sorting limit--
FOR wantedHops: CARDINAL IN [0..lastHops]
DO lastPass: BOOLEAN = (wantedHops=lastHops);
FOR i: CARDINAL IN [0..addrInfo.found)
DO IF DeathWish[] OR sent >= addrInfo.found
THEN GOTO getUsOutOfHere;
IF addrInfo.preSorted
OR (lastPass AND addrInfo.addresses[i].hops>=wantedHops)
OR addrInfo.addresses[i].hops = wantedHops
THEN BEGIN
b: PupDefs.PupBuffer = PupDefs.GetFreePupBuffer[];
b.source ← from;
b.pupType ← echoMe;
b.dest ← addrInfo.addresses[i].addr;
-- force socket number, because database may not have GV test-mode socket numbers --
b.dest.socket ← GVProtocol.GetSocket[RSPoll];
b.pupWords[0] ← i;
PupDefs.SetPupContentsWords[b, 1];
PupDefs.PupRouterSendThis[b];
sent ← sent + 1;
END;
REPEAT
getUsOutOfHere => EXIT -- from outer loop! --
ENDLOOP;
ENDLOOP;
END;
Slurp: PROCEDURE =
TRUSTED BEGIN
UNTIL DeathWish[]
DO b: PupDefs.PupBuffer = socket.get[];
IF b # NIL
THEN BEGIN
SELECT b.pupType FROM
iAmEcho =>
IF b.pupWords[0] < addrInfo.found
THEN NoteReply[b.pupWords[0], b.source];
ENDCASE => NULL;
PupDefs.ReturnFreePupBuffer[b];
END;
ENDLOOP;
END;
prodder: PROCESS = FORK SendProd[];
slurper: PROCESS = FORK Slurp[];
Process.SetTimeout[@cond, Process.MsecToTicks[1500]];
info.found ← FALSE;
UNTIL info.found
DO IF NOT More[] THEN EXIT-- time-out on "cond" --;
FOR i: CARDINAL IN [0..addrInfo.found)
UNTIL info.found
DO IF TestReply[i]
THEN BEGIN
info.where ← addrInfo.addresses[i].addr;
IF accept[info.where] THEN info.found ← TRUE;
END;
ENDLOOP;
ENDLOOP;
KillSiblings[];
JOIN prodder;
JOIN slurper;
END;
GetGVRegServer: PROCEDURE RETURNS[ info: AddressInfo ] =
BEGIN
addressesInGVRS: CARDINAL = 10; -- we take the 10 closest --
Work: PROCEDURE[addr: PupDefs.PupAddress] RETURNS[ BOOLEAN ] =
BEGIN
-- terminate if we have enough addresses --
-- Note: EnumeratePupAddresses gives them closest first --
IF info.found = info.possible THEN RETURN[TRUE];
info.addresses[info.found] ← [
addr: [ net: addr.net, host: addr.host, socket: GVProtocol.GetSocket[RSEnquiry]],
hops: 0 -- ignored, because of "preSorted" flag --,
reply: FALSE ];
info.found ← info.found + 1;
RETURN[FALSE]
END;
Top-level contacting R-Servers. Any method is valid! Try local broadcast, and try Name Lookup Server.
info ← NEW[AddressInfoObject[addressesInGVRS]];
info.preSorted ← TRUE;
info.found ← 0;
[] ← Work[ [net:[0], host:[0], socket: GVProtocol.GetSocket[RSEnquiry] ] ];
[] ← PupDefs.EnumeratePupAddresses["GrapevineRServer", Work !
PupDefs.PupNameTrouble => CONTINUE ];
END;
GetGroupInfo: PROC[ who: GVBasics.RName] RETURNS[ state: GVLocate.FoundState, info: AddressInfo ← NIL, local: GVBasics.RName ← NIL ] =
TRUSTED BEGIN
mInfo: GVNames.MemberInfo = GVNames.GetMembers[who];
WITH m: mInfo SELECT FROM
notFound, individual => state ← notFound;
allDown => state ← allDown;
group =>
BEGIN
state ← found;
info ← NEW[AddressInfoObject[m.count]];
info.preSorted ← FALSE;
info.found ← 0;
FOR member: GVNames.RListHandle ← m.members, member.rest UNTIL member = NIL DO
cInfo: GVNames.NameType;
connect: GVBasics.Connect;
[cInfo, connect] ← GVNames.GetConnect[member.first];
SELECT cInfo FROM
individual =>
BEGIN
addr: PupDefs.PupAddress =
PupDefs.GetPupAddress[GVProtocol.GetSocket[RSEnquiry], connect !
PupDefs.PupNameTrouble => GOTO cant];
info.addresses[info.found] ← [
addr: addr,
hops: MIN[PupDefs.GetHopsToNetwork[addr.net], LAST[HopCount]],
reply: FALSE ];
info.found ← info.found + 1;
IF GVProtocol.IsLocal[addr] THEN local ← member.first;
EXITS cant => NULL;
END;
ENDCASE => NULL -- ignore others --;
ENDLOOP;
END;
ENDCASE => ERROR;
END;
FindNearestServer: PUBLIC SAFE PROCEDURE[list: GVBasics.RName, accept: PROC[PupDefs.PupAddress]RETURNS[BOOLEAN] ] RETURNS[info: GVLocate.FoundServerInfo] =
TRUSTED BEGIN
socket: PupDefs.PupSocket = PupDefs.PupSocketMake[
local: PupTypes.fillInSocketID,
remote:, ticks: PupDefs.veryLongWait ];
addrInfo: AddressInfo;
state: GVLocate.FoundState;
IF list.Equal["gv.gv", FALSE]
THEN { state ← found; addrInfo ← GetGVRegServer[] }
ELSE [state, addrInfo, ] ← GetGroupInfo[list];
SELECT state FROM
notFound => info ← [notFound[]];
allDown => info ← [allDown[]];
found =>
BEGIN
THROUGH [1..4] -- re-tries for lost packets --
DO reply: ReplyInfo = Prod[socket, addrInfo, accept];
IF reply.found THEN { info ← [found[reply.where]]; EXIT }
REPEAT
FINISHED => info ← [allDown[]]
ENDLOOP;
END;
ENDCASE => ERROR;
PupDefs.PupSocketDestroy[socket];
END --FindNearestServer--;
FindLocalServer: PUBLIC SAFE PROC[list: GVBasics.RName]
RETURNS[ state: GVLocate.FoundState, local: GVBasics.RName ] =
BEGIN
[state, , local] ← GetGroupInfo[list];
IF local = NIL THEN state ← notFound;
END;
FindRegServer: PUBLIC SAFE PROC[ who: GVBasics.RName, accept: PROC[PupDefs.PupAddress]RETURNS[BOOLEAN] ] RETURNS[ foundInfo: GVLocate.FoundServerInfo ] =
BEGIN
-- find a registration server for given R-Name --
sep: CHARACTER = '.; -- SN sep NA --
length: INT = who.Length[];
i: INT;
FOR i DECREASING IN [0..length)
DO IF who.Fetch[i] = sep THEN EXIT;
REPEAT FINISHED => RETURN[ [notFound[]] ]
ENDLOOP;
RETURN[ FindNearestServer[Rope.Cat[who.Substr[start: i+1, len: length-i-1], ".gv"], accept] ];
END; --FindRegServer--
AcceptFirst: PUBLIC SAFE PROC[PupDefs.PupAddress]RETURNS[BOOLEAN] =
{ RETURN[TRUE] };
END.