-- File: DispatcherImpl.mesa
--   Edited by: BLyon on: March 21, 1981  10:47 AM

DIRECTORY
  BufferDefs USING [
    Buffer, Dequeue, Enqueue, Queue, QueueCleanup, QueueInitialize, QueueLength,
    QueueObject],
  CommFlags USING [doStats, doDebug],
  DriverDefs USING [
    GetGiantVector, Network, ReturnFreeBuffer, Router, RouterObject],
  Process USING [SetTimeout, MsecToTicks],
  SpecialCommunication USING [SpyProc],
  StatsDefs USING [StatIncr];

DispatcherImpl: MONITOR
  IMPORTS BufferDefs, DriverDefs, Process, StatsDefs
  EXPORTS BufferDefs, DriverDefs, SpecialCommunication
  SHARES BufferDefs =
  BEGIN

  -- EXPORTed TYPEs
  Network: PUBLIC TYPE = DriverDefs.Network;

  mainFork: PROCESS;
  dispatcherPleaseDie: BOOLEAN;
  dispatcherReady: CONDITION;
  globalInputQueue: BufferDefs.Queue = NEW[BufferDefs.QueueObject];
  globalOutputQueue: BufferDefs.Queue = NEW[BufferDefs.QueueObject];
  pupRouter, oisRouter: PUBLIC DriverDefs.Router;
  dummyRouter: DriverDefs.Router;

  dummyRouterObject: DriverDefs.RouterObject ←
    [input: DummyInputer, broadcast: DummyBroadcaster, addNetwork: DummyAddDelete,
      removeNetwork: DummyAddDelete, stateChanged: DummyStateChanged];

  -- interface of peeking
  bufferSpy: SpecialCommunication.SpyProc ← NIL;

  -- Cold procedures

  SetSpyProc: PUBLIC PROCEDURE [spy: SpecialCommunication.SpyProc] =
    BEGIN bufferSpy ← spy; END;

  DummyInputer: PROCEDURE [b: BufferDefs.Buffer] =
    BEGIN
    IF CommFlags.doStats THEN StatsDefs.StatIncr[statPacketsDiscarded];
    b.requeueProcedure[b];
    END;

  DummyBroadcaster: PROCEDURE [b: BufferDefs.Buffer] =
    BEGIN b.requeueProcedure[b]; END;

  DummyAddDelete: PROCEDURE [Network] = BEGIN END;

  DummyStateChanged: PROCEDURE [Network] = BEGIN END;

  SetPupRouter: PUBLIC PROCEDURE [router: DriverDefs.Router] =
    BEGIN
    IF router = NIL THEN router ← @dummyRouterObject;
    pupRouter ← router;
    END;

  SetOisRouter: PUBLIC PROCEDURE [router: DriverDefs.Router] =
    BEGIN
    IF router = NIL THEN router ← @dummyRouterObject;
    oisRouter ← router;
    END;

  GetPupRouter: PUBLIC PROCEDURE RETURNS [DriverDefs.Router] =
    BEGIN RETURN[pupRouter]; END;

  GetOisRouter: PUBLIC PROCEDURE RETURNS [DriverDefs.Router] =
    BEGIN RETURN[oisRouter]; END;

  DispatcherOn: PUBLIC PROCEDURE =
    BEGIN
    bufferSpy ← NIL;
    dispatcherPleaseDie ← FALSE;
    BufferDefs.QueueInitialize[globalInputQueue];
    BufferDefs.QueueInitialize[globalOutputQueue];
    mainFork ← FORK MainDispatcher[];
    END;

  DispatcherOff: PUBLIC PROCEDURE =
    BEGIN
    DispatcherOffLocked[];
    JOIN mainFork;
    BufferDefs.QueueCleanup[globalInputQueue];
    BufferDefs.QueueCleanup[globalOutputQueue];
    END;

  DispatcherOffLocked: ENTRY PROCEDURE = INLINE
    BEGIN dispatcherPleaseDie ← TRUE; NOTIFY dispatcherReady; END;

  -- Hot procedures

  MainDispatcher: PUBLIC PROCEDURE =
    BEGIN
    b: BufferDefs.Buffer;
    network: Network;
    UNTIL dispatcherPleaseDie DO
      WHILE BufferDefs.QueueLength[globalOutputQueue] # 0 DO
	-- give back free buffers first
	b ← GrabOutputBuffer[];
	IF b.allNets THEN SendToNextNetwork[b] ELSE b.requeueProcedure[b];
	ENDLOOP;
      IF BufferDefs.QueueLength[globalInputQueue] # 0 THEN
	BEGIN
	b ← GrabInputBuffer[];
	IF (bufferSpy = NIL) OR bufferSpy[b] THEN
	  BEGIN
	  network ← b.network;
	  -- give it to the right router, and it requeues the buffer
	  SELECT network.decapsulateBuffer[b] FROM
	    pup =>
	      BEGIN
	      b.type ← pup;
	      pupRouter.input[b];
	      END;
	    ois =>
	      BEGIN
	      b.type ← ois;
	      oisRouter.input[b];
	      END;
	    rejected => dummyRouter.input[b];
	    processed => NULL;
	    ENDCASE => ERROR;  -- UnknownDecapsulation
	  END; -- end of process input buffer clause
	LOOP;
	END; -- end of input queue not empty clause
      Wait[];
      ENDLOOP;
    END;

  GrabOutputBuffer: ENTRY PROCEDURE RETURNS [BufferDefs.Buffer] = INLINE
    BEGIN RETURN[BufferDefs.Dequeue[globalOutputQueue]]; END;

  GrabInputBuffer: ENTRY PROCEDURE RETURNS [BufferDefs.Buffer] = INLINE
    BEGIN RETURN[BufferDefs.Dequeue[globalInputQueue]]; END;

  Wait: ENTRY PROCEDURE = INLINE
    BEGIN
    IF BufferDefs.QueueLength[globalInputQueue] = 0 AND BufferDefs.QueueLength[
      globalOutputQueue] = 0 THEN WAIT dispatcherReady;
    END;

  SendToNextNetwork: PROCEDURE [b: BufferDefs.Buffer] =
    BEGIN
    network: Network ← b.network;
    b.network ← network ← network.next;
    -- dangerous we are playing with monitor data
    IF network = NIL THEN
      BEGIN
      b.allNets ← FALSE;  -- this is where it gets turned off
      b.requeueProcedure[b];
      RETURN;
      END;
    SELECT b.type FROM
      pup =>
	BEGIN
	pupRouter.broadcast[b]; -- this requeues b
	END;
      ois =>
	BEGIN
	oisRouter.broadcast[b]; -- this requeues b
	END;
      ENDCASE => ERROR; -- dispatcher only sends out pups or ois's
    END;

  -- Locked procedures
  -- These are called from interrupt routines on the Alto; they must be locked in memory.

  PutOnGlobalInputQueue: PUBLIC ENTRY PROCEDURE [b: BufferDefs.Buffer] =
    BEGIN BufferDefs.Enqueue[globalInputQueue, b]; NOTIFY dispatcherReady; END;

  PutOnGlobalDoneQueue: PUBLIC PROCEDURE [b: BufferDefs.Buffer] =
    BEGIN
    -- This test avoids an unnecessary process switch in returning buffers to system pool.
    IF ~b.allNets AND b.requeueProcedure = DriverDefs.ReturnFreeBuffer THEN
      DriverDefs.ReturnFreeBuffer[b]
    ELSE PutOnGlobalDoneQueueLocked[b];
    END;

  PutOnGlobalDoneQueueLocked: ENTRY PROCEDURE [b: BufferDefs.Buffer] = INLINE
    BEGIN BufferDefs.Enqueue[globalOutputQueue, b]; NOTIFY dispatcherReady; END;

  -- initialization
  pupRouter ← @dummyRouterObject;
  oisRouter ← @dummyRouterObject;
  dummyRouter ← @dummyRouterObject;
  Process.SetTimeout[@dispatcherReady, Process.MsecToTicks[30000]];
  IF CommFlags.doDebug THEN
    BEGIN
    DriverDefs.GetGiantVector[].globalInputQueue ← globalInputQueue;
    DriverDefs.GetGiantVector[].globalOutputQueue ← globalOutputQueue;
    END;

  END.  -- DispatcherImpl module

LOG

Time: April 21, 1980  2:56 PM  By: Dalal  Action: created file for Pilot 5.0.
Time: April 21, 1980  2:57 PM  By: Dalal  Action: merged the dispatchers.
Time: May 7, 1980  10:34 AM  By: BLyon  Action: Made pupRouter & oisRouter PUBLIC (they are used by Boss).
Time: May 8, 1980  1:24 PM  By: BLyon  Action: Made PutOnGlobalDoneQueue more efficient (hopefully it still works).