-- 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..