-- PsbPack.mesa             
-- Edited by:
--            Sandman on May 20, 1980  4:46 PM
--            Barbara on April 6, 1979  3:02 PM
--            Bruce on July 9, 1980  3:56 PM

DIRECTORY
  Actions USING [CallInterpreter],
  Ascii USING [CR, DEL, SP],
  ComData USING [idLOCK],
  Commands USING [GetComment, Prompt, WriteError],
  DebugOps USING [fileSW, Foo, Interpret, InvalidNumber, ShortCopyREAD, ShortREAD, StringExpToOctal, UserAborted],
  DI USING [Foo, GetValue, TypeForSe],
  DOutput USING [Char, EOL, Line, Octal, Text],
  Frames USING [Invalid],
  Init USING [CheckSymTabLength],
  Inline USING [COPY],
  Lf USING [Display, GF, Handle, NoPrevious, PC, Previous],
  Lookup USING [Fail],
  MachineDefs USING [FHandle, PHandle, Priority],
  PrincOps USING [StateVector, SVPointer],
  ProcessDefs USING [DefaultPriority, Priority],
  ProcessOps USING [CurrentPSB, FirstProcess, FirstStateVector, LastProcess, Queue, ReadyList],
  Psb USING [Handle],
  PSBDefs USING [Empty, MonitorLock, PSB],
  Source USING [Display, FileMissing],
  State USING [GetGS, GSHandle, SetParse],
  Storage USING [Free, Node],
  StringDefs USING [UpperCase],
  SymbolOps USING [FirstCtxSe, NextSe],
  Symbols USING [bodyType, CBTIndex, CBTNull, CTXIndex, CTXNull, ISEIndex, SENull, seType],
  SymbolTable USING [Missing],
  Table USING [AddNotify, Base, DropNotify, Notifier, Overflow],
  TextSW USING [BlinkingCaret],
  UserInput USING [ResetUserAbort];

