-- File: PupSockets.mesa,  Last Edit: HGM  March 14, 1981  11:50 AM

DIRECTORY
  Process USING [SetTimeout, DisableTimeout],
  Runtime USING [GlobalFrame],
  CommFlags USING [doDebug],
  CommUtilDefs USING [EnableAborts, GetReturnFrame],
  PupRouterDefs USING [
    PupRouterForgetAboutSocket, PupRouterKnowAboutSocket, routerLock,
    PupRouterSocketObject],
  PupDefs USING [
    GetLocalPupAddress, DequeuePup, PupAddress, PupBuffer, PupRouterSendThis,
    PupSocket, PupSocketID, Pair, PupSocketObject, Tocks, veryLongWait,
    veryShortWait],
  BufferDefs,
  PupTypes;

PupSockets: MONITOR LOCKS PupRouterDefs.routerLock
  IMPORTS Process, Runtime, CommUtilDefs, PupRouterDefs, PupDefs
  EXPORTS PupRouterDefs, PupDefs
  SHARES BufferDefs =
  BEGIN OPEN PupRouterDefs, PupDefs;

  -- Manager data
  free: POINTER TO FRAME[PupSockets] ← LOOPHOLE[Runtime.GlobalFrame[
    PupSocketMake]];

  GetInstance: ENTRY PROCEDURE RETURNS [him: POINTER TO FRAME[PupSockets]] =
    BEGIN
    IF free = NIL THEN
      BEGIN
      him ← NEW PupSockets;
      START him; -- Do initialization code
      RETURN;
      END;
    him ← free;
    free ← free.next;
    END;

  FreeInstance: ENTRY PROCEDURE [him: POINTER TO FRAME[PupSockets]] =
    BEGIN him.next ← free; free ← him; END;


  next: POINTER TO FRAME[PupSockets] ← NIL;

  myPupSocket: PupSocketObject ←
    [ -- data for the client
      put: Put, get: Get, setRemoteAddress: SetRemoteAddress,
      getLocalAddress: GetLocalAddress];

  dontWait, kick: BOOLEAN ← FALSE;

  myPupRouterSocket: PupRouterSocketObject;

  PupSocketMake: PUBLIC PROCEDURE [
    local: PupSocketID, remote: PupAddress, ticks: Tocks, id: Pair]
    RETURNS [PupSocket] =
    BEGIN
    him: POINTER TO FRAME[PupSockets] ← GetInstance[];
    him.myPupRouterSocket.local ← GetLocalPupAddress[local, @remote];
    him.myPupRouterSocket.remote ← remote;
    him.myPupRouterSocket.id ← id;
    him.kick ← FALSE;
    PupRouterKnowAboutSocket[@him.myPupRouterSocket];
    SELECT ticks FROM
      veryShortWait => dontWait ← TRUE;
      veryLongWait => Process.DisableTimeout[@him.myPupRouterSocket.ready];
      ENDCASE => Process.SetTimeout[@him.myPupRouterSocket.ready, ticks];
    CommUtilDefs.EnableAborts[@him.myPupRouterSocket.ready];
    RETURN[@him.myPupSocket];
    END;

  PupSocketMakeFull: PUBLIC PROCEDURE [local, remote: PupAddress, ticks: Tocks]
    RETURNS [PupSocket] =
    BEGIN
    him: POINTER TO FRAME[PupSockets] ← GetInstance[];
    -- We need to check local to verify that it is valid
    him.myPupRouterSocket.local ← local;
    him.myPupRouterSocket.remote ← remote;
    him.myPupRouterSocket.id ← [0,0];
    him.kick ← FALSE;
    PupRouterKnowAboutSocket[@him.myPupRouterSocket];
    SELECT ticks FROM
      veryShortWait => dontWait ← TRUE;
      veryLongWait => Process.DisableTimeout[@him.myPupRouterSocket.ready];
      ENDCASE => Process.SetTimeout[@him.myPupRouterSocket.ready, ticks];
    CommUtilDefs.EnableAborts[@him.myPupRouterSocket.ready];
    RETURN[@him.myPupSocket];
    END;

  PupSocketKick: PUBLIC ENTRY PROCEDURE [s: PupSocket] =
    BEGIN
    him: POINTER TO FRAME[PupSockets] ← LOOPHOLE[Runtime.GlobalFrame[s.put]];
    kick ← TRUE;
    NOTIFY him.myPupRouterSocket.ready;
    END;

  PupSocketDestroy: PUBLIC PROCEDURE [s: PupSocket] =
    BEGIN
    him: POINTER TO FRAME[PupSockets] ← LOOPHOLE[Runtime.GlobalFrame[s.put]];
    PupRouterForgetAboutSocket[@him.myPupRouterSocket];
    FreeInstance[him];
    END;

  Get: ENTRY PROCEDURE RETURNS [b: PupBuffer] =
    BEGIN
    IF ~dontWait AND myPupRouterSocket.input.length = 0 THEN
      BEGIN
      IF ~kick THEN WAIT myPupRouterSocket.ready [ ! UNWIND => NULL];
      END;
    kick ← FALSE;
    IF myPupRouterSocket.input.length = 0 THEN RETURN[NIL];
    b ← PupDefs.DequeuePup[@myPupRouterSocket.input];
    IF CommFlags.doDebug AND b # NIL THEN
      b.debug ← CommUtilDefs.GetReturnFrame[].accesslink;
    END;

  Put: PROCEDURE [b: PupBuffer] =
    BEGIN
    b.dest ← myPupRouterSocket.remote;
    b.source ← myPupRouterSocket.local;
    PupRouterSendThis[b];
    END;

  SetRemoteAddress: ENTRY PROCEDURE [a: PupAddress] =
    BEGIN myPupRouterSocket.remote ← a; END;

  GetLocalAddress: ENTRY PROCEDURE RETURNS [PupAddress] =
    BEGIN RETURN[myPupRouterSocket.local]; END;

  -- initialization

  END.