EFTPImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Hal Murray, June 3, 1986 2:43:48 pm PDT
DIRECTORY
Endian USING [FFromInt, FWORD],
Rope USING [ROPE],
PrincOpsUtils USING [ByteBlt],
Process USING [SetTimeout, MsecToTicks],
Pup USING [Address],
PupBuffer USING [maxDataBytes],
PupHop USING [InitialTimeout],
PupSocket USING [AllocBuffer, AppendRope, Buffer, CreateEphemeral, Destroy, ExtractAbortRope, FreeBuffer, Get, GetRemoteAddress, Put, SetGetTimeout, SetUserBytes, SetUserHWords, Socket],
EFTP USING [AbortCode, Milliseconds];
EFTPImpl:
CEDAR
MONITOR
LOCKS handle
USING handle: Handle
IMPORTS Endian, PrincOpsUtils, Process, PupHop, PupSocket
EXPORTS EFTP = {
Handle: TYPE = REF Object;
Object:
PUBLIC
TYPE =
MONITORED
RECORD [
mode: {send, recv, dead},
sequenceNumber: INT,
socket: PupSocket.Socket,
buffer: PupSocket.Buffer,
tries: NAT,
code: EFTP.AbortCode,
err: Rope.ROPE,
cond: CONDITION ];
Errors
BlockTooBig: PUBLIC ERROR = CODE;
InvalidCallSequence: PUBLIC ERROR = CODE;
Timeout: PUBLIC SIGNAL = CODE;
Trouble: PUBLIC ERROR [code: EFTP.AbortCode, err: Rope.ROPE] = CODE;
Create/Delete
CreateSender:
PUBLIC
PROC [him: Pup.Address, waitForAck:
BOOL]
RETURNS [Handle] = {
timeout: INT ← PupHop.InitialTimeout[him.net, 1000];
handle: Handle ← NEW[Object];
handle.mode ← send;
handle.sequenceNumber ← 0;
handle.socket ← PupSocket.CreateEphemeral[
remote: him,
sendBuffers: 1,
recvBuffers: 1,
getTimeout: timeout ];
handle.buffer ← PupSocket.AllocBuffer[handle.socket];
handle.tries ← 10;
handle.code ← ok;
handle.err ← NIL;
TRUSTED { Process.SetTimeout[@handle.cond, Process.MsecToTicks[timeout]]; };
IF waitForAck THEN PutBlock[handle, ""];
RETURN[handle];
};
CreateReceiver:
PUBLIC
PROC [me: Pup.Address]
RETURNS [Handle] = {
handle: Handle ← NEW[Object];
handle.mode ← recv;
RETURN[handle];
};
GetHisAddress:
PUBLIC
PROC [handle: Handle]
RETURNS [Pup.Address] = {
IF handle.socket = NIL THEN ERROR InvalidCallSequence;
RETURN[PupSocket.GetRemoteAddress[handle.socket]];
};
Abort:
PUBLIC
PROC [handle: Handle, rope: Rope.
ROPE] = {
ENABLE UNWIND => NULL;
IF handle.err # NIL THEN ERROR Trouble[handle.code, handle.err];
SELECT handle.mode
FROM
send => {
IF handle.buffer = NIL THEN handle.buffer ← PupSocket.AllocBuffer[handle.socket];
handle.code ← externalSenderAbort;
handle.err ← rope;
handle.buffer.type ← eAbort;
handle.buffer.id ← Endian.FFromInt[handle.sequenceNumber];
handle.buffer.hWord[0] ← handle.code.ORD;
PupSocket.SetUserHWords[handle.buffer, 1];
PupSocket.AppendRope[handle.buffer, handle.err];
PupSocket.Put[handle.socket, handle.buffer];
handle.buffer ← NIL; };
recv => ERROR; -- ???
dead => RETURN;
ENDCASE => ERROR;
};
Finish:
PUBLIC
ENTRY
PROC [handle: Handle] = {
ENABLE UNWIND => NULL;
IF handle.err # NIL THEN ERROR Trouble[handle.code, handle.err];
SELECT handle.mode
FROM
send => {
id: Endian.FWORD ← Endian.FFromInt[handle.sequenceNumber];
DO
FOR i:
NAT
IN [0..handle.tries)
DO
IF handle.buffer = NIL THEN handle.buffer ← PupSocket.AllocBuffer[handle.socket];
handle.buffer.type ← eEnd;
handle.buffer.id ← id;
PupSocket.SetUserBytes[handle.buffer, 0];
PupSocket.Put[handle.socket, handle.buffer];
handle.buffer ← NIL;
handle.buffer ← PupSocket.Get[handle.socket];
IF handle.buffer = NIL THEN LOOP;
SELECT handle.buffer.type
FROM
eAck => IF handle.buffer.id = id THEN GOTO Done;
eAbort =>
{
handle.code ← VAL[handle.buffer.hWord[0]];
handle.err ← PupSocket.ExtractAbortRope[handle.buffer];
EFTP Abort packet happens to be the same format as a BSP Abort.
ERROR Trouble[handle.code, handle.err]; };
ENDCASE => NULL;
ENDLOOP;
SIGNAL Timeout;
REPEAT Done => NULL;
ENDLOOP; };
recv => ERROR; -- ???
dead => RETURN;
ENDCASE => ERROR;
IF handle.buffer # NIL THEN PupSocket.FreeBuffer[handle.buffer];
handle.buffer ← NIL;
PupSocket.Destroy[handle.socket];
handle.socket ← NIL;
handle.err ← NIL;
handle.mode ← dead;
};
Sending
SetSendTimeout:
PUBLIC
PROC [handle: Handle, milliseconds:
EFTP.Milliseconds, tries:
NAT] = {
ENABLE UNWIND => NULL;
SELECT handle.mode
FROM
send => {
TRUSTED { Process.SetTimeout[@handle.cond, Process.MsecToTicks[milliseconds]]; };
PupSocket.SetGetTimeout[handle.socket, milliseconds];
handle.tries ← tries; };
recv => ERROR InvalidCallSequence;
dead => ERROR InvalidCallSequence;
ENDCASE => ERROR;
};
PutBlock:
PUBLIC
ENTRY
PROC [handle: Handle, text:
REF
TEXT] = {
ENABLE UNWIND => NULL;
IF handle.err # NIL THEN ERROR Trouble[handle.code, handle.err];
SELECT handle.mode
FROM
send => {
id: Endian.FWORD ← Endian.FFromInt[handle.sequenceNumber];
DO
FOR i:
NAT
IN [0..handle.tries)
DO
IF handle.buffer = NIL THEN handle.buffer ← PupSocket.AllocBuffer[handle.socket];
handle.buffer.type ← eData;
handle.buffer.id ← id;
IF text.length > PupBuffer.maxDataBytes THEN ERROR BlockTooBig;
TRUSTED {
base: LONG POINTER = LOOPHOLE[text, LONG POINTER]+TEXT[0].SIZE;
[] ← PrincOpsUtils.ByteBlt[
to: [
blockPointer: @handle.buffer.byte,
startIndex: 0,
stopIndexPlusOne: text.length],
from: [
blockPointer: base,
startIndex: 0,
stopIndexPlusOne: text.length] ]; };
PupSocket.SetUserBytes[handle.buffer, text.length];
PupSocket.Put[handle.socket, handle.buffer];
handle.buffer ← NIL;
handle.buffer ← PupSocket.Get[handle.socket];
IF handle.buffer = NIL THEN LOOP;
SELECT handle.buffer.type
FROM
eAck => IF handle.buffer.id = id THEN GOTO Done;
eAbort =>
{
handle.code ← VAL[handle.buffer.hWord[0]];
handle.err ← PupSocket.ExtractAbortRope[handle.buffer];
EFTP Abort packet happens to be the same format as a BSP Abort.
ERROR Trouble[handle.code, handle.err]; };
ENDCASE => NULL;
ENDLOOP;
SIGNAL Timeout;
REPEAT Done => NULL;
ENDLOOP; };
recv => ERROR InvalidCallSequence;
dead => ERROR InvalidCallSequence;
ENDCASE => ERROR;
IF handle.buffer # NIL THEN PupSocket.FreeBuffer[handle.buffer];
handle.buffer ← NIL;
handle.sequenceNumber ← handle.sequenceNumber.SUCC;
};
}.