<> <> <> DIRECTORY ConvertUnsafe USING [ToRope], Rope USING [ROPE], Process USING [SetTimeout, MsecToTicks], CommUtilDefs USING [CopyLong], PupDefs USING [DataWordsPerPupBuffer, GetFreePupBuffer, ReturnFreePupBuffer, GetPupContentsBytes, SetPupContentsBytes, PupAddress, PupBuffer, PupSocketID, PupSocket, PupSocketMake, PupSocketDestroy, SecondsToTocks, UniqueLocalPupSocketID], PupTypes USING [maxDataBytesPerGatewayPup], EFTPDefs USING [EFTPAbortCode], DriverDefs USING [Network], BufferDefs; EFTPSend: MONITOR IMPORTS ConvertUnsafe, Process, CommUtilDefs, PupDefs EXPORTS BufferDefs, EFTPDefs SHARES BufferDefs = BEGIN OPEN PupDefs, EFTPDefs; <> Network: PUBLIC TYPE = DriverDefs.Network; EFTPTimeOut: PUBLIC SIGNAL = CODE; EFTPAlreadySending: PUBLIC ERROR = CODE; EFTPNotSending: PUBLIC ERROR = CODE; EFTPTroubleSending: PUBLIC ERROR [e: EFTPAbortCode, s: Rope.ROPE] = CODE; send: CONDITION; senderStop: BOOLEAN; senderFork: PROCESS; sendSocket: PupSocket _ NIL; sendHim: PupAddress; sendSeqNumber: CARDINAL; sendAbortCode: EFTPAbortCode; sendState: {acked, waitingForAck}; sendErrorText: Rope.ROPE _ NIL; retransmissions: CARDINAL; dataBytesPerPup: CARDINAL; EFTPSetSendTimeout: PUBLIC PROCEDURE [ms, tries: CARDINAL] = BEGIN retransmissions _ tries; Process.SetTimeout[@send, Process.MsecToTicks[ms]]; END; EFTPOpenForSending: PUBLIC PROCEDURE [who: PupAddress, waitForAck: BOOLEAN] = BEGIN EFTPOpenCheck[who]; BEGIN ENABLE UNWIND => EFTPKillSending[]; IF waitForAck THEN EFTPOpenLocked[]; END; END; EFTPOpenCheck: ENTRY PROCEDURE [to: PupAddress] = BEGIN ENABLE UNWIND => NULL; me: PupSocketID _ UniqueLocalPupSocketID[]; IF sendSocket # NIL THEN ERROR EFTPAlreadySending; dataBytesPerPup _ MIN[ 2*DataWordsPerPupBuffer[], PupTypes.maxDataBytesPerGatewayPup]; sendHim _ to; sendSocket _ PupSocketMake[me, sendHim, SecondsToTocks[1]]; sendSeqNumber _ LAST[CARDINAL]; sendAbortCode _ eftpOK; sendState _ acked; senderStop _ FALSE; senderFork _ FORK EFTPReplyForSender[]; END; EFTPOpenLocked: ENTRY PROCEDURE = BEGIN ENABLE UNWIND => NULL; b: PupBuffer; sendState _ waitingForAck; sendSeqNumber _ sendSeqNumber + 1; UNTIL sendState = acked DO THROUGH [0..10) UNTIL sendState = acked DO b _ GetFreePupBuffer[]; b.pupType _ eData; b.pupID _ [0, sendSeqNumber]; SetPupContentsBytes[b, 0]; sendSocket.put[b]; WAIT send; -- 10*1 sec ENDLOOP; IF sendState = acked THEN EXIT; SIGNAL EFTPTimeOut; ENDLOOP; IF sendAbortCode # eftpOK THEN BEGIN ERROR EFTPTroubleSending[sendAbortCode, sendErrorText]; END; END; EFTPSendBlock: PUBLIC ENTRY PROCEDURE [p: LONG POINTER, l: CARDINAL] = BEGIN ENABLE UNWIND => NULL; n: CARDINAL; b: PupBuffer; IF sendSocket = NIL THEN ERROR EFTPNotSending; UNTIL l = 0 DO sendSeqNumber _ sendSeqNumber + 1; sendState _ waitingForAck; n _ MIN[l, dataBytesPerPup]; DO THROUGH [0..retransmissions) UNTIL sendState = acked DO b _ GetFreePupBuffer[]; b.pupType _ eData; b.pupID _ [0, sendSeqNumber]; CommUtilDefs.CopyLong[from: p, nwords: (n + 1)/2, to: @b.pupBytes]; SetPupContentsBytes[b, n]; sendSocket.put[b]; WAIT send; -- 25*1 sec ENDLOOP; IF sendAbortCode # eftpOK THEN BEGIN ERROR EFTPTroubleSending[sendAbortCode, sendErrorText]; END; IF sendState = acked THEN EXIT; SIGNAL EFTPTimeOut; ENDLOOP; p _ p + (n + 1)/2; l _ l - n; ENDLOOP; END; EFTPAbortSending: PUBLIC PROCEDURE [s: STRING] = BEGIN IF sendSocket = NIL THEN RETURN; EFTPAbortSendingLocked[s]; EFTPKillSending[]; END; EFTPAbortSendingLocked: ENTRY PROCEDURE [s: STRING] = BEGIN i, end: CARDINAL; b: PupBuffer; IF sendAbortCode = eftpOK THEN BEGIN b _ GetFreePupBuffer[]; b.pupType _ eAbort; b.pupWords[0] _ LOOPHOLE[EFTPAbortCode[eftpExternalSenderAbort]]; end _ MIN[s.length + 2, dataBytesPerPup]; FOR i _ 2, i + 1 UNTIL i = end DO b.pupChars[i] _ s[i - 2]; ENDLOOP; SetPupContentsBytes[b, end]; sendSocket.put[b]; END; END; EFTPFinishSending: PUBLIC PROCEDURE = BEGIN EFTPFinishSendingLocked[]; EFTPKillSending[]; END; EFTPFinishSendingLocked: ENTRY PROCEDURE = BEGIN ENABLE UNWIND => NULL; b: PupBuffer; IF sendSocket = NIL THEN ERROR EFTPNotSending; sendSeqNumber _ sendSeqNumber + 1; sendState _ waitingForAck; UNTIL sendState = acked DO THROUGH [0..10) UNTIL sendState = acked DO b _ GetFreePupBuffer[]; b.pupType _ eEnd; b.pupID _ [0, sendSeqNumber]; SetPupContentsBytes[b, 0]; sendSocket.put[b]; WAIT send; -- 10*1 sec ENDLOOP; IF sendAbortCode # eftpOK THEN ERROR EFTPTroubleSending[sendAbortCode, sendErrorText]; IF sendState = acked THEN EXIT; SIGNAL EFTPTimeOut; ENDLOOP; <> sendSeqNumber _ sendSeqNumber + 1; b _ GetFreePupBuffer[]; b.pupType _ eEnd; b.pupID _ [0, sendSeqNumber]; SetPupContentsBytes[b, 0]; sendSocket.put[b]; IF sendAbortCode # eftpOK THEN ERROR EFTPTroubleSending[sendAbortCode, sendErrorText]; END; <> EFTPReplyForSender: PROCEDURE = BEGIN b: PupBuffer; UNTIL senderStop DO b _ sendSocket.get[]; IF b # NIL AND (b.source = sendHim OR CheckSource[b]) THEN EFTPReplyForSenderLocked[b]; IF b # NIL THEN ReturnFreePupBuffer[b]; ENDLOOP; END; <> CheckSource: PROCEDURE [b: PupBuffer] RETURNS [BOOLEAN] = BEGIN network: Network _ b.network; IF sendHim.net = 0 THEN sendHim.net _ [network.netNumber.b]; RETURN[b.source = sendHim]; END; <> EFTPReplyForSenderLocked: ENTRY PROCEDURE [b: PupBuffer] = BEGIN SELECT b.pupType FROM eAck => IF b.pupID = [0, sendSeqNumber] THEN BEGIN sendState _ acked; NOTIFY send; END; eAbort => BEGIN sendState _ acked; NOTIFY send; sendAbortCode _ LOOPHOLE[b.pupWords[0]]; IF sendErrorText = NIL THEN BEGIN s: STRING = [100]; FOR i: CARDINAL IN [0..MIN[GetPupContentsBytes[b] - 2,s.maxlength]) DO s[i] _ b.pupChars[i + 2] ENDLOOP; sendErrorText _ ConvertUnsafe.ToRope[s]; END; END; error => IF b.errorCode = noProcessPupErrorCode THEN BEGIN sendState _ acked; NOTIFY send; sendAbortCode _ eftpRejected; IF sendErrorText = NIL THEN BEGIN s: STRING = [100]; FOR i: CARDINAL IN [0..MIN[GetPupContentsBytes[b] - 2*(10 + 1 + 1),s.maxlength]) DO s[i] _ b.errorText[i] ENDLOOP; sendErrorText _ ConvertUnsafe.ToRope[s]; END; END; ENDCASE; END; <> EFTPKillSending: PROCEDURE = BEGIN senderStop _ TRUE; JOIN senderFork; IF sendSocket # NIL THEN PupSocketDestroy[sendSocket]; sendSocket _ NIL; END; <> EFTPSetSendTimeout[1000, 25]; END.