-- PacketExchangeImpl.mesa (last edited by: BLyon March 2, 1981 11:43 AM)
-- Last Edited by: Levin, August 9, 1983 9:18 am

DIRECTORY
Basics USING [LongNumber],
BufferDefs USING [Buffer, BufferAccessHandle,FreeBufferPool, MakeBufferPool, OisBuffer],
CommunicationInternal USING [],
DriverDefs USING [DriverXmitStatus, Glitch],
PacketExchange USING [ErrorReason, maxBlockLength, RequestHandle, RequestObject],
OISCP USING [GetFreeSendOisBufferFromPool, ReturnFreeOisBuffer, unknownNetID],
OISCPTypes USING [
bytesPerExchangeHeader, bytesPerPktHeader, ExchangeID, ExchangeClientType, WaitTime],
PacketExchangeInternal USING [],
PrincOps USING [ByteBltBlock],
PrincOpsUtils USING [ByteBlt],
Process USING [InitializeCondition, MsecToTicks],
ProcessorFace USING [GetClockPulses],
Router USING [FindMyHostID],
NSAddress USING [broadcastHostNu
mber, NetworkAddress, nullNetworkAddress, SocketNumber],
Socket USING [

A
bort, AssignNetworkAddress, ChannelAborted, ChannelHandle, Create, Delete,
GetPacket, PutPacket, SetW
aitTime, TimeOut];

PacketExchangeImpl: MONITOR
IMPORTS DriverDefs, PrincOpsUtils, OISCP, Process, ProcessorFace, Router,
Socket
EXPORTS CommunicationInternal, PacketExchange, PacketExchangeInternal
SHARES BufferDefs, PacketExchange
=
BEGIN

-- The Packet Exchange Protocol is a request/reply protocol that provides a little more
-- reliability than raw packets transmitetd from a socket, and less reliabilty than the
-- Sequenced Packet Protocol. Usually one end will provide some service at a
-- well known socket or at a network address that is available through a binding
-- process involving the Clearinghouse. The other end acquires a socket and sends a
-- request. The request will be retransmitted a number of times with the interval
-- between retransmissions based on the delay to the remot
e entity.

NetworkAddress: PUBLIC TYPE = NSAddress.NetworkAddress;
ExchangeHandle: TYPE = REF PacketExchangeObject;
PacketExchangeObject: PUBLIC TYPE = RECORD [
cH: Socket.ChannelHandle,
sendBufferAccessHandle: BufferDefs.BufferAccessHandle];

sendCompleted: CONDITION;
uniqueNetworkAddress: PUBLIC NetworkAddress ← NSAddress.nullNetworkAddress;
sendInProgress: CARDINAL = 1;
bufferSent: CARDINAL = 2;
sendAborted: CARDINAL = 3;
retryCount: CARDINAL = 2;
bytesPerExchangePacketHeader: CARDINAL = OISCPTypes.bytesPerPktHeader + OISCPTypes.bytesPerExchangeHeader;

T
imeout: PUBLIC SIGNAL = CODE;
Er
ror: PUBLIC ERROR [why: PacketExchange.ErrorReason] = CODE;
Il
legalUseOfRequeueProcedure: PUBLIC ERROR = CODE;

Ge
tFreeSendPacket: PUBLIC PROCEDURE [h: ExchangeHandle] RETURNS [BufferDefs.OisBuffer] =
BEGIN
RETURN[OISCP.GetFreeSendOisBufferFromPool[h.sendBufferAccessHandle]];
END;

No
tifySendCompleted: PRIVATE ENTRY PROCEDURE [b: BufferDefs.Buffer] =
BEGIN
b.requeueData ← bufferSent;
b.requeueProcedure ← LostBuffer;
BROADCAST sendCompleted;
END;

Lo
stBuffer: PRIVATE PROCEDURE [b: BufferDefs.Buffer] =
BEGIN
DriverDefs.Glitch[IllegalUseOfRequeueProcedure];
END;

--
Caller has responsibility for returning requestB ,
-- and replyB if not NIL. The pktLength, destination and exchangeType must be set
-- by the caller.
Se
ndRequestPacket: PUBLIC PROCEDURE [h: ExchangeHandle, requestB: BufferDefs.OisBuffer,
replyFilter: OISCPTypes.ExchangeClientType]
RETURNS [replyB: BufferDefs.OisBuffer] =
BEGIN

