-- SocketImpl.mesa (last edited by: BLyon on: March 26, 1981 10:13 AM) -- Function: The implementation module for Pilot OISCP Socket Channels. -- Last Edited by: Levin, August 9, 1983 9:31 am 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; -- EXPORTED TYPES and READONLY Variables ChannelHandle: TYPE = REF ChannelObject; ChannelObject: PUBLIC TYPE = SocketInternal.SocketObject; NetworkAddress: PUBLIC TYPE = NSAddress.NetworkAddress; -- others uniqueNetworkAddr: PUBLIC NSAddress.NetworkAddress ← NSAddress.nullNetworkAddress; -- 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 myHostID: NSAddress.HostNumber; -- Signals and Errors TimeOut: PUBLIC ERROR = CODE; ChannelAborted: PUBLIC ERROR = CODE; ChannelError: PUBLIC ERROR = CODE; -- Hot Procedures GetPulsesIntervalTime: PROCEDURE [startTime: LONG CARDINAL] RETURNS [LONG CARDINAL] = INLINE BEGIN RETURN[ProcessorFace.GetClockPulses[] - startTime]; END; -- GetPulsesIntervalTime -- 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 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]]; -- assume all will go OK END; -- PutLocked -- fix up the buffer 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 -- 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. 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]]; -- assume all will go OK END; -- PutLocked -- fix up the buffer 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 -- This procedure blocks until a buffer appears the socket's completedUserGetQueue. 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; --Cool Procedures -- This procedure assigns a temporary socket number in a NetworkAddress. AssignNetworkAddress: PUBLIC PROCEDURE RETURNS [NSAddress.NetworkAddress] = BEGIN RETURN[Router.AssignOisAddress[]]; END; -- AssignNetworkAddress -- 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 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]; -- 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. 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]; -- initialize the condition variables and queues 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]]; -- tell the router of this socket Router.AddSocket[sH]; END; -- Create -- 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 PROCEDURE [sH: ChannelHandle] = BEGIN DeleteLocked: ENTRY PROCEDURE = INLINE BEGIN ENABLE UNWIND => NULL; BufferDefs.QueueCleanup[sH.completedUserGetQueue]; BufferDefs.FreeBufferPool[sH.pool]; END; -- DeleteLocked -- 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. Router.RemoveSocket[sH]; DeleteLocked[]; --Heap.FreeNode[p: sH];-- END; -- -- This procedure aborts I/O on this socket channel. 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; -- This procedure gets the status of this socket channel. GetStatus: PUBLIC ENTRY PROCEDURE [sH: ChannelHandle] RETURNS [status: SocketStatus] = BEGIN status ← [localAddr: sH.localAddr, state: sH.channelState, incompleteGets: sH.completedUserGetQueue.length]; END; -- GetStatus -- This procedure sets the wait time for this socket channel. 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]; -- fix the condition variables to reflect this change -- this is a bit hairy because ticks are (short) CARDINALs IF time > lastCard THEN BEGIN timeInSeconds: LONG CARDINAL ← time/1000; IF timeInSeconds > lastCard THEN condTime ← LAST[CARDINAL] - 1 -- still too big !!! 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 -- we must be carefull about multiplication overflow since milliSeconds must be -- converted to microSeconds 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 -- This procedure returns the buffer pool for this socket. It needn't lock the monitor. GetBufferPool: PUBLIC PROCEDURE [sH: SocketHandle] RETURNS [BufferDefs.BufferAccessHandle] = BEGIN RETURN[sH.pool]; END; -- 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. SocketOn: PUBLIC PROCEDURE = BEGIN -- find myHostID from the Router myHostID ← Router.FindMyHostID[]; END; -- SocketOn -- initialization (Cold) 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.