-- File: StatsCold.mesa - last edit:
-- AOF                 15-Feb-88 16:52:54
-- Copyright (C) 1983, 1988 by Xerox Corporation. All rights reserved. 

DIRECTORY
  Heap USING [systemZone],
  Process USING [Detach, SetTimeout, SecondsToTicks],
  System USING [
    GreenwichMeanTime, GetGreenwichMeanTime, Pulses, GetClockPulses, Microseconds,
    PulsesToMicroseconds],
  Time USING [AppendCurrent],
  Stats USING [StatBump, StatCounterIndex, StringProc],
  StatsOps USING [
    StatArray, statGrand, StatPrintCounters, StatsPrint, StatsStrings],
  StatsOpsExtras USING [GetDoStats],
  String USING [AppendString];

StatsCold: MONITOR LOCKS statLock
  IMPORTS Heap, Process, System, Time, Stats, StatsOps, StatsOpsExtras, String
  EXPORTS Stats, StatsOps =
  BEGIN

  statLock: PUBLIC MONITORLOCK;
  CantDo: ERROR = CODE;  --used to be able to, but can't now.
  -- our copy of grand at last call to StatSince or StatReady
  StatGrand: TYPE = ARRAY Stats.StatCounterIndex OF LONG CARDINAL;
  recent: StatsOps.StatArray ← NIL;  --allocated from heap

  initDateAndTime: STRING = [18]; -- when we were initialized

  StatsGetCounters: PUBLIC PROC
    RETURNS [POINTER TO ARRAY Stats.StatCounterIndex OF LONG CARDINAL] =
    BEGIN RETURN WITH <<UNEXPORTED>> ERROR CantDo; END;

  StatUpdater: ENTRY PROC =
    BEGIN
    updater: CONDITION;
    Process.SetTimeout[@updater, Process.SecondsToTicks[15*60]];
    DO
      -- forever
      WAIT updater;
      StatUpdateLocked[];
      ENDLOOP;
    END;

  StatUpdate: PUBLIC ENTRY PROC = {StatUpdateLocked[]};

  <<
  Update various things.

  It can be called anytime, but must be called "often enough".  The 39ms clock
  overflows every 41 min, so be sure to call it more often than that.  StatsCold
  FORKs to a PROCESS that calls it every 20 minutes.

  Also, it should be called before looking at grand[StatTime or statSeconds],
  as in when printing things out.

  One idea to make things go faster is to actually count things in 16 bit mode,
  and call somebody before the small counters overflow to copy the info out and
  reset them.  This is where that should get done, but its a hard problem because
  there are no MESA instructions that do an atomic read and reset.

  Our copies of the time sampled when we last looked at the clock.
  >>

  oldSeconds: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
  oldPulses: System.Pulses ← System.GetClockPulses[];
  -- Time may go backwards when it gets reset.
  spareSeconds: LONG INTEGER ← 0; -- remembers fractions of an hour

  StatUpdateLocked: INTERNAL PROC =
    BEGIN
    nowPulses: System.Pulses ← System.GetClockPulses[];
    recent: LONG CARDINAL;
    delta: System.Microseconds;
    newSeconds: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
    delta ← System.PulsesToMicroseconds[System.Pulses[nowPulses - oldPulses]];
    StatsOps.statGrand↑[statTime] ← StatsOps.statGrand↑[statTime] + delta/1000;
    oldPulses ← nowPulses;
    recent ← newSeconds - oldSeconds;
    oldSeconds ← newSeconds;
    StatsOps.statGrand↑[statSeconds] ← StatsOps.statGrand↑[statSeconds] + recent;
    spareSeconds ← spareSeconds + recent;
    UNTIL spareSeconds < 3600 DO
      spareSeconds ← spareSeconds - 3600;
      StatsOps.statGrand↑[statHours] ← StatsOps.statGrand↑[statHours] + 1;
      ENDLOOP;
    END;

  -- remember current date and time for header lines

  StatNew: PUBLIC ENTRY PROC =
    BEGIN
    initDateAndTime.length ← 0;
    Time.AppendCurrent[initDateAndTime];
    StatZap[];
    END;


  -- reset counters, and print a here-we-go line

  StatStart: PUBLIC ENTRY PROC[proc: Stats.StringProc, header: LONG STRING] =
    BEGIN
    string: STRING ← [100];
    StatZap[];
    Time.AppendCurrent[string];
    String.AppendString[string, "  "L];
    proc[string]; string.length ← 0;
    String.AppendString[string, header];
    IF (initDateAndTime # NIL) AND (initDateAndTime.length # 0) THEN
      BEGIN
      String.AppendString[string, " of "L];
      String.AppendString[string, initDateAndTime];
      END;
    proc[string];
    END;


  -- print out current numbers

  StatPrintCurrent: PUBLIC ENTRY PROC[proc: Stats.StringProc] =
    BEGIN
    string: STRING ← [80];
    StatUpdateLocked[];
    Time.AppendCurrent[string];
    String.AppendString[string, "  Current counters."L];
    proc[string];
    StatsOps.StatPrintCounters[proc, StatsOps.statGrand];
    END;


  -- print out current numbers

  StatFinish: PUBLIC ENTRY PROC[proc: Stats.StringProc] =
    BEGIN
    string: STRING ← [40];
    StatUpdateLocked[];
    Time.AppendCurrent[string];
    String.AppendString[string, "  Grand Totals."L];
    proc[string];
    StatsOps.StatPrintCounters[proc, StatsOps.statGrand];
    StatZap[]; -- just in case

    END;


  -- reset the world to be nice and clean again

  StatZapit: ENTRY PROC = BEGIN StatZap[]; END;

  StatZap: INTERNAL PROC =
    BEGIN
    StatUpdateLocked[]; -- init most internal stuff
    spareSeconds ← 0;
    recent↑ ← StatsOps.statGrand↑ ← ALL[0];
    END;

  -- setup things for StatSince or StatSummary
  StatReady: PUBLIC ENTRY PROC =
    BEGIN
    StatUpdateLocked[];
    recent↑ ← StatsOps.statGrand↑;
    END;

  -- print out new numbers since last call to StatReady or StatSince

  StatSince: PUBLIC PROC[proc: Stats.StringProc] =
    BEGIN
    GetCopies: ENTRY PROC = INLINE
      BEGIN
      StatUpdateLocked[];
      temp↑ ← StatsOps.statGrand↑;
      END;
    string: STRING ← [80];
    temp: StatsOps.StatArray ← Heap.systemZone.NEW[StatGrand];
    GetCopies[];
    FOR i: Stats.StatCounterIndex IN Stats.StatCounterIndex DO
      recent[i] ← temp[i] - recent[i]; ENDLOOP;
    Time.AppendCurrent[string];
    String.AppendString[string, "  Recent Statistics.\n"L];
    proc[string];
    StatsOps.StatPrintCounters[proc, recent];
    recent↑ ← temp↑;
    Heap.systemZone.FREE[@temp];
    END;

  -- AppendCurrent has format of dd-mmm-yy hh:mm:ss

  StatPrintDateAndTime: PUBLIC PROC[proc: Stats.StringProc] =
    BEGIN
    string: STRING ← [40];
    Time.AppendCurrent[string];
    String.AppendString[string, "  "L];
    proc[string];
    END;

  Init: PROC[] =
    BEGIN
    IF StatsOpsExtras.GetDoStats[] THEN
      BEGIN
      recent ← Heap.systemZone.NEW[StatGrand];
      Stats.StatBump[statMouseTrap, 0];
      START StatsOps.StatsPrint;
      START StatsOps.StatsStrings;
      StatZapit[];
      Process.Detach[FORK StatUpdater[]];
      END;
    END;  --Init

  -- initialization
  Init[];  --maybe stop some nonsense early

  END.