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