-- Copyright (C) 1980, 1981, 1983, 1984, 1985  by Xerox Corporation. All rights reserved. 
-- File: MDSWatcher.mesa

-- HGM, 14-Sep-85 22:16:20
-- Ted Wobber		 8-Aug-83 11:11:17
-- Randy Gobbel		 7-Aug-81 16:11:44
-- Roy Levin		10-Sep-80 18:46:53
-- Andrew Birrell	16-Oct-80 18:30:01
-- Hankins		23-Aug-84 10:38:03

DIRECTORY
  Ascii USING [NUL],
  BufferOps USING [GetStatistics, Statistics],
  EnquiryDefs USING [DisplayStats, Histograms, RemoteServers, PolicyControls],
  GlassDefs USING [Handle, HandleObject, StringType],
  LogDefs USING [DisplayNumber],
  LogPrivateDefs USING [tty],
  Process USING [
    Detach, InvalidProcess, Pause, Priority, SecondsToTicks, SetPriority, Ticks,
    ValidateProcess],
  ProcessPriorities USING [priorityFrameFault],
  ProcessOperations USING [
    DisableInterrupts, EnableInterrupts, Notify, ReEnter, Wait],
  PSB USING [
    Condition, FaultIndex, PDABase, PDA, PsbIndex, PsbNull, qPageFault, StartPsb],
  SpecialSpace USING [MakeProcedureResident],
  RestartDefs,
  TTY USING [GetChar, PutChar, PutDecimal, PutLongDecimal, PutString];

