-- File:  SequinImplA.mesa
-- Last edited by Levin:   3-Feb-82  9:29:58
-- Loosely derived (after extensive discussions with Wobber) from Butterfield's
--    Sequin.mesa of August 27, 1979  2:49 PM.

DIRECTORY
  BufferDefs USING [Buffer, PupBufferObject, QueueCleanup, QueueInitialize],
  Frame USING [Free],
  Heap USING [systemZone],
  Process USING [Detach],
  PupDefs USING [
    DataWordsPerPupBuffer, GetFreePupBuffer, GetHopsToNetwork,
    MsToTocks, PupAddress, PupBuffer, PupSocketDestroy,
    PupSocketMake, SetPupContentsBytes, Tocks,
    UniqueLocalPupSocketID],
  PupTypes USING [maxDataWordsPerGatewayPup, PupType],
  Sequin USING [Broken],
  SequinPrivate USING [
    Handle, MakeRequeueClosure, Seconds, Send, SequenceNumber, SequinID,
    SequinObject, SocketWarmer];

SequinImplA: MONITOR LOCKS sequin.LOCK USING sequin: SequinPrivate.Handle
  IMPORTS BufferDefs, Frame, Heap, Process, PupDefs, Sequin, SequinPrivate
  EXPORTS Sequin, SequinPrivate =

  BEGIN OPEN PupDefs, SequinPrivate;


  -- Types --

  Handle: PUBLIC TYPE = SequinPrivate.Handle;


  -- Variables exported to SequinPrivate --

  maxBytes: PUBLIC CARDINAL;

  maxAllocate: PUBLIC CARDINAL;


  -- Global Variables --

  zone: UNCOUNTED ZONE ← Heap.systemZone;

  nSequins: CARDINAL ← 0;


  -- Miscellaneous Declarations --

  SequinsInUse: ERROR = CODE;


  -- Procedures exported to Sequin --
  
  SetZone: PUBLIC PROCEDURE [z: UNCOUNTED ZONE] RETURNS [old: UNCOUNTED ZONE] =
    BEGIN
    IF nSequins ~= 0 THEN ERROR SequinsInUse;
    old ← zone;
    zone ← z;
    END;

  Create: PUBLIC PROCEDURE [dest: PupAddress, pupType: PupTypes.PupType]
    RETURNS [sequin: Handle] =
    BEGIN

    GetTicks: PROCEDURE RETURNS [Tocks] =
      BEGIN
      hops: CARDINAL ← GetHopsToNetwork[dest.net];
      IF hops = LAST[CARDINAL] THEN hops ← 0;  -- no route
      RETURN [[MsToTocks[hops * 750 + 500]]]
      END;

    sequin ← zone.NEW[SequinObject ← [pupType: pupType]];
    BufferDefs.QueueInitialize[@sequin.retransmitQueue];
    BufferDefs.QueueInitialize[@sequin.getQueue];
    sequin.socket ← PupSocketMake[UniqueLocalPupSocketID[], dest, GetTicks[]];
    sequin.id.allocate ← maxAllocate;
    sequin.closure ← MakeRequeueClosure[sequin];
    Process.Detach[FORK SocketWarmer[sequin]];
    nSequins ← nSequins + 1;
    END;

  Destroy: PUBLIC PROCEDURE [sequin: Handle] =
    BEGIN
    WaitUntilAllQuiet: ENTRY PROCEDURE [sequin: Handle] = INLINE
     BEGIN
     UNTIL sequin.state = destroyed DO WAIT sequin.goAhead ENDLOOP;
     UNTIL sequin.buffersToRequeue = 0 DO WAIT sequin.goAhead ENDLOOP;
     END;

    buffer: PupBuffer ← GetFreePupBuffer[];
    SetPupContentsBytes[buffer, 0];
    Send[sequin, buffer, destroy ! Sequin.Broken => CONTINUE];
    WaitUntilAllQuiet[sequin];
    BufferDefs.QueueCleanup[@sequin.retransmitQueue];
    BufferDefs.QueueCleanup[@sequin.getQueue];
    PupSocketDestroy[sequin.socket];
    Frame.Free[sequin.closure];
    zone.FREE[@sequin];
    nSequins ← nSequins - 1;
    END;

  -- Initialization --

  Initialize: PROCEDURE =
    BEGIN
    maxBytes ← 2*MIN[DataWordsPerPupBuffer[], PupTypes.maxDataWordsPerGatewayPup];
    -- 10 = leaf overhead/packet; +1 roundup; +1 for parallelism
    maxAllocate ← 511/(maxBytes-10) + 1 + 1;
    END;

  -- Main body --

  Initialize[];

  END.