<> <> <> <> <<>> DIRECTORY Allocator USING [NHeaderP, NormalHeader], Basics USING [BITAND, bytesPerWord, LowHalf], BasicTime USING [GetClockPulses], Booting USING [switches], Checksum USING [ComputeChecksum], CommBuffer USING [Direction, Encapsulation], CommBufferExtras USING [gapNoList], CommDriver USING [AllocBuffer, Buffer, BufferObject, FreeBuffer, GetNetworkChain, InsertReceiveProc, Network], DebuggerSwap USING [CallDebugger], Endian USING [bytesPerHWord, FFromCard, FWORD, HWORD], PrincOpsUtils USING [LongCopy], Process USING [Detach, DisableTimeout, EnableAborts, MsecToTicks, Pause, priorityBackground, SecondsToTicks, SetPriority, SetTimeout, Ticks], Pup USING [Address, allHosts, allNets, Host, nullAddress, nullHost, nullNet, nullSocket, Socket], PupBuffer USING [abortOverheadBytes, Buffer, BufferObject, ByteAlloc, ByteIndex, errorOverheadBytes, FWordIndex, HWordIndex, noChecksum, RoundUpForChecksum, WordsWithoutChecksum], PupInternal USING [Route], PupName USING [], PupSocket USING [dontWait, Milliseconds, waitForever], PupSocketBackdoor USING [ReceiveProc], PupType USING [bytesInPupHeader, bytesOfPupOverhead, CardFromSocket, ErrorCode, HeaderWithoutChecksum, SocketFromCard], PupWKS USING [rpc], RefText USING [ObtainScratch, ReleaseScratch], Rope USING [Fetch, FromRefText, Length, ROPE], SafeStorage USING [EnableFinalization, EstablishFinalization, FinalizationQueue, FQEmpty, FQNext, NewFQ]; PupSocketImpl: CEDAR MONITOR LOCKS socket USING socket: Socket IMPORTS Basics, BasicTime, Booting, Checksum, CommDriver, DebuggerSwap, Endian, PrincOpsUtils, Process, PupBuffer, PupInternal, PupType, RefText, Rope, SafeStorage EXPORTS PupInternal, PupName, PupSocket, PupSocketBackdoor = { Buffer: TYPE = PupBuffer.Buffer; Direction: TYPE = CommBuffer.Direction; ErrorCode: TYPE = PupType.ErrorCode; FWORD: TYPE = Endian.FWORD; HeaderWithoutChecksum: TYPE = PupType.HeaderWithoutChecksum; HWORD: TYPE = Endian.HWORD; Milliseconds: TYPE = PupSocket.Milliseconds; Network: TYPE = CommDriver.Network; ROPE: TYPE = Rope.ROPE; ReceiveProc: TYPE = PupSocketBackdoor.ReceiveProc; gapNoList: [0..16384) ~ CommBufferExtras.gapNoList; gapSocket: [0..16384) ~ 10; -- b.ovh.gap=gapSocket while b is on the socket queue bytesInPupHeader: NAT = PupType.bytesInPupHeader; bytesOfPupOverhead: NAT = PupType.bytesOfPupOverhead; bytesPerWord: NAT = Basics.bytesPerWord; bytesPerHWord: NAT = Endian.bytesPerHWord; outOfLineChecksum: BOOL _ TRUE; < no performance loss.>> OutOfLineChecksum: PROC [ cs: WORD _ 0, nWords: CARDINAL, p: LONG POINTER] RETURNS [checksum: WORD] = TRUSTED { RETURN[Checksum.ComputeChecksum[cs, nWords, p]]; }; globalLock: Socket _ NEW[Object]; Socket: TYPE = REF Object; <> Object: PUBLIC TYPE = MONITORED RECORD [ local, remote: Pup.Address, sendBuffersInUse: CARDINAL _ 0, sendBuffersAlloc: CARDINAL, recvBuffersInUse: CARDINAL _ 0, recvBuffersAlloc: CARDINAL, sendChecksum: BOOL _ TRUE, recvChecksum: BOOL _ TRUE, dead, noErrors: BOOL _ FALSE, dontWait: BOOL _ FALSE, waitForInput: CONDITION, waitForSendBuffer: CONDITION, waitForRecvBuffer: CONDITION, routing: Routing _ NIL, -- NIL => no cached route firstInput, lastInput: Buffer _ NIL, next: Socket _ NIL, -- Only used when a server socket gets captured proc: ReceiveProc _ NIL, user: REF ANY ]; Routing: TYPE = REF RoutingInfo; RoutingInfo: TYPE = RECORD [ network: Network, immediate: Pup.Host, encap: CommBuffer.Encapsulation ]; <> RealDriverBuffer: PROC [b: Buffer] RETURNS [CommDriver.Buffer] = TRUSTED INLINE { nhp: Allocator.NHeaderP _ LOOPHOLE[b, Allocator.NHeaderP]-SIZE[Allocator.NormalHeader]; nhp.type _ CODE[CommDriver.BufferObject]; b.ovh.direction _ none; b.ovh.socket _ NIL; RETURN[LOOPHOLE[b]]; }; TempDriverBuffer: PROC [b: Buffer] RETURNS [CommDriver.Buffer] = TRUSTED INLINE { RETURN[LOOPHOLE[b]]; }; RealPupBuffer: PROC [b: CommDriver.Buffer, d: Direction] RETURNS [Buffer] = TRUSTED INLINE { nhp: Allocator.NHeaderP _ LOOPHOLE[b, Allocator.NHeaderP]-SIZE[Allocator.NormalHeader]; nhp.type _ CODE[PupBuffer.BufferObject]; b.ovh.next _ NIL; b.ovh.direction _ d; RETURN[LOOPHOLE[b]]; }; Next: PROC [b: Buffer] RETURNS [Buffer] = TRUSTED INLINE { RETURN[LOOPHOLE[b.ovh.next]]; }; <> SocketNotWellKnown: PUBLIC ERROR = CODE; <> IsWellKnown: PUBLIC PROC [local: Pup.Socket] RETURNS [yes: BOOL] = { socketNumber: LONG CARDINAL = PupType.CardFromSocket[local]; IF local = Pup.nullSocket THEN RETURN[FALSE]; IF socketNumber >= maxWellKnownSockets THEN RETURN[FALSE]; RETURN[TRUE]; }; CreateServer: PUBLIC PROC [ local: Pup.Socket, sendBuffers: NAT _ 1, recvBuffers: NAT _ 5, getTimeout: Milliseconds _ 10000 ] RETURNS [socket: Socket] = { socketNumber: LONG CARDINAL = PupType.CardFromSocket[local]; IF local = Pup.nullSocket THEN ERROR SocketNotWellKnown; IF socketNumber >= maxWellKnownSockets THEN ERROR SocketNotWellKnown; socket _ CreateCommon[local, Pup.nullAddress, sendBuffers, recvBuffers, getTimeout]; }; CreateEphemeral: PUBLIC PROC [ remote: Pup.Address, sendBuffers: NAT _ 1, recvBuffers: NAT _ 5, getTimeout: Milliseconds _ 10000 ] RETURNS [socket: Socket] = { socket _ CreateCommon[Pup.nullSocket, remote, sendBuffers, recvBuffers, getTimeout]; <> }; CreateCommon: PROC [ local: Pup.Socket, remote: Pup.Address, sendBuffers: NAT, recvBuffers: NAT, getTimeout: Milliseconds ] RETURNS [socket: Socket] = { network: Network; socket _ NEW [Object _ [ local: [Pup.nullNet, Pup.nullHost, local], remote: remote, sendBuffersAlloc: sendBuffers, recvBuffersAlloc: recvBuffers ] ]; network _ PupInternal.Route[remote].network; IF network = NIL THEN network _ CommDriver.GetNetworkChain[]; IF network # NIL THEN { socket.local.net _ network.pup.net; socket.local.host _ network.pup.host; }; TRUSTED { Process.EnableAborts[@socket.waitForInput]; Process.EnableAborts[@socket.waitForSendBuffer]; Process.EnableAborts[@socket.waitForRecvBuffer]; }; SetGetTimeout[socket, getTimeout]; CreateInner[globalLock, socket]; SafeStorage.EnableFinalization[socket]; }; socketsCreated: INT _ 0; CreateInner: ENTRY PROC [socket: Socket, new: Socket] = { ENABLE UNWIND => NULL; <> AddSocket[new]; socketsCreated _ socketsCreated.SUCC; }; SetRemoteAddress: PUBLIC PROC [socket: Socket, remote: Pup.Address] = { socket.remote _ remote; }; GetRemoteAddress: PUBLIC PROC [socket: Socket] RETURNS [Pup.Address] = { RETURN[socket.remote]; }; GetLocalAddress: PUBLIC PROC [socket: Socket] RETURNS [Pup.Address] = { RETURN[socket.local]; }; SetGetTimeout: PUBLIC PROC [socket: Socket, ms: Milliseconds] = { socket.dontWait _ FALSE; SELECT ms FROM PupSocket.dontWait => socket.dontWait _ TRUE; PupSocket.waitForever => TRUSTED { Process.DisableTimeout[@socket.waitForInput]; }; < CARDINAL.LAST => TRUSTED { Process.SetTimeout[@socket.waitForInput, Process.MsecToTicks[ms] ]; }; ENDCASE => TRUSTED { Process.SetTimeout[@socket.waitForInput, Process.SecondsToTicks[ms/1000] ]; }; }; <<>> SetSoftwareChecksumming: PUBLIC PROC [socket: Socket, send, recv: BOOL] = { socket.sendChecksum _ send; socket.recvChecksum _ recv; }; <<>> SetNoErrors: PUBLIC PROC [socket: Socket] = { socket.noErrors _ TRUE; }; Kick: PUBLIC ENTRY PROC [socket: Socket] = { BROADCAST socket.waitForInput; }; <<>> Destroy: PUBLIC ENTRY PROC [socket: Socket] = { BROADCAST socket.waitForInput; socket.dead _ TRUE; UNTIL socket.firstInput = NIL DO b: Buffer _ socket.firstInput; socket.firstInput _ Next[b]; b.ovh.next _ NIL; -- DKW: just to be careful ... IF b.ovh.gap#gapSocket THEN DebuggerSwap.CallDebugger["Clobbered buffer in socket queue!"]; b.ovh.gap _ gapNoList; -- DKW: b now removed from the queue socket.recvBuffersInUse _ socket.recvBuffersInUse - 1; CommDriver.FreeBuffer[RealDriverBuffer[b]]; ENDLOOP; socket.lastInput _ NIL; -- Help Buffer Finalization socket.routing _ NIL; <> }; DestroyInner: ENTRY PROC [socket: Socket, old: Socket] = { <> RemoveSocket[old]; }; <> AllocBuffer: PUBLIC PROC [socket: Socket] RETURNS [b: Buffer] = { InnerAllocBuffer[socket]; b _ RealPupBuffer[CommDriver.AllocBuffer[], send]; b.ovh.network _ NIL; b.ovh.socket _ socket; -- Needed for Finalization }; <<>> InnerAllocBuffer: ENTRY PROC [socket: Socket] = { <> ENABLE UNWIND => NULL; UNTIL socket.sendBuffersInUse < socket.sendBuffersAlloc DO WAIT socket.waitForSendBuffer; ENDLOOP; socket.sendBuffersInUse _ socket.sendBuffersInUse + 1; }; SetUserBytes: PUBLIC PROC [b: Buffer, bytes: PupBuffer.ByteIndex] = { boundsCheck: PupBuffer.ByteIndex _ bytes; -- In case somebody is using TRUSTED. b.byteLength _ bytes + bytesOfPupOverhead; }; SetUserHWords: PUBLIC PROC [b: Buffer, hWords: PupBuffer.HWordIndex] = { boundsCheck: PupBuffer.HWordIndex _ hWords; b.byteLength _ hWords*SIZE[HWORD, bytesPerWord] + bytesOfPupOverhead; }; SetUserFWords: PUBLIC PROC [b: Buffer, fWords: PupBuffer.FWordIndex] = { boundsCheck: PupBuffer.FWordIndex _ fWords; b.byteLength _ fWords*SIZE[FWORD, bytesPerWord] + bytesOfPupOverhead; }; SetUserSize: PUBLIC PROC [b: Buffer, sizeUnits: NAT] = { boundsCheck: PupBuffer.ByteIndex = sizeUnits*SIZE[WORD, bytesPerWord]; b.byteLength _ boundsCheck + bytesOfPupOverhead; }; <<>> Broadcast: PUBLIC PROC [socket: Socket, b: Buffer] = { BroadcastInner[socket, b, socket.remote.socket]; }; <<>> BroadcastInner: PROC [socket: Socket, b: Buffer, dest: Pup.Socket] = { bytes: NAT = PupBuffer.RoundUpForChecksum[b.byteLength]; b.hopCount _ 0; b.spares _ 0; b.dest.host _ Pup.allHosts; b.dest.socket _ dest; b.source.socket _ socket.local.socket; FOR network: Network _ CommDriver.GetNetworkChain[], network.next UNTIL network = NIL DO b.ovh.encap _ network.pup.getEncapsulation[network, Pup.allHosts]; b.dest.net _ network.pup.net; b.source.net _ network.pup.net; b.source.host _ network.pup.host; SetChecksum[b]; network.pup.send[network, TempDriverBuffer[b], bytes]; ENDLOOP; FreeBuffer[b]; }; <<>> Put: PUBLIC PROC [socket: Socket, b: Buffer] = { Send[socket, b, socket.remote]; }; <<>> Send: PUBLIC PROC [socket: Socket, b: Buffer, dest: Pup.Address] = { bytes: NAT = PupBuffer.RoundUpForChecksum[b.byteLength]; network: Network; immediate: Pup.Host; IF dest.net = Pup.allNets THEN { BroadcastInner[socket, b, dest.socket]; RETURN; }; [network, immediate] _ PupInternal.Route[dest]; IF network = NIL THEN { FreeBuffer[b]; RETURN; }; b.hopCount _ 0; b.spares _ 0; b.dest _ dest; b.source _ socket.local; b.ovh.encap _ network.pup.getEncapsulation[network, immediate]; TRUSTED { words: NAT = PupBuffer.WordsWithoutChecksum[b.byteLength]; checksumLoc: LONG POINTER TO HWORD _ @b.byteLength + words; IF socket.sendChecksum THEN { IF outOfLineChecksum THEN checksumLoc^ _ OutOfLineChecksum[0, words, @b.byteLength] ELSE checksumLoc^ _ Checksum.ComputeChecksum[0, words, @b.byteLength]; } ELSE checksumLoc^ _ PupBuffer.noChecksum; }; network.pup.send[network, TempDriverBuffer[b], bytes]; FreeBuffer[b]; }; <<>> <> Get: PUBLIC ENTRY PROC [socket: Socket] RETURNS [b: Buffer] = { ENABLE UNWIND => NULL; IF socket.firstInput = NIL THEN { IF socket.dontWait THEN RETURN[NIL]; IF socket.dead THEN RETURN[NIL]; WAIT socket.waitForInput; }; IF socket.firstInput = NIL THEN RETURN[NIL]; b _ socket.firstInput; socket.firstInput _ Next[b]; b.ovh.next _ NIL; -- DKW: just to be careful ... IF b.ovh.gap#gapSocket THEN DebuggerSwap.CallDebugger["Clobbered buffer in socket queue!"]; b.ovh.gap _ gapNoList; -- DKW: b now removed from the queue b.ovh.socket _ socket; -- Needed for Finalization }; <<>> GetUserBytes: PUBLIC PROC [b: Buffer] RETURNS [bytes: PupBuffer.ByteIndex] = { bytes _ b.byteLength - bytesOfPupOverhead; }; GetUserHWords: PUBLIC PROC [b: Buffer] RETURNS [hWords: PupBuffer.HWordIndex] = { bytes: PupBuffer.ByteIndex _ b.byteLength - bytesOfPupOverhead; hWords _ bytes/SIZE[HWORD]/bytesPerWord; }; GetUserFWords: PUBLIC PROC [b: Buffer] RETURNS [fWords: PupBuffer.FWordIndex] = { bytes: PupBuffer.ByteIndex _ b.byteLength - bytesOfPupOverhead; fWords _bytes/SIZE[FWORD]/bytesPerWord; }; GetUserSize: PUBLIC PROC [b: Buffer] RETURNS [sizeUnits: NAT] = { bytes: PupBuffer.ByteIndex _ b.byteLength - bytesOfPupOverhead; sizeUnits _ bytes/SIZE[WORD]/bytesPerWord; }; FreeBuffer: PUBLIC PROC [b: Buffer] = { socket: Socket _ NARROW[b.ovh.socket]; FreeBufferInner[socket, b]; }; <<>> FreeBufferInner: ENTRY PROC [socket: Socket, b: Buffer] = INLINE { ENABLE UNWIND => NULL; SELECT b.ovh.direction FROM send => { socket.sendBuffersInUse _ socket.sendBuffersInUse - 1; NOTIFY socket.waitForSendBuffer; }; recv => { socket.recvBuffersInUse _ socket.recvBuffersInUse - 1; NOTIFY socket.waitForRecvBuffer; }; ENDCASE => ERROR; CommDriver.FreeBuffer[RealDriverBuffer[b]]; }; <<>> FixupSourceAndDest: PUBLIC PROC [b: Buffer] = { network: Network _ NARROW[b.ovh.network]; IF b.source.net = Pup.nullNet THEN b.source.net _ network.pup.net; IF b.dest.net = Pup.nullNet THEN b.dest.net _ network.pup.net; IF b.dest.host = Pup.allHosts THEN b.dest.host _ network.pup.host; }; <<>> ReturnToSender: PUBLIC PROC [b: Buffer] = { ReturnToSenderNoFree[b]; FreeBuffer[b]; }; ReturnToSenderNoFree: PUBLIC PROC [b: Buffer] = { socket: Socket _ NARROW[b.ovh.socket]; network: Network _ NARROW[b.ovh.network]; bytes: NAT = PupBuffer.RoundUpForChecksum[b.byteLength]; temp: Pup.Address; FixupSourceAndDest[b]; b.hopCount _ 0; b.spares _ 0; temp _ b.source; b.source _ b.dest; b.dest _ temp; TRUSTED { words: NAT = PupBuffer.WordsWithoutChecksum[b.byteLength]; checksumLoc: LONG POINTER TO HWORD _ @b.byteLength + words; IF socket = NIL OR socket.sendChecksum THEN { IF outOfLineChecksum THEN checksumLoc^ _ OutOfLineChecksum[0, words, @b.byteLength] ELSE checksumLoc^ _ Checksum.ComputeChecksum[0, words, @b.byteLength]; } ELSE checksumLoc^ _ PupBuffer.noChecksum; }; IF b.source.host = Pup.allHosts OR network.toBroadcast[network, TempDriverBuffer[b]] THEN RETURN; network.pup.return[network, TempDriverBuffer[b], bytes]; }; <<>> SetChecksum: PUBLIC PROC [b: Buffer] = TRUSTED { words: NAT = PupBuffer.WordsWithoutChecksum[b.byteLength]; checksumLoc: LONG POINTER TO HWORD _ @b.byteLength + words; IF outOfLineChecksum THEN checksumLoc^ _ OutOfLineChecksum[0, words, @b.byteLength] ELSE checksumLoc^ _ Checksum.ComputeChecksum[0, words, @b.byteLength]; }; <<>> errorSize: NAT = SIZE[HeaderWithoutChecksum] + SIZE[ErrorCode] + SIZE[HWORD]; ReturnError: PUBLIC PROC [b: Buffer, code: ErrorCode, rope: ROPE] = { ReturnErrorNoFree[b, code, rope]; FreeBuffer[b]; }; <<>> ReturnErrorNoFree: PUBLIC PROC [b: Buffer, code: ErrorCode, rope: ROPE] = { socket: Socket _ NARROW[b.ovh.socket]; IF b.type = error THEN RETURN; TRUSTED { PrincOpsUtils.LongCopy[ from: @b.byteLength, nwords: SIZE[HeaderWithoutChecksum], to: @b.error.header]; }; b.type _ error; b.error.code _ code; b.error.options _ 0; SetUserSize[b, errorSize]; IF rope = NIL THEN SELECT code FROM badChecksum => rope _ "Bad Software Checksum"; noSocket => rope _ "No such Port"; resourceLimits => rope _ "Buffers full"; ENDCASE => NULL; AppendRope[b, rope]; IF code = iAmNotAGateway THEN { -- Fill in our return address network: Network = NARROW[b.ovh.network]; b.dest.net _ network.pup.net; b.dest.host _ network.pup.host; b.dest.socket _ Pup.nullSocket; }; ReturnToSenderNoFree[b]; }; <<>> <> newRoutingInfo: INT _ 0; PutFirst: PUBLIC PROC [socket: Socket, b: Buffer] = { routing: Routing _ socket.routing; -- ATOMIC new: RoutingInfo; IF socket.remote.socket = Pup.nullSocket THEN GOTO CantGetThere; [new.network, new.immediate] _ PupInternal.Route[socket.remote]; IF new.network = NIL THEN GOTO CantGetThere; new.encap _ new.network.pup.getEncapsulation[new.network, new.immediate]; IF routing = NIL OR routing^ # new THEN { routing _ NEW[RoutingInfo _ new]; socket.routing _ routing; -- ATOMIC newRoutingInfo _ newRoutingInfo.SUCC; }; PutAgain[socket, b]; EXITS CantGetThere => { socket.routing _ NIL; }; }; <<>> PutAgain: PUBLIC PROC [socket: Socket, b: Buffer] = { bytes: NAT = PupBuffer.RoundUpForChecksum[b.byteLength]; routing: Routing _ socket.routing; -- ATOMIC network: Network; IF routing = NIL THEN RETURN; network _ routing.network; IF network = NIL THEN RETURN; b.ovh.encap _ routing.encap; b.hopCount _ 0; b.spares _ 0; b.dest _ socket.remote; b.source _ socket.local; TRUSTED { words: NAT = PupBuffer.WordsWithoutChecksum[b.byteLength]; checksumLoc: LONG POINTER TO HWORD _ @b.byteLength + words; IF socket.sendChecksum THEN { IF outOfLineChecksum THEN checksumLoc^ _ OutOfLineChecksum[0, words, @b.byteLength] ELSE checksumLoc^ _ Checksum.ComputeChecksum[0, words, @b.byteLength]; } ELSE checksumLoc^ _ PupBuffer.noChecksum; }; IF network # NIL THEN network.pup.send[network, TempDriverBuffer[b], bytes]; }; <<>> Resend: PUBLIC PROC [b: Buffer, checksum: BOOL _ TRUE] = { bytes: NAT = PupBuffer.RoundUpForChecksum[b.byteLength]; network: Network _ NARROW[b.ovh.network]; IF network = NIL THEN RETURN; TRUSTED { words: NAT = PupBuffer.WordsWithoutChecksum[b.byteLength]; checksumLoc: LONG POINTER TO HWORD _ @b.byteLength + words; IF checksum THEN { IF outOfLineChecksum THEN checksumLoc^ _ OutOfLineChecksum[0, words, @b.byteLength] ELSE checksumLoc^ _ Checksum.ComputeChecksum[0, words, @b.byteLength]; } ELSE checksumLoc^ _ PupBuffer.noChecksum; }; network.pup.send[network, TempDriverBuffer[b], bytes]; }; <<>> SetDirectReceive: PUBLIC ENTRY PROC [socket: Socket, proc: ReceiveProc, user: REF] = { socket.proc _ proc; socket.user _ user; }; <<>> UseNormalPath: PUBLIC PROC [b: Buffer] = { socket: Socket _ NARROW[b.ovh.socket]; UseNormalPathInner[socket, b]; }; UseNormalPathInner: ENTRY PROC [socket: Socket, b: Buffer] = { b.ovh.socket _ NIL; IF b.ovh.gap#gapNoList THEN DebuggerSwap.CallDebugger["Buffer already in a list!"]; b.ovh.gap _ gapSocket; -- DKW: b is now in the socket queue IF socket.firstInput = NIL THEN socket.firstInput _ b ELSE socket.lastInput.ovh.next _ b; socket.lastInput _ b; NOTIFY socket.waitForInput; }; AllocRecvBuffer: PUBLIC PROC [socket: Socket] RETURNS [b: Buffer] = { InnerAllocRecvBuffer[socket]; b _ RealPupBuffer[CommDriver.AllocBuffer[], recv]; b.ovh.network _ NIL; b.ovh.socket _ socket; -- Needed for Finalization }; <<>> InnerAllocRecvBuffer: ENTRY PROC [socket: Socket] = { <> ENABLE UNWIND => NULL; UNTIL socket.recvBuffersInUse < socket.recvBuffersAlloc DO WAIT socket.waitForRecvBuffer; ENDLOOP; socket.recvBuffersInUse _ socket.recvBuffersInUse + 1; }; <> CopyRope: PUBLIC PROC [b: Buffer, rope: Rope.ROPE] = { chars: PupBuffer.ByteIndex _ MIN[Rope.Length[rope], PupBuffer.ByteAlloc.LAST]; FOR i: PupBuffer.ByteAlloc IN [0..chars) DO b.char[i] _ Rope.Fetch[rope, i]; ENDLOOP; SetUserBytes[b, chars]; }; AppendRope: PUBLIC PROC [b: Buffer, rope: Rope.ROPE] = { start: PupBuffer.ByteIndex _ GetUserBytes[b]; chars: PupBuffer.ByteIndex _ MIN[Rope.Length[rope], PupBuffer.ByteAlloc.LAST-start]; FOR i: PupBuffer.ByteAlloc IN [0..chars) DO b.char[start+i] _ Rope.Fetch[rope, i]; ENDLOOP; SetUserBytes[b, start+chars]; }; <<>> ExtractRope: PUBLIC PROC [b: Buffer] RETURNS [rope: Rope.ROPE] = { bytes: NAT _ GetUserBytes[b]; text: REF TEXT _ RefText.ObtainScratch[bytes]; FOR i: PupBuffer.ByteAlloc IN [0..bytes) DO text[i] _ b.char[i]; ENDLOOP; text.length _ bytes; rope _ Rope.FromRefText[text]; RefText.ReleaseScratch[text]; }; <<>> ExtractErrorRope: PUBLIC PROC [b: Buffer] RETURNS [rope: Rope.ROPE] = { bytes: NAT _ GetUserBytes[b]; text: REF TEXT _ RefText.ObtainScratch[bytes]; IF bytes < PupBuffer.errorOverheadBytes THEN RETURN[NIL]; bytes _ bytes - PupBuffer.errorOverheadBytes; FOR i: NAT IN [0..bytes) DO text[i] _ b.error.text[i]; ENDLOOP; text.length _ bytes; rope _ Rope.FromRefText[text]; RefText.ReleaseScratch[text]; }; <<>> ExtractAbortRope: PUBLIC PROC [b: Buffer] RETURNS [rope: Rope.ROPE] = { bytes: NAT _ GetUserBytes[b]; text: REF TEXT _ RefText.ObtainScratch[bytes]; IF bytes < PupBuffer.abortOverheadBytes THEN RETURN[NIL]; bytes _ bytes - PupBuffer.abortOverheadBytes; FOR i: NAT IN [0..bytes) DO text[i] _ b.abort.text[i]; ENDLOOP; text.length _ bytes; rope _ Rope.FromRefText[text]; RefText.ReleaseScratch[text]; }; <> unique: LONG CARDINAL _ BasicTime.GetClockPulses[]; GetUniqueID: PUBLIC PROC RETURNS [FWORD] = { new: LONG CARDINAL _ GetUniqueIDInner[globalLock]; RETURN[Endian.FFromCard[new]]; }; GetUniqueIDInner: ENTRY PROC [socket: Socket] RETURNS [LONG CARDINAL] = { unique _ unique.SUCC; RETURN[unique]; }; IsThisMe: PUBLIC PROC [address: Pup.Address] RETURNS [yes: BOOL] = { network: Network _ PupInternal.Route[address].network; IF network = NIL THEN RETURN[FALSE]; IF network.pup.net # address.net THEN RETURN[FALSE]; IF network.pup.host # address.host THEN RETURN[FALSE]; RETURN[TRUE]; }; <<>> GetMyAddress: PUBLIC PROC RETURNS [address: Pup.Address] = { network: Network _ CommDriver.GetNetworkChain[]; IF network = NIL THEN RETURN[Pup.nullAddress]; address.net _ network.pup.net; address.host _ network.pup.host; address.socket _ Pup.nullSocket; }; <<>> <> <> <<>> <> <<>> <> <<>> <> ClumpOfSockets: TYPE = RECORD [sockets: SEQUENCE count: CARDINAL OF Socket]; wellKnownSockets: REF ClumpOfSockets _ NEW[ClumpOfSockets[PupWKS.rpc.d]]; ephemeralSockets: REF ClumpOfSockets _ NEW[ClumpOfSockets[16]]; <> <<>> maxWellKnownSockets: CARDINAL = 512; -- Must be a power of 2 (for wraparound) maxEphemeralSockets: CARDINAL = 1024; -- Must be a power of 2 (for masking into table) ephemeralSocketMask: CARDINAL = maxEphemeralSockets - 1; wraparoundSocket: CARDINAL _ MAX[maxWellKnownSockets, maxEphemeralSockets]; nextEphemeralSocket: LONG CARDINAL _ BasicTime.GetClockPulses[]; <<>> IndexFromSocket: PROC [socket: Pup.Socket] RETURNS [index: CARDINAL] = { socketNumber: LONG CARDINAL = PupType.CardFromSocket[socket]; index _ IndexFromLC[socketNumber]; }; IndexFromLC: PROC [socketNumber: LONG CARDINAL] RETURNS [index: CARDINAL] = { index _ Basics.BITAND[Basics.LowHalf[socketNumber], ephemeralSocketMask]; }; CantSpecifyArbitraryLocalSocketNumber: PUBLIC ERROR = CODE; AddSocket: INTERNAL PROC [new: Socket] = { socketNumber: LONG CARDINAL = PupType.CardFromSocket[new.local.socket]; SELECT TRUE FROM new.local.socket = Pup.nullSocket => AddEphemeralSocket[new]; socketNumber < maxWellKnownSockets => AddWellKnownSocket[new]; ENDCASE => ERROR CantSpecifyArbitraryLocalSocketNumber; }; AddWellKnownSocket: INTERNAL PROC [new: Socket] = { socketNumber: CARDINAL = PupType.CardFromSocket[new.local.socket]; IF socketNumber >= wellKnownSockets.count THEN wellKnownSockets _ Expand[wellKnownSockets, socketNumber+1]; new.next _ wellKnownSockets[socketNumber]; wellKnownSockets[socketNumber] _ new; }; IndexAdjustmentConfusion: ERROR = CODE; -- Bug in this code SocketNumberWraparoundConfusion: ERROR = CODE; -- Bug in this code AddEphemeralSocket: INTERNAL PROC [new: Socket] = { <> localSocket: LONG CARDINAL _ nextEphemeralSocket; FOR i: CARDINAL IN [0..ephemeralSockets.count) DO index: CARDINAL _ IndexFromLC[localSocket]; IF ~(index < ephemeralSockets.count) THEN { <> localSocket _ localSocket + (maxEphemeralSockets-index); IF localSocket < maxWellKnownSockets THEN localSocket _ wraparoundSocket; index _ IndexFromLC[localSocket]; IF index # 0 THEN ERROR IndexAdjustmentConfusion; }; IF ephemeralSockets[index] = NIL THEN EXIT; localSocket _ localSocket + 1; IF localSocket < maxWellKnownSockets THEN localSocket _ wraparoundSocket; REPEAT FINISHED => { index: CARDINAL _ IndexFromLC[nextEphemeralSocket]; IF ephemeralSockets.count = maxEphemeralSockets THEN DebuggerSwap.CallDebugger["Socket table full"]; localSocket _ nextEphemeralSocket + (ephemeralSockets.count-index); IF localSocket < maxWellKnownSockets THEN localSocket _ wraparoundSocket; index _ IndexFromLC[localSocket]; IF index # ephemeralSockets.count THEN ERROR IndexAdjustmentConfusion; ephemeralSockets _ Expand[ephemeralSockets, ephemeralSockets.count+1]; }; ENDLOOP; IF localSocket < maxWellKnownSockets THEN ERROR SocketNumberWraparoundConfusion; new.local.socket _ PupType.SocketFromCard[localSocket]; nextEphemeralSocket _ localSocket + 1; ephemeralSockets[IndexFromSocket[new.local.socket]] _ new; }; Expand: INTERNAL PROC [old: REF ClumpOfSockets, size: CARDINAL] RETURNS [new: REF ClumpOfSockets] = { new _ NEW[ClumpOfSockets[size]]; FOR i: CARDINAL IN [0..old.count) DO new[i] _ old[i]; ENDLOOP; }; FindSocket: PROC [localSocket: Pup.Socket] RETURNS [h: Socket _ NIL] = { socketNumber: LONG CARDINAL = PupType.CardFromSocket[localSocket]; IF socketNumber < maxWellKnownSockets THEN h _ FindWellKnownSocket[socketNumber] ELSE h _ FindEphemeralSocket[localSocket]; }; FindWellKnownSocket: PROC [socketNumber: CARDINAL] RETURNS [h: Socket _ NIL] = { IF socketNumber < wellKnownSockets.count THEN h _ wellKnownSockets[socketNumber]; DO IF h = NIL THEN RETURN; IF ~h.dead THEN RETURN; h _ h.next; <> ENDLOOP; }; FindEphemeralSocket: PROC [localSocket: Pup.Socket] RETURNS [h: Socket _ NIL] = { index: CARDINAL = IndexFromSocket[localSocket]; IF index < ephemeralSockets.count THEN h _ ephemeralSockets[index]; IF h = NIL THEN RETURN; IF localSocket # h.local.socket THEN h _ NIL; }; RemoveSocket: INTERNAL PROC [old: Socket] = { socketNumber: LONG CARDINAL = PupType.CardFromSocket[old.local.socket]; IF socketNumber < maxWellKnownSockets THEN RemoveWellKnownSocket[old, socketNumber] ELSE RemoveEphemeralSocket[old]; old.next _ NIL; }; RemoveWellKnownSocket: INTERNAL PROC [old: Socket, socketNumber: CARDINAL] = { head: Socket _ wellKnownSockets[socketNumber]; IF head = old THEN { wellKnownSockets[socketNumber] _ head.next; RETURN; }; DO -- NIL fault if not on chain IF head.next = old THEN { head.next _ old.next; EXIT; }; head _ head.next; ENDLOOP; }; RemoveEphemeralSocket: INTERNAL PROC [old: Socket] = { index: CARDINAL = IndexFromSocket[old.local.socket]; IF ephemeralSockets[index] # old THEN ERROR; -- Or bounds fault if bogus ephemeralSockets[index] _ NIL; }; <> <> errorTooShort: INT _ 0; errorNoGateway: INT _ 0; errorNoSocket: INT _ 0; errorDeadSocket: INT _ 0; errorBadChecksum: INT _ 0; errorBuffersFull: INT _ 0; TakeThis: PUBLIC PROC [network: Network, buffer: CommDriver.Buffer, bytes: NAT] RETURNS [CommDriver.Buffer] = { b: Buffer = RealPupBuffer[buffer, recv]; bytesNeeded: NAT = PupBuffer.RoundUpForChecksum[b.byteLength]; dest: Pup.Address _ b.dest; localSocket: Pup.Socket = dest.socket; socket: Socket; proc: ReceiveProc; user: REF ANY; IF bytes < bytesNeeded OR b.byteLength < bytesOfPupOverhead THEN { errorTooShort _ errorTooShort.SUCC; RETURN[RealDriverBuffer[b]]; }; BEGIN <> SELECT TRUE FROM (dest.host = network.pup.host) => { SELECT TRUE FROM (dest.net = network.pup.net) => NULL; -- Main line case (network.pup.net = Pup.nullNet) => NULL; -- We don't know, he does (dest.net = Pup.nullNet) => NULL; -- He doesn't know, we do ENDCASE => GOTO TryBackDoor; }; (dest.host = Pup.allHosts) => { SELECT TRUE FROM (dest.net = network.pup.net) => NULL; -- Main line case (network.pup.net = Pup.nullNet) => NULL; -- We don't know, he does (dest.net = Pup.nullNet) => NULL; -- He doesn't know, we do ENDCASE => GOTO TryBackDoor; }; ENDCASE => GOTO TryBackDoor; EXITS TryBackDoor => { [network, ] _ PupInternal.Route[dest]; -- Back door fixup IF network = NIL THEN network _ NARROW[b.ovh.network]; -- Argh. Our Initialization or his confusion IF dest.net # network.pup.net OR dest.host # network.pup.host THEN RETURN[RealDriverBuffer[forwarder[b]]]; }; END; socket _ FindSocket[localSocket]; IF socket = NIL THEN { errorNoSocket _ errorNoSocket.SUCC; ReturnErrorNoFree[b, noSocket, "No socket at this machine"]; RETURN[RealDriverBuffer[b]]; }; IF socket.dead THEN { errorDeadSocket _ errorDeadSocket.SUCC; IF ~socket.noErrors THEN ReturnErrorNoFree[b, noSocket, "Socket has been deleted"]; RETURN[RealDriverBuffer[b]]; }; IF socket.recvChecksum THEN TRUSTED { words: NAT = PupBuffer.WordsWithoutChecksum[b.byteLength]; checksumLoc: LONG POINTER TO HWORD _ @b.byteLength + words; hisChecksum: HWORD _ checksumLoc^; IF hisChecksum # PupBuffer.noChecksum THEN { checksum: WORD; IF outOfLineChecksum THEN checksum _ OutOfLineChecksum[0, words, @b.byteLength] ELSE checksum _ Checksum.ComputeChecksum[0, words, @b.byteLength]; IF checksum # hisChecksum THEN { errorBadChecksum _ errorBadChecksum.SUCC; ReturnErrorNoFree[b, badChecksum, "Bad software checksum"]; RETURN[RealDriverBuffer[b]]; }; }; }; [proc, user] _ CheckDirect[socket]; IF proc # NIL THEN { new: Buffer; b.ovh.socket _ socket; new _ proc[socket, b, user]; IF new = NIL THEN RETURN[NIL]; UnbumpRecvCount[socket]; RETURN[RealDriverBuffer[new]]; }; IF TakeThisInner[socket, b] THEN RETURN[NIL] ELSE { errorBuffersFull _ errorBuffersFull.SUCC; IF ~socket.noErrors THEN ReturnErrorNoFree[b, resourceLimits, "Buffer allocation limit"]; RETURN[RealDriverBuffer[b]]; }; }; <<>> CheckDirect: ENTRY PROC [socket: Socket] RETURNS [proc: ReceiveProc, user: REF ANY] = { IF socket.proc = NIL THEN RETURN[NIL, NIL]; IF socket.recvBuffersInUse < socket.recvBuffersAlloc THEN { socket.recvBuffersInUse _ socket.recvBuffersInUse.SUCC; RETURN[socket.proc, socket.user]; }; RETURN[NIL, NIL]; }; UnbumpRecvCount: ENTRY PROC [socket: Socket] = INLINE { socket.recvBuffersInUse _ socket.recvBuffersInUse.PRED; }; TakeThisInner: ENTRY PROC [socket: Socket, b: Buffer] RETURNS [ok: BOOL] = { IF socket.dead THEN { CommDriver.FreeBuffer[RealDriverBuffer[b]]; RETURN[TRUE]; }; IF socket.recvBuffersInUse < socket.recvBuffersAlloc THEN { socket.recvBuffersInUse _ socket.recvBuffersInUse.SUCC; IF b.ovh.gap#gapNoList THEN DebuggerSwap.CallDebugger["Buffer already in a list!"]; b.ovh.gap _ gapSocket; -- DKW: b is now in the socket queue IF socket.firstInput = NIL THEN socket.firstInput _ b ELSE socket.lastInput.ovh.next _ b; socket.lastInput _ b; NOTIFY socket.waitForInput; RETURN[TRUE]; }; RETURN[FALSE]; }; <> forwarder: PROC [b: Buffer] RETURNS [Buffer] _ DummyForwarder; DummyForwarder: PROC [b: Buffer] RETURNS [Buffer] = { errorNoGateway _ errorNoGateway.SUCC; ReturnErrorNoFree[b, iAmNotAGateway, "I'm not a gateway (yet)"]; RETURN[b]; }; CaptureForwarding: PUBLIC PROC [proc: PROC [Buffer] RETURNS [Buffer] ] = { forwarder _ proc; }; <> sfqMaxCount: INT _ 0; -- DKW: see how full sfq can get in 10 seconds droppedSockets: INT _ 0; finishedSockets: INT _ 0; finishedErrors: INT _ 0; finishedDelayed: INT _ 0; finishedBatches: INT _ 0; droppedBuffers: INT _ 0; oneSecond: Process.Ticks = Process.SecondsToTicks[1]; SocketFinalizer: PROC = { Process.SetPriority[Process.priorityBackground]; DO sfqCount: INT _ 0; -- DKW: count the number of sockets in the queue each iteration list: LIST OF Socket _ NIL; -- Batch up sockets to be Destroyed, avoiding 10 seconds each DO socket: Socket _ NARROW[SafeStorage.FQNext[sfq]]; sfqCount _ sfqCount+1; IF ~socket.dead THEN { -- User forgot to call Destroy SafeStorage.EnableFinalization[socket]; Destroy[socket]; droppedSockets _ droppedSockets.SUCC; } ELSE { -- Normal end of life IF socket.noErrors THEN list _ CONS[socket, list] ELSE DestroyInner[globalLock, socket]; finishedSockets _ finishedSockets.SUCC; }; socket _ NIL; IF SafeStorage.FQEmpty[sfq] THEN EXIT; ENDLOOP; sfqMaxCount _ MAX[sfqMaxCount, sfqCount]; IF list = NIL THEN LOOP; Process.Pause[10*oneSecond]; UNTIL list = NIL DO DestroyInner[globalLock, list.first]; finishedDelayed _ finishedDelayed.SUCC; list _ list.rest; ENDLOOP; finishedBatches _ finishedBatches.SUCC; ENDLOOP; }; BufferFinalizer: PROC = { <> Process.SetPriority[Process.priorityBackground]; DO b: Buffer _ NARROW[SafeStorage.FQNext[bfq]]; SafeStorage.EnableFinalization[b]; FreeBuffer[b]; <> b _ NIL; droppedBuffers _ droppedBuffers.SUCC; ENDLOOP; }; DropTest: PROC [n: NAT] = { FOR i: NAT IN [0..n) DO socket: Socket _ CreateEphemeral[remote: Pup.nullAddress]; [] _ AllocBuffer[socket]; SetNoErrors[socket]; ENDLOOP; }; bfq: SafeStorage.FinalizationQueue _ SafeStorage.NewFQ[]; sfq: SafeStorage.FinalizationQueue _ SafeStorage.NewFQ[length: 300]; <> SafeStorage.EstablishFinalization[type: CODE[PupBuffer.BufferObject], npr: 0, fq: bfq]; SafeStorage.EstablishFinalization[type: CODE[Object], npr: 1, fq: sfq]; TRUSTED { Process.Detach[FORK SocketFinalizer[]]; }; TRUSTED { Process.Detach[FORK BufferFinalizer[]]; }; IF ~Booting.switches[c] THEN CommDriver.InsertReceiveProc[NIL, pup, TakeThis]; }.