PsbPack: PROGRAM
  IMPORTS
    Actions, com: ComData, Commands, DebugOps, DI, DOutput, Init, Inline, Frames,
    Lf, Lookup, Source, st: State, Storage, StringDefs, SymbolOps,
    SymbolTable, Table, TextSW, UserInput
  EXPORTS Psb =
  BEGIN

  PSB: TYPE = PSBDefs.PSB;
  Handle: TYPE = Psb.Handle;

  NotAnXfer: ERROR = CODE;
  Invalid: PUBLIC SIGNAL [psb: Handle] = CODE;

  data: st.GSHandle ← st.GetGS[];

  Head: POINTER TO PItem ← NIL;
  lastRead: PSB;
  first: Handle ← NIL;
  last: Handle ← NIL;
  current: Handle ← NIL;

  PItem: TYPE = RECORD [
    link: POINTER TO PItem,
    p: Handle,
    psb: PSB];

  Read: PUBLIC PROCEDURE [p: Handle] RETURNS [local: Handle] = 
    BEGIN
    l: POINTER TO PItem;
    Check[p];
    IF (l ← CheckCache[p]) # NIL THEN RETURN[@l.psb];
    DebugOps.ShortCopyREAD[from: p, to: @lastRead, nwords: SIZE[PSB]];
    RETURN[IF lastRead.state # dead THEN @Cache[p].l.psb ELSE @lastRead];
    END;

  CheckCache: PROCEDURE [p: Handle] RETURNS [l: POINTER TO PItem] = 
    BEGIN
    FOR l ← Head, l.link UNTIL l = NIL DO
      IF l.p = p THEN RETURN;
      ENDLOOP;
    END;

  Cache: PROCEDURE [p: Handle] RETURNS [l: POINTER TO PItem] = 
    BEGIN
    IF (l ← CheckCache[p]) # NIL THEN RETURN;
    l ← Storage.Node[SIZE[PItem]];
    l↑ ← [link: Head, p: p, psb:];
    Head ← l;
    Inline.COPY[from: @lastRead, to: @l.psb, nwords: SIZE[PSB]];
    END;

  ResetCache: PUBLIC PROCEDURE =
    BEGIN OPEN ProcessOps;
    p, next: POINTER TO PItem;
    FOR p ← Head, next UNTIL p = NIL DO
      next ← p.link;
      Storage.Free[p];
      ENDLOOP;
    first ← DebugOps.ShortREAD[FirstProcess];
    last ← DebugOps.ShortREAD[LastProcess];
    current ← DebugOps.ShortREAD[CurrentPSB];
    Head ← NIL;
    END;

  First: PUBLIC PROC RETURNS [Handle] = {RETURN[first]};

  Last: PUBLIC PROC RETURNS [Handle] = {RETURN[last]};

  Next: PUBLIC PROC [psb: Handle] RETURNS [Handle] =
    BEGIN
    head: Handle ← psb;
    Check[psb];
    DO
      psb ← IF psb # last THEN psb+SIZE[PSB] ELSE first;
      IF psb = head THEN RETURN[NIL];
      IF Validate[psb] AND StateOK[psb] THEN RETURN[psb];
      ENDLOOP;
    END;

  Check: PUBLIC PROC [p: Handle] =
    BEGIN IF ~Validate[p] THEN SIGNAL Invalid[p] END;

  Validate: PUBLIC PROCEDURE [p: Handle] RETURNS [BOOLEAN] =
    BEGIN
    IF LOOPHOLE[p,CARDINAL] < LOOPHOLE[first,CARDINAL]
      OR LOOPHOLE[p,CARDINAL] > LOOPHOLE[last,CARDINAL] THEN RETURN[FALSE];
    RETURN[
      LOOPHOLE[(p-LOOPHOLE[first, CARDINAL]), CARDINAL] MOD SIZE[PSB] = 0]
    END;

  Priority: PUBLIC PROCEDURE [p: Handle] RETURNS [MachineDefs.Priority] =
    BEGIN RETURN[Read[p].local.priority] END;

  WaitingCV: PUBLIC PROCEDURE [p: Handle] RETURNS [BOOLEAN] =
    BEGIN
    p1: Handle ← Read[p];
    RETURN[~p1.enterFailed AND p1.waitingOnCV]
    END;

  WaitingML: PUBLIC PROCEDURE [p: Handle] RETURNS [BOOLEAN] =
    BEGIN RETURN[Read[p].local.enterFailed] END;

  Running: PUBLIC PROCEDURE [p: Handle] RETURNS [BOOLEAN] =
    BEGIN
    p1: Handle ← Read[p];
    RETURN[~p1.enterFailed AND ~p1.waitingOnCV AND p1.state = alive]
    END;

  Frame: PUBLIC PROC [psb: Handle] RETURNS [MachineDefs.FHandle] =
    --get the frame for the currently running process from the StateVector
    BEGIN 
    RETURN[IF psb # current THEN Read[psb].local.frame
	ELSE DebugOps.ShortREAD[@State[].sv.dest]]
    END;

  State: PUBLIC PROC RETURNS [sv: PrincOps.SVPointer] =
    BEGIN
    priority: CARDINAL ← IF Validate[current]
      THEN Priority[current] ELSE ProcessDefs.DefaultPriority;
    fsv: POINTER ← DebugOps.ShortREAD[ProcessOps.FirstStateVector];
    RETURN[fsv + priority*SIZE[PrincOps.StateVector]]
    END;

  StateOK: PROCEDURE [psb: Handle] RETURNS [BOOLEAN] =
    BEGIN RETURN[Read[psb].local.state # dead] END;

  ListProcesses: PUBLIC PROCEDURE =
    BEGIN
    i: Handle ← last;
    DO
      i ← Next[i];
      DumpPSB[i]; DOutput.EOL[];
      IF i = last THEN EXIT;
      ENDLOOP;
    RETURN
    END;

  StackType: TYPE = {process, queue};
  dumping: StackType;
  headPSB, currentPSB, qHead: Handle;

  Caret: PROC = {DOutput.EOL[]; DOutput.Text["  >"L]};

  DumpStack: PROCEDURE [psb: Handle, s: StackType] =
    BEGIN
    headPSB ← currentPSB ← psb;
    dumping ← s;
    Caret[];
    st.SetParse[StackCommands];
    RETURN
    END;

  StackCommands: PROCEDURE [char: CHARACTER] =
    BEGIN ENABLE Table.Overflow => {Init.CheckSymTabLength[]; RETRY};
    IF char # Ascii.SP AND char # Ascii.DEL THEN
    {DOutput.Char[char]; TextSW.BlinkingCaret[DebugOps.fileSW, off]};
    SELECT StringDefs.UpperCase[char] FROM
      'N  =>
	BEGIN
	currentPSB ← IF dumping = process THEN Next[currentPSB]
	  ELSE DebugOps.ShortREAD[@currentPSB.link];
	IF currentPSB = headPSB OR currentPSB = NIL THEN 
	  BEGIN Commands.Prompt[]; RETURN END
	ELSE DumpPSB[currentPSB];
	END;
      'P  => DumpPriority[currentPSB ! DebugOps.UserAborted => {ControlDel[]; CONTINUE}];
      'Q, Ascii.DEL  => BEGIN Commands.Prompt[]; RETURN END;
      'R  => DumpRoot[currentPSB ! DebugOps.UserAborted => {ControlDel[]; CONTINUE}];
      'L  => IF dumping = process THEN DumpSource[currentPSB, FALSE
	  ! DebugOps.UserAborted => {ControlDel[]; CONTINUE}]
	ELSE BadChar[];
      'S  => IF dumping = process THEN DumpSource[currentPSB, TRUE
	  ! DebugOps.UserAborted => {ControlDel[]; CONTINUE}]
	ELSE BadChar[];
      Ascii.SP => Actions.CallInterpreter[];
      '- => {Commands.GetComment[FALSE]; RETURN};
      '? => DOutput.Text[IF dumping = process THEN 
	" --Options are: List source, Next, Priority, Quit, Root, Source"L
	ELSE " --Options are: Next, Priority, Quit, Root"L];
      ENDCASE => BadChar[];
    Caret[]; TextSW.BlinkingCaret[DebugOps.fileSW, on];
    RETURN
    END;

  BadChar: PROC = {DOutput.Char['?]};

  DisplayQueue: PUBLIC PROCEDURE [q: STRING] =
    BEGIN
    IF (qHead ← StringToPSB[q]) # NIL THEN
      BEGIN
      DOutput.Text[" condition variable? [Y or N]"L];
      st.SetParse[CheckCondition];
      END
    ELSE
      BEGIN
      cv: BOOLEAN;
      [qHead, cv] ← Queue[q];
      DumpQueue[cv];
      END;
    END;

  DisplayReadyList: PUBLIC PROCEDURE =
    {qHead ← DebugOps.ShortREAD[ProcessOps.ReadyList]; DumpQueue[FALSE]};

  CheckCondition: PROCEDURE [char: CHARACTER] =
    BEGIN
    SELECT char FROM
      'y, 'Y, Ascii.CR  => {DOutput.Text[" yes"L]; DumpQueue[TRUE]};
      ENDCASE => {DOutput.Text[" no"L]; DumpQueue[FALSE]};
    RETURN
    END;

  DumpQueue: PROCEDURE [cv: BOOLEAN] =
    BEGIN
    IF (qHead ← StartQueue[cv]) = NIL THEN 
      BEGIN
      DOutput.Text[" Queue empty!"L];
      Commands.Prompt[];
      RETURN
      END;
    DumpPSB[qHead];
    DumpStack[qHead, queue];
    RETURN
    END;

  StartQueue: PROCEDURE [cv: BOOLEAN]
    RETURNS [Handle] =
    BEGIN
    cleanupLink, local: Handle;
    IF qHead = NIL THEN RETURN[NIL];
    local ← Read[qHead];
    IF ~cv THEN RETURN[local.link];
    cleanupLink ← local.cleanup;
    IF cleanupLink = NIL THEN RETURN[local.link];
    UNTIL cleanupLink = NIL OR cleanupLink = qHead DO
      qHead ← cleanupLink;
      cleanupLink ← DebugOps.ShortREAD[@cleanupLink.cleanup];
      ENDLOOP;
    RETURN[IF cleanupLink = NIL THEN qHead ELSE NIL];
    END;

  DisplayProcess: PUBLIC PROCEDURE [p: STRING] =
    BEGIN
    psb: Handle;
    DumpPSB[psb ← StringToPSB[p]];
    DumpStack[psb, process];
    RETURN
    END;

  DumpPSB: PROCEDURE [psb: Handle] =
    BEGIN
    f: MachineDefs.FHandle;
    Check[psb]; DOutput.EOL[];
    DOutput.Text["PSB: "L];
    DOutput.Octal[psb]; IF psb = current THEN DOutput.Char['*];
    DOutput.Text[", "L];
    SELECT TRUE FROM
      WaitingML[psb] => DOutput.Text["waiting ML, "L];
      WaitingCV[psb] => DOutput.Text["waiting CV, "L];
      ENDCASE;
    IF (f ← Frame[psb]) = NIL THEN DOutput.Line["No frame!"L]
    ELSE Lf.Display[f ! Frames.Invalid => 
      {DOutput.Octal[f]; DOutput.Text[" is not a valid frame!"L]; CONTINUE}];
    RETURN
    END;

  ControlDel: PROC = {UserInput.ResetUserAbort[]; DOutput.Text[" ... aborted"L]};

  DumpSource: PROCEDURE [psb: Handle, loadSource: BOOLEAN]=
    BEGIN
    frame: MachineDefs.FHandle ← Frame[psb];
    DOutput.EOL[];
    Source.Display[Lf.GF[frame], Lf.PC[frame], loadSource ! 
      Source.FileMissing => {
	Commands.WriteError[file];
	IF name # NIL THEN DOutput.Text[name] ELSE Commands.WriteError[compress];
	CONTINUE};
      DebugOps.UserAborted => {ControlDel[]; CONTINUE};
      SymbolTable.Missing--[seg]-- =>
        BEGIN DOutput.Text[" No symbol table."L]; CONTINUE END];
    RETURN
    END;

  DumpRoot: PROCEDURE [psb: Handle] =
    BEGIN
    f: MachineDefs.FHandle ← Frame[psb];
    DO
      f ← Lf.Previous[f ! Lf.NoPrevious => EXIT];
      ENDLOOP;
    IF f = NIL THEN RETURN;
    DOutput.EOL[];
    Lf.Display[f];
    RETURN
    END;
  
  DumpPriority: PROCEDURE [psb: Handle] =
    BEGIN DOutput.Text["riority "L]; DOutput.Octal[Priority[psb]] END;
  
  StringToPSB: PROCEDURE [p: STRING] RETURNS [psb: Handle] =
    BEGIN
    psb ← NIL;
    psb ← LOOPHOLE[
      DebugOps.StringExpToOctal[p !DebugOps.InvalidNumber => CONTINUE]];
    IF psb # NIL THEN Check[psb];
    END;

  seb: Table.Base;
  bb: Table.Base;

  Notify: Table.Notifier =
    BEGIN
    seb ← base[Symbols.seType];
    bb ← base[Symbols.bodyType];
    END;

Queue: PUBLIC PROC [q: STRING]
    RETURNS [qHead: MachineDefs.PHandle, cv: BOOLEAN] =
  BEGIN
  FindQueue: PROCEDURE [f: DebugOps.Foo] =
    BEGIN
    PSBBase: CARDINAL = 0;
    mLock: PSBDefs.MonitorLock;
    Table.AddNotify[Notify];
    WITH seb[DI.TypeForSe[f.tsei]] SELECT FROM
      record => IF fieldCtx = MLCtx THEN cv ← FALSE;
      ENDCASE =>
	BEGIN
	found: BOOLEAN ← FALSE;
	[found, cv] ← SearchML[f !UNWIND => Table.DropNotify[Notify]];
	IF ~found THEN {Table.DropNotify[Notify]; RETURN};
	END;
    Table.DropNotify[Notify];
    DI.GetValue[f];
    mLock ← f.addr.base↑;
    qHead ←
      IF mLock.queue = PSBDefs.Empty THEN NIL ELSE mLock.queue + PSBBase;
    RETURN
    END;

  qHead ← NIL;
  DebugOps.Interpret[q, FindQueue ! ANY => CONTINUE];
  IF qHead = NIL THEN SIGNAL Lookup.Fail[q]; 
  RETURN
  END;
  
MLCtx: Symbols.CTXIndex = LOOPHOLE[8];
CVCtx: Symbols.CTXIndex = LOOPHOLE[10];

SearchML: PROCEDURE [f: DebugOps.Foo] RETURNS [found, cv: BOOLEAN] =
  BEGIN
  cbti: Symbols.CBTIndex;
  c: Symbols.CTXIndex ← Symbols.CTXNull;
  WITH seb[DI.TypeForSe[f.tsei]] SELECT FROM
    record =>
      IF monitored THEN c ← fieldCtx
      ELSE
	IF fieldCtx = CVCtx THEN
	  BEGIN
	  f.tsei ← SymbolOps.FirstCtxSe[fieldCtx];
	  RETURN[TRUE, TRUE];
	  END
	ELSE RETURN[FALSE,FALSE];
    transfer =>
      IF mode = program THEN 
	BEGIN
	WITH seb[f.tsei] SELECT FROM
	  id => cbti ← idInfo;
	  ENDCASE => ERROR NotAnXfer;
	IF cbti # Symbols.CBTNull THEN c ← bb[cbti].localCtx;
      END;
    ENDCASE;
  IF c = Symbols.CTXNull THEN RETURN[FALSE, FALSE];
  RETURN[SearchCtxForLock[f, c], FALSE]
  END;

SearchCtxForLock: PROCEDURE [f: DebugOps.Foo, c: Symbols.CTXIndex]
  RETURNS [BOOLEAN] =
  BEGIN OPEN SymbolOps;
  sei: Symbols.ISEIndex;
  FOR sei ← FirstCtxSe[c], NextSe[sei] UNTIL sei = Symbols.SENull DO
    IF sei # com.idLOCK THEN LOOP;
    f.tsei ← DI.TypeForSe[sei];
    RETURN[TRUE]
    ENDLOOP;
  RETURN[FALSE]
  END;

END..