-- File: PtSocket.mesa, Last Edit: HGM November 19, 1979 12:03 PM

DIRECTORY
InlineDefs: FROM "InlineDefs" USING [COPY],
IODefs: FROM "IODefs" USING [WriteChar, WriteDecimal, WriteLine, WriteOctal, WriteString],
ProcessDefs: FROM "ProcessDefs" USING [Detach, Yield, Abort, Aborted],
StatsDefs: FROM "StatsDefs" USING [StatBump, StatIncr, StatCounterIndex],
PupDefs: FROM "PupDefs" USING [
GetFreePupBuffer, ReturnFreePupBuffer, PupAddress, PupBuffer,
PupSocket, PupSocketDestroy, PupSocketMake, PrintErrorPup,
SecondsToTocks, SetPupContentsWords, veryLongWait],
PupTypes: FROM "PupTypes" USING [fillInPupAddress, statisticsAre, statisticsRequest, statSoc],
BufferDefs: FROM "BufferDefs" USING [ReturnFreeBuffer, Buffer],
PtDefs: FROM "PtDefs" USING [
CursorBits, echoIndex, lostIndex, recvIndex, sendIndex, turnIndex,
packetCycle,
PtInterface, PtLookerReady, Random,
statRawPacketsEarly, statRawPacketsLate, statRawPacketsMissed,
statRawPacketsRecv, statRawPacketsSent, statRawPacketsTurnedBack,
statRawPacketsUnexpected,
SeeStorms, Done, WaitUntilDone];

PtSocket: PROGRAM [
pt: PtDefs.PtInterface]
IMPORTS InlineDefs, IODefs, ProcessDefs, BufferDefs, StatsDefs, PupDefs, PtDefs
EXPORTS PtDefs = BEGIN
OPEN
pt, IODefs, StatsDefs, PupDefs, PtDefs;

hardWay: BOOLEAN ← FALSE; -- patch via debugger

pushSock: PupSocket ← NIL;
pullSock: PupSocket ← NIL;

Header: PROCEDURE [s, t: STRING, remote: POINTER TO PupAddress] =
BEGIN
WriteString[s];
SELECT length FROM
short => WriteString[" short"];
long => WriteString[" long"];
random => WriteString[" random length"];
cyclic => WriteString[" cyclic length"];
ignore => WriteString[" unknown length"];
ENDCASE => ERROR;
WriteString[" pups"];
IF remote.host=myHost AND remote.net=myNet THEN WriteString[" locally"]
ELSE BEGIN WriteString[" "]; WriteString[t];
IF remote.net#myNet THEN BEGIN
WriteString[" net "]; WriteOctal[remote.net] END;
WriteString[" host "]; WriteOctal[remote.host]; END;
WriteLine["."];
SeeStorms[];
END;

EatUnexpectedPups: PROCEDURE [soc: PupSocket, stop: POINTER TO BOOLEAN] =
BEGIN
b: PupBuffer;
UNTIL stop↑ DO
b ← soc.get[];
IF b#NIL THEN BEGIN PrintErrorPup[b]; ReturnFreePupBuffer[b]; END;
ENDLOOP;
END;

RequeueProc: PROCEDURE [b: BufferDefs.Buffer] =
BEGIN
BufferDefs.ReturnFreeBuffer[b];
END;