Wa
itSendCompleted: ENTRY PROCEDURE [waitBuffer: BufferDefs.OisBuffer] RETURNS [DriverDefs.DriverXmitStatus] =
INLINE BEGIN
WHILE (waitBuffer.requeueData=sendInProgress) DO
WAIT sendCompleted;
ENDLOOP;
RETURN[LOOPHOLE[waitBuffer.status]];
END;

channelAborted: BOOLEAN ← FALSE;
exchangeID: OISCPTypes.ExchangeID;
time: LONG CARDINAL ← ProcessorFace.GetClockPulses[];
exchangeID.a ← LOOPHOLE[time, Basics.LongNumber].highbits;
exchangeID.b ← LOOPHOLE[time, Basics.LongNumber].lowbits;
requestB.ois.transCntlAndPktTp.packetType ← packetExchange;
requestB.ois.exchan
geID ← exchangeID;
THROUGH [0..retryCount) DO -- try twice
replyB ← NIL;
requestB.requeueProcedure ← NotifySendComple
ted;
requestB.requeueData ← sendInProgress;
Socket.PutPacket[h.cH, requestB ! Socket.ChannelAborted =>
BEGIN
requestB.requeueData ← sendAborted;
channelAborted ← TRUE;
EXIT;
END];
SELECT WaitSendCompleted[requestB] FROM
goodCompletion => DO -- do gets until satisfied or timed out
replyB ← Socket.GetPacket[h.cH !
Socket.TimeOut => EXIT;
Socket.ChannelAborted =>
BEGIN
channelAborted ← TRUE;
EXIT;
END];
IF replyB.ois.transCntlAndPktTp.packetType=packetExchange
AND replyB.ois.exchangeID=exchangeID
AND ((replyB.ois.exchangeType=replyFilter) OR (replyFilter=unspecified))
AND (replyB.ois.source.host = requestB.ois.destination.host OR
requestB.ois.destination.host = NSAddress.broadcastHostNumber)
AND replyB.ois.source.socket=requestB.ois.destination.socket THEN RETURN
ELSE
BEGIN
OISCP.ReturnFreeOisBuffer[replyB];
re
plyB ← NIL;
END;
ENDLOOP; -- goodCompletion
noRouteToNetwork => RETURN WITH ERROR Error[noRouteToDestination];
ENDCASE => RETURN WITH ERROR Error[hardwareProblem];
ENDLOOP;
RETURN WITH ERROR Error[IF channe
lAborted THEN aborted ELSE timeout];
END; -- SendRequestPacket

SendRequest: PUBLIC PROCEDURE [h: ExchangeHandle, remote: NetworkAddress,
requestBlk, replyBlk: PrincOps.ByteBltBlock,
requestType: OISCPTypes.ExchangeClientType]
RETURNS [nBytes: CARDINAL, replyType: OISCPTypes.ExchangeClientType] =
BEGIN
blkSizeOK: BOOLEAN;
requestB, replyB: BufferDefs.OisBuffer;
blkByteLen: CARDINAL ← requestBlk.stopIndexPlusOne-requestBlk.startIndex;
IF blkByteLen>PacketExchange.maxBlockLength THEN RETURN WITH ERROR Error[blockTooBig];
requestB ← OISCP.GetFreeSendOisBufferFromPool[h.sendBufferAccessHandle];
-- move all info into the requestB
requestB.ois.pktLength ← bytesPerExchangePacketHeader + blkByteLen;
requestB.ois.transCntlAndPktTp.packetType ← packetExchange;
requestB.ois.destination ← remote;
requestB.ois.exchangeType ← requestType;
[] ← PrincOpsUtils.ByteBlt[[@requ
estB.ois.exchangeBody, 0, blkByteLen], requestBlk];

