-- PacketExchangeImpl.mesa (last edited by: BLyon March 2, 1981 11:43 AM)

DIRECTORY
BufferDefs USING [Buffer, BufferAccessHandle,FreeBufferPool, MakeBufferPool, OisBuffer],
ByteBlt USING [ByteBlt],
CommunicationInternal USING [],
DriverDefs USING [DriverXmitStatus, Glitch],
Environment USING [Block],
PacketExchange USING [ErrorReason, maxBlockLength, RequestHandle, RequestObject],
Heap USING [FreeNode, MakeNode],
Inline USING [HighHalf, LowHalf],
OISCP USING [GetFreeSendOisBufferFromPool, ReturnFreeOisBuffer, unknownNetID],
OISCPTypes USING [
bytesPerExchangeHeader, bytesPerPktHeader, ExchangeID, ExchangeClientType, WaitTime],
PacketExchangeInternal USING [],
Process USING [InitializeCondition, MsecToTicks],
Router USING [FindMyHostID],
SpecialSystem USING [broadcastHostNumber, NetworkAddress, SocketNumber],
Socket USING [

Abort,
AssignNetworkAddress, ChannelAborted, ChannelHandle, Create, Delete,
GetPacket, PutPacket, SetWaitTime, TimeOut],
System USING [GetClockPulses, nullNetworkAddress];

PacketExchangeImpl: MONITOR
IMPORTS ByteBlt, DriverDefs, Heap, Inline, OISCP, Process, Router,
Socket, System
EXPORTS CommunicationInternal, PacketExchange, PacketExchangeInternal, System
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 remote entity.

NetworkAddress: PUBLIC TYPE = SpecialSystem.NetworkAddress;
ExchangeHandle: PUBLIC TYPE = LONG POINTER TO PacketExchangeObject;
PacketExchangeObject: TYPE = RECORD [
cH: Socket.ChannelHandle,
sendBufferAccessHandle: BufferDefs.BufferAccessHandle];

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

Timeout: PUBLIC SIGNAL = CODE;
Error: PUBLIC ERROR [why: PacketExchange.ErrorReason] = CODE;
IllegalUseOfRequeueProcedure: PUBLIC ERROR = CODE;

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

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

LostBuffer: 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.
SendRequestPacket: PUBLIC PROCEDURE [h: ExchangeHandle, requestB: BufferDefs.OisBuffer,
replyFilter: OISCPTypes.ExchangeClientType]
RETURNS [replyB: BufferDefs.OisBuffer] =
BEGIN

WaitSendCompleted: 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 ← System.GetClockPulses[];
exchangeID.a ← Inline.HighHalf[time];
exchangeID.b ← Inline.LowHalf[time];
requestB.ois.transCntlAndPktTp.packetType ← packetExchange;
requestB.ois.exchangeID ← exchangeID;
THROUGH [0..retryCount) DO
-- try twice
replyB ← NIL;
requestB.requeueProcedure ← NotifySendCompleted;
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 = SpecialSystem.broadcastHostNumber)
AND replyB.ois.source.socket=requestB.ois.destination.socket THEN RETURN
ELSE
BEGIN
OISCP.ReturnFreeOisBuffer[replyB];
replyB ← NIL;
END;
ENDLOOP;
-- goodCompletion
noRouteToNetwork => RETURN WITH ERROR Error[noRouteToDestination];
ENDCASE => RETURN WITH ERROR Error[hardwareProblem];
ENDLOOP;
RETURN WITH ERROR Error[IF channelAborted THEN aborted ELSE timeout];
END;
-- SendRequestPacket

SendRequest
: PUBLIC PROCEDURE [h: ExchangeHandle, remote: NetworkAddress,
requestBlk, replyBlk: Environment.Block,
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;
[] ← ByteBlt.ByteBlt[[@requestB.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
-- inside catch phrases geneated new signal and catch RESUME
SIGNAL Timeout;
RETRY;
-- caller RESUMEs => RETRY; caller does anything else => UNWIND.
END
-- 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 ← ByteBlt.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
channelAborted: 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[requestB];
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: Environment.Block,
requiredRequestType: OISCPTypes.ExchangeClientType]
RETURNS [rH: PacketExchange.RequestHandle ← NIL] =
BEGIN
blkSizeOK: BOOLEAN;
nBytes, blkByteLen: CARDINAL;
requestB: BufferDefs.OisBuffer;
requestB ← WaitForRequestPacket[h, requiredRequestType
! Error => BEGIN
IF why=timeout THEN
BEGIN
-- the compiler should love this:
SIGNAL Timeout;
RETRY;
-- caller RESUMEs => RETRY
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 ← Heap.MakeNode[n: SIZE[PacketExchange.RequestObject]];
rH↑ ← [
nBytes: ByteBlt.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 THEN 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 channelAborted THEN RETURN WITH ERROR Error[aborted];
END;
-- SendReplyPacket

-- we now own rH and we if no errors are encountered free it
SendReply: PUBLIC PROCEDURE [h: ExchangeHandle, rH: PacketExchange.RequestHandle, replyBlk: Environment.Block, 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.GetFreeSendOisBufferFromPool[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;
[] ← ByteBlt.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 [localSocket: SpecialSystem.SocketNumber,
receiveRequestCount: CARDINAL,
privateBuffers: BOOLEAN,
waitTime: OISCPTypes.WaitTime]
RETURNS [
h: ExchangeHandle] =
BEGIN
sendRequestCount: CARDINAL = 1;
me: NetworkAddress = [OISCP.unknownNetID, Router.FindMyHostID[],
localSocket];
h ← Heap.MakeNode[n: SIZE[PacketExchangeObject]];
h.sendBufferAccessHandle ←
BufferDefs.MakeBufferPool[sendRequestCount, sendRequestCount, 0, 0, FALSE];
h.cH ← Socket.Create[me
, 0, receiveRequestCount, 0, privateBuffers];
Socket.SetWaitTime[h.cH, (waitTime/retryCount)+1];
END;
-- CreateInternal

CreateRequestor: PUBLIC PROCEDURE [waitTime: OISCPTypes.WaitTime] RETURNS [h: ExchangeHandle] =
BEGIN
me: NetworkAddress ← Socket.AssignNetworkAddress[];
h ←
Create[me.socket, 2, TRUE, waitTime];
END;

CreateReplyer: PUBLIC PROCEDURE [localSocket: SpecialSystem.SocketNumber, requestCount: CARDINAL,
waitTime: OISCPTypes.WaitTime
]
RETURNS [h: ExchangeHandle] =
BEGIN
h ←
Create[localSocket, requestCount, TRUE, waitTime];
END;

Delete: PUBLIC PROCEDURE [h: ExchangeHandle] =
BEGIN
Socket.Abort[h.
cH];
BufferDefs.
FreeBufferPool[h.sendBufferAccessHandle];
Socket.Delete[h.
cH];
Heap.FreeNode[p: h];
END; -- Delete


SetWaitTime: PUBLIC PROCEDURE [h: ExchangeHandle, waitTime: OISCPTypes.WaitTime] =
BEGIN
Socket.SetWaitTime[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.