-- File: SocketImpl.mesa - last edit: -- AOF 2-Feb-88 17:12:37 -- SMA 23-May-86 16:59:42 -- MI 18-Jul-86 16:18:01 -- Copyright (C) 1984, 1985, 1986, 1987, 1988 by Xerox Corporation. All rights reserved. --Function: The implementation module for Pilot NS Socket Channels. DIRECTORY Buffer USING [dataLinkReserve], BufferOps USING [GetSizes], CommFlags USING [doDebug], CommHeap USING [zone], CommUtil USING [PulsesToTicks], Driver USING [GetDeviceChain, Glitch, Device, PutOnGlobalDoneQueue], Frame USING [GetReturnFrame, ReadGlobalLink], HostNumbers USING [IsMulticastID], NSBuffer USING [ AccessHandle, Body, DestroyPool, MakePool, Buffer, Dequeue, QueueInitialize, QueueCleanup, QueueObject, GetBuffer], NSTypes USING [bytesPerIDPHeader, PacketType, wordsPerIDPHeader], Process USING [ Abort, EnableAborts, InitializeCondition, SetTimeout, DisableTimeout, DisableAborts, SetPriority], ProcessPrioritiesExtras USING [priorityPilotRealtimeSwappable], Protocol1 USING [GetContext], Router USING [AssignAddress, FindDestinationRelativeNetID, FindMyHostID], RouterInternal USING [ AddSocket, BroadcastThisPacket, RemoveSocket, socketRouterLock, SendPacket], RoutingTable USING [NetworkContext], Socket USING [defaultWaitTime, SocketStatus, WaitTime], SocketInternal USING [ ListenerProcType, SocketHandle, SocketObject], SpecialSystem USING [ broadcastHostNumber, HostNumber, NetworkAddress, NetworkNumber, SocketNumber, nullSocketNumber, nullNetworkNumber, nullNetworkAddress], System USING [ GetClockPulses, Pulses, NetworkAddress, MicrosecondsToPulses, nullHostNumber, nullSocketNumber]; SocketImpl: MONITOR LOCKS RouterInternal.socketRouterLock IMPORTS Buffer, BufferOps, CommHeap, CommUtil, Driver, Frame, HostNumbers, NSBuffer, Process, Protocol1, Router, RouterInternal, System EXPORTS Buffer, Socket, SocketInternal, System = BEGIN Device: PUBLIC <<Buffer>> TYPE = Driver.Device; ChannelHandle: PUBLIC TYPE = SocketInternal.SocketHandle; HostNumber: PUBLIC TYPE = SpecialSystem.HostNumber; NetworkNumber: PUBLIC TYPE = SpecialSystem.NetworkNumber; SocketNumber: PUBLIC TYPE = SpecialSystem.SocketNumber; NetworkAddress: PUBLIC TYPE = SpecialSystem.NetworkAddress; NormalSocket: TYPE = LONG POINTER TO normal SocketInternal.SocketObject; --EXPORTED Variables nullChannelHandle: PUBLIC ChannelHandle ← NIL; uniqueNetworkAddr: PUBLIC NetworkAddress ← SpecialSystem.nullNetworkAddress; --SIGNALS AND ERRORS TimeOut: PUBLIC ERROR = CODE; --GLITCH IllegalNSPktLength: ERROR = CODE; --the following constants are used in setting wait times, etc maxMsecToPulses: LONG CARDINAL = LAST[LONG CARDINAL] / 1000; listener: RECORD[ process: PROCESS, condition: CONDITION, queue: NSBuffer.QueueObject, count: CARDINAL]; clientAborted: CONDITION; --All socket are covered by one lock. << This procedure puts the buffer containing a packet out from this socket channel. The send is asynchronous; the caller owns the buffer and gets it back from the system via the dispatcher using b.requeueProcedure. >> PutPacket: PUBLIC PROC[cH: ChannelHandle, b: NSBuffer.Buffer] = BEGIN nsb: NSBuffer.Body = b.ns; nsb.source ← cH.address; IF nsb.source.net = SpecialSystem.nullNetworkNumber THEN nsb.source.net ← Router.FindDestinationRelativeNetID[ nsb.destination.net]; IF nsb.destination.host = System.nullHostNumber OR nsb.destination.socket = System.nullSocketNumber THEN {b.fo.status ← invalidDestAddr; Driver.PutOnGlobalDoneQueue[LOOPHOLE[b]]} ELSE BEGIN b.fo.status ← goodCompletion; SELECT TRUE FROM (nsb.destination.net # SpecialSystem.nullNetworkNumber) => RouterInternal.SendPacket[b]; (~HostNumbers.IsMulticastID[@nsb.destination.host]) => RouterInternal.SendPacket[b]; ENDCASE => RouterInternal.BroadcastThisPacket[b]; END; END; --PutPacket << This procedure puts the buffer containing a packet out from this socket channel. This packet will go out to all hosts on all connected networks. The send is asynchronous; the caller owns the buffer and gets it back from thesystem via the dispatcher using b.requeueProcedure. >> BroadcastPacketToAllConnectedNets: PUBLIC PROC[ cH: ChannelHandle, b: NSBuffer.Buffer] = BEGIN b.fo.status ← goodCompletion; b.ns.source ← cH.address; IF b.ns.destination.socket = System.nullSocketNumber THEN {b.fo.status ← invalidDestAddr; Driver.PutOnGlobalDoneQueue[LOOPHOLE[b]]} ELSE RouterInternal.BroadcastThisPacket[b]; END; --PutPacketToAllConnectedNets << This procedure blocks until a buffer is on the socket's input queue. >> GetPacket: PUBLIC ENTRY PROC[sH: ChannelHandle] RETURNS [b: NSBuffer.Buffer] = BEGIN startTime: System.Pulses ← System.GetClockPulses[]; WITH cH: sH SELECT FROM normal => WHILE (b ← NSBuffer.Dequeue[@cH.queue]) = NIL DO ENABLE UNWIND => cH.getsOutstanding ← cH.getsOutstanding - 1; IF cH.aborted THEN {NOTIFY clientAborted; RETURN WITH ERROR ABORTED}; IF (System.GetClockPulses[] - startTime) >= cH.waitTime THEN RETURN WITH ERROR TimeOut; cH.getsOutstanding ← cH.getsOutstanding + 1; WAIT cH.newUserInput; --propogate ERROR ABORTED cH.getsOutstanding ← cH.getsOutstanding - 1; ENDLOOP; ENDCASE => b ← NIL; IF CommFlags.doDebug THEN sH.pool.frame ← Frame.ReadGlobalLink[Frame.GetReturnFrame[]]; END; LocalAddressFromSocket: PUBLIC PROC[socket: SocketNumber] RETURNS [NetworkAddress] = {RETURN[ IF socket = SpecialSystem.nullSocketNumber THEN Router.AssignAddress[] ELSE [Router.FindDestinationRelativeNetID[SpecialSystem.nullNetworkNumber], Router.FindMyHostID[], socket]]}; BroadcastAddressFromSocket: PUBLIC PROC[socket: SocketNumber] RETURNS [NetworkAddress] = {RETURN[[Router.FindDestinationRelativeNetID[SpecialSystem.nullNetworkNumber], SpecialSystem.broadcastHostNumber, socket]]}; FixupUnknownSocketNumber: PUBLIC PROC[ address: LONG POINTER TO NetworkAddress, socket: SocketNumber] = BEGIN IF address.socket = System.nullSocketNumber THEN address.socket ← socket; END; << This procedure makes a socket channel with the specified socket number, and makes the right kind of buffer pool for the socket. If the value of local is uniqueAddress then an unused address is assigned to the socket. Some day we will check to see that the socket number requested is valid. >> Create: PUBLIC PROC[ socket: SocketNumber, --will be used as source address when sending send, receive: CARDINAL, --limits how many input buffers may be queued type: NSTypes.PacketType] --only packets of this type RETURNS [sH: ChannelHandle] = BEGIN sH ← CommHeap.zone.NEW[SocketInternal.SocketObject ← [ next: NIL, packetType: type, checksums: TRUE, address: LocalAddressFromSocket[socket], pool: NSBuffer.MakePool[send, receive, normalPool], flair: normal[aborted: FALSE, waitTime: , newUserInput: , queue: ]]]; SetWaitTime[sH, Socket.defaultWaitTime]; WITH cH: sH SELECT FROM normal => BEGIN NSBuffer.QueueInitialize[@cH.queue]; Process.InitializeCondition[@cH.newUserInput, 0]; Process.EnableAborts[@cH.newUserInput]; END; ENDCASE; RouterInternal.AddSocket[sH ! UNWIND => {NSBuffer.DestroyPool[sH.pool]; CommHeap.zone.FREE[@sH]}]; sH.pool.frame ← Frame.ReadGlobalLink[Frame.GetReturnFrame[]]; END; --Create CreateListen: PUBLIC PROC[ socket: SocketNumber, --will be used as source address when sending callback: SocketInternal.ListenerProcType, --call me when one arrives clientData: LONG POINTER, --client association send: CARDINAL ← 0, --listen only that wants to send?? receive: CARDINAL ← 2, --limits how many input buffers may be queued type: NSTypes.PacketType ← private] --only packets of this type RETURNS [ChannelHandle] = BEGIN sH: ChannelHandle ← CommHeap.zone.NEW[SocketInternal.SocketObject ← [ next: NIL, packetType: type, checksums: TRUE, address: LocalAddressFromSocket[socket], pool: NSBuffer.MakePool[send, receive, listenPool], flair: listen[ callout: callback, arrival: @listener.condition, clientData: clientData, queue: @listener.queue]]]; RouterInternal.AddSocket[sH ! UNWIND => {NSBuffer.DestroyPool[sH.pool]; CommHeap.zone.FREE[@sH]}]; IF TestAndSetListener[1] = 1 THEN listener.process ← FORK Listener[]; sH.pool.frame ← Frame.ReadGlobalLink[Frame.GetReturnFrame[]]; RETURN[sH]; END; --Create TestAndSetListener: PUBLIC <<SocketInternal>> ENTRY PROC[ modify: INTEGER[-1..1]] RETURNS[CARDINAL] = BEGIN --Watch out this doesn't go large negative RETURN[listener.count ← listener.count + modify]; END; --ModifyListenerCount << This procedure deletes this socket channel by deleting the socket from the router's tables, and cleans up the data structures associated with this socket. >> Delete: PUBLIC PROC[sH: ChannelHandle] = BEGIN AbortSocket: ENTRY PROC[cH: NormalSocket] = BEGIN cH.aborted ← TRUE; BROADCAST cH.newUserInput; UNTIL cH.getsOutstanding = 0 DO WAIT clientAborted; ENDLOOP; END; --AbortSocket RouterInternal.RemoveSocket[sH]; --inhibit new input being queued WITH cH: sH SELECT FROM normal => BEGIN AbortSocket[@cH]; --get rid of socket clients NSBuffer.QueueCleanup[@cH.queue]; --flush input queue END; listen => IF TestAndSetListener[-1] = 0 THEN BEGIN Process.Abort[listener.process]; JOIN listener.process; listener.process ← NIL; NSBuffer.QueueCleanup[@listener.queue]; --flush input queue END; ENDCASE; NSBuffer.DestroyPool[sH.pool]; --free the pool CommHeap.zone.FREE[@sH]; --delete socket object END; --Delete GetStatus: PUBLIC ENTRY PROC[sH: ChannelHandle] RETURNS [status: Socket.SocketStatus] = {status ← [sH.address, sH.pool.receiveInUse]}; Listener: PROC = BEGIN ListenSocket: TYPE = LONG POINTER TO listen SocketInternal.SocketObject; --This is the generic listener process routine Dequeue: ENTRY PROC = BEGIN ENABLE UNWIND => NULL; sH: ListenSocket; --FOREVER-- DO WHILE listener.queue.length # 0 DO b ← NSBuffer.Dequeue[@listener.queue]; sH ← NARROW[b.requeueData, ListenSocket]; callback ← sH.callout; clientData ← sH.clientData; RETURN; --this causes next call to start at beginning of list ENDLOOP; WAIT listener.condition; --Router will wake us with something to do ENDLOOP; END; --Dequeue b: NSBuffer.Buffer; clientData: LONG POINTER; callback: SocketInternal.ListenerProcType; Process.SetPriority[ProcessPrioritiesExtras.priorityPilotRealtimeSwappable]; DO Dequeue[ ! ABORTED => EXIT]; callback[b, clientData]; ENDLOOP; END; --Listener --This procedure sets the wait time for this socket channel. SetWaitTime: PUBLIC ENTRY PROC[cH: ChannelHandle, time: Socket.WaitTime] = BEGIN WITH nH: cH SELECT FROM normal => BEGIN nH.waitTime ← IF time > maxMsecToPulses THEN LAST[LONG CARDINAL] ELSE System.MicrosecondsToPulses[time*1000]; IF nH.waitTime = LAST[LONG CARDINAL] THEN Process.DisableTimeout[@nH.newUserInput] ELSE Process.SetTimeout[ @nH.newUserInput, CommUtil.PulsesToTicks[[nH.waitTime]]]; END; ENDCASE; END; --SetWaitTime GetSendBuffer: PUBLIC PROC[cH: ChannelHandle] RETURNS [NSBuffer.Buffer] = BEGIN IF CommFlags.doDebug THEN BEGIN b: NSBuffer.Buffer ← NSBuffer.GetBuffer[cH.pool, send]; b.fo.debug ← Frame.ReadGlobalLink[Frame.GetReturnFrame[]]; RETURN[b]; END ELSE RETURN[NSBuffer.GetBuffer[cH.pool, send]]; END; --GetSendBuffer SwapSourceAndDestination: PUBLIC PROC[b: NSBuffer.Buffer] = BEGIN nsb: NSBuffer.Body = b.ns; network: Device ← LOOPHOLE[b.fo.network]; context: RoutingTable.NetworkContext ← b.fo.context; temp: NetworkAddress ← nsb.source; nsb.source ← nsb.destination; nsb.destination ← temp; -- If the packet came from us, there will be no network object (which we -- use to set null source and destination nets). Try to find a network number -- from the networks on the device chain; if that doesn't work, let it run -- with net 0. IF network = NIL THEN FOR network ← Driver.GetDeviceChain[], network.next UNTIL network = NIL DO SELECT TRUE FROM ((context ← Protocol1.GetContext[network, ns]) = NIL) => NULL; (~network.alive) => NULL; (context.netNumber # SpecialSystem.nullNetworkNumber) => EXIT; ENDCASE; --LOOP ENDLOOP; IF (nsb.source.net = SpecialSystem.nullNetworkNumber) AND (context # NIL) THEN nsb.source.net ← context.netNumber; IF (nsb.destination.net = SpecialSystem.nullNetworkNumber) AND (context # NIL) THEN nsb.destination.net ← context.netNumber; IF HostNumbers.IsMulticastID[@nsb.source.host] THEN nsb.source.host ← Router.FindMyHostID[]; END; GetSource: PUBLIC PROC[b: NSBuffer.Buffer] RETURNS [NetworkAddress] = {RETURN[b.ns.source]}; GetDestination: PUBLIC PROC[b: NSBuffer.Buffer] RETURNS [NetworkAddress] = {RETURN[b.ns.destination]}; SetDestination: PUBLIC PROC[b: NSBuffer.Buffer, d: NetworkAddress] = {b.ns.destination ← d}; GetPacketBytes: PUBLIC PROC[b: NSBuffer.Buffer] RETURNS [bytes: CARDINAL] = {RETURN[b.ns.pktLength - NSTypes.bytesPerIDPHeader]}; SetPacketWords: PUBLIC PROC[b: NSBuffer.Buffer, words: CARDINAL] = {SetPacketBytes[b, 2*words]}; SetPacketBytes: PUBLIC PROC[b: NSBuffer.Buffer, bytes: CARDINAL] = BEGIN -- Buffer.dataLinkReserve has been changed from words to bytes. -- That is why / 2 has been added here overhead: CARDINAL = Buffer.dataLinkReserve / 2 + NSTypes.wordsPerIDPHeader; SELECT TRUE FROM ~CommFlags.doDebug => NULL; --don't bother checking ((bytes / 2) > (BufferOps.GetSizes[][largeBuffer] - overhead)) => Driver.Glitch[IllegalNSPktLength]; --probably too late, but.... ENDCASE; b.ns.pktLength ← bytes + NSTypes.bytesPerIDPHeader END; --This procedure returns the buffer pool for this socket. GetBufferPool: PUBLIC PROC[sH: ChannelHandle] RETURNS [NSBuffer.AccessHandle] = {RETURN[sH.pool]}; --This procedure returns the full address assigned to this socket. GetAssignedAddress: PUBLIC PROC[sH: ChannelHandle] RETURNS [NetworkAddress] = {RETURN[sH.address]}; --MAINLINE CODE Process.DisableAborts[@clientAborted]; Process.SetTimeout[@clientAborted, 20]; listener.count ← 0; listener.process ← NIL; Process.InitializeCondition[@listener.condition, 0]; Process.DisableTimeout[@listener.condition]; Process.EnableAborts[@listener.condition]; NSBuffer.QueueInitialize[@listener.queue]; END. --SocketImpl module LOG time - by - action 14-May-84 14:01:40 AOF Post Klamath. 12-Mar-85 8:31:44 AOF Replace test for broadcast with multicast. 10-Dec-85 10:23:41 AOF Restructure listening. 13-Dec-85 9:56:51 AOF Allowing for arbitrary sized packets. 23-May-86 10:22:32 SMA Remove dependencies on Buffer and encapsulation. 18-Jul-86 16:17:11 MI Devide Buffer.dataLinkReserve by two to get words count. 18-Aug-86 18:08:20 AOF Local caching of b.ns in nsb. 10-Sep-86 17:03:26 AOF Listener runs at priorityPilotRealtimeSwappable. 8-Jan-87 11:39:03 AOF More debugging code.