-- Copyright (C) 1984, 1985  by Xerox Corporation. All rights reserved. 
-- IdleTime.mesa 
-- HGM, 21-May-85 23:35:01	
-- last edited by Hankins 14-Aug-84  8:44:29	

DIRECTORY
  Inline USING [LowHalf],
  LogDefs USING [DisplayNumber, Percentage],
  Process USING [
    Detach, GetPriority, Milliseconds, MsecToTicks, Priority, SetPriority,
    SetTimeout, Yield],
  ProcessPriorities USING [priorityClientHigh, priorityClientLow],
  ProcessorFace USING [SetMP],
  SpecialSpace USING [MakeProcedureResident],
  System USING [
    GetClockPulses, MicrosecondsToPulses, Pulses, PulsesToMicroseconds];

IdleTime: MONITOR IMPORTS Inline, LogDefs, Process, ProcessorFace, SpecialSpace, System =
  BEGIN

  -- Types, Constants, Vars:

  updateDisplay: CONDITION;
  displayUpdateMsec: Process.Milliseconds ← 1000;
  priorityPublisher: Process.Priority ← ProcessPriorities.priorityClientHigh;
  priorityIdler: Process.Priority ← ProcessPriorities.priorityClientLow;

  -- CPU utilization related declarations:
  cycles: LONG CARDINAL ← 0;
  cyclesPerRefresh, pulsesPerRefresh: LONG CARDINAL ← 0;  -- set by Calibrate
  idleFive: CARDINAL ← 0;  -- last sec, average last 10 sec (percentage*100)
  idlePercent: LogDefs.Percentage ← LAST[LogDefs.Percentage];

  -- Internal Procedures --

  Idler: PROCEDURE = {
    DO
      THROUGH [0..10000) DO cycles ← cycles.SUCC; Process.Yield[]; ENDLOOP;
      ENDLOOP};

  Calibrate: PROCEDURE =
    BEGIN
    cyclesToSample: CARDINAL = 10000;  -- about a second on a DLion
    priorityPrev: Process.Priority ← Process.GetPriority[];
    first, last: System.Pulses;
    nanoSecPerCycle: LONG CARDINAL;
    Process.SetPriority[Process.Priority.LAST];
    first ← System.GetClockPulses[];
    THROUGH [0..cyclesToSample) DO cycles ← cycles.SUCC; Process.Yield[]; ENDLOOP;
    -- This seems slow if an extra timeout scan happens, packet arrives or ...
    -- Unfortunately, that causes confusion when idle > 100%
    last ← System.GetClockPulses[];
    cycles ← (cycles - cyclesToSample);
    Process.SetPriority[priorityPrev];
    nanoSecPerCycle ←
      System.PulsesToMicroseconds[[LONG[1000] * (last - first)]] / cyclesToSample;
    cyclesPerRefresh ← (1000000 * displayUpdateMsec) / nanoSecPerCycle;
    pulsesPerRefresh ← System.MicrosecondsToPulses[
      displayUpdateMsec * LONG[1000]];
    END;

  Publisher: ENTRY PROCEDURE =
    BEGIN
    startPulses: LONG CARDINAL ← System.GetClockPulses[];
    startCycles: LONG CARDINAL ← cycles;
    pulsesThisRefresh, cyclesThisRefresh: LONG CARDINAL ← 0;
    idle: CARDINAL ← 0;
    Process.SetPriority[priorityPublisher];
    DO
      startPulses ← (startPulses + pulsesThisRefresh);
      startCycles ← (startCycles + cyclesThisRefresh);
      -- wait for a second  
      WAIT updateDisplay;
      -- now update 
      pulsesThisRefresh ← (System.GetClockPulses[] - startPulses);
      cyclesThisRefresh ← (cycles - startCycles);
      THROUGH [0..10) DO
        cyclesThisTry: LONG CARDINAL =
          cyclesThisRefresh * pulsesPerRefresh / pulsesThisRefresh;
        idle ← Inline.LowHalf[cyclesThisTry * 10000 / cyclesPerRefresh];
        IF idle > 10000 THEN  -- Calibration screwed up
          {cyclesPerRefresh ← cyclesPerRefresh + 1; LOOP}
        ELSE EXIT;
        REPEAT FINISHED => idle ← 10000;
        ENDLOOP;
      idlePercent ← idle / 100;
      --idleTen ← Inline.LongDiv[idle + Inline.LongMult[idleTen,9], 10];
      -- this latter would be more interesting info (slower fluctuations) but 
      -- not sure of the cost of latter calculation so won't use, instead try this:
      idlePercent ← idleFive ← (idlePercent + idleFive * 4) / 5;
      ProcessorFace.SetMP[100-idlePercent];
      ENDLOOP;
    END;

  Initialize: PROCEDURE =
    BEGIN  -- once-only initialization.
    priorityPrev: Process.Priority;
    Process.SetTimeout[@updateDisplay, Process.MsecToTicks[displayUpdateMsec]];
    SpecialSpace.MakeProcedureResident[Idler];
    Calibrate[];
    -- start up Publisher process
    Process.Detach[FORK Publisher[]];
    priorityPrev ← Process.GetPriority[];
    -- start up Idle process
    Process.SetPriority[priorityIdler];
    Process.Detach[FORK Idler[]];
    Process.SetPriority[priorityPrev];
    LogDefs.DisplayNumber["Idle time"L, [percent[@idlePercent]]];
    END;  --Initialize


  -- Main Body: 

  Initialize[];

  END.

13-Aug-84  9:40:40: blh - threw away old code and copied from Klamath ActivityImpl hack.