-- send the buffer (many times in
case of timeouts) and extract info from reply buffer.
BEGIN
ENABLE UNWIND => OISCP.ReturnFreeOisBuffer[requestB];
replyB ← SendRequestPacket[h, requestB, unspecified
! Error => BEGIN
IF why=timeout THEN
BEGIN
-- insid
e catch phrases geneated new signal and catch RESUME
SIGNAL Timeout;
RETRY; -- caller RE
SUMEs => RETRY; caller does anything else => UNWIND.
E
ND
-- all other error shot through to the client;
END];
END;

OISCP.ReturnFreeOisBuffer[requestB];
replyType ← replyB.ois.exchangeType;
nBytes ← replyB.ois.pktLength - bytesPerExchangePacketHeader;
blkByteLen ← replyBlk.stopIndexPlusOne - replyBlk.startIndex;
IF blkSizeOK ← ((nBytes<=blkByteLen)
AND (replyBlk.startIndex<=replyBlk.stopIndexPlusOne))
THEN nBytes ← PrincOpsUtils.ByteBlt[replyBlk, [@replyB.ois.exchangeBody, 0, blkByteLen]];
OISCP.ReturnFreeOisBuffer[replyB];
IF NOT blkSizeOK THEN
BEGIN
nBytes ← 0;
RETURN WITH ERROR Error[blockTooSmall];
END;
END; -- SendRequest

WaitForRequestPacket: PUBLIC PROCEDURE [h: ExchangeHandle, request: OISCPTypes.ExchangeClientType] RETURNS [requestB: BufferDefs.OisBuffer] =
BEGIN
ch
annelAborted: BOOLEAN ← FALSE;
THROUGH [0..retryCount) DO -- try twice
requestB ← NIL;
DO -- do gets until satisfied or timed out
requestB ← Socket.GetPacket[h.cH !
Socket.TimeOut => EXIT;
Socket.ChannelAborted =>
BEGIN
channelAborted ← TRUE;
EXIT;
END];
IF requestB.ois.transCntlAndPktTp.packetType=packetExchange
AND ((requestB.ois.exchangeType=request) OR (request=unspecified)) THEN RETURN
ELSE
BEGIN
OISCP.ReturnFreeOisBuffer[req
uestB];
requestB ← NIL;
END;
ENDLOOP; -- get loop
ENDLOOP;
RETURN WITH ERROR
Error[IF channelAborted THEN aborted ELSE timeout];
END; -- WaitForRequestPacket

WaitForRequest: PUBLIC PROCEDURE [h: ExchangeHandle, requestBlk: PrincOps.ByteBltBlock,
requiredRequestType: OISCPTypes.ExchangeClientType]
RETURNS [rH: PacketExchange.RequestHandle ← NIL] =
BEGIN
blkSizeOK: BOOLEAN;
nBytes, blkByteLen: CARDINAL;
requestB: BufferDefs.OisBuffer;
requestB ← WaitForRequestPacket[h, requiredReques
tType
! Error => BEGIN
IF why=t
imeout THEN
BEGIN
-- th
e compiler should love this:
SIGNA
L Timeout;
RETRY; -- caller RESUMEs => RETR
Y
END;
-- all other error shot through to the client;
END];
nBytes ← requestB.ois.pktLength - bytesPerExchangePacketHeader;
blkByteLen ← requestBlk.stopIndexPlusOne - requestBlk.startIndex;
IF blkSizeOK ← ((nBytes<=blkByteLen)
AND (requestBlk.startIndex<=requestBlk.stopIndexPlusOne)) THEN
BEGIN
rH ← NEW[PacketExchange.RequestObject];
rH↑ ← [
nBytes: PrincOpsUtils.ByteBlt[requestBlk, [@requestB.ois.exchangeBody, 0, nBytes]],
requestType: requestB.ois.exchangeType,
requestersAddress: requestB.ois.source,
requestersExchangeID: requestB.ois.exchangeID];
END;
OISCP.ReturnFreeOisBuffer[requestB];
IF NOT blkSizeOK T
HEN RETURN WITH ERROR Error[blockTooSmall];
END; -- WaitForRequest

