-- Copyright (C) 1981, 1984  by Xerox Corporation. All rights reserved. 
-- File:  SequinImplA.mesa

-- HGM: 10-Dec-84 21:23:47
-- last edited by Hankins:  24-Jul-84 11:01:29	Klamath update (Pup changes)
-- Last edited by Levin:   6-Jul-81 16:17:08
-- Loosely derived (after extensive discussions with Wobber) 
-- from Butterfield's Sequin.mesa of August 27, 1979  2:49 PM.

DIRECTORY
  Buffer USING [
    AccessHandle, Buffer, GetBuffer, MakePool, QueueCleanup, QueueInitialize],
  Process USING [Detach],
  PupDefs USING [
    DataWordsPerPupBuffer, GetHopsToNetwork, MsToTocks, PupAddress, PupBuffer,
    PupSocketDestroy, PupSocketMake, SetPupContentsBytes, Tocks,
    UniqueLocalPupSocketID],
  PupTypes USING [maxDataWordsPerGatewayPup, PupType],
  Sequin USING [Broken],
  SequinPrivate USING [Handle, Send, SequinObject, SocketWarmer],
  Storage USING [Node, Free];

SequinImplA: MONITOR LOCKS sequin.LOCK USING sequin: SequinPrivate.Handle
  IMPORTS Buffer, Process, PupDefs, Sequin, SequinPrivate, Storage
  EXPORTS Sequin, SequinPrivate =

  BEGIN

  -- Types --

  Handle: PUBLIC TYPE = SequinPrivate.Handle;

  -- Variables exported to SequinPrivate --

  maxBytes: PUBLIC CARDINAL;
  maxAllocate: PUBLIC CARDINAL;
  bufferPool: PUBLIC Buffer.AccessHandle;

  -- 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: PupDefs.PupAddress, pupType: PupTypes.PupType]
    RETURNS [sequin: Handle] =
    BEGIN
    GetTicks: PROCEDURE RETURNS [PupDefs.Tocks] =
      BEGIN
      hops: CARDINAL ← PupDefs.GetHopsToNetwork[dest.net];
      IF hops = LAST[CARDINAL] THEN hops ← 0;  -- no route
      RETURN[[PupDefs.MsToTocks[hops * 750 + 500]]]
      END;

    sequin ← MDS.NEW[SequinPrivate.SequinObject ← [pupType: pupType]];
    Buffer.QueueInitialize[@sequin.retransmitQueue];
    Buffer.QueueInitialize[@sequin.getQueue];
    sequin.socket ← PupDefs.PupSocketMake[
      PupDefs.UniqueLocalPupSocketID[], dest, GetTicks[]];
    sequin.id.allocate ← maxAllocate;
    Process.Detach[FORK SequinPrivate.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: PupDefs.PupBuffer ← Buffer.GetBuffer[
      type: pup, aH: bufferPool, function: send];
    PupDefs.SetPupContentsBytes[buffer, 0];
    SequinPrivate.Send[sequin, buffer, destroy ! Sequin.Broken => CONTINUE];
    WaitUntilAllQuiet[sequin];
    Buffer.QueueCleanup[@sequin.retransmitQueue];
    Buffer.QueueCleanup[@sequin.getQueue];
    PupDefs.PupSocketDestroy[sequin.socket];
    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(2): 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[
        PupDefs.DataWordsPerPupBuffer[], PupTypes.maxDataWordsPerGatewayPup];
    -- 10 = leaf overhead/packet; +1 roundup; +1 for parallelism
    maxAllocate ← 511 / (maxBytes - 10) + 1 + 1;
    bufferPool ← Buffer.MakePool[send: 25, receive: 0];
    END;

  -- Main body --

  Initialize[];

  END.