DIRECTORY BasicTime USING[ GetClockPulses, MicrosecondsToPulses, Pulses ], Booting USING[ CheckpointProc, RegisterProcs, RollbackProc ], BufferDefs USING[ PupBuffer ], CommUtilDefs USING[ AllocateIocb ], DriverDefs USING[ ChangeNumberOfInputBuffers ], DriverTypes USING[ Encapsulation, ethernetEncapsulationBytes, ethernetEncapsulationOffset ], EthernetOneFace USING[ ControlBlock, ControlBlockRecord, controlBlockSize, DeviceHandle, GetNextDevice, GetStatus, hearSelf, nullDeviceHandle, QueueOutput ], PrincOpsUtils USING[ Free, IsBound, LongCOPY, PsbHandleToIndex, ReadPSB ], Process USING[ Detach, MsecToTicks, Pause, Yield ], PrincOps USING[ PsbIndex, PsbNull ], PupDefs USING[ AnyLocalPupAddress, GetFreePupBuffer, GetHopsToNetwork, PupAddress, PupPackageMake, PupRouterSendThis, PupSocket, PupSocketDestroy, PupSocketMake, ReturnFreePupBuffer, veryLongWait], PupTypes USING[ PupAddress, PupHostID, PupNetID, PupSocketID ], RPC USING[ CallFailed, unencrypted ], RPCInternal USING[ DecryptPkt, DoSignal, EncryptPkt, ReplyToRFA, RPCBinding, RPCPktStreams, RPCSecurity, ServerMain ], RPCLupine USING[ DataLength, Dispatcher, Header, RPCPkt ], RPCPkt USING[ CallCount, Header, Machine, PktExchangeState, PktID, pktLengthOverhead ], RPCPrivate USING[ GetRPCPackets, rpcSocket, ReturnBuffer ], RPCWatch USING[]; RPCPktIO: MONITOR IMPORTS BasicTime, Booting, CommUtilDefs, DriverDefs, EthernetOneFace, PrincOpsUtils, Process, PupDefs, RPC, RPCInternal, RPCPrivate EXPORTS RPCLupine--Encapsulation,Header--, RPCPkt--PktExchange,IdleReceive--, RPCWatch--SetSpyProc-- SHARES BufferDefs, RPCLupine = { Encapsulation: PUBLIC TYPE = DriverTypes.Encapsulation; Header: PUBLIC TYPE = RPCPkt.Header; ConcreteHeader: PROC [abstract: LONG POINTER TO RPCLupine.Header] RETURNS [LONG POINTER TO Header] = INLINE { RETURN [ abstract ]; }; callSequence: RPCPkt.CallCount _ 0; -- monotonic from this host -- IOCBrecord: TYPE = RECORD[next: IOCB]; IOCB: TYPE = LONG POINTER TO IOCBrecord; iocbSize: CARDINAL = MAX[EthernetOneFace.controlBlockSize, SIZE[IOCBrecord]]; freeIOCBs: IOCB _ NIL; firstDevice: EthernetOneFace.DeviceHandle = EthernetOneFace.GetNextDevice[EthernetOneFace.nullDeviceHandle]; myAddr: PupTypes.PupAddress; myNet: PupTypes.PupNetID; myDeviceHost: PupTypes.PupHostID; myHost: RPCPkt.Machine; sent: CARDINAL _ 0; recvd: CARDINAL _ 0; retransmitted: CARDINAL _ 0; minRetransmitMsecs: CARDINAL = 100; -- retransmit delay for local net -- msecsPerHop: CARDINAL = 500; -- approximate typical gateway hop delay? -- minPingMsecs: CARDINAL = 5000; -- initial interval between pings -- maxPingSecs: CARDINAL = 300; -- long-term ping interval -- maxTransmissions: CARDINAL _ 20; -- give up after too many transmissions -- signalTimeout: BOOL _ TRUE; -- debugging switch -- minRetransmitPulses: BasicTime.Pulses = BasicTime.MicrosecondsToPulses[LONG[1000] * minRetransmitMsecs]; pulsesPerHop: BasicTime.Pulses = BasicTime.MicrosecondsToPulses[LONG[1000] * msecsPerHop]; minPingPulses: BasicTime.Pulses = BasicTime.MicrosecondsToPulses[LONG[1000] * minPingMsecs]; maxPingPulses: BasicTime.Pulses = BasicTime.MicrosecondsToPulses[LONG[1000]*LONG[1000] * maxPingSecs]; transmitLocalPkts: BOOL _ TRUE; shortWait: BasicTime.Pulses = -- 10 msecs -- BasicTime.MicrosecondsToPulses[LONG[1000] * 10]; longerWait: BasicTime.Pulses = -- 5 secs -- BasicTime.MicrosecondsToPulses[LONG[1000]*LONG[1000] * 5]; PktExchange: PUBLIC PROC [inPkt: RPCLupine.RPCPkt, length: RPCLupine.DataLength, maxlength: RPCLupine.DataLength, state: RPCPkt.PktExchangeState, signalHandler: RPCLupine.Dispatcher _ NIL] RETURNS [newPkt: BOOL, newLength: RPCLupine.DataLength] = { outPkt: RPCLupine.RPCPkt _ inPkt; -- altered after a signal -- outPktFrame: POINTER _ NIL; -- DoSignal's local frame -- DO -- loop for signal handlers -- sentTime: BasicTime.Pulses; -- initialized after sending any packet -- iocb: IOCB _ NIL; GetIOCB: ENTRY PROC = INLINE { IF freeIOCBs # NIL THEN { iocb _ freeIOCBs; freeIOCBs _ freeIOCBs.next } ELSE { iocb _ CommUtilDefs.AllocateIocb[iocbSize] } }; ControlBlock: PROC RETURNS [ EthernetOneFace.ControlBlock ] = INLINE { RETURN [LOOPHOLE[iocb]] }; ReturnIOCB: PROC = INLINE { InnerReturn: ENTRY PROC = INLINE { iocb.next _ freeIOCBs; freeIOCBs _ iocb; }; IF iocb # NIL THEN { WaitUntilSent[]; InnerReturn[] }; }; WaitUntilSent: PROC = INLINE { WHILE EthernetOneFace.GetStatus[ControlBlock[]] = pending DO IF BasicTime.GetClockPulses[] - sentTime < shortWait THEN Process.Yield[] ELSE IF BasicTime.GetClockPulses[] - sentTime < longerWait THEN Process.Pause[1] ELSE EXIT -- assume someone reset the microcode --; ENDLOOP; }; NewCallNumber: ENTRY PROC RETURNS [RPCPkt.CallCount] = INLINE { RETURN [ callSequence _ callSequence+1 ]; }; reply: BufferDefs.PupBuffer; recvdHeader: LONG POINTER TO Header; myPSB: PrincOps.PsbIndex = PrincOpsUtils.PsbHandleToIndex[PrincOpsUtils.ReadPSB[]]; acked: BOOL; thisPktID: RPCPkt.PktID; header: LONG POINTER TO Header = @outPkt.header; localHost: BOOL = header.destHost = myHost; localNet: BOOL = header.destHost.net = myNet; pingPulses: BasicTime.Pulses _ minPingPulses; header.srceHost _ myHost; header.srceSoc _ RPCPrivate.rpcSocket; header.srcePSB _ myPSB; IF header.pktID.pktSeq = 0 -- first packet of a call; yucky interface! -- THEN { header.type _ SELECT state FROM sending => [0, rpc, notEnd, pleaseAck, call], call => [0, rpc, end, dontAck, call], authReq => [0, rpc, end, dontAck, rfa], ENDCASE => --receiving, endCall-- ERROR; header.pktID.callSeq _ NewCallNumber[]; header.pktID.pktSeq _ 1; acked _ FALSE; } ELSE { header.type _ SELECT state FROM sending => [0, rpc, notEnd, pleaseAck, data], receiving => [0, rpc, end, dontAck, ack], call => [0, rpc, end, dontAck, data], endCall => [0, rpc, end, dontAck, data], ENDCASE => --authReq-- ERROR; IF state # receiving --header.type.class = data -- THEN { header.pktID.pktSeq _ header.pktID.pktSeq+1; acked_FALSE } ELSE acked _ TRUE; }; thisPktID _ header.pktID; SetWanting[myPSB]; DO -- loop for pings -- ENABLE UNWIND => { ClearWanting[myPSB]; ReturnIOCB[]; IF outPktFrame # NIL THEN PrincOpsUtils.Free[outPktFrame]; }; { transmissions: CARDINAL _ 0; retransmitPulses: BasicTime.Pulses _ IF localNet THEN minRetransmitPulses ELSE minRetransmitPulses + pulsesPerHop * PupDefs.GetHopsToNetwork[header.destHost.net]; IF outPkt.convHandle # RPC.unencrypted THEN header.length _ RPCInternal.EncryptPkt[outPkt, length] ELSE header.length _ length + RPCPkt.pktLengthOverhead; header.oddByte _ no; DO -- loop for retransmissions -- { IF NOT localNet OR firstDevice = EthernetOneFace.nullDeviceHandle THEN GeneralSend[outPkt] ELSE { IF localHost AND NOT( EthernetOneFace.hearSelf AND transmitLocalPkts ) THEN LocalSend[outPkt]; IF NOT localHost OR transmitLocalPkts THEN { IF iocb = NIL THEN GetIOCB[] ELSE WaitUntilSent[]; -- Pup checksum -- outPkt.data[header.length-RPCPkt.pktLengthOverhead] _ 177777B; outPkt.encapsulation _ Encapsulation[ethernetOne[,,,,,, header.destHost.host, myDeviceHost, pup]]; EthernetOneFace.QueueOutput[firstDevice, @outPkt.encapsulation + DriverTypes.ethernetEncapsulationOffset, header.length + (1+DriverTypes.ethernetEncapsulationBytes)/2, ControlBlock[]] }; }; sentTime _ BasicTime.GetClockPulses[]; sent _ sent+1; DO -- loop for each incoming packet (including garbage) -- reply _ MyReceive[ myPSB, sentTime, (IF acked THEN pingPulses ELSE retransmitPulses)]; IF reply = NIL THEN IF acked THEN GOTO ping ELSE { header.type.ack _ pleaseAck; GOTO retransmit }; recvdHeader _ LOOPHOLE[@reply.pupLength]; IF recvdHeader.type.class = rfa THEN { [] _ RPCInternal.ReplyToRFA[ reply, header--encrypted--, thisPktID--clear--, inPkt.convHandle]; LOOP; }; IF outPkt.convHandle # RPC.unencrypted AND recvdHeader.conv = header.conv AND recvdHeader.srceHost = header.destHost THEN [,newLength] _ RPCInternal.DecryptPkt[recvdHeader, outPkt.convHandle] ELSE newLength _ recvdHeader.length - RPCPkt.pktLengthOverhead; IF recvdHeader.conv = header.conv AND recvdHeader.srceHost = header.destHost AND recvdHeader.pktID.activity = thisPktID.activity THEN -- pkt is related to our call -- SELECT TRUE FROM recvdHeader.pktID.callSeq = thisPktID.callSeq => SELECT TRUE FROM recvdHeader.pktID.pktSeq = 1+thisPktID.pktSeq => { IF state = sending OR state = endCall THEN ERROR RPC.CallFailed[runtimeProtocol ! UNWIND => GiveBackBuffer[reply] ]; SELECT recvdHeader.type.class FROM data => GOTO done; ack => { acked _ TRUE; GiveBackBuffer[reply]; }; ENDCASE => --call,rfa-- ERROR RPC.CallFailed[runtimeProtocol ! UNWIND => GiveBackBuffer[reply] ]; }; recvdHeader.pktID.pktSeq = thisPktID.pktSeq => { SELECT recvdHeader.type.class FROM ack => { IF header.type.class = call THEN header.destPSB _ recvdHeader.srcePSB; acked _ TRUE; IF state = sending OR state = endCall THEN {GiveBackBuffer[reply]; reply_NIL; GOTO done} ELSE -- state = call, authReq, or receiving -- GiveBackBuffer[reply]; }; data, call => -- retransmisson of his packet -- IF state = receiving THEN IF recvdHeader.type.ack = pleaseAck THEN { GiveBackBuffer[reply]; reply _ NIL; GOTO retransmit -- we're already sending an ack -- } ELSE GiveBackBuffer[reply] ELSE ERROR RPC.CallFailed[runtimeProtocol ! UNWIND => GiveBackBuffer[reply] ]; ENDCASE => --rfa -- ERROR RPC.CallFailed[runtimeProtocol ! UNWIND => GiveBackBuffer[reply] ]; }; recvdHeader.pktID.pktSeq < thisPktID.pktSeq => GiveBackBuffer[reply]; -- no need to ack it -- ENDCASE => ERROR RPC.CallFailed[runtimeProtocol ! UNWIND => GiveBackBuffer[reply] ]; recvdHeader.pktID.callSeq > thisPktID.callSeq AND state=endCall => { IF recvdHeader.type.class # call THEN ERROR RPC.CallFailed[runtimeProtocol ! UNWIND => GiveBackBuffer[reply] ]; GOTO done } ENDCASE => { recvdHeader.destPSB _ PrincOps.PsbNull; EnqueueAgain[reply]; } ELSE { recvdHeader.destPSB _ PrincOps.PsbNull; EnqueueAgain[reply]; }; ENDLOOP--for each incoming packet--; EXITS retransmit => { transmissions _ transmissions+1; IF (transmissions = maxTransmissions AND signalTimeout) OR state = authReq THEN { SIGNAL RPC.CallFailed[timeout]; transmissions _ 0 }; retransmitted _ retransmitted+1; retransmitPulses _ retransmitPulses + minRetransmitPulses; }; }; ENDLOOP-- for each transmission --; EXITS ping => { header.type _ [0, rpc, end, pleaseAck, ack]; length _ 0; header.pktID _ thisPktID; acked _ FALSE; pingPulses _ MIN[maxPingPulses, pingPulses * 2]; }; }; REPEAT done => { ClearWanting[myPSB]; ReturnIOCB[]; IF outPktFrame # NIL THEN PrincOpsUtils.Free[outPktFrame]; IF reply = NIL THEN { --restore clear pktID-- header.pktID _ thisPktID; RETURN [FALSE,] } ELSE { IF recvdHeader.outcome = signal AND state = call AND signalHandler # NIL THEN [outPkt, length, outPktFrame] _ RPCInternal.DoSignal[reply, newLength, signalHandler, inPkt.convHandle] ELSE { IF newLength > maxlength THEN ERROR RPC.CallFailed[runtimeProtocol ! UNWIND => GiveBackBuffer[reply]]; PrincOpsUtils.LongCOPY[ from: recvdHeader, to: @inPkt.header, nwords: recvdHeader.length]; GiveBackBuffer[reply]; RETURN [TRUE,newLength] }; } }; ENDLOOP-- for each ping--; ENDLOOP-- for signal handlers --; }; GeneralSend: PROC [pkt: RPCLupine.RPCPkt] = { b: BufferDefs.PupBuffer = PupDefs.GetFreePupBuffer[]; PrincOpsUtils.LongCOPY[from: @(pkt.header), to: @(b.pupLength), nwords: ConcreteHeader[@pkt.header].length]; PupDefs.PupRouterSendThis[b]; }; LocalSend: PROC [pkt: RPCLupine.RPCPkt] = { b: BufferDefs.PupBuffer = PupDefs.GetFreePupBuffer[]; PrincOpsUtils.LongCOPY[ from: @(pkt.header), to: @(b.pupLength), nwords: ConcreteHeader[@pkt.header].length]; IF NOT AcceptPkt[b] THEN PupDefs.ReturnFreePupBuffer[b]; }; idlerPkt: BufferDefs.PupBuffer _ NIL; idlerCond: CONDITION _ [timeout:0]; waiterCond: CONDITION _ [timeout:Process.MsecToTicks[minRetransmitMsecs]]; WaiterArray: TYPE = ARRAY PrincOps.PsbIndex OF BufferDefs.PupBuffer; waiterPkts: REF WaiterArray _ NEW[WaiterArray _ ALL[NIL]]; wanting: PACKED ARRAY PrincOps.PsbIndex OF BOOL _ ALL[FALSE]; -- PSB expects a pkt -- SetWanting: ENTRY PROC [myPSB: PrincOps.PsbIndex] = INLINE { wanting[myPSB] _ TRUE; }; ClearWanting: PROC [myPSB: PrincOps.PsbIndex] = INLINE { spare: BufferDefs.PupBuffer; InnerClear: ENTRY PROC = INLINE { wanting[myPSB] _ FALSE; IF (spare _ waiterPkts[myPSB]) # NIL THEN waiterPkts[myPSB] _ NIL; }; InnerClear[]; IF spare # NIL THEN GiveBackBuffer[spare] --ignore it, outside monitor--; }; MyReceive: ENTRY PROC [myPSB: PrincOps.PsbIndex, sentTime, waitTime: BasicTime.Pulses] RETURNS [recvd: BufferDefs.PupBuffer] = INLINE { ENABLE UNWIND => NULL; DO IF (recvd _ waiterPkts[myPSB]) = NIL THEN IF BasicTime.GetClockPulses[] - sentTime < waitTime THEN WAIT waiterCond ELSE EXIT ELSE { waiterPkts[myPSB] _ NIL; EXIT }; ENDLOOP; }; RecvdPktTooLong: ERROR = CODE; KillServer: ERROR = CODE; servers: CARDINAL _ 0; serversMax: CARDINAL _ 20; idlers: CARDINAL _ 0; idlersMax: CARDINAL = 6; idlersMin: CARDINAL = 2; IdleReceive: PUBLIC PROC [pkt: RPCLupine.RPCPkt, maxlength: CARDINAL] = { b: BufferDefs.PupBuffer; InnerIdler: ENTRY PROC = { IF idlers >= idlersMax THEN { servers_servers-1; RETURN WITH ERROR KillServer[] }; idlers _ idlers + 1; DO WAIT idlerCond; IF idlerPkt # NIL THEN EXIT ENDLOOP; IF idlers < idlersMin AND servers < serversMax THEN { servers_servers+1; Process.Detach[FORK Server[]]; }; b _ idlerPkt; { recvdHeader: LONG POINTER TO Header = LOOPHOLE[@b.pupLength]; idlerPkt _ LOOPHOLE[idlerPkt.next,BufferDefs.PupBuffer]; b.next _ NIL; IF recvdHeader.length > maxlength THEN ERROR RecvdPktTooLong[] --NULL??-- ELSE PrincOpsUtils.LongCOPY[ from: @b.pupLength, to: @pkt.header, nwords: recvdHeader.length]; }; }; InnerIdler[]; GiveBackBuffer[b];--outside monitor-- }; QueuesScrambled: ERROR = CODE; spyProc: PROC [BufferDefs.PupBuffer] _ NIL; SetSpyProc: PUBLIC ENTRY SAFE PROC [p: PROC [BufferDefs.PupBuffer]] = CHECKED { spyProc _ p; }; EnqueueRecvd: ENTRY PROC [b: BufferDefs.PupBuffer, report: BOOL] RETURNS [BOOL] = { header: LONG POINTER TO Header = LOOPHOLE[@b.pupLength]; IF report AND spyProc # NIL THEN spyProc[b]; IF header.destHost = myHost AND header.type.subType = rpc THEN { destPSB: PrincOps.PsbIndex = header.destPSB; recvd _ recvd+1; IF destPSB NOT IN (PrincOps.PsbNull..LAST[PrincOps.PsbIndex]] OR NOT wanting[destPSB] THEN { IF idlers = 0 THEN RETURN [FALSE]; -- too busy, drop the packet b.next _ idlerPkt; idlerPkt _ b; idlers _ idlers-1; NOTIFY idlerCond; } ELSE { IF waiterPkts[destPSB] # NIL THEN RETURN [FALSE]; waiterPkts[destPSB] _ b; BROADCAST waiterCond; }; RETURN [TRUE] } ELSE RETURN [FALSE]; }; GiveBackBuffer: PROC [b: BufferDefs.PupBuffer] = IF PrincOpsUtils.IsBound[RPCPrivate.ReturnBuffer] THEN RPCPrivate.ReturnBuffer ELSE PupDefs.ReturnFreePupBuffer; AcceptPkt: PROC [b: BufferDefs.PupBuffer] RETURNS [BOOL] = { RETURN [ EnqueueRecvd[b: b, report: TRUE] ]; }; EnqueueAgain: PUBLIC PROC [b: BufferDefs.PupBuffer] = { IF NOT EnqueueRecvd[b: b, report: FALSE] THEN GiveBackBuffer[b]; }; Listener: PROC = { soc: PupDefs.PupSocket = PupDefs.PupSocketMake[ local: RPCPrivate.rpcSocket, remote:, ticks: PupDefs.veryLongWait]; DO ENABLE ABORTED => EXIT; b: BufferDefs.PupBuffer = soc.get[]; IF b = NIL THEN LOOP; IF NOT AcceptPkt[b] THEN PupDefs.ReturnFreePupBuffer[b]; ENDLOOP; PupDefs.PupSocketDestroy[soc]; }; listenerProcess: PROCESS; Server: PROC = { RPCInternal.ServerMain[ ! KillServer => CONTINUE ]; }; Initialize: ENTRY PROC = { PupDefs.PupPackageMake[]; DriverDefs.ChangeNumberOfInputBuffers[TRUE]; myAddr _ PupDefs.AnyLocalPupAddress[RPCPrivate.rpcSocket]; myNet _ myAddr.net; myDeviceHost _ myAddr.host; myHost _ [myNet, myAddr.host]; START RPCInternal.RPCBinding; -- exports "RPCInternal.exportTable" -- START RPCInternal.RPCSecurity; -- exports "RPCInternal.firstConversation" -- START RPCInternal.RPCPktStreams; -- initialize connection states -- Booting.RegisterProcs[c: Checkpoint, r: Rollback]; servers _ servers+1; Process.Detach[FORK Server[]]; listenerProcess _ FORK Listener[]; }; Checkpoint: Booting.CheckpointProc = TRUSTED { RPCPrivate.GetRPCPackets[NIL]; }; Rollback: Booting.RollbackProc = TRUSTED { InnerRollback: ENTRY PROC = TRUSTED { RESTART RPCInternal.RPCPktStreams; -- forget connection counts -- RESTART RPCInternal.RPCSecurity; -- invalidate local conversations -- RESTART RPCInternal.RPCBinding; -- nothing? -- }; InnerRollback[]; RPCPrivate.GetRPCPackets[AcceptPkt]; }; Initialize[]; RPCPrivate.GetRPCPackets[AcceptPkt]; }. nRPCPktIO.mesa Copyright c 1984 by Xerox Corporation. All rights reserved. RPC: Reliable transmission and reception of packets HGM, February 22, 1984 8:59:19 pm PST, maxTransmissions _ 100 Andrew Birrell October 24, 1983 4:26 pm Russ Atkinson October 31, 1984 4:47:48 pm PST Bob Hagmann, November 1, 1984 7:45:11 am PST, maxTransmissions _ 20 == this was set up to 100 during the time when Luther was thrashing ******** IOCB management ******** IOCB's are kept on free chain. Allocation and freeing are imbedded in "PktExchange" procedure. IOCB's are allocated in first 64K of memory, in resident memory. Use "ControlBlock" proc to get pointer to give to EthernetFace. ******** sending and receiving packets ******** "PktExchange" implements a reliable packet exchange. There are five cases: sending: transmit data, wait for ack receiving: transmit ack, wait for data call: transmit data, wait for data endCall: transmit data, wait for ack or data (data => start of new call) authReq: transmit RFA, wait for data (like "call", but no retransmissions) Data and RFA packets are retransmitted until an ack is received. Ack packets are retransmitted only in response to "pleaseAck" requests. No acknowledgement after 14 transmissions is fatal (CallFailed[timeout]). When the transmitted packet has been acknowledged (if needed), a ping is sent periodically (ack packet saying pleaseAck). If necessary the ping is retransmitted until it has been acked. Failure to receive an ack for the ping is fatal (CallFailed[timeout]). Provided pings continue to be acknowledged, there is no limit on how long we will wait for the next data packet. If the client gets impatient, he can abort this process. (This corresponds to the local-machine semantics of a procedure call.) The retransmission delay is incremented by minPingMsecs each time we timeout, but is reset when we receive the desired packet. If n=maxTransmissions and i=minRetransmitMsecs and j=hops*msecsPerHop, we give up after i*(n*n+n)/2+n*j msecs. For the local network, that comes to 21 seconds. For a two-hop route, that comes to 41 seconds. The ping delay is doubled at each ping, up to maxPingSecs, which is 5 minutes. The maxPingSecs value is reached after about 5 minutes. Constants for WaitUntilSent (inside PktExchange) On exit if newPkt=TRUE, the packet has been decrypted if needed. Normally, transmits inPkt and copies result into inPkt. If a signal occurs, calls DoSignal which handles the signal protocol up to the last resumption packet. DoSignal returns the last resume packet for us to transmit, which we do by assigning it to outPkt; that packet was allocated in DoSignal's local frame, which we must later deallocate. IOCB management procs are imbedded for efficiency! Horrible! We must ensure that the ethernet microcode isn't still using our IOCB when we free or re-use it. There are two problems: (a) a suitable response coming in before we've finished retransmitting our packet; (b) the standard EthernetOneDriver resetting the microcode (so it forgets our request). To handle (a) we busy-wait for up to 10msec, then wait for single ticks thereafter; to handle (b) we assume our iocb is no longer being used after it's pending for 5 secs. To do better seems to require changes to the EthernetOneFace. wait for response: an ack or the reply or a timeout can't set "acked", because we must retransmit our data packet until we obtain the correct destPSB from some ack pkt pkt is in our call a) pkt has next sequence number to ours he's not allowed to generate that pktSeq! This can happen if state=call and callee sent next data pkt, but it was lost and now he is responding to our retransmission or ping. Soon, he will retransmit his data pkt. b) pkt has same sequence number as ours acknowledgement of our packet Even if "pleaseAck", we don't need to ack it, because other end should send data pkt soon -- c) pkt has earlier sequence number than ours d) pkt has some future sequence number acks our last packet, so we can handle the call wrong call: let someone else do it wrong conversation or activity: let someone else do it Incoming RFA packets don't reach here. Don't retransmit RFA: caller will retransmit call pkt. Otherwise, if a spurious worker process occurred because of call pkt retransmission before response to RFA, then the spurious worker process sits needlessly retransmitting RFA's until it times out. only exit from loop is "GOTO done" or UNWIND This isn't covered by any UNWIND We get here only after coming back from a call of DoSignal we can't get here! handle talking to ourselves if the ethernet head can't Returns NIL if no packet arrives within waitTime pulses after sentTime Number of server processes will never exceed "serversMax". If number of idle servers exceeds "idlersMax", one will abort. If number of idle servers drops below "idlersMin", one is forked. Dispatch packet to appropriate RPC process, if any. Packet is known to be a Pup, and is addressed to our socket. give to idle process to deal with Pkts are dealt with LIFO by idlers, but it doesn't matter (much) someone wants this packet: give them it 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! Catch any packets that get as far as the Pup socket level RRA: be prepared for a few glitchy NILs to get through ******** Initialization ******** ΚC˜šœ ™ Jšœ Οmœ1™Jšœ žœžœŸ˜8J˜JšžœŸ˜!˜JšœŸ*˜FJšœ žœžœ˜J˜Jšœ2™2J˜š œžœžœžœ˜šžœ ž˜Jšžœ1˜5Jšžœ/˜3—Jšœ˜J˜—š  œžœžœ#žœ˜FJšžœžœ ˜J˜—š  œžœžœ˜š  œžœžœžœ˜"Jšœ˜Jšœ˜Jšœ˜—Jšžœžœžœ$˜6Jšœ˜J˜—š  œžœžœ˜šœƒ™ƒJšœR™RJšœW™W—Jšœλ™λšžœ4ž˜<šžœ2˜4Jšžœ˜šžœžœ3˜:Jšžœ˜JšžœžœŸ(œ˜3——Jšžœ˜—Jšœ˜J˜—š   œžœžœžœžœ˜?Jšžœ#˜)Jšœ˜J˜—J˜Jšœ žœžœžœ˜$JšœS˜SJšœžœ˜ J˜Jšœžœžœžœ˜0Jšœ žœ˜+Jšœ žœ˜-J˜-J˜J˜J˜&J˜J˜šžœŸ/˜Jšžœ˜šœžœž˜J˜-J˜*J˜,JšžœŸœžœ˜(—J˜'J˜Jšœžœ˜Jšœ˜—šžœ˜šœžœž˜J˜-J˜)J˜&J˜(JšžœŸ œžœ˜—šžœŸ˜2Jšžœ6žœ˜AJšžœ žœ˜—Jšœ˜——J˜J˜J˜šžœŸ˜šžœžœ˜Jšœ˜Jšœ ˜ Jšžœžœžœ!˜:Jšœ˜—Jšœ˜Jšœžœ˜˜$šžœ ˜ Jšžœ˜šžœT˜XJ˜———šžœžœ ˜&Jšžœ7˜;Jšžœ3˜7—J˜J˜šžœŸ˜!Jšœ˜šžœžœ žœ/˜AJšžœ˜šžœ˜šžœ žœžœžœ˜FJšžœ˜—šžœžœ žœžœ˜,Jšžœžœžœ žœ˜2JšŸ˜J˜>˜7J˜*—˜(J˜@J˜=J˜—Jšœ˜—Jšœ˜——J˜&J˜J˜Jšœ3™3J˜šžœŸ7˜:šœ˜Jšœžœžœ žœ˜C—J˜šžœ ž˜šžœ˜Jšžœžœ˜Jšžœ žœ˜6——J˜Jšœžœ˜)J˜šžœžœ˜&šœ˜Jšœ Ÿ œ Ÿ œ˜B—JšŸs™sJšžœ˜Jšœ˜—J˜šžœžœ ˜&Jšžœ˜"Jšžœ'˜*JšžœF˜JJšžœ;˜?—J˜šžœ˜!Jšžœ'˜*Jšžœ0˜3šžœŸ ˜%šžœžœž˜˜0Jšœ™šžœžœž˜Jšœ'™'šœ2˜2šžœžœž˜*Jšœ)™)šžœžœ˜$Jšœžœ˜$——šžœž˜"Jšœžœ˜šœ˜Jšœ¬™¬Jšœžœ˜ J˜Jšœ˜——šžœŸ ˜šžœžœ˜$Jšœžœ˜$——Jšœ˜—Jšœ'™'šœ0˜0šžœž˜"šœ˜Jšœ™šžœž˜ Jšœ%˜%—Jšœžœ˜ šžœžœ˜%Jšžœžœžœ˜2šžœŸ)˜.Jšœ-™-Jšœ.™.J˜——Jšœ˜—šœŸ!˜/šžœ˜šžœžœ!˜(šžœ˜Jšœžœ˜#Jšžœ Ÿ"˜2Jšœ˜—Jšžœ˜—šžœžœžœ˜)Jšœžœ˜$————šžœŸ˜šžœžœ˜$Jšœžœ˜$——Jšœ˜—Jšœ,™,˜.JšœŸ˜.—Jšœ&™&—šžœ˜ Jšžœžœžœ˜I——šœ.žœ˜DJšžœ˜ Jšžœžœžœžœ˜NJšœ/™/Jšžœ˜ Jšœ˜——šžœ˜ Jšœ"™"Jšœ'˜'Jšœ˜Jšœ˜——šžœ˜Jšœ6™6Jšœ'˜'Jšœ˜Jšœ˜——Jšœ&™&J˜JšžŸœ˜$—šž˜šœ˜J˜ šžœ#žœžœ˜JJšœύ™ύJšžœžœžœ*˜;—J˜ J˜:Jšœ˜——Jšœ˜—J˜JšžŸœ˜#šž˜šœ ˜ J˜,J˜%Jšœžœ˜Jšœ žœ ˜0Jšœ˜——Jšœ˜—J˜Jšœ,™,šžœ ˜Jšœ ™ J˜J˜ Jšžœžœžœ!˜:šžœ ž˜JšžœŸœžœžœ˜Jšžœ˜šžœžœ ˜0Jšžœž˜šžœ ˜$J˜G—šžœ˜šžœž˜Jšžœžœžœ˜H—˜J˜B—J˜Jšžœžœ ˜Jšœ˜——Jšœ˜——Jšœ˜—J˜JšžŸœ˜J˜Jšœ:™:—J˜JšžŸœ˜!J˜Jšœ™J˜Jšœ˜J˜—š  œžœ˜-J˜5˜?J˜,—J˜Jšœ˜J˜—š  œžœ˜+Jšœ6™6J˜5˜J˜U—Jšžœžœžœ ˜8Jšœ˜J˜Jšœ!žœ˜%Jšœ ž œ˜#Jšœ ž œ5˜JJšœ žœžœžœ˜DJš œ žœžœžœžœ˜:Jšœ žœžœžœžœžœžœŸ˜U—J˜š  œžœžœžœ˜