DIRECTORY Basics USING [LongNumber], BufferDefs USING [BufferAccessHandle, FreeBufferPool, MakeBufferPool, OisBuffer, QueueInitialize, QueueCleanup, QueueObject], CommunicationInternal USING [], CommUtilDefs USING [MaybeShorten, EnableAborts], DriverDefs USING [PutOnGlobalDoneQueue], OISCP USING [DequeueOis, uniqueAddress, unknownHostID, unknownNetID, unknownSocketID], Process USING [InitializeCondition, MsecToTicks, SecondsToTicks, SetTimeout, Ticks], ProcessorFace USING [GetClockPulses, microsecondsPerHundredPulses], Router USING [AddSocket, AssignOisAddress, BroadcastThisPacket, FindDestinationRelativeNetID, FindMyHostID, RemoveSocket, socketRouterLock, SendPacket, XmitStatus], Socket USING [defaultWaitTime, SocketStatus, WaitTime], SocketInternal USING [SocketHandle, SocketObject], NSAddress USING [HostNumber, NetworkAddress, NetworkNumber, nullNetworkAddress]; SocketImpl: MONITOR LOCKS Router.socketRouterLock IMPORTS BufferDefs, DriverDefs, CommUtilDefs, OISCP, Process, Router, ProcessorFace EXPORTS CommunicationInternal, Socket, SocketInternal SHARES BufferDefs = BEGIN OPEN OISCP, Socket, SocketInternal; ChannelHandle: TYPE = REF ChannelObject; ChannelObject: PUBLIC TYPE = SocketInternal.SocketObject; NetworkAddress: PUBLIC TYPE = NSAddress.NetworkAddress; -- others uniqueNetworkAddr: PUBLIC NSAddress.NetworkAddress _ NSAddress.nullNetworkAddress; myHostID: NSAddress.HostNumber; TimeOut: PUBLIC ERROR = CODE; ChannelAborted: PUBLIC ERROR = CODE; ChannelError: PUBLIC ERROR = CODE; GetPulsesIntervalTime: PROCEDURE [startTime: LONG CARDINAL] RETURNS [LONG CARDINAL] = INLINE BEGIN RETURN[ProcessorFace.GetClockPulses[] - startTime]; END; -- GetPulsesIntervalTime PutPacket: PUBLIC PROCEDURE [cH: ChannelHandle, b: BufferDefs.OisBuffer] = BEGIN PutLocked: ENTRY PROCEDURE = INLINE BEGIN IF cH.channelState # active THEN RETURN WITH ERROR ChannelAborted; b.status _ LOOPHOLE[Router.XmitStatus[goodCompletion]]; END; -- PutLocked PutLocked[]; b.ois.source _ cH.localAddr; IF b.ois.source.net = unknownNetID THEN b.ois.source.net _ Router.FindDestinationRelativeNetID[b.ois.destination.net]; IF b.ois.destination.host = unknownHostID OR b.ois.destination.socket = unknownSocketID THEN BEGIN b.status _ LOOPHOLE[Router.XmitStatus[invalidDestAddr]]; -- this is safe! DriverDefs.PutOnGlobalDoneQueue[b]; RETURN; END; Router.SendPacket[b]; END; -- PutPacket PutPacketToAllConnectedNets: PUBLIC PROCEDURE [cH: ChannelHandle, b: BufferDefs.OisBuffer] = BEGIN PutLocked: ENTRY PROCEDURE = INLINE BEGIN IF cH.channelState # active THEN RETURN WITH ERROR ChannelAborted; b.status _ LOOPHOLE[Router.XmitStatus[goodCompletion]]; END; -- PutLocked PutLocked[]; b.ois.source _ cH.localAddr; IF b.ois.destination.socket = unknownSocketID THEN BEGIN b.status _ LOOPHOLE[Router.XmitStatus[invalidDestAddr]]; -- this is safe! DriverDefs.PutOnGlobalDoneQueue[b]; RETURN; END; Router.BroadcastThisPacket[b]; END; -- PutPacketToAllConnectedNets GetPacket: PUBLIC ENTRY PROCEDURE [sH: ChannelHandle] RETURNS [b: BufferDefs.OisBuffer] = BEGIN ENABLE UNWIND => NULL; startTime: LONG CARDINAL _ ProcessorFace.GetClockPulses[]; IF (sH.channelState = aborted) THEN RETURN WITH ERROR ChannelAborted; WHILE (b _ DequeueOis[sH.completedUserGetQueue]) = NIL DO IF GetPulsesIntervalTime[startTime] > sH.waitTime THEN RETURN WITH ERROR TimeOut; WAIT sH.newUserInput; -- propogate ERROR ABORTED IF (sH.channelState = aborted) THEN RETURN WITH ERROR ChannelAborted; ENDLOOP; END; AssignNetworkAddress: PUBLIC PROCEDURE RETURNS [NSAddress.NetworkAddress] = BEGIN RETURN[Router.AssignOisAddress[]]; END; -- AssignNetworkAddress Create: PUBLIC PROCEDURE [ local: NetworkAddress, send, receive, reserve: CARDINAL, privateBuffers: BOOLEAN _ TRUE] RETURNS [sH: SocketHandle] = BEGIN totalBuffers: CARDINAL _ IF privateBuffers THEN send + receive + reserve ELSE 0; sH _ NEW[SocketObject]; sH.pool _ BufferDefs.MakeBufferPool[ totalBuffers, send, receive, reserve, ~privateBuffers]; sH.localAddr _ IF local = OISCP.uniqueAddress THEN Router.AssignOisAddress[] ELSE local; sH.channelState _ active; sH.waitTime _ MilliSecondsToPulses[Socket.defaultWaitTime]; sH.completedUserGetQueue _ NEW[BufferDefs.QueueObject]; BufferDefs.QueueInitialize[sH.completedUserGetQueue]; Process.InitializeCondition[ CommUtilDefs.MaybeShorten[@sH.newUserInput], Process.MsecToTicks[LOOPHOLE[Socket.defaultWaitTime, Basics.LongNumber].lowbits] + 1]; CommUtilDefs.EnableAborts[CommUtilDefs.MaybeShorten[@sH.newUserInput]]; Router.AddSocket[sH]; END; -- Create Delete: PUBLIC PROCEDURE [sH: ChannelHandle] = BEGIN DeleteLocked: ENTRY PROCEDURE = INLINE BEGIN ENABLE UNWIND => NULL; BufferDefs.QueueCleanup[sH.completedUserGetQueue]; BufferDefs.FreeBufferPool[sH.pool]; END; -- DeleteLocked Router.RemoveSocket[sH]; DeleteLocked[]; END; -- Abort: PUBLIC ENTRY PROCEDURE [sH: ChannelHandle] = BEGIN temp: CONDITION; sH.channelState _ aborted; BROADCAST sH.newUserInput; -- tell all potential waiters Process.InitializeCondition[@temp, Process.MsecToTicks[10]]; WAIT temp; -- Yield to let waiter leave the MONITOR END; -- Abort Reset: PUBLIC ENTRY PROCEDURE [sH: ChannelHandle] = BEGIN sH.channelState _ active; END; GetStatus: PUBLIC ENTRY PROCEDURE [sH: ChannelHandle] RETURNS [status: SocketStatus] = BEGIN status _ [localAddr: sH.localAddr, state: sH.channelState, incompleteGets: sH.completedUserGetQueue.length]; END; -- GetStatus SetWaitTime: PUBLIC ENTRY PROCEDURE [cH: ChannelHandle, time: WaitTime] = BEGIN ticks: Process.Ticks; condTime: CARDINAL _ LOOPHOLE[time, Basics.LongNumber].lowbits; lastCard: LONG CARDINAL = LAST[CARDINAL]; -- no, not last LONG CARDINAL !! cH.waitTime _ MilliSecondsToPulses[time]; IF time > lastCard THEN BEGIN timeInSeconds: LONG CARDINAL _ time/1000; IF timeInSeconds > lastCard THEN condTime _ LAST[CARDINAL] - 1 ELSE condTime _ LOOPHOLE[timeInSeconds, Basics.LongNumber].lowbits; ticks _ Process.SecondsToTicks[condTime]; END ELSE ticks _ Process.MsecToTicks[condTime] + 1; Process.SetTimeout[CommUtilDefs.MaybeShorten[@cH.newUserInput], ticks]; Process.SetTimeout[CommUtilDefs.MaybeShorten[@cH.newUserInput], ticks]; END; -- SetWaitTime MilliSecondsToPulses: PROCEDURE [ms: LONG CARDINAL] RETURNS [pulses: LONG CARDINAL] = BEGIN micro: LONG CARDINAL = MIN[LAST[LONG CARDINAL]/1000,ms] * 1000; hundreds: LONG CARDINAL = micro / ProcessorFace.microsecondsPerHundredPulses; pulses _ MIN[LAST[LONG CARDINAL]/100,hundreds]*100; END; -- end MilliSecondsToPulses GetBufferPool: PUBLIC PROCEDURE [sH: SocketHandle] RETURNS [BufferDefs.BufferAccessHandle] = BEGIN RETURN[sH.pool]; END; SocketOn: PUBLIC PROCEDURE = BEGIN myHostID _ Router.FindMyHostID[]; END; -- SocketOn END. -- SocketImpl module LOG Time: January 7, 1980 3:11 PM By: Dalal Action: converted to new SocketInternal. Time: January 21, 1980 5:47 PM By: Dalal Action: all sockets under one lock. Time: March 12, 1980 5:32 PM By: BLyon Action: added send, receive to CreateInternal. Time: March 18, 1980 12:07 PM By: BLyon Action: Added Reset, Getpacket, PutPacket (body identicle to old Put), modified Put. Modified TransferWait & TransferWaitAny to abort like PutPacket. Modified Abort. Time: June 3, 1980 3:43 PM By: BLyon Action: Replace SimpleHeap references with Heap. Time: June 3, 1980 3:43 PM By: HGM Action: Add MaybeShortens. Time: June 18, 1980 2:44 PM By: BLyon Action: Added EXPORTED TYPES. Time: September 18, 1980 3:45 PM By: BLyon Action: flushed primaryNetID concept. Time: October 8, 1980 9:10 AM By: BLyon Action: Time units are now pulses and not milliseconds. Time: November 19, 1980 5:12 PM By: BLyon Action: Post Mokelumne rework. Time: January 23, 1981 4:27 PM By: BLyon Action: Added ability to abort GetPacket. άSocketImpl.mesa - implementation module for Pilot OISCP Socket Channels Copyright c 1985 by Xerox Corporation. All rights reserved. BLyon on: March 26, 1981 10:13 AM Levin, August 9, 1983 9:31 am EXPORTED TYPES and READONLY Variables All socket are covered by one lock. We must be careful when we go to multiple MDSs as we must make sure to specify where the lock really is, i.e. in outerspace. constants for various kinds of buffer pools local copies for speed Signals and Errors Hot Procedures 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. assume all will go OK fix up the buffer 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 the system via the dispatcher using b.requeueProcedure. assume all will go OK fix up the buffer This procedure blocks until a buffer appears the socket's completedUserGetQueue. Cool Procedures This procedure assigns a temporary socket number in a NetworkAddress. 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. fill in all the specific fields of the socket object appropriately, make the appropriate buffer pool, which can be the the spp buffer pool per socket, the listener buffer pool per socket, or just a pool of small buffers called crates. initialize the condition variables and queues tell the router of this socket 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. This procedure is a bit tricky to avoid any monitor problems with socket handles. We want to make sure that the process that from the router always has a valid socket handle, and that there are no deadlocks. Heap.FreeNode[p: sH];-- This procedure aborts I/O on this socket channel. This procedure gets the status of this socket channel. This procedure sets the wait time for this socket channel. fix the condition variables to reflect this change this is a bit hairy because ticks are (short) CARDINALs still too big !!! we must be carefull about multiplication overflow since milliSeconds must be converted to microSeconds This procedure returns the buffer pool for this socket. It needn't lock the monitor. GetBufferPool Cold Procedures This procedure turns this module on. The Communication software is written with the idea of eventually turning the code on and off during execution, and so we should get the latest value the hostID when being turned on by the Router. find myHostID from the Router initialization (Cold) Κ ˜codešœG™GKšœ Οmœ1™Kšœ™K˜—Kšžœ žœ+˜CK˜)Kšž˜—Kšžœ+˜/K˜GK˜GKšžœŸ˜K˜—š œž œžœžœ˜3Kšžœ žœžœ˜!Kšž˜KšœL™LKšœ™Kš œžœžœžœžœžœžœ˜?Kšœ žœžœ7˜NKš œ žœžœžœžœ˜3KšžœŸ˜ K˜K˜—KšœU™UK˜š  œžœž œ˜2Kšžœ#žœžœ žœ˜E—Kšœ ™ K˜Kšœ™K˜KšœT™TKšœQ™QKšœC™CK˜š œžœž œ˜Kšž˜Kšœ™K˜!KšžœŸ ˜K˜—Kšœ™K˜KšžœŸ˜K˜—Kšž˜K˜Kšœžœ5˜SKšœžœ0˜OKšœžœ;˜XKšœžœ²˜ΠKšœžœ>˜YKšœžœžœ˜AKšœžœžœžœ˜GKšœžœ3˜TKšœžœE˜cKšœžœ+˜KKšœžœ6˜UK˜K˜—…—f4H