DIRECTORY
Arpa USING [ Address, nullAddress ],
ArpaUDP USING [ Port ],
Basics USING [ Card16FromH, LowHalf, HFromCard16 ],
CardTab USING [ Ref, Create, Fetch, Store ],
ConvertExtras USING [ ArpaAddressFromRope ],
IO,
RefID USING [ ID ],
RefTab USING [ Create, Delete, Fetch, Ref, Store ],
Rope USING [ FromRefText, ROPE ],
RPC USING [ Conversation, GenerateConversation ],
RuntimeError USING [ UNCAUGHT ],
SunPMap USING [ ipProtocolUDP, udpPort ],
SunPMapLocal USING [ SetLocal, ipProtocolUDP ],
SunPMapClient USING [ GetPort ],
SunRPC USING [ Create, CreateServer, Destroy, DestroyServer, Error, GetServerPort, Handle, nullPort, ReleaseReply, SendCallAndReceiveReply, Server, ServerProc, SetRemote, StartCall ],
SunRPCAuth USING [ Initiate ],
SunYPAgent USING [Error, Handle, Match, ObtainHandle, ReleaseHandle, Tokenize],
SRPCCalls USING [ Conversation, ReportProc, SHandle, SunRPCHandleBody, TimeoutEnable ],
VoiceUtilsExtras USING [ SunProtocol, SunAddrFromRope ];
Handle Management
NewRPCConversation creates a Conversation, an SHandle, and a dummy RPC Handle, but does not actually import the interface. Instead, the interface is imported any time a call is made when the SHandle indicates that the remote server is disconnected. In addition, whenever a call fails due to timeout or other potentially correctable problems, an attempt is made to re-bind to the remote service and retry the call before giving up. But only one attempt is made.
Conversation: TYPE ~ SRPCCalls.Conversation;
SHandle: TYPE ~ SRPCCalls.SHandle;
SHandleBody: TYPE ~ SRPCCalls.SunRPCHandleBody;
TimeoutEnable: TYPE ~ SRPCCalls.TimeoutEnable;
ID: TYPE ~ RefID.ID;
conversationToSHandleMap: RefTab.Ref ← RefTab.Create[];
handleToSHandleMap: RefTab.Ref ← RefTab.Create[];
NewRPCConversation:
PUBLIC
PROC [
serverName: Rope.
ROPE, rpcProgram:
CARD, rpcVersion:
CARD,
clientData: REF, reportProc: SRPCCalls.ReportProc ← NIL, timeoutEnable: TimeoutEnable, timeoutInMs: INT, retries: INT] RETURNS [conversation: Conversation] ~ {
sHandle: SHandle;
conversation ← RPC.GenerateConversation[];
sHandle ← NEW[SHandleBody ← [serverName: serverName, rpcProgram: rpcProgram, rpcVersion: rpcVersion, timeoutEnable: timeoutEnable, timeoutInMs: timeoutInMs, retries: retries, clientData: clientData, conversation: conversation, reportProc: reportProc, handle: SunRPC.Create[]]];
[]𡤌onversationToSHandleMap.Store[conversation, sHandle];
[]←handleToSHandleMap.Store[sHandle.handle, sHandle];
-- Dummy handle until really imported
};
ReleaseConversation:
PUBLIC
PROC [conversation: Conversation] ~ {
sHandle: SHandle;
IF conversation = NIL THEN RETURN;
sHandle ← GetSHandle[conversation];
IF sHandle = NIL THEN RETURN;
[]𡤌onversationToSHandleMap.Delete[conversation];
IF sHandle.handle#
NIL
THEN {
[]←handleToSHandleMap.Delete[sHandle.handle];
SunRPC.Destroy[sHandle.handle];
};
sHandle.connected ← FALSE;
sHandle.conversation ← NIL; -- Break any
sHandle.handle ← NIL; -- loops
sHandle.clientData ← NIL;
};
GetSHandle:
PUBLIC PROC[conversation: Conversation]
RETURNS [sHandle: SHandle] ~ {
RETURN[NARROW[conversationToSHandleMap.Fetch[conversation].val]];
};
SHandleFromHandle:
PROC[handle: Handle]
RETURNS [sHandle: SHandle] ~ {
RETURN[NARROW[handleToSHandleMap.Fetch[handle].val]];
};
ImportInterface:
INTERNAL
PROC[sHandle: SHandle]
RETURNS[errCode: ATOM ← NIL, expl: ROPE ← NIL] ~ {
ENABLE {
SunRPC.Error => {
errCode ← code;
SELECT code
FROM
$unreachable, $timeout => NULL; ENDCASE => sHandle.enabled ← FALSE;
CONTINUE;
};
RuntimeError.
UNCAUGHT => {
errCode ← $UnknownError; sHandle.enabled ← FALSE; CONTINUE; };
};
server: Rope.ROPE ← sHandle.serverName;
addr: Arpa.Address ← Arpa.nullAddress;
handle, oldHandle: Handle;
sHandle.connected ← FALSE;
oldHandle ← sHandle.handle;
IF VoiceUtilsExtras.SunProtocol[server] THEN {
uPort: ArpaUDP.Port;
[addr, uPort] ← VoiceUtilsExtras.SunAddrFromRope[server];
handle ← SunRPC.Create[addr, uPort];
}
ELSE IF server#
NIL
THEN {
port: CARD;
addr ← SunYPNameToAddress[server];
handle ← SunRPC.Create[addr, Basics.HFromCard16[SunPMap.udpPort]];
port ← SunPMapClient.GetPort[handle, SunRPCAuth.Initiate[], sHandle.rpcProgram, sHandle.rpcVersion, SunPMap.ipProtocolUDP ! SunRPC.Error =>
{
errCode ← code;
expl ← IO.PutFR["Can't contact remote server: %g.", IO.rope[server]];}];
IF errCode#NIL THEN RETURN;
IF port = 0
THEN
RETURN [$CantConnect, IO.PutFR["ThParty not exported on server: %g.", IO.rope[server]]];
handle ←
SunRPC.SetRemote[handle, addr, Basics.HFromCard16[Basics.LowHalf[port]]];
};
IF addr = Arpa.nullAddress
THEN
RETURN[$CantOpenSchema, IO.PutFR["Unknown server: %g.", IO.rope[server]]];
[]←handleToSHandleMap.Store[handle, sHandle];
sHandle.handle ← handle;
IF oldHandle#
NIL
THEN {
[]←handleToSHandleMap.Delete[oldHandle];
SunRPC.Destroy[oldHandle];
};
sHandle.connected ← TRUE;
};
ypMap: ROPE ← "hosts.byname";
SunYPNameToAddress:
PROC [name:
ROPE]
RETURNS [addr: Arpa.Address ← Arpa.nullAddress] ~ {
ENABLE SunYPAgent.Error => CONTINUE;
mapEntry: REF TEXT ← NIL;
h: SunYPAgent.Handle ← SunYPAgent.ObtainHandle[]; -- use default domain
mapEntry ← SunYPAgent.Match[h, ypMap, name];
SunYPAgent.ReleaseHandle[h];
IF mapEntry #
NIL
THEN {
map entry is something like "13.0.208.72 baobab baobab.parc.xerox.com"
addrText: REF TEXT ← SunYPAgent.Tokenize[mapEntry][0]; -- address is at front of mapEntry
addr ← ConvertExtras.ArpaAddressFromRope[Rope.FromRefText[addrText]];
};
};
ExportSunRPCInterface:
PUBLIC
PROC [
previousPort: CARD𡤀, server: SunRPC.ServerProc, rpcPgm: CARD, rpcPgmVersion: CARD]
RETURNS [uniquePort: CARD] ~ {
ENABLE SunRPC.Error => Error[code, "export failed"];
ok: BOOL; -- Do something with this!!
theServer: SunRPC.Server ← GetServer[previousPort]; -- in case server already running
IF theServer#NIL THEN SunRPC.DestroyServer[theServer]; -- remove old server
theServer ← SunRPC.CreateServer[pgm~rpcPgm, serverProc~server, version~rpcPgmVersion, port: SunRPC.nullPort ];
uniquePort ← Basics.Card16FromH[SunRPC.GetServerPort[theServer]];
ok ← SunPMapLocal.SetLocal[program~rpcPgm, version~rpcPgmVersion, protocol~SunPMapLocal.ipProtocolUDP, port~uniquePort];
[] ← sunServerTable.Store[key: uniquePort, val: NEW[SunRPC.Server ← theServer] ];
};
A table is kept to map ports to servers, so that an old version of a server can be removed before a new one is exported. The SunPMap package does a similar job, but it distinguishes only on the basis of program and version, so doesn't support multiple instances of an interface exported from the same machine.
sunServerTable: CardTab.Ref ← CardTab.Create[];
WrapOpaqueType:
TYPE =
REF SunRPC.Server;
Can't NARROW or LOOPHOLE to an opaque type
GetServer:
PROC [port:
CARD]
RETURNS [SunRPC.Server] =
INLINE {
wrapper: WrapOpaqueType ← NARROW[CardTab.Fetch[x: sunServerTable, key: port].val];
RETURN[IF wrapper=NIL THEN NIL ELSE wrapper^]
};
}.