-- File:  SequinImplA.mesa
-- Last edited by Levin:   6-Jul-81 15:26:16
-- 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],
  FrameOps USING [Free],
  ProcessDefs 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],
  Storage USING [Node, Free];

SequinImplA: MONITOR LOCKS sequin.LOCK USING sequin: SequinPrivate.Handle
  IMPORTS BufferDefs, FrameOps, ProcessDefs, PupDefs, Sequin, SequinPrivate, Storage
  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 --

  MDS: MDSZone ← LOOPHOLE[@mdsZone];

  nSequins: CARDINAL ← 0;


  -- Miscellaneous Declarations --

  SequinsInUse: ERROR = CODE;


  -- Procedures exported to Sequin --
  
  SetZone: PUBLIC PROCEDURE [z: MDSZone] RETURNS [old: MDSZone] =
    BEGIN
    IF nSequins ~= 0 THEN ERROR SequinsInUse;
    old ← MDS;
    MDS ← 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 ← MDS.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];
    ProcessDefs.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];
    FrameOps.Free[sequin.closure];
    MDS.FREE[@sequin];
    nSequins ← nSequins - 1;
    END;

  -- Papier mache MDSZone over Storage interface --

  MDSZoneHandle: TYPE = POINTER TO MDSZoneObject;

  MDSZoneObject: TYPE = MACHINE DEPENDENT RECORD [
    procs (0:0..15): POINTER TO AllocProcs];

  AllocProcs: TYPE = MACHINE DEPENDENT RECORD [
    alloc (0): PROCEDURE [zone: MDSZoneHandle, size: CARDINAL] RETURNS [POINTER],
    dealloc (1): PROCEDURE [zone: MDSZoneHandle, object: POINTER]];

  mdsZone: MDSZoneObject ← [procs: @allocProcs];

  allocProcs: AllocProcs ← [alloc: Alloc, dealloc: DeAlloc];

  Alloc: PROCEDURE [zone: MDSZoneHandle, size: CARDINAL] RETURNS [POINTER] =
    {RETURN[Storage.Node[size]]};

  DeAlloc: PROCEDURE [zone: MDSZoneHandle, object: POINTER] =
    {Storage.Free[object]};

  -- 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.