SocketImpl.mesa - implementation module for Pilot OISCP Socket Channels
Copyright © 1985 by Xerox Corporation. All rights reserved.
BLyon on: March 26, 1981 10:13 AM
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: BOOLEANTRUE] 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: CARDINALLOOPHOLE[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.