GVLocateImpl.mesa (Cedar) - Location of servers
Copyright © 1985 by Xerox Corporation. All rights reserved.
Andrew Birrell May 13, 1983 1:32 pm
Mike Schroeder March 21, 1981 10:59 AM
Hal Murray, June 3, 1986 3:46:17 pm PDT
DIRECTORY
GVBasics USING [Connect, RName],
GVLocate USING [FoundServerInfo, FoundState],
GVNames USING [GetConnect, GetMembers, MemberInfo, NameType, RListHandle],
GVProtocol USING [GetSocket, IsLocal],
Process USING [MsecToTicks, SetTimeout ],
Pup USING [Address, allHosts, allNets, nullAddress, Socket],
PupBuffer USING [Buffer],
PupHop USING [GetHop, Hop],
PupName USING [Error, HisAddresses, NameLookup],
PupSocket USING [AllocBuffer, CreateEphemeral, Destroy, Get, FreeBuffer, Kick, Send, SetNoErrors, SetUserHWords, Socket, waitForever],
Rope USING [Cat, Equal, Fetch, Length, Substr];
GVLocateImpl:
CEDAR
MONITOR
IMPORTS GVNames, GVProtocol, Process, PupHop, PupName, PupSocket, 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: Pup.Address,
hop: PupHop.Hop,
reply: BOOLEAN];
ReplyInfo: TYPE = RECORD[ found: BOOLEAN, where: Pup.Address ];
Prod:
PROC [socket: PupSocket.Socket, addrInfo: AddressInfo, accept:
PROC [Pup.Address]
RETURNS[
BOOLEAN] ]
RETURNS [info: ReplyInfo ] = {
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.
replyRecvd: BOOLEAN ← FALSE;-- "reply-waiting" flag
cond: CONDITION; -- notified when reply comes in
pleaseDie: BOOLEAN ← FALSE; -- for killing auxiliary processes
DeathWish: ENTRY PROC RETURNS [BOOLEAN] = { RETURN[pleaseDie] };
KillSiblings: ENTRY PROC = { pleaseDie ← TRUE; PupSocket.Kick[socket] };
NoteReply:
ENTRY
PROC [i:
CARDINAL, addr: Pup.Address] = {
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 ~replyRecvd THEN NOTIFY cond;
replyRecvd ← TRUE;
};
TestReply:
ENTRY
PROC [i:
CARDINAL]
RETURNS [yes:
BOOLEAN] = {
yes ← addrInfo.addresses[i].reply; addrInfo.addresses[i].reply ← FALSE };
More:
ENTRY
PROC
RETURNS[yes:
BOOLEAN] =
CHECKED {
IF ~replyRecvd THEN WAIT cond;
yes ← replyRecvd; replyRecvd ← FALSE;
};
SendProd:
PROCEDURE = {
sent: CARDINAL ← 0;
lastHop: CARDINAL = 4; --sorting limit--
FOR wantedHop:
CARDINAL
IN [0..lastHop)
DO
lastPass: BOOLEAN = (wantedHop=lastHop-1);
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].hop>=wantedHop)
OR addrInfo.addresses[i].hop = wantedHop
THEN {
b: PupBuffer.Buffer = PupSocket.AllocBuffer[socket];
b.type ← echoMe;
b.dest ← addrInfo.addresses[i].addr;
force socket number, because database doesn't have GV test-mode socket numbers
b.dest.socket ← GVProtocol.GetSocket[RSPoll];
b.hWord[0] ← i;
PupSocket.SetUserHWords[b, 1];
PupSocket.Send[socket, b, b.dest];
sent ← sent + 1; };
REPEAT
GetUsOutOfHere => EXIT -- from outer loop!
ENDLOOP;
ENDLOOP;
};
Slurp:
PROCEDURE = {
UNTIL DeathWish[]
DO
b: PupBuffer.Buffer = PupSocket.Get[socket];
IF b #
NIL
THEN {
SELECT b.type
FROM
iAmEcho =>
IF b.hWord[0] < addrInfo.found THEN NoteReply[b.hWord[0], b.source];
ENDCASE => NULL;
PupSocket.FreeBuffer[b]; };
ENDLOOP;
};
prodder: PROCESS;
slurper: PROCESS;
TRUSTED {
prodder ← FORK SendProd[];
slurper ← FORK Slurp[];
Process.SetTimeout[@cond, Process.MsecToTicks[1500]]; };
info.found ← FALSE;
UNTIL info.found
DO
IF ~More[] THEN EXIT-- time-out on "cond" --;
FOR i:
CARDINAL
IN [0..addrInfo.found)
UNTIL info.found
DO
IF TestReply[i]
THEN
{
info.where ← addrInfo.addresses[i].addr;
IF accept[info.where] THEN info.found ← TRUE;
};
ENDLOOP;
ENDLOOP;
KillSiblings[];
TRUSTED {
JOIN prodder;
JOIN slurper; };
};
GetGVRegServer:
PROC
RETURNS[ info: AddressInfo ] = {
addressesInGVRS: CARDINAL = 10; -- we take the 10 closest
socket: Pup.Socket = GVProtocol.GetSocket[RSEnquiry];
addresses: LIST OF Pup.Address;
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;
info.addresses[0] ← [
addr: [net: Pup.allNets, host: Pup.allHosts, socket: socket],
hop: PupHop.GetHop[Pup.allNets],
reply: FALSE ];
info.found ← 1;
addresses ← PupName.HisAddresses["GrapevineRServer", socket ! PupName.Error => CONTINUE ];
UNTIL addresses =
NIL
DO
info.addresses[info.found] ← [
addr: addresses.first,
hop: PupHop.GetHop[addresses.first.net],
reply: FALSE ];
info.found ← info.found + 1;
IF info.found = addressesInGVRS THEN EXIT;
addresses ← addresses.rest;
ENDLOOP;
};
GetGroupInfo:
PROC [who: GVBasics.RName]
RETURNS[ state: GVLocate.FoundState, info: AddressInfo ←
NIL, local: GVBasics.RName ←
NIL] = {
mInfo: GVNames.MemberInfo = GVNames.GetMembers[who];
SELECT mInfo.type
FROM
notFound, individual => state ← notFound;
allDown => state ← allDown;
group => {
m: group GVNames.MemberInfo = NARROW[mInfo, group GVNames.MemberInfo];
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: Pup.Address =
PupName.NameLookup[connect, GVProtocol.GetSocket[RSEnquiry] !
PupName.Error => GOTO Cant];
info.addresses[info.found] ← [
addr: addr,
hop: PupHop.GetHop[addr.net],
reply: FALSE ];
info.found ← info.found + 1;
IF GVProtocol.IsLocal[addr] THEN local ← member.first;
EXITS Cant => NULL;
END;
ENDCASE => NULL -- ignore others --;
ENDLOOP; };
ENDCASE => ERROR;
};
FindNearestServer:
PUBLIC
PROC [
list: GVBasics.RName,
accept: PROC [Pup.Address] RETURNS [BOOL] ]
RETURNS [info: GVLocate.FoundServerInfo] = {
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 => RETURN[[notFound[]]];
allDown => RETURN[[allDown[]]];
found => {
socket: PupSocket.Socket = PupSocket.CreateEphemeral[
remote: Pup.nullAddress, getTimeout: PupSocket.waitForever ];
PupSocket.SetNoErrors[socket];
FOR i:
NAT
IN [0..4)
DO
-- re-tries for lost packets
reply: ReplyInfo = Prod[socket, addrInfo, accept];
IF reply.found
THEN {
PupSocket.Destroy[socket];
RETURN[[found[reply.where]]]; };
REPEAT
FINISHED => {
PupSocket.Destroy[socket];
RETURN[[allDown[]]]; };
ENDLOOP; };
ENDCASE => ERROR;
};
FindLocalServer:
PUBLIC
PROC [list: GVBasics.RName]
RETURNS [state: GVLocate.FoundState, local: GVBasics.RName] = {
[state, , local] ← GetGroupInfo[list];
IF local = NIL THEN state ← notFound;
};
FindRegServer:
PUBLIC
PROC [
who: GVBasics.RName, accept: PROC [Pup.Address] RETURNS [BOOL] ]
RETURNS [foundInfo: GVLocate.FoundServerInfo] = {
find a registration server for given R-Name
sep: CHARACTER = '.; -- SN sep NA
length: INT = who.Length[];
FOR i:
INT
DECREASING
IN [0..length)
DO
IF who.Fetch[i] = sep
THEN
RETURN[FindNearestServer[Rope.Cat[who.Substr[start: i+1, len: length-i-1], ".gv"], accept]];
REPEAT FINISHED => RETURN[[notFound[]]];
ENDLOOP;
};
END.