MDSWatcher: PROGRAM
  IMPORTS
    BufferOps, EnquiryDefs, LogDefs, LogPrivateDefs, Process, ProcessOperations,
    SpecialSpace, TTY
  EXPORTS RestartDefs =
  BEGIN

  screen: GlassDefs.Handle = @screenObject;
  screenObject: GlassDefs.HandleObject ← [
    ReadChar: ReadCharNull, ReadString: ReadStringNull, WriteChar: MyWriteChar,
    WriteString: MyWriteString, WriteDecimal: MyWriteDecimal,
    WriteLongDecimal: MyWriteLongDecimal, SendNow: NullProc,
    CharsLeft: CardNullProc, LinesLeft: CardNullProc, SetWidth: SetWidthNull,
    SetHeight: SetHeightNull, DelTyped: BoolNullProc, Synch: NullProc,
    Flush: NullProc];



  availablePages, availablePSBs, availablePups: CARDINAL ← 0;

  savedStorageFaults, storageFaults: CARDINAL ← 0;

  oneMinute: Process.Ticks = Process.SecondsToTicks[60];
  tenSeconds: Process.Ticks = Process.SecondsToTicks[10];

  StartKeyWatcher: PUBLIC PROCEDURE =
    BEGIN
    Process.Detach[FORK KeyboardWatcher[]];
    END;

  Init: PROCEDURE =
    BEGIN
    --  LogDefs.DisplayNumber["Free MDS"L, [short[@availablePages]]];
    LogDefs.DisplayNumber["Free PSBs"L, [short[@availablePSBs]]];
    LogDefs.DisplayNumber["Free Pup Buffers"L, [short[@availablePups]]];
    LogDefs.DisplayNumber["Page faults/min"L, [short[@savedStorageFaults]]];
    END;

  WatchMDS: PROCEDURE =
    BEGIN
    << this is looking at MDS and seeing how much has been carved up for new, smaller, spaces.  
   this is no longer possible to do in any clean way (could import global from of some Pilot impl and look at a variable therein (SpecialSpaceImpl?) so we'll just not calculate it any longer.
  firstPage: Space.PageNumber = Space.PageFromLongPointer[Space.MDS[].pointer];
  lastPage: Space.PageNumber = firstPage + Environment.maxPagesInMDS;
     should also be equivalent to firstPage+Space.MDS[].count? >>
    DO
      available: CARDINAL ← 0;
      <<   FOR page: Space.PageNumber ← firstPage, SUCC[page] UNTIL page > lastPage DO
	IF Space.GetHandle[page] = Space.mds THEN available ← available + 1;
	ENDLOOP;
    availablePages ← available;
    available ← 0;
 >>
      FOR i: CARDINAL IN [PSB.StartPsb..PSB.StartPsb + PSB.PDA.count) DO
        Process.ValidateProcess[
          LOOPHOLE[i, PROCESS] !
          Process.InvalidProcess => {available ← available + 1; CONTINUE}];
        ENDLOOP;
      availablePSBs ← available;
      savedStorageFaults ← storageFaults;
      storageFaults ← 0;
      Process.Pause[oneMinute];
      ENDLOOP;
    END;

  WatchPupBuffers: PROCEDURE =
    BEGIN
    stats: ARRAY BufferOps.Statistics OF CARDINAL;
    DO
      stats ← BufferOps.GetStatistics[];
      availablePups ← stats[available];
      Process.Pause[tenSeconds];
      ENDLOOP
    END;

  KeyboardWatcher: PROCEDURE =
    BEGIN
    DO
      c: CHAR ← TTY.GetChar[LogPrivateDefs.tty];
      SELECT c FROM
        'h, 'H => EnquiryDefs.Histograms[screen];
        'o, 'O => EnquiryDefs.RemoteServers[screen];
        'p, 'P => EnquiryDefs.PolicyControls[screen];
        ENDCASE => EnquiryDefs.DisplayStats[screen];
      ENDLOOP;
    END;


  -- Stuff stolen from BLyon's UsageMonitor tool

  priorityStorageFaultRecorders: Process.Priority ←
    ProcessPriorities.priorityFrameFault - 1;
  -- should be higher than Pilot's page and write fault handlers' priorities.

  pda: PSB.PDABase = PSB.PDA;

  qPageFault: PSB.FaultIndex = PSB.qPageFault;
  pPageFaultCondition: LONG POINTER TO PSB.Condition = @pda.fault[
    qPageFault].condition;
  pPageFaultCONDITION: LONG POINTER TO CONDITION = LOOPHOLE[pPageFaultCondition];

  PageFaultRecorder: --EXTERNAL-- PROCEDURE [] =
    BEGIN
    pageFaultLock: MONITORLOCK;
    shouldNotifyPilot: BOOLEAN ← FALSE;

    Process.SetPriority[priorityStorageFaultRecorders];
    -- NO PAGE FAULTS or calls to swappable code from this point on! (else the state vector goblin will get ya)

    --FOREVER--
    DO
      -- wait for a page fault..
      ProcessOperations.Wait[@pageFaultLock, pPageFaultCONDITION, --timeout:-- 1];
      UNTIL ProcessOperations.ReEnter[@pageFaultLock, pPageFaultCONDITION] DO
        NULL ENDLOOP;
      -- either a new page fault came along or we timed out..
      IF pda.fault[qPageFault].queue.tail = PSB.PsbNull THEN
        -- no page fault, just timed out..
        BEGIN
        IF shouldNotifyPilot
          AND pda.fault[qPageFault].condition.tail ~= PSB.PsbNull THEN {
          NakedNotify[pPageFaultCONDITION]; shouldNotifyPilot ← FALSE};
        LOOP;  -- go back and wait again.
        END;
      -- wake up the Pilot fault handler:
      ProcessOperations.DisableInterrupts[];
      IF pPageFaultCondition↑.tail = PSB.PsbNull THEN shouldNotifyPilot ← TRUE  -- Pilot not ready for this fault yet..
      ELSE NakedNotify[pPageFaultCONDITION];
      ProcessOperations.EnableInterrupts[];
      -- log the fault:
      storageFaults ← SUCC[storageFaults];
      ENDLOOP;
    -- Note: following code never executed in current implementation.
    -- Must get back to ordinary priority before exiting since Process.End is swappable..
    -- Process.SetPriority[Process.priorityForeground];
    END;  -- PageFaultRecorder (process disappears)

  NakedNotify: PROCEDURE [pCondition: LONG POINTER TO CONDITION] = INLINE {
    -- Used ONLY to notify a condition from a high priority process outside the relevant monitor.
    pCond: LONG POINTER TO PSB.Condition = LOOPHOLE[pCondition];
    ProcessOperations.DisableInterrupts[];
    IF pCond↑.tail = PSB.PsbNull THEN {
      pCond↑.wakeup ← TRUE; ProcessOperations.EnableInterrupts[]}
    ELSE {
      ProcessOperations.EnableInterrupts[];
      ProcessOperations.Notify[pCondition]}};

  -- GlassDefs.HandleOject implementors

  NullProc: PROCEDURE = {};
  BoolNullProc: PROCEDURE RETURNS [BOOLEAN] = {RETURN[FALSE]};
  CardNullProc: PROCEDURE RETURNS [CARDINAL] = {RETURN[0]};
  ReadCharNull: PROCEDURE RETURNS [CHARACTER] = {RETURN[Ascii.NUL]};
  ReadStringNull: PROC [prompt, s: LONG STRING, type: GlassDefs.StringType]
    RETURNS [CHARACTER] = {RETURN[Ascii.NUL]};
  SetWidthNull, SetHeightNull: PROCEDURE [CARDINAL] = {};
  MyWriteChar: PROCEDURE [c: CHARACTER] = {TTY.PutChar[LogPrivateDefs.tty, c]};
  MyWriteString: PROC [s: LONG STRING] = {TTY.PutString[LogPrivateDefs.tty, s]};
  MyWriteDecimal: PROCEDURE [n: CARDINAL] = {
    TTY.PutDecimal[LogPrivateDefs.tty, n]};
  MyWriteLongDecimal: PROCEDURE [n: LONG CARDINAL] = {
    TTY.PutLongDecimal[LogPrivateDefs.tty, n]};


  Init[];
  SpecialSpace.MakeProcedureResident[PageFaultRecorder];
  Process.Detach[FORK  WatchMDS[]];
  Process.Detach[FORK WatchPupBuffers[]];

  END.