DIRECTORY BufferDefs USING [PupBuffer], DebuggerSwap USING [CallDebugger], PrincOps USING [PsbIndex, PsbNull], PrincOpsUtils USING [BITAND, BITXOR, IsBound, LongCopy, PsbHandleToIndex, ReadPSB], Process USING [Detach], PupDefs USING [AnyLocalPupAddress, GetFreePupBuffer, PupRouterSendThis, ReturnFreePupBuffer], PupTypes USING [PupAddress, PupHostID, PupNetID, PupSocketID], RPC USING [CallFailed, Conversation, unencrypted], RPCInternal USING [ConversationObject, DecryptPkt, EncryptPkt, exportTable, firstConversation, GetConnectionState, ImportInstance], RPCLupine USING [DataLength, Dispatcher, GetRPCPkt, Header, maxDataLength, maxPupWords, pktOverhead, RPCPkt], RPCPkt USING [CallCount, ConnectionID, DispatcherDetails, EnqueueAgain, Header, IdleReceive, Machine, noDispatcher, Outcome, PktExchange, pktLengthOverhead, SetupResponse], RPCPrivate USING [ReturnBuffer, rpcSocket], SafeStorage USING [CantEstablishFinalization, EnableFinalization, EstablishFinalization, FinalizationQueue, FQNext, NewFQ, ReEstablishFinalization], VM USING [AddressForPageNumber, Interval, nullInterval, PagesForWords, Pin, SimpleAllocate, Unpin]; RPCPktStreams: MONITOR IMPORTS DebuggerSwap, PrincOpsUtils, Process, PupDefs, RPC, RPCLupine, RPCInternal, RPCPkt, RPCPrivate, SafeStorage, VM EXPORTS RPC, --Header,ConversationObject RPCInternal, --DoSignal, ServerMain RPCLupine -- others SHARES BufferDefs, RPCLupine = { Header: PUBLIC TYPE = RPCPkt.Header; HeaderPtr: TYPE = LONG POINTER TO Header; LupineHeader: TYPE = RPCLupine.Header; LupineHeaderPtr: TYPE = LONG POINTER TO LupineHeader; ConcreteHeader: PROC [abstract: LupineHeaderPtr] RETURNS [HeaderPtr] = INLINE { RETURN [abstract]; }; myHost: RPCPkt.Machine; GiveBackBuffer: PROC [b: BufferDefs.PupBuffer] = IF PrincOpsUtils.IsBound[LOOPHOLE[RPCPrivate.ReturnBuffer]] THEN RPCPrivate.ReturnBuffer ELSE PupDefs.ReturnFreePupBuffer; CallDestHint: TYPE = ARRAY PrincOps.PsbIndex OF PrincOps.PsbIndex; lastCallDest: REF CallDestHint = NEW[CallDestHint _ ALL[PrincOps.PsbNull]]; RecordCallDest: ENTRY PROC [header: HeaderPtr] = INLINE { lastCallDest[header.destPSB--myPSB--] _ header.srcePSB; }; ImportInstance: PUBLIC TYPE = RPCInternal.ImportInstance; ConversationObject: PUBLIC TYPE = RPCInternal.ConversationObject; Conversation: TYPE = REF ConversationObject; MisusedConversation: ERROR = CODE; StartCall: PUBLIC ENTRY PROC [callPkt: RPCLupine.RPCPkt, interface: REF ImportInstance, localConversation: Conversation _ RPC.unencrypted] = { ENABLE UNWIND => NULL; myPSB: PrincOps.PsbIndex = PrincOpsUtils.PsbHandleToIndex[PrincOpsUtils.ReadPSB[]]; header: HeaderPtr = @callPkt.header; header.destHost _ interface.host; header.destSoc _ RPCPrivate.rpcSocket; header.destPSB _ lastCallDest[myPSB]; callPkt.convHandle _ localConversation; IF localConversation = RPC.unencrypted THEN header.conv _ RPCInternal.firstConversation ELSE { header.conv _ [localConversation.id.count.ls, caller, localConversation.id.count.ms]; IF localConversation.id.originator # myHost THEN { IF header.destHost # localConversation.id.originator THEN ERROR MisusedConversation[]; header.conv.originator _ callee; }; }; header.pktID.activity _ myPSB; header.pktID.pktSeq _ 0; -- => new call -- header.dispatcher _ interface.dispatcher; }; Call: PUBLIC PROC [pkt: RPCLupine.RPCPkt, callLength: RPCLupine.DataLength, maxReturnLength: RPCLupine.DataLength, signalHandler: RPCLupine.Dispatcher _ NIL] RETURNS [ returnLength: RPCLupine.DataLength, lastPkt: BOOL] = { recvdHeader: HeaderPtr = @pkt.header; returnLength _ RPCPkt.PktExchange[pkt, callLength, maxReturnLength, call, signalHandler ].newLength; RecordCallDest[recvdHeader]; SELECT recvdHeader.outcome FROM result => NULL; unbound => ERROR RPC.CallFailed[unbound]; protocol => ERROR RPC.CallFailed[runtimeProtocol]; signal => ERROR -- handled inside RPCPkt.PktExchange --; unwind => { RPCPkt.SetupResponse[recvdHeader]; ERROR UnwindRequested[]; }; ENDCASE => ERROR RPC.CallFailed[runtimeProtocol]; RPCPkt.SetupResponse[recvdHeader]; RETURN[ returnLength, recvdHeader.type.eom = end ] }; SendPrelimPkt: PUBLIC PROC [pkt: RPCLupine.RPCPkt, length: RPCLupine.DataLength] = { [] _ RPCPkt.PktExchange[pkt, length, 0, sending]; }; ReceiveExtraPkt: PUBLIC PROC [pkt: RPCLupine.RPCPkt] RETURNS [length: RPCLupine.DataLength, lastPkt: BOOL] = { recvdHeader: HeaderPtr; length _ RPCPkt.PktExchange[pkt, 0, RPCLupine.maxDataLength, receiving].newLength; recvdHeader _ @pkt.header; RPCPkt.SetupResponse[recvdHeader]; RETURN[ length, recvdHeader.type.eom = end ] }; idlerAckCount: CARDINAL _ 0; idlerRequeueCount: CARDINAL _ 0; GenerateIdlerResponse: PROC [recvd: RPCLupine.RPCPkt] = { ackPkt: BufferDefs.PupBuffer = PupDefs.GetFreePupBuffer[]; header: HeaderPtr = LOOPHOLE[@ackPkt.pupLength]; recvdHeader: HeaderPtr = @recvd.header; workerPSB: PrincOps.PsbIndex = recvdHeader.destPSB; -- as adjusted by FindCallee -- idlerAckCount _ idlerAckCount+1; RPCPkt.SetupResponse[recvdHeader]; header^ _ recvdHeader^; header.length _ recvdHeader.length; header.oddByte _ no; header.type _ [0,rpc,end,dontAck,ack]; header.srceHost _ myHost; header.srceSoc _ RPCPrivate.rpcSocket; header.srcePSB _ workerPSB; PupDefs.PupRouterSendThis[ackPkt]; }; EnqueueForNewPSB: PROC [recvd: RPCLupine.RPCPkt] = { pupPkt: BufferDefs.PupBuffer = PupDefs.GetFreePupBuffer[]; header: HeaderPtr = LOOPHOLE[@pupPkt.pupLength]; recvdHeader: HeaderPtr = @recvd.header; idlerRequeueCount _ idlerRequeueCount+1; PrincOpsUtils.LongCopy[from: recvdHeader, to: header, nwords: recvdHeader.length]; RPCPkt.EnqueueAgain[pupPkt]; }; CalleeState: TYPE = REF CalleeStateRec; CalleeStateRec: TYPE = RECORD [ entered: BOOL _ FALSE, free: BOOL _ FALSE, next: CalleeState _ NIL, callee: PrincOps.PsbIndex _ PrincOps.PsbNull, state: HeaderPtr _ NIL, space: VM.Interval _ VM.nullInterval]; callees: CalleeState _ NIL; calleesCount: INT _ 0; freeCallees: CalleeState _ NIL; freeCount: INT _ 0; worldSwap: BOOL _ FALSE; RPCInternalFailure: ERROR = CODE; Crash: PROC = { IF worldSwap THEN DebuggerSwap.CallDebugger["RPC internal failure!"L] ELSE ERROR RPCInternalFailure; }; AllocCalleeState: ENTRY PROC RETURNS [CalleeState] = { myPSB: PrincOps.PsbIndex = PrincOpsUtils.PsbHandleToIndex[PrincOpsUtils.ReadPSB[]]; state: CalleeState _ freeCallees; space: VM.Interval _ VM.nullInterval; IF freeCallees # NIL THEN { freeCallees _ state.next; freeCount _ freeCount - 1; space _ state.space; } ELSE { state _ NEW[CalleeStateRec]; }; IF space.count = 0 THEN space _ VM.SimpleAllocate[ VM.PagesForWords[serverDataLength+RPCLupine.pktOverhead]]; VM.Pin[space]; state^ _ [free: FALSE, entered: FALSE, next: NIL, callee: myPSB, state: NIL, space: space]; SafeStorage.EnableFinalization[state]; RETURN [state]; }; FreeCalleeState: ENTRY PROC [state: CalleeState] = { IF state.free THEN RETURN; IF state.entered THEN RemoveCalleeInternal[state]; VM.Unpin[state.space]; state.free _ TRUE; state.next _ freeCallees; freeCallees _ state; freeCount _ freeCount + 1; }; AddCallee: ENTRY PROC [state: CalleeState] = INLINE { AddCalleeInternal[state]; }; AddCalleeInternal: INTERNAL PROC [state: CalleeState] = { IF state.entered THEN Crash[]; state.entered _ TRUE; state.next _ callees; callees _ state; calleesCount _ calleesCount + 1; }; RemoveCallee: ENTRY PROC [state: CalleeState] = INLINE { RemoveCalleeInternal[state]; }; RemoveCalleeInternal: INTERNAL PROC [state: CalleeState] = { lag: CalleeState _ NIL; IF state # NIL AND state.entered THEN FOR p: CalleeState _ callees, p.next WHILE p # NIL DO SELECT p FROM state => { IF lag = NIL THEN callees _ p.next ELSE lag.next _ p.next; state.entered _ FALSE; calleesCount _ calleesCount - 1; RETURN; }; NIL => Crash[]; ENDCASE; lag _ p; ENDLOOP; Crash[]; }; FindCallee: ENTRY PROC [given: HeaderPtr] RETURNS [BOOL] = { IF given # NIL THEN FOR p: CalleeState _ callees, p.next WHILE p # NIL DO state: HeaderPtr _ p.state; SELECT TRUE FROM state = NIL => {}; state.conv = given.conv --AND same originator .... -- AND state.pktID.activity = given.pktID.activity AND state.pktID.callSeq = given.pktID.callSeq => { given^.destPSB _ p.callee; RETURN [TRUE]; }; ENDCASE; ENDLOOP; RETURN [FALSE]; }; FinalizeCalleeBlocks: PROC [fq: SafeStorage.FinalizationQueue] = { DO WITH SafeStorage.FQNext[fq] SELECT FROM state: CalleeState => FreeCalleeState[state]; ENDCASE; ENDLOOP; }; HashKey: TYPE = [0..127]; ConnectionData: TYPE = RECORD[next: Connection, id: RPCPkt.ConnectionID, call: RPCPkt.CallCount, conv: RPC.Conversation -- NB: opaque type --]; Connection: TYPE = REF ConnectionData; connections: REF ARRAY HashKey OF Connection = NEW[ARRAY HashKey OF Connection _ ALL[NIL]]; ForgetConnections: INTERNAL PROC = { FOR hash: HashKey IN HashKey DO connections[hash] _ NIL ENDLOOP; }; serverDataLength: RPCLupine.DataLength = RPCLupine.maxDataLength; ServerMain: PUBLIC PROC = { myStateBlock: CalleeState _ AllocCalleeState[]; pktSpace: VM.Interval = myStateBlock.space; myPkt: RPCLupine.RPCPkt = RPCLupine.GetRPCPkt[VM.AddressForPageNumber[pktSpace.page]]; recvdHeader: HeaderPtr _ myStateBlock.state _ @myPkt.header; newPkt: BOOL _ FALSE; -- Whether packet is valid -- decrypted: BOOL _ FALSE; -- if "newPkt", whether it's been decrypted -- newLength: RPCLupine.DataLength; -- iff "newPkt" and "decrypted", pkt's length -- connection: Connection; LookupCaller: ENTRY PROC [id: RPCPkt.ConnectionID] RETURNS [{new, old, phoney, unknown}] = { ENABLE UNWIND => Crash[]; connection _ connections[ PrincOpsUtils.BITAND[ PrincOpsUtils.BITXOR[LOOPHOLE[id.caller, WORD], id.activity], LAST[HashKey]]]; DO SELECT TRUE FROM connection = NIL => { IF recvdHeader.type.class # call THEN RETURN[old]; RETURN[unknown]; }; id.conv = connection.id.conv AND id.caller = connection.id.caller AND recvdHeader.srcePSB = connection.id.activity => { myPkt.convHandle _ connection.conv; IF NOT decrypted THEN { IF connection.conv # RPC.unencrypted THEN { ok: BOOL; [ok, newLength] _ RPCInternal.DecryptPkt[recvdHeader, myPkt.convHandle]; decrypted _ TRUE; IF NOT ok THEN RETURN[phoney]; } ELSE { newLength _ recvdHeader.length - RPCPkt.pktLengthOverhead; decrypted _ TRUE; }; }; IF recvdHeader.pktID.activity # recvdHeader.srcePSB THEN RETURN[phoney]; IF recvdHeader.type.class # call THEN RETURN[old]; IF recvdHeader.pktID.callSeq > connection.call THEN { IF recvdHeader.pktID.pktSeq # 1 THEN RETURN[phoney]; connection.call _ recvdHeader.pktID.callSeq; AddCalleeInternal[myStateBlock]; RETURN[new] } ELSE RETURN[old] }; ENDCASE => connection _ connection.next; ENDLOOP; }; NoteConnection: ENTRY PROC [id: RPCPkt.ConnectionID, call: RPCPkt.CallCount, conv: RPC.Conversation] = { prev: Connection _ NIL; hash: HashKey = PrincOpsUtils.BITAND[PrincOpsUtils.BITXOR[LOOPHOLE[id.caller, WORD], id.activity], LAST[HashKey]]; connection _ connections[hash]; DO SELECT TRUE FROM connection = NIL => { connection _ NEW[ConnectionData _ [next: NIL, id: id, call: call-1, conv: conv] ]; IF prev = NIL THEN connections[hash] _ connection ELSE prev.next _ connection; EXIT }; id.conv = connection.id.conv AND id.caller = connection.id.caller AND id.activity = connection.id.activity => -- already there! -- EXIT; ENDCASE => { prev _ connection; connection _ connection.next }; ENDLOOP; }; DO ENABLE { ABORTED => EXIT; UNWIND => FreeCalleeState[myStateBlock] }; IF NOT newPkt THEN { RPCPkt.IdleReceive[myPkt, RPCLupine.maxPupWords]; newPkt _ TRUE; decrypted _ FALSE; }; SELECT LookupCaller[[recvdHeader.conv, recvdHeader.srceHost, recvdHeader.srcePSB]] FROM new => { target: RPCPkt.DispatcherDetails = recvdHeader.dispatcher; resultLength: RPCLupine.DataLength; RPCPkt.SetupResponse[recvdHeader]; IF target.dispatcherHint >= RPCInternal.exportTable.used OR target.dispatcherID = RPCPkt.noDispatcher OR target.dispatcherID # RPCInternal.exportTable[target.dispatcherHint].id THEN { Reject[myPkt, unbound]; resultLength _ 0 } ELSE resultLength _ RPCInternal.exportTable[target.dispatcherHint].dispatcher[ myPkt, newLength, recvdHeader.type.eom = end, connection.conv ! RPC.CallFailed => TRUSTED { newPkt _ FALSE; RemoveCallee[myStateBlock]; LOOP; }; UnwindRequested => { resultLength _ 0; CONTINUE; }; RejectUnbound => { Reject[myPkt, unbound]; resultLength _ 0; CONTINUE; }; RejectProtocol => { Reject[myPkt, protocol]; resultLength _ 0; CONTINUE; }; ]; RemoveCallee[myStateBlock]; [newPkt, newLength] _ RPCPkt.PktExchange[myPkt, resultLength, serverDataLength, endCall ! RPC.CallFailed => TRUSTED {newPkt _ FALSE; CONTINUE}]; IF newPkt THEN decrypted _ TRUE; }; unknown => { ok: BOOL; id: RPCPkt.ConnectionID; call: RPCPkt.CallCount; conv: RPC.Conversation; l: RPCLupine.DataLength; [ok, id, call, conv, l] _ RPCInternal.GetConnectionState[decrypted, myPkt ! RPC.CallFailed => TRUSTED {newPkt_FALSE; LOOP}]; IF ok THEN { IF NOT newPkt THEN ERROR; IF NOT decrypted THEN {decrypted _ TRUE; newLength _ l}; NoteConnection[id, call, conv]; } ELSE newPkt _ FALSE; }; phoney => newPkt _ FALSE; old => { oldDest: PrincOps.PsbIndex = recvdHeader.destPSB; knownCallee: BOOL = decrypted AND FindCallee[recvdHeader]--may alter destPSB--; IF knownCallee AND recvdHeader.destPSB # oldDest THEN { IF decrypted THEN recvdHeader.length _ IF myPkt.convHandle = RPC.unencrypted THEN RPCPkt.pktLengthOverhead + newLength ELSE RPCInternal.EncryptPkt[myPkt, newLength]; EnqueueForNewPSB[myPkt]; } ELSE { IF recvdHeader.type.ack = pleaseAck AND recvdHeader.type.eom = end AND( recvdHeader.type.class = data OR knownCallee ) THEN { recvdHeader.length _ IF NOT decrypted OR myPkt.convHandle = RPC.unencrypted THEN RPCPkt.pktLengthOverhead ELSE RPCInternal.EncryptPkt[myPkt, 0]; GenerateIdlerResponse[myPkt]; }; }; newPkt _ FALSE; }; ENDCASE => ERROR; ENDLOOP; FreeCalleeState[myStateBlock]; }; StartSignal: PUBLIC PROC [signalPkt: RPCLupine.RPCPkt] = { ConcreteHeader[@signalPkt.header].outcome _ signal; }; UnwindRequested: ERROR = CODE; -- internal: remote machine is unwinding a signal PktData: TYPE = REF PktSpace; PktSpace: TYPE = ARRAY [1..serverDataLength + RPCLupine.pktOverhead] OF WORD; DoSignal: PUBLIC PROC [b: BufferDefs.PupBuffer, pktLength: RPCLupine.DataLength, signalHandler: RPCLupine.Dispatcher, convHandle: RPC.Conversation] RETURNS [resumePkt: RPCLupine.RPCPkt _ NIL, resumeLength: RPCLupine.DataLength _ 0, myLocalFrame: POINTER _ NIL] = { IF pktLength > serverDataLength THEN { GiveBackBuffer[b]; ERROR RPC.CallFailed[runtimeProtocol] } ELSE { myStateBlock: CalleeState _ AllocCalleeState[]; pktSpace: VM.Interval = myStateBlock.space; pkt: RPCLupine.RPCPkt = RPCLupine.GetRPCPkt[VM.AddressForPageNumber[pktSpace.page]]; recvdHeader: HeaderPtr _ myStateBlock.state _ @pkt.header; PrincOpsUtils.LongCopy[from: @b.pupLength, to: recvdHeader, nwords: pktLength+SIZE[Header]]; { ENABLE UNWIND => FreeCalleeState[myStateBlock]; handlerFailed: BOOL _ FALSE; -- CallFailed raised inside signalHandler! -- pkt.convHandle _ convHandle; GiveBackBuffer[b]; AddCallee[myStateBlock]; RPCPkt.SetupResponse[recvdHeader]; IF signalHandler = NIL THEN { Reject[pkt, unbound]; resumeLength _ 0 } ELSE resumeLength _ signalHandler[ pkt, pktLength, recvdHeader.type.eom = end, convHandle ! RPC.CallFailed => TRUSTED {handlerFailed _ TRUE}; UNWIND => IF NOT handlerFailed THEN { recvdHeader.outcome _ unwind; resumeLength _ RPCPkt.PktExchange[pkt, 0, serverDataLength, call, signalHandler].newLength; SELECT recvdHeader.outcome FROM result => NULL -- let our UNWIND propagate --; signal => ERROR -- handled inside RPCPkt.PktExchange--; ENDCASE => ERROR RPC.CallFailed[runtimeProtocol]; RPCPkt.SetupResponse[recvdHeader]; }; UnwindRequested => { resumeLength _ 0; CONTINUE; }; RejectUnbound => { Reject[pkt, unbound]; resumeLength _ 0; CONTINUE }; RejectProtocol => { Reject[pkt, protocol]; resumeLength _ 0; CONTINUE }; ]; }; FreeCalleeState[myStateBlock]; }; }; RejectUnbound: PUBLIC ERROR = CODE; RejectProtocol: PUBLIC ERROR = CODE; Reject: PROC [pkt: RPCLupine.RPCPkt, rejection: RPCPkt.Outcome] = { header: HeaderPtr = @pkt.header; UNTIL header.type.eom = end DO [,] _ ReceiveExtraPkt[pkt ! RPC.CallFailed => TRUSTED {rejection _ protocol; EXIT}] ENDLOOP; header.outcome _ rejection; }; Initialize: ENTRY PROC = { myAddr: PupTypes.PupAddress = PupDefs.AnyLocalPupAddress[RPCPrivate.rpcSocket]; fq: SafeStorage.FinalizationQueue _ SafeStorage.NewFQ[48]; SafeStorage.EstablishFinalization[CODE[CalleeStateRec], 0, fq ! SafeStorage.CantEstablishFinalization => { SafeStorage.ReEstablishFinalization[CODE[CalleeStateRec], 0, fq] }]; myHost _ [net: myAddr.net, host: myAddr.host]; Process.Detach[FORK FinalizeCalleeBlocks[fq]]; }; Restart: ENTRY PROC = { ForgetConnections[] }; Initialize[]; DO STOP; Restart[]; ENDLOOP; }. bRPCPktStreams.mesa - Call-oriented packet streams, based on PktExchange Copyright c 1985, 1986 by Xerox Corporation. All rights reserved. Andrew Birrell September 7, 1983 3:52 pm Bob Hagmann February 11, 1985 4:36:22 pm PST Russ Atkinson (RRA) April 25, 1986 10:08:15 am PST NOTE: calls of this must be made outside our monitor, because RPCPrivate.ReturnBuffer acquires the EthernetDriver monitor, and the EthernetDriver may call EnqueueRecvd which acquires our monitor! ******** Caller ******** For each PSB that initiates a call, record last callee PSB, and use that PSB as destPSB hint for next call, to obtain implicit ack of last result packet. The fact that the destPSB will be wrong of we next talk to a different server host is only a slight pessimization. During a call, a single packet is used for buffering all data sent and received. Whenever the client of RPCLupine has possesion of the buffer (after StartCall), the buffer is set up correctly for transmitting. I.e. buffer.header.dest = the remote machine. Thus, this is true on exit from StartCall, and on entry and exit to/from SendPrelimPkt, ReceiveExtraPkt, Call, and the dispatchers. This causes an extra call of SetupResponse in Call in the case where there will be no subsequent call of ReceiveExtraPkt, but it preserves my sanity. ?? header.conv _ RPCInternal.GetPktConversation[localConversation] header.pktID.callSeq gets filled in by PktExchange -- This is legal only if we were called to raise a remote signal; UnwindRequested should be caught where we called the dispatcher that noticed the signal unwind,garbage ******** Protocol implementation: multi-packet case ******** -- ******** Protocol implementation: callee and packets-while-notWanting ******** -- packet is encrypted! packet is encrypted! We must maintain globally accessible state indicating current calls in the callee, so that the callee can respond to pings. This flag can be used to force world-swap debugging (teledebugging) for really delicate errors. Most bugs can be found just due to RPCInternalFailure being raised. This one can be called multiple times, since it is involved in cleanup activity Remove has been called for something that is not on the callee chain. This should not happen! Returns TRUE iff there is a current callee for this call, even if the callee's pktSeq differs. If result is TRUE, updates "given"s destPSB to match callee's. Assumes pkt has been decrypted. For each calling RPCPkt.ConnectionID we must maintain a sequence number, being the last call initiated on that conversation, so that we can eliminate duplicate call request packets. This information is maintained as a hash table with linked overflow. The hash function is (connection.caller XOR connection.activity) MOD 128. The hash table is altered by LookupCaller and EndConnection, which are nested inside ServerMain for sordid efficiency reasons, and by NoteCaller. Forget connection state, so that subsequent calls will cause an RFA Received packets are dispatched to "ServerMain" processes (through IdleReceive) if the addressed process is not wanting to receive any packets at the time, or if the destPSB is PsbNull. Thus ServerMain serves both as the listener waiting for RFC's on a conventional rendezvous protocol, and as the process listening to the incoming per-connection socket in more heavyweight protocols. There are several cases. The packet may be the first packet of a new call - in this case, this process will handle the call. The packet may be an old duplicate packet from a dead call - in this case the packet can be ignored. The packet may be a retransmission in a current call - in this case an ack may be required. Remember that packets can arrive here in both the caller and callee hosts! Implicitly, recvdHeader is a parameter of LookupCaller. If pkt starts call and ConnectionID is unknown, returns "unknown"; If pkt starts call and isn't duplicate, adds us as callee, returns "new"; If pkt is part of some previously initiated call, returns "old"; If pkt is part of some call with unknown ConnectionID, returns "phoney" If decrypted pkt is inconsistent, returns "phoney". Otherwise, returns "old". On entry, packet has previously been decrypted iff "decrypted". On exit if result is "new", pkt is decrypted On exit if "decrypted", then myPkt.convHandle is set. Note that if result is "old", pkt may or may not be decrypted. newPkt = TRUE at top of loop iff we have the first pkt of next call already. At top of loop, myPkt is decrypted if newPkt = TRUE. start of new call The dispatcher raised a remote signal which the remote machine is unwinding The dispatcher wants caller to get CallFailed[unbound] The dispatcher wants caller to get CallFailed[badProtocol] now newPkt=FALSE or myPkt is decrypted and contains start of new call need to ask other end for connection state ignorable packet Pkt may or may not have been decrypted. If the packet came to us because it had an incorrect destPSB, we should try correcting it and giving it to the correct process. This ensures that destPSB is only a hint. Also, because of the restrictions on generating ack's (described below), there are cases where an ack is required but only the correct worker process is allowed to generate it. destPSB his was wrong: requeue pkt for correct process; note that if correct process doesn't want the packet right now, it may come back to an idler process, but it will have correct destPSB We're here because the packet doesn't start a new call. We should respond if the packet is a retransmission or a ping. We generate an ack only if the packet has eom-end. Therefore, the last packet in any direction may only be sent when the worker process has generated the ack for the preceding packet in that direction. Therefore, the last packet in any direction comes to an idler process only after the worker process has received a previous transmission of that packet (because of the way "wanting" is set in PktExchange). We assume that class=data isn't used for pings. If we're still working on the call, we generate an ack containing the worker process's PSBIndex. Beware when caller and callee are on the same host! ******** Remote signalling ******** We must register as a callee, in case other end pings during signal. See comments in ServerMain. unbound,protocol,unwind,garbage ELSE the UNWIND was in response to us raising CallFailed, so there's no point in talking to the other machine The signalHandler raised a remote signal which the remote machine is unwinding! ******** Remote call failure ******** ******** Initialization ******** Bob Hagmann February 8, 1985 4:08:52 pm PST changes to: ServerMain Bob Hagmann February 11, 1985 4:36:22 pm PST changes to: GiveBackBuffer ΚI˜codešœG™GKšœ Οmœ7™BKšœ)™)K™,K™2—˜šΟk ˜ Kšœ žœ ˜Kšœ žœ˜"Kšœ žœ˜#Kšœžœžœžœ0˜SKšœžœ ˜KšœžœP˜]Kšœ žœ0˜>Kšžœžœ)˜2Kšœ žœr˜ƒKšœ žœ^˜mKšœžœ ˜¬Kšœ žœ˜+Kšœ žœƒ˜”Kšžœžœ[˜c——headšœž˜Kšžœ0žœ;ž˜wšž˜KšžœΟc˜ Kšœ Ÿ˜#Kšœ Ÿ ˜—Kšžœ˜ —˜Kšœžœžœ˜$Kš œ žœžœžœžœ˜)Kšœžœ˜&Kš œžœžœžœžœ˜5—˜šΟnœžœžœžœ˜OKšžœ ˜Kšœ˜K˜—K˜K˜š œžœ˜0KšœΓ™Γšžœžœ˜;Kšžœ˜Kšžœ˜!—K˜——šœ™K˜Kšœ™K˜Kšœžœžœžœ˜Bšœžœžœžœ˜KK˜—š œžœžœžœ˜9KšœŸ œ˜7Kšœ˜K˜—Kšœžœžœ˜9K˜Kšœ™K˜Kšœžœžœ"˜AK˜Kšœžœžœ˜,K˜Kšœžœžœ˜"K˜š   œžœžœžœ(žœ3žœ˜ŽKšžœžœžœ˜KšœS˜SKšœ$˜$K˜!K˜&K˜%K˜'šžœžœ ˜&Kšžœ-˜1šžœ˜KšœB™BK˜Ušžœ)žœ˜2šžœ2ž˜9Kšžœ˜—K˜ Kšœ˜—Kšœ˜——K˜Kšœ5™5KšœŸ˜*K˜)Kšœ˜K˜—š  œžœžœˆžœžœ0žœ˜ήKšœ%˜%˜2K˜1—K˜šžœž˜Kšœ žœ˜Kšœ žœžœ˜)Kšœ žœžœ˜2Kšœ žœŸ'œ˜8šœ ˜ Kšœ–™–Kšœ"˜"Kšžœ˜Kšœ˜—šžœ˜ Kšœ™Kšžœžœ˜&——K˜"Kšžœ,˜2Kšœ˜K˜——šœ?™?K˜š  œžœžœ:˜TKšœ1˜1Kšœ˜K˜—š  œžœžœžœ)žœ˜nKšœ˜K˜RK˜K˜"Kšžœ&˜,Kšœ˜K˜——šœQ™QK˜Kšœžœ˜Kšœžœ˜ K˜š œžœ˜9Kšœ™K˜:Kšœžœ˜0Kšœ'˜'Kšœ4Ÿ˜SK˜ K˜"K˜K˜#K˜K˜&K˜K˜&K˜K˜"Kšœ˜K˜—š œžœ˜4Kšœ™K˜:Kšœžœ˜0Kšœ'˜'K˜(KšœR˜RK˜Kšœ˜K˜—Kšœ{™{K˜Kšœ žœžœ˜'šœžœžœ˜Kšœ žœžœ˜Kšœžœžœ˜Kšœžœ˜Kšœ-˜-Kšœžœ˜Kšœžœ žœ˜&K˜—Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœ žœ˜šœ žœžœ˜Kšœ€™€—K˜Kš œžœžœ˜!K˜š œžœ˜šžœ ˜ Kšžœ4˜8Kšžœžœ˜—K˜—K˜š œžœžœžœ˜6KšœS˜SKšœ!˜!Kšœžœ žœ˜%šžœž˜šžœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—šžœ˜Kšœžœ˜K˜——šžœž˜šœžœ˜Kšžœ8˜:——Kšžœ ˜Kš œžœ žœžœžœ˜[Kšœ&˜&Kšžœ ˜K˜K˜—š œžœžœ˜4KšœO™OKšžœ žœžœ˜Kšžœžœ˜2Kšžœ˜Kšœ žœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—K˜š  œžœžœžœ˜5Kšœ˜Kšœ˜K˜—š œžœžœ˜9Kšžœžœ ˜Kšœžœ˜Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜K˜—š  œžœžœžœ˜8Kšœ˜Kšœ˜K˜—š œžœžœ˜™>Kšžœžœ ˜˜Kš œžœžœžœ žœžœ ˜d—šž˜šžœžœž˜šœ žœ˜Kšžœžœžœ˜2Kšžœ ˜Kšœ˜—šœžœ!žœ2˜wK˜#šžœžœ žœ˜šžœžœ ˜$šžœ˜Kšœžœ˜ K˜HKšœ žœ˜Kšžœžœžœžœ ˜Kšœ˜—šžœ˜K˜:Kšœ žœ˜Kšœ˜——Kšœ˜—Kšžœ1žœžœ ˜HKšžœžœžœ˜2šžœ,˜.šžœ˜Kšžœžœžœ ˜4K˜,Kšœ ˜ Kšžœ˜ Kšœ˜—Kšžœžœ˜—Kšœ˜—Kšžœ!˜(—Kšžœ˜—Kšœ˜K˜—š œžœžœ9žœ˜hKšœžœ˜Kš œžœžœžœ žœžœ ˜rKšœ˜šž˜šžœžœž˜šœ žœ˜Kšœ žœžœ&˜Ršžœž˜ Kšžœ˜#Kšžœ˜—Kšž˜Kšœ˜—Kš œžœ!žœ(Ÿœžœ˜ˆKšžœ8˜?—Kšžœ˜—Kšœ˜K˜—KšœLŸ6™‚šž˜Kšžœžœžœžœ$˜Dšžœžœžœ˜Kšœ1˜1Kšœ žœ˜Kšœ žœ˜Kšœ˜—šžœMž˜Wšœ˜Kšœ™K˜:K˜#K˜"šžœ6˜8Kšžœ*˜,KšžœH˜JKšžœ-˜1šžœŠ˜Žšžœžœ˜Kšœ žœ˜Kšœ˜Kšžœ˜Kšœ˜—šœ˜Kšœ>™>Kšœ ™ Kšœ˜Kšžœ˜ Kšœ˜—šœ˜Kšœ6™6Kšœ˜Kšœ˜Kšžœ˜ Kšœ˜—šœ˜Kšœ:™:Kšœ˜Kšœ˜Kšžœ˜ Kšœ˜—K˜——K˜˜=K˜Kš œžœžœ žœžœ˜8—Kšžœžœ žœ˜ KšœE™EKšœ˜—šœ ˜ Kšœ*™*Kšœžœ˜ K˜K˜Kšœžœ˜K˜˜˜1Kšžœžœžœžœ˜0——šžœ˜šžœ˜Kšžœžœžœžœ˜Kšžœžœ žœžœ˜8K˜Kšœ˜—Kšžœ žœ˜—Kšœ˜—šœ ˜ Kšœ™Kšœ žœ˜—šœ˜Kšœ…™…Kšœ1˜1Kšœ žœ žœŸœ˜Ošžœ žœžœ˜7KšœzŸD™Ύšžœ ž˜šœ˜šžœžœ ˜%Kšžœ%˜)Kšžœ*˜.———K˜Kšœ˜—šžœ˜KšœΪ™Ϊš žœ!žœžœ žœžœ˜}˜Kšžœžœ žœžœ ˜6Kšžœ˜Kšžœ#˜'—K˜Kšœ˜—Kšœ˜—Kšœ žœ˜Kšœ˜—Kšžœžœ˜—Kšžœ˜—Kšœ˜Kšœ˜K˜——šœ#™#K˜š  œžœžœ"˜:Kšœ3˜3Kšœ˜K˜—KšœžœžœŸ1˜PK˜Kšœ žœžœ ˜Kš œ žœžœ/žœžœ˜MK˜š œžœžœmžœžœ žœ8žœžœ˜ˆšžœ˜šžœ˜K˜Kšžœžœ˜%K˜—šžœ˜Kšœ/˜/Kšœ žœ˜+Kšœ,žœ&˜TKšœ:˜:Kšœ`™`KšœNžœ ˜\šœ˜Kšžœžœ"˜/KšœžœžœŸ-˜JK˜K˜Kšœ˜K˜"šžœž˜Kšžœ+˜/šžœ˜"K˜8Kšžœžœžœ˜1šžœ˜ šžœžœžœ˜K˜K˜[šžœž˜Kšœ žœŸœ˜.Kšœ žœŸ&œ˜7šžœ˜ Kšœ™Kšžœžœ˜&——K˜"Kšœ˜—Kšœ<Ÿ1™m—šœ˜KšœO™OKšœ˜Kšžœ˜ Kšœ˜—Kšœ;žœ˜FKšœ=žœ˜HK˜——Kšœ˜—Kšœ˜K˜——Kšœ˜K˜——šœ%™%K˜Kšœžœžœžœ˜$Kšœžœžœžœ˜$K˜š œžœ7˜CKšœ ˜ šžœž˜Kšœžœžœžœ˜SKšžœ˜—K˜Kšœ˜K˜——šœ ™ K˜š  œžœžœ˜KšœO˜OKšœ:˜:šœ"žœ˜=šœ,˜,Kšœ$žœ˜@Kšœ˜——K˜.Kšœžœ˜.Kšœ˜K˜—š œžœžœ˜K˜Kšœ˜K˜—K˜ Kšžœžœ žœ˜K˜—Kšœ˜™+Kšœ Οr ™—™,Kšœ ‘™—K™—…—A pΛ