-- 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 [broadcastHostNumber, NetworkAddress, nullNetworkAddress, SocketNumber],
Socket USING [
Abort, AssignNetworkAddress, ChannelAborted, ChannelHandle, Create, Delete,
GetPacket, PutPacket, SetWaitTime, 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 remote 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;
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 ← ProcessorFace.GetClockPulses[];
exchangeID.a ← LOOPHOLE[time, Basics.LongNumber].highbits;
exchangeID.b ← LOOPHOLE[time, Basics.LongNumber].lowbits;
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 = NSAddress.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: 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[[@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 ← 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
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: PrincOps.ByteBltBlock,
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 ← 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 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: 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.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;
[] ← 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 [localSocket: NSAddress.SocketNumber,
receiveRequestCount: CARDINAL, privateBuffers: BOOLEAN,
waitTime: OISCPTypes.WaitTime]
RETURNS [h: ExchangeHandle] =
BEGIN
sendRequestCount: CARDINAL = 1;
me: NetworkAddress = [OISCP.unknownNetID, Router.FindMyHostID[], localSocket];
h ← NEW[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: NSAddress.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.