-- The length must
be set by the caller. We now own buffer replyB.
SendReplyPacket: PUBLIC PROCEDURE [h: ExchangeHandle, replyB: BufferDefs.OisBuffer] =
BEGIN
channelAborted: BOOLEAN ← FALSE;
Socket.PutPacket[h.cH, replyB! Socket.ChannelAborted =>
BEGIN
replyB.requeueProcedure[replyB];
channelAborted ← TRUE;
CONTINUE;
END];
IF channelAbo
rted THEN RETURN WITH ERROR Error[aborted];
END; -- SendReplyPacket

-- we now o
wn rH and we if no errors are encountered free it
SendReply: PUBLIC PROCEDURE [h: ExchangeHandle, rH: PacketExchange.RequestHandle, replyBlk: PrincOps.ByteBltBlock, replyType: OISCPTypes.ExchangeClientType] =
BEGIN
replyB: BufferDefs.OisBuffer;
blkByteLen: CARDINAL ← replyBlk.stopIndexPlusOne-replyBlk.startIndex;
IF blkByteLen>PacketExchange.maxBlockLength THEN RETURN WITH ERROR Error[blockTooBig];
replyB ← OISCP.Ge
tFreeSendOisBufferFromPool[h.sendBufferAccessHandle];
-- move all info into the replyB
replyB.ois.pktLength ← bytesPerExchangePacketHeader + blkByteLen;
replyB.ois.transCntlAndPktTp.packetType ← packetExchange;
replyB.ois.destination ← rH.requestersAddress;
replyB.ois.exchangeType ← replyType;
replyB.ois.exchangeID ← rH.requestersExchangeID;
[] ← PrincOpsUtils.ByteBlt[[@replyB.ois.exchangeBody, 0, blkByteLen], replyBlk];
SendReplyPacket[h, replyB
! Error => IF why=aborted THEN OISCP.
ReturnFreeOisBuffer[replyB]];
--Heap.FreeNode[p: rH];--
END; -- SendReply

Create: PUBLIC PROCEDURE [localSocke
t: NSAddress.SocketNumber,
receiveRequestCount: CARDINAL, privateBuffers: BOOLEAN,
waitTime: OISCPTypes.WaitTime]
RETURN
S [h: ExchangeHandle] =
BEGIN
sendRequestCount: CARDINAL = 1;
me: N
etworkAddress = [OISCP.unknownNetID, Router.FindMyHostID[], localSocket];
h ← NEW[PacketExcha
ngeObject];
h.sendBuffer
AccessHandle ← BufferDefs.MakeBufferPool[sendRequestCount, sendRequestCount, 0, 0, FALSE];
h.cH ← Sock
et.Create[me, 0, receiveRequestCount, 0, privateBuffers];
Socket.SetWaitTi
me[h.cH, (waitTime/retryCount)+1];
END; -- CreateInternal

CreateRequestor: PUBLIC P
ROCEDURE [waitTime: OISCPTypes.WaitTime] RETURNS [h: ExchangeHandle] =
BEGIN
me: NetworkAddres
s ← Socket.AssignNetworkAddress[];
h ← Creat
e[me.socket, 2, TRUE, waitTime];
END;

Creat
eReplyer: PUBLIC PROCEDURE [localSocket: NSAddress.SocketNumber, requestCount: CARDINAL,
waitT
ime: OISCPTypes.WaitTime]
RETURNS
[h: ExchangeHandle] =
BEGIN
h
← Create[localSocket, requestCount, TRUE, waitTime];
END;

Delete
: PUBLIC PROCEDURE [h: ExchangeHandle] =
BEGI
N
Socket.Abo
rt[h.cH];
BufferDefs.Fr
eeBufferPool[h.sendBufferAccessHandle];
Socket.
Delete[h.cH];
--Heap.FreeNode[p: h];--
END; --
Delete


SetWaitTime: PUBLIC PROCEDURE [h: Exch
angeHandle, waitTime: OISCPTypes.WaitTime] =
BEGIN
Socket.Set
WaitTime[h.cH, (waitTime/retryCount)+1];
END;
-- SetWaitTime

--
MainLine Code
Process.InitializeCondition[
@sendCompleted, Process.MsecToTicks[2000]];

END.

LOG

Time: November 10, 1980 9:57 AM By: Dalal Action: created file.
Time: January 13, 1981 6:06 PM By: BLyon Action: Moved to Pilot Communication.