-- File: EFTPSend.mesa, Last Edit: BLyon January 16, 1981 1:49 PM DIRECTORY Process USING [SetTimeout, MsecToTicks], Storage USING [String, FreeString], 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 Process, Storage, CommUtilDefs, PupDefs EXPORTS BufferDefs, EFTPDefs SHARES BufferDefs = BEGIN OPEN PupDefs, EFTPDefs; -- EXPORTed TYPEs Network: PUBLIC TYPE = DriverDefs.Network; EFTPTimeOut: PUBLIC SIGNAL = CODE; EFTPAlreadySending: PUBLIC ERROR = CODE; EFTPNotSending: PUBLIC ERROR = CODE; EFTPTroubleSending: PUBLIC ERROR [e: EFTPAbortCode, s: STRING] = CODE; send: CONDITION; senderStop: BOOLEAN; senderFork: PROCESS; sendSocket: PupSocket ← NIL; sendHim: PupAddress; sendSeqNumber: CARDINAL; sendAbortCode: EFTPAbortCode; sendState: {acked, waitingForAck}; sendErrorText: STRING ← 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; -- send the second END to keep the receiver from dallying any more 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; -- internal procedures 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; -- kludge to get around net0 troubles 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; -- extra procedure to get lock EFTPReplyForSenderLocked: ENTRY PROCEDURE [b: PupBuffer] = BEGIN i, len: CARDINAL; 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 len ← GetPupContentsBytes[b] - 2; len ← MIN[len, 100]; sendErrorText ← Storage.String[len]; FOR i IN [0..len) DO sendErrorText[i] ← b.pupChars[i + 2]; ENDLOOP; sendErrorText.length ← len; END; END; error => IF b.errorCode = noProcessPupErrorCode THEN BEGIN sendState ← acked; NOTIFY send; sendAbortCode ← eftpRejected; IF sendErrorText = NIL THEN BEGIN len ← GetPupContentsBytes[b] - 2*(10 + 1 + 1); len ← MIN[len, 100]; sendErrorText ← Storage.String[len]; FOR i IN [0..len) DO sendErrorText[i] ← b.errorText[i]; ENDLOOP; sendErrorText.length ← len; END; END; ENDCASE; END; -- Don't call this if you have the lock. EFTPReplyForSenderLocked may need it too. EFTPKillSending: PROCEDURE = BEGIN senderStop ← TRUE; JOIN senderFork; IF sendSocket # NIL THEN PupSocketDestroy[sendSocket]; sendSocket ← NIL; IF sendErrorText # NIL THEN BEGIN Storage.FreeString[sendErrorText]; sendErrorText ← NIL; END; END; -- initialization EFTPSetSendTimeout[1000, 25]; END.