Echo: PROCEDURE =
BEGIN
l: CARDINAL;
b: PupBuffer;
r: CARDINAL ← 0;
packetNumber: CARDINAL ← 0;
stop: BOOLEAN ← FALSE;
eat: PROCESS ← FORK EatUnexpectedPups[pushSock,@stop];
SELECT length FROM -- can’t use zero
short => l←1;
cyclic => l←1; -- start at short
long, ignore => l←dataWordsPerPup;
random => NULL;
ENDCASE => ERROR;
UNTIL stopFlag DO
FOR pushI IN [0..packetCycle) UNTIL stopFlag DO
b ← GetFreePupBuffer[];
SELECT length FROM
short, long, ignore => NULL;
cyclic => l←pushI;
random => l←((r←Random[r]) MOD dataWordsPerPup)+1;
ENDCASE => ERROR;
SetPupContentsWords[b,l];
b.pupID.a←b.pupID.b ← (packetNumber←packetNumber+1);
StatIncr[statDataPacketsSent];
StatBump[statDataBytesSent,b.pupLength-22];
pushSock.put[b];
StatIncr[statRawPacketsSent];
UNTIL (b←pullSock
.get[])=NIL DO
SELECT TRUE FROM
(b.pupType#data) => WriteChar[’E];
(b.source#pullHim) => WriteChar[’A];
(b.pupID.b=packetNumber) =>
BEGIN
StatIncr[statDataPacketsReceived];
StatBump[statDataBytesReceived,b.pupLength-22];
CursorBits[echoIndex] ← CursorBits[echoIndex]+1;
StatIncr[statRawPacketsRecv];
EXIT;
END;
ENDCASE => StatIncr[statRawPacketsUnexpected];
ReturnFree
PupBuffer[b];
ENDLOOP;
IF b#NIL THEN ReturnFree
PupBuffer[b]
ELSE
BEGIN
CursorBits[lostIndex] ← CursorBits[lostIndex]+1;
IF info THEN WriteChar[’?];
END;
ENDLOOP;
CursorBits[sendIndex] ← CursorBits[sendIndex]+1;
IF info THEN WriteChar[’s];
ENDLOOP;
stop ← TRUE; JOIN eat;
PupSocketDestroy[pullSock];
PupSocketDestroy[pushSock];
pushSock ← pullSock ← NIL;
Done[];
END;

pushI: CARDINAL;
-- global for debugging
Push: PROCEDURE =
BEGIN
l: CARDINAL;
b: PupBuffer;
r: CARDINAL ← 0;
packetNumber: CARDINAL ← 0;
stop: BOOLEAN ← FALSE;
eat: PROCESS ← FORK EatUnexpectedPups[pushSock,@stop];
SELECT length FROM -- can’t use zero
short => l←1;
cyclic => l←1; -- start at short
long, ignore => l←dataWordsPerPup;
random => NULL;
ENDCASE => ERROR;
UNTIL stopFlag DO
FOR pushI IN [0..packetCycle) UNTIL stopFlag DO
b ← GetFreePupBuffer[];
SELECT length FROM
short, long, ignore => NULL;
cyclic => l←pushI;
random => l←((r←Random[r]) MOD dataWordsPerPup)+1;
ENDCASE => ERROR;
SetPupContentsWords[b,l];
IF
hardWay THEN b.requeueProcedureRequeueProc;
b.pupID.
a←b.pupID.b ← (packetNumber←packetNumber+1);
StatIncr[statDataPacketsSent];
StatBump[statDataBytesSent,b.pupLength-22];
pushSock.put[b];
StatIncr[statRawPacketsSent];
CursorBits[sendIndex] ← CursorBits[sendIndex]+1;
IF
(pushI MOD 16)=0 THEN ProcessDefs.Yield[];
ENDLOOP;
IF
info THEN WriteChar[’s];
ENDLOOP;
stop ← TRUE; JOIN eat;
PupSocketDestroy[pushSock];
pushSock ← NIL;
Done[];
END;

pullI: CARDINAL;
-- global for debugging
Pull: PROCEDURE =
BEGIN
b: PupBuffer;
packetNumber: CARDINAL ← 0;
i: INTEGER;
pullI ← 1;
UNTIL stopFlag DO
UNTIL (b←pullSock
.get[])=NIL DO
SELECT TRUE FROM
(b.pupType#data) => WriteChar[’E];
(b.source#pullHim) => WriteChar[’A];
ENDCASE =>
BEGIN
i ← b
.pupID.b-(packetNumber←packetNumber+1);
StatIncr[statRawPacketsRecv];
SELECT i FROM
=
0 =>
BEGIN
StatIncr[statDataPacketsReceived];
StatBump[statDataBytesReceived,b.pupLength-22];
CursorBits[recvIndex] ← CursorBits[recvIndex]+1;
EXIT;
END;
IN (0..250] =>
BEGIN
StatBump[statRawPacketsMissed,i];
StatIncr[statRawPacketsEarly];
IF info THEN
BEGIN
WriteChar[’m];
IF i#1 THEN WriteDecimal[i];
END;
CursorBits[lostIndex] ← CursorBits[lostIndex]+CARDINAL[i];
packetNumber ← b.pupID.b;
END;
IN [-250..0) =>
BEGIN
StatIncr[statRawPacketsLate];
IF info THEN
BEGIN
IF i=-1 THEN WriteChar[’D]
ELSE BEGIN WriteChar[’L]; WriteDecimal[-i]; END;
END;
packetNumberpacketNumber-1;
END;
ENDCASE =>
BEGIN
IF info THEN
BEGIN
WriteLine[""];
WriteLine["Packet number way out of range. Resyncing."];
END;
packetNumber←b.pupID.b;
EXIT;
END;
END;
ReturnFree
PupBuffer[b]
ENDLOOP;
IF b#NIL THEN ReturnFree
PupBuffer[b]
ELSE IF info THEN WriteChar[’?];
IF (pullI←pullI+1)>packetCycle THEN
BEGIN
pullI ← 1;
IF info THEN WriteChar[’r];
END;
ENDLOOP;
PupSocketDestroy[pullSock];
pullSock ← NIL;
Done[];
END;

Turnabout: PROCEDURE =
BEGIN
packetNumber: CARDINAL ← 0;
b: PupBuffer;
i: INTEGER;
stop: BOOLEAN ← FALSE;
eat: PROCESS ← FORK EatUnexpectedPups[pushSock,@stop];
pullI ← 1;
UNTIL stopFlag DO
IF (b←pullSock.get[])#NIL THEN
BEGIN
i ← b
.pupID.b-(packetNumber←packetNumber+1);
StatIncr[statRawPacketsTurnedBack];
SELECT i FROM
=0 =>
BEGIN
StatIncr[statDataPacketsReceived];
StatBump[statDataBytesReceived,b.pupLength-22];
END;
IN (0..250] =>
BEGIN
StatBump[statRawPacketsMissed,i];
StatIncr[statRawPacketsEarly];
IF info THEN
BEGIN
WriteChar[’m];
IF i#1 THEN WriteDecimal[i];
END;
CursorBits[lostIndex] ← CursorBits[lostIndex]+CARDINAL[i];
packetNumber ← b.pupID.b;
END;
IN [-250..0) =>
BEGIN
StatIncr[statRawPacketsLate];
IF info THEN
BEGIN
IF i=-1 THEN WriteChar[’D]
ELSE BEGIN WriteChar[’L]; WriteDecimal[-i]; END;
END;
packetNumberpacketNumber-1;
END;
ENDCASE =>
BEGIN
IF info THEN
BEGIN
WriteLine[""];
WriteLine["Packet number way out of range. Resyncing."];
END;
packetNumber ← b.pupID.b;
END;
StatIncr[statDataPacketsSent];
StatBump[statDataBytesSent,b.pupLength-22];
pushSock
.put[b];
CursorBits[turnIndex] ← CursorBits[turnIndex]+1;
IF (pullI←pullI+1)>packetCycle THEN
BEGIN
pullI ← 1;
CursorBits[recvIndex] ← CursorBits[recvIndex]+1;
IF info THEN WriteChar[’t];
END;
END
ELSE IF info THEN WriteChar[’?];
ENDLOOP;
stop ← TRUE; JOIN eat;
PupSocketDestroy[pullSock];
PupSocketDestroy[pushSock];
pushSock ← pullSock ← NIL;
Done[];
END;

-- statsistics via Network - lives here because its raw packet mode

statsFork: PROCESS;
statSocket: PupSocket ← NIL;
statStop: BOOLEAN;

PtStatsOn: PUBLIC PROCEDURE =
BEGIN
statStop ← FALSE;
statSocket ← PupSocketMake[
PupTypes.statSoc,
PupTypes.fillInPupAddress,
veryLongWait];
statsFork ← FORK PtStatsSender[];
ProcessDefs.Yield[]; -- let it get started to avoid Abort BUG
END;

PtStatsOff: PUBLIC PROCEDURE =
BEGIN
statStop ← TRUE;
ProcessDefs.Abort[statsFork];
JOIN statsFork[];
PupSocketDestroy[statSocket];
statSocket ← NIL;
END;

-- use the incomming buffer to send back the desired info
PtStatsSender: PROCEDURE =
BEGIN
b: PupBuffer;
size: CARDINAL = LOOPHOLE[LAST[StatsDefs.StatCounterIndex]];
UNTIL statStop DO
IF (b←statSocket.get[ ! ProcessDefs.Aborted => EXIT])#NIL THEN
BEGIN
IF b.pupType=PupTypes.statisticsRequest THEN WriteChar[’↑] ELSE WriteChar[’←];
statSocket.setRemoteAddress[b.source];
InlineDefs.COPY[to:@b.pupWords,from:NIL,nwords:size];
b.pupType ← PupTypes.statisticsAre;
Set
PupContentsWords[b,size];
statSocket.put[b];
END;
ENDLOOP;
END;

PtSocEcho: PUBLIC PROCEDURE =
BEGIN
IF pullHim.socket=[0,0] OR pushMe.socket=[0,0] THEN
BEGIN WriteLine["Need a SocketNumber."]; RETURN; END;
Header["Echoing","from",@pushHim];
PtLookerReady[];
pushSock ← PupSocketMake[pushMe.socket,pushHim,SecondsToTocks[1]];
pullSock ← PupSocketMake[pullMe.socket,pullHim,SecondsToTocks[1]];
ProcessDefs.Detach[FORK Echo[]];
WaitUntilDone[1];
END;

PtSocPushPull: PUBLIC PROCEDURE =
BEGIN
IF pullHim.socket=[0,0] OR pushMe.socket=[0,0] THEN
BEGIN WriteLine["Need a SocketNumber."]; RETURN; END;
Header["Exchanging","with",@pushHim];
PtLookerReady[];
pushSock ← PupSocketMake[pushMe.socket,pushHim,SecondsToTocks[1]];
pullSock ← PupSocketMake[pullMe.socket,pullHim,SecondsToTocks[1]];
ProcessDefs.Detach[FORK Pull[]];
ProcessDefs.Detach[FORK Push[]];
WaitUntilDone[2];
END;

PtSocRecv: PUBLIC PROCEDURE =
BEGIN
IF pullHim.socket=[0,0] THEN
BEGIN WriteLine["Need a SocketNumber."]; RETURN; END;
Header["Receiving","from",@pullHim];
PtLookerReady[];
pullSock ← PupSocketMake[pullMe.socket,pullHim,SecondsToTocks[1]];
ProcessDefs.Detach[FORK Pull[]];
WaitUntilDone[1];
END;

PtSocSend: PUBLIC PROCEDURE =
BEGIN
IF pushMe.socket=[0,0] THEN
BEGIN WriteLine["Need a SocketNumber."]; RETURN; END;
Header["Sending","to",@pushHim];
PtLookerReady[];
pushSock ← PupSocketMake[pushMe.socket,pushHim,SecondsToTocks[1]];
ProcessDefs.Detach[FORK Push[]];
WaitUntilDone[1];
END;

PtSocTurnabout: PUBLIC PROCEDURE =
BEGIN
IF pullHim.socket=[0,0] OR pushMe.socket=[0,0] THEN
BEGIN WriteLine["Need a SocketNumber."]; RETURN; END;
Header["Turnabouting","to",@pushHim];
PtLookerReady[];
pushSock ← PupSocketMake[pushMe.socket,pushHim,SecondsToTocks[1]];
pullSock ← PupSocketMake[pullMe.socket,pullHim,SecondsToTocks[1]];
ProcessDefs.Detach[FORK Turnabout[]];
WaitUntilDone[1];
END;

-- initialization
END.