-- File: TcpMgr.mesa - last edit:
-- AOF                  3-Mar-88 14:14:48
-- JAV                  4-May-87 11:59:34
-- SMA                 14-Apr-86 12:19:10
-- Copyright (C) 1985, 1986, 1987, 1988 by Xerox Corporation. All rights reserved. 

DIRECTORY
  ArpaFlags USING [doDebug],
  ArpaPort USING [AssignPort, nullPort],
  ArpaPortInternal USING [AddrMatch, BuildMasks],
  ArpaRouter USING [InternetAddress, GetAddress, unknownInternetAddress, Port],
  ArpaTypes USING [InternetAddress, Port],
  CommHeap USING [zone],
  Driver USING [Glitch],
  Environment USING [Block],
  Runtime USING [GlobalFrame, UnNew],
  TcpImpl USING [connection, start, stop],
  TcpStream USING [defaultWaitTime, FailureReason, Handle,
    infiniteWaitTime, NotifyListenStartedProc, Precedence, Security, 
    SuspendReason, WaitTime];
  
TcpMgr: MONITOR
  IMPORTS
    ArpaPort, ArpaPortInternal, ArpaRouter, CommHeap, Driver,
    Runtime, tcpStrmInst: TcpImpl, TcpStream
  EXPORTS ArpaRouter, TcpStream =
  BEGIN
  
  InternetAddress: PUBLIC <<ArpaRouter>> TYPE = ArpaTypes.InternetAddress;
  Port: PUBLIC <<ArpaRouter>> TYPE = ArpaTypes.Port;

  Instance: TYPE = LONG POINTER TO FRAME[TcpImpl];
  instance0: Instance ← NIL;
  
  
  connection: PUBLIC RECORD[
    head: ConnTableEntry,
    active: CARDINAL] ← [NIL, 0];

  ConnTableEntry: TYPE = LONG POINTER TO ConnTableObject;
  ConnTableObject: TYPE = RECORD [
    next: ConnTableEntry,
    remote: InternetAddress,
    remotePort: Port];
  
  infiniteWaitTime: PUBLIC TcpStream.WaitTime ← LAST[TcpStream.WaitTime];
  uniqueAddr: PUBLIC InternetAddress ← ArpaRouter.unknownInternetAddress;
  uniquePort: PUBLIC Port ← ArpaPort.nullPort;
    
  --Errors and glitches
  NotInConnectionTable: ERROR = CODE;
  ListenTimeout: PUBLIC SIGNAL = CODE;  
  Suspended: PUBLIC ERROR [why: TcpStream.SuspendReason] = CODE;
  Failed: PUBLIC SIGNAL [why: TcpStream.FailureReason] = CODE;
  Closing: PUBLIC SIGNAL = CODE;
  Closed: PUBLIC ERROR = CODE;

  
  ConnectionAlreadyThere: PUBLIC ENTRY PROC[
    remote: InternetAddress, remotePort: Port]
    RETURNS [BOOLEAN] =
    BEGIN
    e: ConnTableEntry;
    FOR e ← connection.head, e.next UNTIL e = NIL DO
      IF (ArpaPortInternal.AddrMatch[
        ArpaPortInternal.BuildMasks[e.remote].hostMask, e.remote, remote])
        AND (e.remotePort = remotePort) THEN
	RETURN[TRUE];
      ENDLOOP;
    RETURN[FALSE];
    END; --ConnectionAlreadyThere

  
  Destroy: PUBLIC PROC [tsH: TcpStream.Handle] =
    BEGIN
    instanceN: Instance ← GlobalFrameFromStream[tsH];
    remote: InternetAddress ← instanceN.connection.remoteAddr;
    remotePort: Port ← instanceN.connection.remotePort;
    instanceN.stop[];  --stop the instance
    UnnewInstance[instanceN];  --delete his frame, maybe
    RemoveConnection[remote, remotePort];  --remove state
    END;  --Destroy
    
    
  GlobalFrameFromStream: PUBLIC PROC [tsH: TcpStream.Handle]
    RETURNS[LONG POINTER --TO FRAME[TcpImpl]--] =
    {RETURN[LOOPHOLE[Runtime.GlobalFrame[LOOPHOLE[tsH.get]]]]};
  
  
  InsertConnection: ENTRY PROC[remote: InternetAddress, remotePort: Port] =
    BEGIN
    ENABLE UNWIND => NULL;
    e: ConnTableEntry;
    connection.active ← connection.active + 1;
    e ← CommHeap.zone.NEW[ConnTableObject ← [
      next: connection.head, remote: remote, remotePort: remotePort]];
    connection.head ← e;
    END; --InsertConnection
    
    
  Listen: PUBLIC PROC [
    localPort: ArpaRouter.Port,
    listenTimeout: TcpStream.WaitTime ← TcpStream.infiniteWaitTime,
    receiveTimeout: TcpStream.WaitTime ← TcpStream.defaultWaitTime,
    precedence: TcpStream.Precedence ← routine,
    security: TcpStream.Security ← NIL,
    options: Environment.Block ← [NIL, 0, 0],
    notifyListenerStarted: TcpStream.NotifyListenStartedProc ← NIL]
    RETURNS [tsH: TcpStream.Handle] =
    BEGIN
    me: InternetAddress ← ArpaRouter.GetAddress[];
    tsH ← Make[me, ArpaRouter.unknownInternetAddress, localPort,
      ArpaPort.nullPort, FALSE, listenTimeout, precedence, security, options, notifyListenerStarted];
    tsH.setWaitTime[receiveTimeout];
    END;  --Listen
    
  
  Make: PUBLIC PROC[local, remote: InternetAddress,
    localPort, remotePort: Port, establishConnection: BOOLEAN,
    timeout: TcpStream.WaitTime, precedence: TcpStream.Precedence,
    security: TcpStream.Security, options: Environment.Block,
    notifyListenerStarted: TcpStream.NotifyListenStartedProc ← NIL]
    RETURNS [tsH: TcpStream.Handle] =
    BEGIN
    GetInstance: ENTRY PROC = INLINE
      {IF instance0 # NIL THEN instanceN ← NEW tcpStrmInst
      ELSE instance0 ← instanceN ← tcpStrmInst};

    instanceN: Instance;
    estdRemotePort: Port;
    estdRemoteAddr: InternetAddress;
    
    GetInstance[];  --get instance's global frame

    BEGIN
    ENABLE UNWIND => {instanceN.stop[]; UnnewInstance[instanceN]};

    IF instanceN # instance0 THEN START instanceN;  --make the globals real

    IF local = ArpaRouter.unknownInternetAddress THEN 
      local ← ArpaRouter.GetAddress[];
    IF localPort = Port[0] THEN localPort ← ArpaPort.AssignPort[];
    [tsH, estdRemoteAddr, estdRemotePort] ← instanceN.start[
      local, remote, localPort, remotePort, timeout, precedence, security,
      options, establishConnection, notifyListenerStarted];
    tsH.destroy ← Destroy;  --this is not instance oriented
    InsertConnection[estdRemoteAddr, estdRemotePort];
    END;
    END;  --Make
    
    
  RemoveConnection: ENTRY PROC[remote: InternetAddress, remotePort: Port] =
    BEGIN
    e, prev: ConnTableEntry;
    e ← connection.head;
    UNTIL e = NIL DO
      IF (ArpaPortInternal.AddrMatch[
        ArpaPortInternal.BuildMasks[e.remote].hostMask, e.remote, remote])
	AND (e.remotePort = remotePort) THEN
	BEGIN
	IF e = connection.head THEN connection.head ← e.next
	ELSE prev.next ← e.next;
	CommHeap.zone.FREE[@e];
	connection.active ← connection.active - 1;
	RETURN;
	END;
      prev ← e;
      e ← e.next;
      ENDLOOP;
    IF ArpaFlags.doDebug THEN Driver.Glitch[NotInConnectionTable];
    END; --RemoveConnection
    
  UnnewInstance: ENTRY PROC [nth: Instance] = INLINE
    {IF nth = instance0 THEN instance0 ← NIL ELSE Runtime.UnNew[LOOPHOLE[nth]]};
    
  --initialization

  START tcpStrmInst;  --always start the first copy
    
  END..
  
LOG

13-Nov-85 15:12:32  SMA  Created file.
10-Jan-86 10:39:17  SMA  ALL compares via AddrMatch or AddrMismatch.
 3-Mar-88 13:59:43  AOF  Use of global start/stop procs and export ArpaPort types.