-- File: PupPktMgr.mesa,  Last Edit: HGM  March 14, 1981  11:43 AM

DIRECTORY
  Process USING [
    InitializeCondition, InitializeMonitor, DisableTimeout, SetTimeout,
    MsecToTicks],
  Storage USING [FreeString],
  System USING [Pulses, MicrosecondsToPulses],
  CommUtilDefs USING [LockCode, EnableAborts, UnlockCode],
  PupPktOps,
  PupStream USING [CloseReason, PupOpenMode],
  PupPktDefs USING [PupPktStream],
  PupDefs USING [
    Pair, defaultPupsToAllocate, DataWordsPerPupBuffer, PupAddress, PupBuffer,
    PupSocketDestroy, PupSocketKick, PupSocketID,
    Tocks, veryLongWait, veryShortWait],
  BufferDefs USING [QueueInitialize, QueueCleanup],
  PupTypes USING [fillInSocketID];

PupPktMgr: MONITOR
  IMPORTS Process, Storage, System, CommUtilDefs, PupPktOps, PupDefs, BufferDefs
  EXPORTS PupPktOps, PupStream, PupPktDefs =
  BEGIN OPEN PupPktOps, PupPktDefs, PupDefs;

  -- Manager data
  free: Instance ← START PupPktOps.PupPktHot;

  GetInstance: ENTRY PROCEDURE RETURNS [him: Instance] =
    BEGIN
    IF free = NIL THEN him ← START (NEW PupPktOps.PupPktHot)
    ELSE
      BEGIN
      him ← free;
      free ← free.next;
      END;
    InitializeEverything[him];
    IF lockRequeueProcedure THEN CommUtilDefs.LockCode[him.me.get];  -- PupPktHot
    END;

  FreeInstance: ENTRY PROCEDURE [him: Instance] =
    BEGIN
    IF lockRequeueProcedure THEN CommUtilDefs.UnlockCode[him.me.get];  -- PupPktHot
    him.next ← free;
    free ← him;
    END;

  lockRequeueProcedure: BOOLEAN ← FALSE;

  myPing: BOOLEAN ← TRUE;
  myMaxAllocate, myPathMaxAllocate: CARDINAL ← defaultPupsToAllocate;
  myMaxBufferSize: CARDINAL ← 0;

  NoBufferToSend: PUBLIC ERROR = CODE;
  StreamAlreadyOpen: PUBLIC ERROR = CODE;

  maxRetransmitPulses: PUBLIC System.Pulses ← System.MicrosecondsToPulses[
    5000000];
  minRetransmitPulses: PUBLIC System.Pulses ← System.MicrosecondsToPulses[100000];
  initialRetransmitPulses: PUBLIC System.Pulses ← System.MicrosecondsToPulses[
    5000000];
  ctlRetransmitPulses: PUBLIC System.Pulses ← System.MicrosecondsToPulses[
    2000000];
  pingPulses: PUBLIC System.Pulses ← System.MicrosecondsToPulses[60000000];

  SetLockRequeueProcedureFlag: PUBLIC PROCEDURE [lockit: BOOLEAN] =
    BEGIN
    lockRequeueProcedure ← lockit;
    END;

  PupPktStreamCreate: PUBLIC PROCEDURE [remote: PupAddress, ticks: Tocks]
    RETURNS [PupPktStream] =
    BEGIN
    RETURN[
      PupPktStreamMake[PupTypes.fillInSocketID, remote, ticks, sendRfc, [0, 0]]];
    END;

  PupPktStreamMake: PUBLIC PROCEDURE [
    local: PupSocketID, remote: PupAddress, ticks: Tocks,
    mode: PupStream.PupOpenMode, id: Pair] RETURNS [PupPktStream] =
    BEGIN
    him: Instance ← GetInstance[];
    SELECT ticks FROM
      veryShortWait => him.dontWait ← TRUE;
      veryLongWait => Process.DisableTimeout[@him.inputReady];
      ENDCASE => Process.SetTimeout[@him.inputReady, ticks];
    MakeLocal[
      him, local, remote, mode, id ! UNWIND => PupPktStreamDestroy[@him.me]];
    RETURN[@him.me];
    END;

  PupPktStreamDestroy: PUBLIC PROCEDURE [ps: PupPktStream] =
    BEGIN
    krock: Instance = LOOPHOLE[1234];
    offset: INTEGER = @krock.me - LOOPHOLE[krock, POINTER];
    him: Instance ← LOOPHOLE[ps - offset];
    DestroyLocal[him];
    FreeInstance[him];
    END;

  SetMaxAllocation: PUBLIC PROCEDURE [n: CARDINAL] =
    BEGIN
    myMaxAllocate ← n;
    myPathMaxAllocate ← MIN[defaultPupsToAllocate, n];
    END;

  SetMaxBufferSize: PUBLIC PROCEDURE [n: CARDINAL] =
    BEGIN myMaxBufferSize ← 2*MIN[n, DataWordsPerPupBuffer[]]; END;

  SetPinging: PUBLIC PROCEDURE [ping: BOOLEAN] = BEGIN myPing ← ping; END;

  InitializeEverything: PROCEDURE [him: Instance] =
    BEGIN
    him.state ← idle;
    him.c ← NIL;
    him.dontWait ← FALSE;
    him.dataBytesPerPup ← 2*DataWordsPerPupBuffer[];
    him.outIntPending ← FALSE;
    him.outEnd ← 0;
    him.probeCounter ← 0;
    him.ping ← myPing;
    him.myMaxAllocate ← myMaxAllocate;
    him.pathMaxAllocate ← myPathMaxAllocate;
    him.hisMaxAllocate ← 0;
    him.throttle ← 0;
    him.unackedPups ← 0;
    him.allocatedPups ← 0;
    him.clumpsSinceBump ← 0;
    him.sentBuffer ← NIL;
    him.pleaseDie ← FALSE;
    him.sameNet ← FALSE;
    him.sendAck ← FALSE;
    him.aDataOut ← FALSE;
    him.retransmitPulses ← initialRetransmitPulses;
    him.whyClosed ← localClose;
    him.text ← NIL;
    him.next ← NIL;
    BufferDefs.QueueInitialize[@him.inputQueue];
    BufferDefs.QueueInitialize[@him.sentQueue];
    BEGIN OPEN Process;
    InitializeCondition[@him.stateChange, MsecToTicks[1000]];
    InitializeCondition[@him.retransmitterReady, MsecToTicks[100]];
    InitializeCondition[@him.inputReady, MsecToTicks[5000]];
    InitializeCondition[@him.waitingForInterrupt, MsecToTicks[5000]];
    DisableTimeout[@him.waitingForInterrupt];
    CommUtilDefs.EnableAborts[@him.waitingForInterrupt];
    InitializeMonitor[@him.lock];
    END;
    IF myMaxBufferSize # 0 THEN him.dataBytesPerPup ← myMaxBufferSize;
    END;

  DestroyLocal: PROCEDURE [him: Instance] =
    BEGIN OPEN him;
    DestroyLocalLocked[him];
    JOIN retransmitterFork;
    PupSocketKick[socket];
    JOIN slurpFork;
    -- IF sentBuffer#NIL THEN ReturnFreePupBuffer[sentBuffer];
    BufferDefs.QueueCleanup[@inputQueue];
    BufferDefs.QueueCleanup[@sentQueue];
    PupSocketDestroy[socket];
    IF text # NIL THEN Storage.FreeString[text];
    END;

  free.next ← NIL;
  END.