-- 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.