-- DBStashImpl.Mesa
-- last edit by Schmidt, January 6, 1983 1:44 pm
-- last edit by Satterthwaite, February 1, 1983 10:08 am
-- This module provides a cache of dependencies (DepSeq's) for the modeller
-- it is designed to operate independently so that the modeller can be changed.
-- without needing to replace this module, so it is loaded separately only
-- once by the Modeller. In particular it is bound in its own config
-- (DBStashPack.Config) and does not rely on any Modeller procedures.
-- The cache is indexed by file create time. There is only one DepSeq per
-- create time.
-- Things get tricky when these DepSeq's are passed to the Modeller, so
-- we adopt the convention that the Modeller never FREE's anything in the DepSeq,
-- including the strings. If necessary, strings may be put into the DepSeq
-- using the depseq.CopyString[] call. This allows the memory for the Modeller
-- and this module to be COMPLETELY separate. In fact the strings in the
-- depseq's stored in no heap at all.
DIRECTORY
CWF: TYPE USING [FWF0, FWF1, FWF2, FWF3, FWF4,
SetWriteProcedure, SWF1, WF0, WF1, WF3, WFCR],
DBStash: TYPE USING [],
Dir: TYPE USING [DepSeq, DepSeqRecord, MaximumDepSize],
Inline: TYPE USING [LongCOPY],
IO: TYPE USING[Handle, PutChar],
LongString: TYPE USING [AppendString, EquivalentString],
Rope: TYPE USING[Text],
RopeInline: TYPE USING[InlineFlatten],
Subr: TYPE USING [AbortMyself, Any,
HugeZone, numberofleaders, PagesUsedInHugeZone,
strcpy, SubrInit, SubrStop],
TimeStamp: TYPE USING [Null, Stamp],
UECP: TYPE USING[Argv, Parse],
UserExec: TYPE USING[CommandProc, GetStreams, RegisterCommand, UserAbort];
DBStashImpl: MONITOR
IMPORTS CWF, Inline, IO, LongString, RopeInline, Subr, UECP, UserExec
EXPORTS DBStash = {
STASHSIZE: CARDINAL = 500;
StashSeq: TYPE = LONG POINTER TO StashSeqRecord;
StashSeqRecord: TYPE = RECORD[
stashzone: UNCOUNTED ZONE ← NIL,
temporarydepseq: Dir.DepSeq ← NIL,
nhits: LONG CARDINAL ← 0,
nmisses: LONG CARDINAL ← 0,
size: CARDINAL ← 0,
body: SEQUENCE maxsize: CARDINAL OF StashRecord
];
ST: TYPE = LONG POINTER TO StashRecord;
StashRecord: TYPE = RECORD[
create: LONG CARDINAL ← 0,
fileName: LONG STRING ← NIL, -- fileName and create are linked
bcdVers: TimeStamp.Stamp ← TimeStamp.Null,
depseq: Dir.DepSeq ← NIL
];
-- MDS usage!!!
stashseq: StashSeq ← NIL;
-- endof mds usage
-- uses linear lookup, should use a hash of some sort
Lookup: PUBLIC ENTRY PROC[create: LONG CARDINAL, fileName: LONG STRING]
RETURNS[depseq: Dir.DepSeq] = {
ENABLE UNWIND => NULL;
RETURN[InternalLookup[create, fileName]];
};
InternalLookup: INTERNAL PROC[create: LONG CARDINAL, fileName: LONG STRING]
RETURNS[depseq: Dir.DepSeq] = {
st: ST;
InternalInit[];
IF create = 0 THEN ERROR;
FOR i: CARDINAL IN [0 .. stashseq.size) DO
st ← @stashseq[i];
IF create = st.create
AND LongString.EquivalentString[fileName, st.fileName] THEN {
stashseq.nhits ← stashseq.nhits + 1;
RETURN[st.depseq];
};
ENDLOOP;
stashseq.nmisses ← stashseq.nmisses + 1;
RETURN[NIL];
};
BcdVersLookup: PUBLIC ENTRY PROC[bcdVers: TimeStamp.Stamp] RETURNS[depseq: Dir.DepSeq] = {
ENABLE UNWIND => NULL;
st: ST;
InternalInit[];
IF bcdVers = TimeStamp.Null THEN ERROR;
FOR i: CARDINAL IN [0 .. stashseq.size) DO
st ← @stashseq[i];
IF bcdVers = st.bcdVers THEN {
stashseq.nhits ← stashseq.nhits + 1;
RETURN[st.depseq];
};
ENDLOOP;
stashseq.nmisses ← stashseq.nmisses + 1;
RETURN[NIL];
};
Insert: PUBLIC ENTRY PROC[create: LONG CARDINAL, fileName: LONG STRING, depseq: Dir.DepSeq]
RETURNS[newdepseq: Dir.DepSeq] = {
ENABLE UNWIND => NULL;
RETURN[InternalInsert[create, fileName, depseq]];
};
-- it is an error to Insert something that is already in the cache
InternalInsert: INTERNAL PROC[create: LONG CARDINAL, fileName: LONG STRING, depseq: Dir.DepSeq]
RETURNS[newdepseq: Dir.DepSeq] = {
newdepseq ← depseq;
InternalInit[];
FOR i: CARDINAL IN [0 .. stashseq.size) DO
IF create = stashseq[i].create
AND LongString.EquivalentString[fileName, stashseq[i].fileName] THEN ERROR;
ENDLOOP;
IF stashseq.size >= stashseq.maxsize THEN {
CWF.WF0["Error - have run out of stash slots.\n"L];
RETURN;
};
stashseq[stashseq.size].create ← create;
stashseq[stashseq.size].fileName ← SpecialCopyString[fileName];
stashseq[stashseq.size].bcdVers ← IF depseq.fromsource THEN TimeStamp.Null
ELSE depseq.bcdVers; -- may be Null
IF depseq ~= stashseq.temporarydepseq THEN ERROR;
-- this allows us to insert only pieces that are really being used
newdepseq ← AllocateDepSeq[depseq.size];
Inline.LongCOPY[to: newdepseq, from: depseq,
nwords: SIZE[Dir.DepSeqRecord[depseq.size]]];
depseq.bcdVers ← TimeStamp.Null;
depseq.size ← 0;
depseq.bcdFileName ← depseq.srcFileName ← depseq.moduleName ← NIL;
stashseq[stashseq.size].depseq ← newdepseq;
stashseq.size ← stashseq.size + 1;
};
GetTemporaryDepSeq: PUBLIC ENTRY PROC RETURNS[Dir.DepSeq] = {
ENABLE UNWIND => NULL;
RETURN[stashseq.temporarydepseq];
};
-- in what follows, I use a different Heap (stashseq.stashzone)
-- for the DepSeqRecord and the Strings in it.
-- A homebuilt Heap is used for the DepSeqRecords, StashSeq's, and strings in it.
SpecialCopyString: PROC[old: LONG STRING, zone: UNCOUNTED ZONE ← NIL]
RETURNS[new: LONG STRING] = {
hugezone: UNCOUNTED ZONE ← stashseq.stashzone;
IF old = NIL THEN ERROR;
new ← hugezone.NEW[StringBody[old.length]
← StringBody[length: 0, maxlength: old.length, text:] ];
Subr.strcpy[new, old];
};
InternalInit: INTERNAL PROC = {
stashzone: UNCOUNTED ZONE;
IF stashseq ~= NIL THEN RETURN;
stashzone ← Subr.HugeZone[];
stashseq ← stashzone.NEW[StashSeqRecord[STASHSIZE]];
stashseq.stashzone ← stashzone;
stashseq.temporarydepseq ← AllocateDepSeq[Dir.MaximumDepSize];
};
-- utility procedures
AllocateDepSeq: PROC[size: CARDINAL] RETURNS[depseq: Dir.DepSeq] = {
depseq ← stashseq.stashzone.NEW[Dir.DepSeqRecord[size]];
-- this so we can be sure we have the right strings
depseq.CopyString ← SpecialCopyString;
};
SetNLeaders: PUBLIC ENTRY PROC = {
InternalInit[];
Subr.numberofleaders ← 0;
};
GetNLeaders: PUBLIC PROC RETURNS[nleaders: CARDINAL] = {
RETURN[Subr.numberofleaders];
};
SetNHits: PUBLIC ENTRY PROC = {
InternalInit[];
stashseq.nhits ← stashseq.nmisses ← 0;
};
GetNHits: PUBLIC PROC RETURNS[nhits, nmisses: LONG CARDINAL]= {
RETURN[stashseq.nhits, stashseq.nmisses];
};
PrintSeparatorLine: PROC[wfproc: PROC[CHAR]] = {
CWF.FWF0[wfproc, "-----------------\n"L];
};
ForceOut: PUBLIC ENTRY PROC RETURNS[npages: CARDINAL] = {
IF stashseq ~= NIL THEN
npages ← Subr.PagesUsedInHugeZone[stashseq.stashzone]
ELSE npages ← 0;
};
-- these are the two commands available in the SimpleExec
Flush: PUBLIC ENTRY UserExec.CommandProc = TRUSTED {
ENABLE UNWIND => NULL;
MyPutChar: PROC[ch: CHAR] = {
out.PutChar[ch];
};
out: IO.Handle = exec.GetStreams[].out;
[] ← CWF.SetWriteProcedure[MyPutChar];
stashseq ← NIL;
Subr.SubrStop[];
CWF.WF0["Dependency Cache Flushed.\n"L];
};
Stat: UserExec.CommandProc = TRUSTED {
MyPutChar: PROC[ch: CHAR] = {
out.PutChar[ch];
};
out: IO.Handle = exec.GetStreams[].out;
[] ← CWF.SetWriteProcedure[MyPutChar];
CWF.WF1["Cache has %u entries:\n"L, @stashseq.size];
FOR i: CARDINAL IN [0 .. stashseq.size) DO
s: LONG STRING;
depseq: Dir.DepSeq;
depseq ← stashseq[i].depseq;
s ← IF depseq.bcdFileName ~= NIL THEN depseq.bcdFileName
ELSE IF depseq.srcFileName ~= NIL THEN depseq.srcFileName
ELSE depseq.moduleName;
CWF.WF1["%s "L, s];
IF UserExec.UserAbort[exec] THEN SIGNAL Subr.AbortMyself;
ENDLOOP;
CWF.WFCR[];
};
PrintEntry: UserExec.CommandProc = TRUSTED {
out: IO.Handle = exec.GetStreams[].out;
depseq: Dir.DepSeq;
filename: STRING ← [100];
argv: UECP.Argv ← UECP.Parse[event.commandLine];
MyPutChar: PROC[ch: CHAR] = {
out.PutChar[ch];
};
[] ← CWF.SetWriteProcedure[MyPutChar];
FOR parm: CARDINAL IN [1 .. argv.argc) DO
flat: Rope.Text ← RopeInline.InlineFlatten[argv[parm]];
Subr.strcpy[filename, LOOPHOLE[flat]];
IF NOT Subr.Any[filename, '.] THEN LongString.AppendString[filename, ".Bcd"L];
FOR i: CARDINAL IN [0 .. stashseq.size) DO
depseq ← stashseq[i].depseq;
IF UserExec.UserAbort[exec] THEN SIGNAL Subr.AbortMyself;
IF (depseq.bcdFileName ~= NIL
AND LongString.EquivalentString[depseq.bcdFileName, filename])
OR (depseq.srcFileName ~= NIL
AND LongString.EquivalentString[depseq.srcFileName, filename]) THEN {
CWF.WF3["Entry under (%s, %lt, %lt):\n"L, stashseq[i].fileName,
@stashseq[i].create, @stashseq[i].bcdVers.time];
PrintDepSeq[depseq, exec.GetStreams[].out];
CWF.WFCR[];
};
REPEAT
FINISHED =>
CWF.WF1["Can't find %s in DB cache.\n"L, filename];
ENDLOOP;
ENDLOOP;
};
PrintDepSeq: PROC[depseq: Dir.DepSeq, window: IO.Handle] = {
zero: STRING ← "(Zero)"L;
tstr: STRING ← [30];
WFPut: PROC[ch: CHAR] = {
window.PutChar[ch];
};
Cvt: PROC[time: LONG CARDINAL] RETURNS[str: STRING] = {
IF time = 0 THEN RETURN[zero]
ELSE CWF.SWF1[tstr, "%lt"L, @time];
RETURN[tstr];
};
IF depseq.bcdFileName ~= NIL THEN
CWF.FWF2[WFPut, "Bcd file %s of %s\n"L, depseq.bcdFileName, Cvt[depseq.bcdVers.time]];
IF depseq.srcFileName ~= NIL THEN {
IF depseq.bcdFileName ~= NIL THEN CWF.FWF0[WFPut, " "L];
CWF.FWF2[WFPut, "Source file %s of %s\n"L, depseq.srcFileName, Cvt[depseq.srcCreate]];
};
CWF.FWF1[WFPut, "ModuleName %s\n"L, depseq.moduleName];
CWF.FWF4[WFPut, " defns %s, config %s, fromsource %s, istablecompiled %s.\n"L,
IF depseq.isdefns THEN "T"L ELSE "F"L,
IF depseq.isconfig THEN "T"L ELSE "F"L,
IF depseq.fromsource THEN "T"L ELSE "F"L,
IF depseq.istablecompiled THEN "T"L ELSE "F"L];
FOR i: CARDINAL IN [0 .. depseq.size) DO
CWF.FWF1[WFPut, " %s "L,
SELECT depseq[i].relation FROM
imports => "Imports: "L,
exports => "Exports: "L,
directory => "Directory:"L,
ENDCASE => ERROR];
CWF.FWF3[WFPut, "%s of %s (%s),\n"L,
depseq[i].bcdFileName, Cvt[depseq[i].bcdVers.time], depseq[i].moduleName];
ENDLOOP;
};
OnceInit: PROC = {
Subr.SubrInit[256];
UserExec.RegisterCommand["XDBFlush.~", Flush];
UserExec.RegisterCommand["XDBPrint.~", PrintEntry];
UserExec.RegisterCommand["XDBStat.~", Stat];
};
-- the start code
OnceInit[];
}.