LoadStateImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Levin on September 20, 1983 4:07 pm
Birrell and Rovner, December 2, 1983 11:13 am
Rovner on December 8, 1983 8:32 am
Russ Atkinson (RRA) March 28, 1986 3:33:58 pm PST
DIRECTORY
BasicLoadState USING [ConfigID, ConfigInfo, Destroy, EnumerateConfigs, EnumerateModulesInConfig, GlobalFrameToType, ModuleIndex, ModuleToGlobalFrame, NullModule],
BasicLoadStateFormat USING [ConfigIndex, LoadState, LoadStateObject, nullModule, versionID],
Basics USING [DivMod],
BcdDefs USING [Base, MTHandle, MTIndex, MTNull],
BcdOps USING [ProcessModules],
DebuggerSwap USING [NoteLoadstate, NoteRealLoadState],
LoadState USING [Access, BcdBase, CopiesList, EnumerationOrder, GlobalFrameHandle, ModuleIndex, nullModule, nullType, Type],
LoadStateFormat USING [CollisionList, CollisionTable, CollisionTableIndex, Config, ConfigObject, ConfigID, ConfigTable, CopyIndex, CopyTable, Entry, fuzzyCopyIndex, Handle, HashIndex, htSize, nullCopyIndex, Object],
PrincOps USING [ControlLink, EmptyGFTItem, EPBias, EPIndex, EPRange, FrameHandle, GFT, GFTIndex, MainBodyIndex, MaxNGfi, NullGlobalFrame, PrefixHandle, sCopy, SD, sGFTLength],
PrincOpsUtils USING [Alloc, BITAND, BITSHIFT, Codebase, Copy, Free, GetReturnFrame, GlobalFrame, MakeFsi, SetReturnLink, LongZero],
SafeStorageOps USING [CopyTypesAndLiterals],
VM USING [AddressForPageNumber, Interval, PagesForWords, SimpleAllocate, WordsForPages];
LoadStateImpl: MONITOR
IMPORTS BasicLoadState, Basics, BcdOps, DebuggerSwap, PrincOpsUtils, SafeStorageOps, VM
EXPORTS LoadState
= BEGIN OPEN LoadState, LoadStateFormat;
Types and Constants
Handle: TYPE = LoadStateFormat.Handle;
Object: PUBLIC TYPE = LoadStateFormat.Object;
ConfigID: PUBLIC TYPE = LoadStateFormat.ConfigID;
nullConfig: PUBLIC ConfigID ← LoadStateFormat.ConfigID.LAST;
local: PUBLIC Handle;
UndoLog: TYPE = LIST OF UndoLogEntry;
UndoLogEntry: TYPE = RECORD [loadState: Handle, configID: ConfigID];
Global variables
released: CONDITION ← [timeout: 0];
undoLog: UndoLog ← NIL;
Synchronization
Acquire: PUBLIC ENTRY SAFE PROC [loadState: Handle, access: Access] = TRUSTED {
DO
SELECT TRUE FROM
access = shared AND loadState.useCount >= 0 => {
loadState.useCount ← loadState.useCount.SUCC; EXIT};
access = exclusive AND loadState.useCount = 0 => {
loadState.useCount ← -1; EXIT};
ENDCASE;
WAIT released;
ENDLOOP;
};
Release: PUBLIC SAFE PROC [loadState: Handle, commit: BOOL] = TRUSTED {
LogProcessingNeeded: ENTRY PROC RETURNS [doLog: BOOL] = {
SELECT loadState.useCount FROM
< 0 => RETURN [TRUE];
> 0 => {
loadState.useCount ← loadState.useCount.PRED;
BROADCAST released;
RETURN [FALSE]
};
ENDCASE => RETURN WITH ERROR CallerBug;
};
LogProcessingDone: ENTRY PROC = {
loadState.useCount ← 0;
BROADCAST released;
};
IF LogProcessingNeeded[] THEN {
IF commit THEN {
loadState.changeCount ← loadState.changeCount.SUCC;
DiscardUndoLog[loadState];
}
ELSE Undo[loadState];
LogProcessingDone[];
};
};
Interrogation Procedures
GetChangeCount: PUBLIC SAFE PROC [loadState: Handle] RETURNS [INT] = TRUSTED {
RETURN[loadState.changeCount];
};
ConfigInfo: PUBLIC SAFE PROC [loadState: Handle, config: ConfigID] RETURNS [bcd: BcdBase, ref: REF ANY] = TRUSTED {
c: Config;
CheckAccess[loadState];
c ← loadState.configTable.configs[config];
RETURN[c.bcd, c.ref]
};
ModuleInfo: PUBLIC SAFE PROC [loadState: Handle, config: ConfigID, module: ModuleIndex] RETURNS [gfh: GlobalFrameHandle, mti: BcdDefs.MTIndex, type: Type, copies: CopiesList] = TRUSTED {
c: Config;
CheckAccess[loadState];
c ← loadState.configTable.configs[config];
IF module >= c.nModules THEN ERROR CallerBug;
IF (mti ← c.modules[module].mti) = BcdDefs.MTNull THEN ERROR CallerBug;
type ← c.modules[module].type;
IF (gfh ← c.modules[module].gfh) = NIL
THEN copies ← NIL
ELSE {
ci: CopyIndex = FindCopyIndex[loadState, gfh];
copies ← IF ci = nullCopyIndex THEN NIL ELSE loadState.copyTable.copies[ci].copies;
};
};
GlobalFrameToModule: PUBLIC SAFE PROC [loadState: Handle, gfh: GlobalFrameHandle] RETURNS [config: ConfigID, module: ModuleIndex] = TRUSTED {
entry: Entry;
CheckAccess[loadState];
ValidateGlobalFrame[loadState, gfh];
entry ← FindEntry[loadState, gfh];
WITH e: entry SELECT FROM
original => RETURN[e.configID, e.module];
copy =>
[config, module] ←
GlobalFrameToModule[loadState, loadState.copyTable.copies[e.copyIndex].parent];
ENDCASE => {config ← nullConfig; module ← BasicLoadState.NullModule};
};
BuildProcDescUsingModule: PUBLIC SAFE PROC [loadState: Handle, config: ConfigID, module: ModuleIndex, ep: PrincOps.EPIndex] RETURNS [PrincOps.ControlLink] = TRUSTED {
gfh: GlobalFrameHandle;
c: Config;
firstModule: ModuleIndex ← module;
CheckAccess[loadState];
c ← loadState.configTable.configs[config];
IF module >= c.nModules THEN ERROR CallerBug;
IF (gfh ← c.modules[module].gfh) = NIL THEN ERROR CallerBug;
FOR i: ModuleIndex DECREASING IN [0..module) UNTIL c.modules[i].gfh ~= gfh DO
firstModule ← i;
ENDLOOP;
See note in interface for an explanation of the following ugliness.
RETURN[
[procedure[
gfi: (IF loadState = local THEN gfh.gfi ELSE 0) + module - firstModule,
ep: ep,
tag: TRUE]]
]
};
BuildProcDescUsingGlobalFrame: PUBLIC SAFE PROC [loadState: Handle, gfh: GlobalFrameHandle, ep: NAT] RETURNS [PrincOps.ControlLink] = TRUSTED {
epb: PrincOps.EPBias;
epi: PrincOps.EPIndex;
CheckLocal[loadState];
IF ep >= PrincOps.MaxNGfi*PrincOps.EPRange THEN ERROR CallerBug;
[epb, epi] ← Basics.DivMod[ep, PrincOps.EPRange];
RETURN[[procedure[gfi: gfh.gfi + epb, ep: epi, tag: TRUE]]]
};
Modification Procedures
LoadStateFull: PUBLIC SAFE ERROR = CODE;
NewConfig: PUBLIC SAFE PROC [loadState: Handle, bcd: BcdBase, ref: REF ANY] RETURNS [configID: ConfigID] = TRUSTED {
NewConfigObject: PROC [bcd: BcdBase] RETURNS [c: Config] = {
nModules: NAT ← 0;
ComputeSize: PROC [mth: BcdDefs.MTHandle, mti: BcdDefs.MTIndex]
RETURNS [BOOLFALSE] = {
nModules ← MAX[nModules, mth.gfi+mth.ngfi];
};
FillInModules: PROC [mth: BcdDefs.MTHandle, mti: BcdDefs.MTIndex]
RETURNS [BOOLFALSE] = {
FOR i: NAT IN [0..mth.ngfi) DO
c.modules[mth.gfi+i].mti ← mti;
ENDLOOP;
};
[] ← BcdOps.ProcessModules[bcd, ComputeSize];
c ← NEW[ConfigObject[nModules]];
c.bcd ← bcd;
c.ref ← ref;
c.nModules ← nModules;
FOR mi: ModuleIndex IN [0..nModules) DO
c.modules[mi] ← [mti: BcdDefs.MTNull, gfh: NIL, type: nullType];
ENDLOOP;
[] ← BcdOps.ProcessModules[bcd, FillInModules];
};
ct: REF ConfigTable;
CheckAccess[loadState, exclusive];
CheckLocal[loadState];
ct ← loadState.configTable;
IF ct.nConfigs = ct.length THEN
IF ct.length = ConfigID.LAST THEN ERROR LoadStateFull
ELSE {
newCT: REF ConfigTable = NEW[ConfigTable[MIN[3*ct.length/2, ConfigID.LAST]]];
newCT.nConfigs ← ct.nConfigs;
FOR ci: ConfigID IN [0..newCT.nConfigs) DO
newCT.configs[ci] ← ct.configs[ci];
ENDLOOP;
loadState.configTable ← ct ← newCT;
};
ct.configs[configID ← ct.nConfigs] ← NewConfigObject[bcd];
ct.nConfigs ← ct.nConfigs.SUCC;
AppendToUndoLog[loadState, configID];
};
SetGlobalFrame: PUBLIC SAFE PROC [ loadState: Handle, config: ConfigID, module: ModuleIndex, gfh: GlobalFrameHandle] = TRUSTED {
c: Config;
mth: BcdDefs.MTHandle;
CheckAccess[loadState, exclusive];
CheckLocal[loadState];
c ← loadState.configTable.configs[config];
IF c.modules[module].gfh ~= NIL OR c.modules[module].mti = BcdDefs.MTNull THEN
ERROR CallerBug;
mth ← @LOOPHOLE[c.bcd + c.bcd.mtOffset, BcdDefs.Base][c.modules[module].mti];
EnterGlobalFrame[gfh, mth.ngfi];
FOR i: NAT IN [0..mth.ngfi) DO
c.modules[module+i].gfh ← gfh;
ENDLOOP;
AddEntry[
loadState, gfh, [original[copyIndex: nullCopyIndex, configID: config, module: module]]];
};
SetType: PUBLIC SAFE PROC [ loadState: Handle, gfh: GlobalFrameHandle, type: Type] = TRUSTED {
entry: Entry;
CheckAccess[loadState, exclusive];
CheckLocal[loadState];
ValidateGlobalFrame[loadState, gfh];
entry ← FindEntry[loadState, gfh];
WITH e: entry SELECT FROM
original =>
loadState.configTable.configs[e.configID].modules[e.module].type ← type;
copy => NULL; -- could verify that it matches the type of the original...
ENDCASE => ERROR;
};
CopyNew: PUBLIC SAFE PROC [loadState: Handle, old: PROGRAM] RETURNS [new: PROGRAM] = TRUSTED {
frame: GlobalFrameHandle = LOOPHOLE[old];
CheckLocal[loadState];
RETURN[LOOPHOLE[LocalCopyNew[frame]]]
};
UnNew: PUBLIC UNSAFE PROC [loadState: Handle, program: PROGRAM] = {
frame: GlobalFrameHandle = LOOPHOLE[program];
CheckLocal[loadState];
ValidateGlobalFrame[loadState, frame];
LocalUnNew[frame];
};
SelfDestruct: PUBLIC UNSAFE PROC = {
destructee: PrincOps.FrameHandle = PrincOpsUtils.GetReturnFrame[];
PrincOpsUtils.SetReturnLink[destructee.returnlink];
LocalUnNew[PrincOpsUtils.GlobalFrame[LOOPHOLE[destructee]]];
PrincOpsUtils.Free[destructee];
};
Enumerators
EnumerateConfigs: PUBLIC SAFE PROC [loadState: Handle, order: EnumerationOrder, proc: PROC [ConfigID] RETURNS [--stop:--BOOL]] RETURNS [config: ConfigID] = TRUSTED {
CheckAccess[loadState];
SELECT order FROM
newestFirst =>
FOR config DECREASING IN [0..loadState.configTable.nConfigs) DO
IF proc[config] THEN EXIT;
REPEAT
FINISHED => RETURN [nullConfig];
ENDLOOP;
oldestFirst =>
FOR config IN [0..loadState.configTable.nConfigs) DO
IF proc[config] THEN EXIT;
REPEAT
FINISHED => RETURN [nullConfig];
ENDLOOP;
ENDCASE;
};
EnumerateModulesInConfig: PUBLIC SAFE PROC [loadState: Handle, config: ConfigID, proc: PROC [ModuleIndex] RETURNS [--stop:--BOOL]] RETURNS [module: ModuleIndex, gfh: GlobalFrameHandle] = TRUSTED {
c: Config;
prev: GlobalFrameHandle ← NIL;
CheckAccess[loadState];
c ← loadState.configTable.configs[config];
FOR module IN [1..c.nModules) DO -- index 0 is always unused
gfh ← c.modules[module].gfh;
IF gfh ~= NIL AND gfh ~= prev THEN {
IF proc[module] THEN RETURN;
prev ← gfh;
};
ENDLOOP;
RETURN [nullModule, NIL]
};
EnumerateAllModules: PUBLIC SAFE PROC [loadState: Handle, order: EnumerationOrder, proc: PROC [ConfigID, ModuleIndex] RETURNS [--stop:--BOOL]] RETURNS [config: ConfigID ← nullConfig, module: ModuleIndex ← nullModule] = TRUSTED {
DoOneConfig: PROC [c: ConfigID] RETURNS [BOOL] = {
PassItOn: PROC [m: ModuleIndex] RETURNS [stop: BOOL] = {
IF (stop ← proc[c, m]) THEN {config ← c; module ← m};
};
RETURN[EnumerateModulesInConfig[loadState, c, PassItOn].module ~= nullModule]
};
CheckAccess[loadState];
[] ← EnumerateConfigs[loadState, order, DoOneConfig];
};
Internal procedures
CallerBug: ERROR = CODE;
Validation
CheckAccess: ENTRY PROC [loadState: Handle, access: Access ← shared] = {
IF loadState.useCount = 0 OR (loadState.useCount > 0 AND access = exclusive) THEN
RETURN WITH ERROR CallerBug;
};
CheckLocal: PROC [loadState: Handle] = {
IF loadState ~= local THEN ERROR CallerBug;
};
ValidateGlobalFrame: PROC [loadState: Handle, frame: GlobalFrameHandle] = {
WITH FindEntry[loadState, frame] SELECT FROM
empty => ERROR CallerBug;
ENDCASE => RETURN;
};
LoadState Maintenance
FindEntry: PROC [loadState: Handle, gfh: GlobalFrameHandle] RETURNS [entry: Entry] = {
Match: PROC [entry: Entry] RETURNS [BOOLFALSE] = {
WITH e: entry SELECT FROM
original =>
IF loadState.configTable.configs[e.configID].modules[e.module].gfh = gfh THEN
RETURN[TRUE];
copy => IF e.gfh = gfh THEN RETURN[TRUE];
ENDCASE;
};
IF Match[entry ← loadState.gfht[Hash[gfh]]] THEN RETURN;
WITH e: entry SELECT FROM
collision =>
FOR list: CollisionList ← loadState.collisionTable.collisions[e.list], list.rest UNTIL list = NIL DO
IF Match[list.first] THEN RETURN[list.first];
ENDLOOP;
ENDCASE;
RETURN[[empty[]]]
};
FindCopyIndex: PROC [loadState: Handle, gfh: GlobalFrameHandle] RETURNS [CopyIndex ← nullCopyIndex] = {
entry: Entry = FindEntry[loadState, gfh];
WITH e: entry SELECT FROM
original => {
SELECT e.copyIndex FROM
nullCopyIndex => RETURN;
fuzzyCopyIndex =>
FOR ci: CopyIndex IN [fuzzyCopyIndex..loadState.copyTable.nCopies) DO
IF loadState.copyTable.copies[ci].parent = gfh THEN RETURN [ci];
ENDLOOP;
ENDCASE => RETURN[e.copyIndex]
};
copy => RETURN[e.copyIndex]
ENDCASE;
};
SetCopyIndex: PROC [loadState: Handle, gfh: GlobalFrameHandle, copyIndex: CopyIndex] = {
entry: LONG POINTER TO Entry = @loadState.gfht[Hash[gfh]];
copyIndex ← MIN[copyIndex, fuzzyCopyIndex];
WITH e: entry SELECT FROM
original => e.copyIndex ← copyIndex;
collision =>
FOR list: CollisionList ← loadState.collisionTable.collisions[e.list], list.rest UNTIL list = NIL DO
WITH f: list.first SELECT FROM
original =>
IF loadState.configTable.configs[f.configID].modules[f.module].gfh = gfh THEN
{f.copyIndex ← copyIndex; EXIT};
copy => NULL;
ENDCASE => ERROR;
REPEAT
FINISHED => ERROR;
ENDLOOP;
ENDCASE => ERROR;
};
AddEntry: PROC [loadState: Handle, gfh: GlobalFrameHandle, entry: Entry] = {
hi: HashIndex = Hash[gfh];
oldEntry: Entry = loadState.gfht[hi];
WITH e: oldEntry SELECT FROM
empty => loadState.gfht[hi] ← entry;
collision =>
loadState.collisionTable.collisions[e.list] ←
CONS[entry, loadState.collisionTable.collisions[e.list]];
ENDCASE => {
ct: REF CollisionTable ← loadState.collisionTable;
ci: CollisionTableIndex;
FOR ci IN [0..ct.nCollisions) DO
IF ct.collisions[ci] = NIL THEN EXIT;
REPEAT
FINISHED => {
IF ct.nCollisions = ct.length THEN {
newCT: REF CollisionTable = NEW[CollisionTable[3*ct.length/2]];
newCT.nCollisions ← ct.nCollisions;
FOR ci: CollisionTableIndex IN [0..newCT.nCollisions) DO
newCT.collisions[ci] ← ct.collisions[ci];
ENDLOOP;
loadState.collisionTable ← ct ← newCT;
};
ci ← ct.nCollisions;
ct.nCollisions ← ct.nCollisions.SUCC;
};
ENDLOOP;
ct.collisions[ci] ← CONS[entry, CONS[loadState.gfht[hi], NIL]];
loadState.gfht[hi] ← [collision[list: ci]];
};
};
RemoveEntry: PROC [loadState: Handle, gfh: GlobalFrameHandle] = {
hi: HashIndex = Hash[gfh];
oldEntry: Entry = loadState.gfht[hi];
WITH e: oldEntry SELECT FROM
original => loadState.gfht[hi] ← [empty[]];
copy => loadState.gfht[hi] ← [empty[]];
collision => {
list: CollisionList ← loadState.collisionTable.collisions[e.list];
prev: CollisionList ← NIL;
UNTIL list = NIL DO
WITH f: list.first SELECT FROM
original =>
IF loadState.configTable.configs[f.configID].modules[f.module].gfh = gfh THEN
GO TO found;
copy => IF f.gfh = gfh THEN GO TO found;
ENDCASE => ERROR;
prev ← list;
list ← list.rest;
REPEAT
found =>
IF prev = NIL THEN loadState.collisionTable.collisions[e.list] ← list.rest
ELSE prev.rest ← list.rest;
FINISHED => ERROR;
ENDLOOP;
IF loadState.collisionTable.collisions[e.list].rest = NIL THEN { -- collisions gone
loadState.gfht[hi] ← loadState.collisionTable.collisions[e.list].first;
loadState.collisionTable.collisions[e.list] ← NIL;
};
};
ENDCASE => ERROR;
};
Hash: PROC [gfh: GlobalFrameHandle] RETURNS [HashIndex] = INLINE {
RETURN [PrincOpsUtils.BITSHIFT[LOOPHOLE[gfh], -2] MOD htSize]
};
Undo Log Maintenance
Undo: PROC [loadState: Handle] = {
Note: This code assumes that, if there are multiple configs to be undone, they appear in descending order of configID on the undo list.
prev: UndoLog ← NIL;
FOR list: UndoLog ← undoLog, list.rest UNTIL list = NIL DO
IF list.first.loadState = loadState THEN {
configID: ConfigID = list.first.configID;
RemoveModule: PROC [module: ModuleIndex] RETURNS [BOOLFALSE] = {
gfh: GlobalFrameHandle = ModuleInfo[loadState, configID, module].gfh;
RemoveGlobalFrame[gfh];
RemoveEntry[loadState, gfh];
};
IF loadState ~= local OR configID ~= loadState.configTable.nConfigs.PRED THEN ERROR;
[] ← EnumerateModulesInConfig[loadState, configID, RemoveModule];
loadState.configTable.configs[configID] ← NIL; -- make collector happy
loadState.configTable.nConfigs ← configID;
IF prev = NIL THEN undoLog ← list.rest ELSE prev.rest ← list.rest;
}
ELSE prev ← list;
ENDLOOP;
};
AppendToUndoLog: PROC [loadState: Handle, config: ConfigID] = {
undoLog ← CONS[UndoLogEntry[loadState, config], undoLog];
};
DiscardUndoLog: PROC [loadState: Handle] = {
ConfigList: TYPE = LIST OF ConfigID;
prev: UndoLog ← NIL;
debuggerLSList: ConfigList ← NIL;
FOR list: UndoLog ← undoLog, list.rest UNTIL list = NIL DO
IF list.first.loadState = loadState THEN {
IF loadState ~= local THEN ERROR;
debuggerLSList ← CONS[list.first.configID, debuggerLSList];
IF prev = NIL THEN undoLog ← list.rest ELSE prev.rest ← list.rest;
}
ELSE prev ← list;
ENDLOOP;
We can now do the debugger loadstate list in the proper order (ascending configIDs).
FOR list: ConfigList ← debuggerLSList, list.rest UNTIL list = NIL DO
UpdateDebuggerLoadState[list.first];
ENDLOOP;
};
New and UnNew
LocalCopyNew: PROC [old: GlobalFrameHandle] RETURNS [new: GlobalFrameHandle] = {
FindOriginal: PROC [old: GlobalFrameHandle] RETURNS [GlobalFrameHandle] = {
entry: Entry = FindEntry[local, old];
WITH e: entry SELECT FROM
original => RETURN[old];
copy => RETURN[local.copyTable.copies[e.copyIndex].parent];
ENDCASE => RETURN [NIL];
};
AllocGlobalFrame: PROC [old: GlobalFrameHandle, cp: PrincOps.PrefixHandle] RETURNS [frame: GlobalFrameHandle, linkspace: CARDINAL] = {
pGFSize: LONG POINTER TO CARDINAL =
LOOPHOLE[cp + CARDINAL[cp.entry[PrincOps.MainBodyIndex].initialpc] - 1];
nLinks: CARDINAL = cp.header.info.nlinks;
nWords: CARDINAL;
linkspace ←
IF old.codelinks THEN 0 ELSE
nLinks + PrincOpsUtils.BITAND[-LOOPHOLE[nLinks, INTEGER], 3B];
nWords ← pGFSize^ + linkspace;
frame ← PrincOpsUtils.Alloc[PrincOpsUtils.MakeFsi[nWords]];
[] ← PrincOpsUtils.LongZero[LONG[frame], nWords];
};
AddCopy: PROC [original: GlobalFrameHandle, copy: GlobalFrameHandle] = {
ct: REF CopyTable ← local.copyTable;
ci: CopyIndex ← FindCopyIndex[local, original];
IF ci = nullCopyIndex THEN {
FOR ci IN [1..ct.nCopies) DO -- slot 0 is never used ( = nullCopyIndex)
IF ct.copies[ci].copies = NIL THEN EXIT; -- reuse an available slot
REPEAT
FINISHED => {
IF ct.nCopies = ct.length THEN {
newCT: REF CopyTable = NEW[CopyTable[3*ct.length/2]];
newCT.nCopies ← ct.nCopies;
FOR ci: CopyIndex IN [0..newCT.nCopies) DO
newCT.copies[ci] ← ct.copies[ci];
ENDLOOP;
local.copyTable ← ct ← newCT;
};
ci ← ct.nCopies;
ct.nCopies ← ct.nCopies.SUCC;
};
ENDLOOP;
ct.copies[ci] ← [parent: original, copies: NIL];
SetCopyIndex[local, original, ci];
};
ct.copies[ci].copies ← CONS[copy, ct.copies[ci].copies];
AddEntry[local, copy, [copy[copyIndex: ci, gfh: copy]]];
};
linkspace: CARDINAL;
codebase: PrincOps.PrefixHandle;
entry: Entry;
ValidateGlobalFrame[local, old];
Acquire[local, exclusive];
old ← FindOriginal[old];
IF old = NIL THEN RETURN;
[new, linkspace] ← AllocGlobalFrame[old, codebase ← PrincOpsUtils.Codebase[old]];
new ← new + linkspace;
new.alloced ← new.shared ← new.copied ← TRUE;
new.started ← new.trapxfers ← FALSE;
new.codelinks ← old.codelinks;
new.code ← old.code;
new.code.out ← TRUE; -- will cause start trap
new.global[0] ← LOOPHOLE[PrincOps.NullGlobalFrame];
IF linkspace ~= 0 THEN
PrincOpsUtils.Copy[from: old - linkspace, to: new - linkspace, nwords: linkspace];
EnterGlobalFrame[new, codebase.header.info.ngfi
! LoadStateFull => {
PrincOpsUtils.Free[new - linkspace];
Release[loadState: local, commit: FALSE];
}
];
old.shared ← TRUE;
entry ← FindEntry[local, old];
WITH e: entry SELECT FROM
original =>
SafeStorageOps.CopyTypesAndLiterals[
bcd: local.configTable[e.configID].bcd, mi: e.module, old: old, new: new];
ENDCASE => ERROR;
AddCopy[original: old, copy: new];
Release[loadState: local, commit: TRUE];
};
LocalUnNew: UNSAFE PROC [frame: GlobalFrameHandle] = {
RemoveCopy: PROC [copy: GlobalFrameHandle] = {
prev: CopiesList ← NIL;
ct: REF CopyTable = local.copyTable;
ci: CopyIndex = FindCopyIndex[local, copy];
IF ci = nullCopyIndex THEN ERROR;
FOR list: CopiesList ← ct.copies[ci].copies, list.rest UNTIL list = NIL DO
IF list.first = copy THEN {
IF prev = NIL THEN ct.copies[ci].copies ← list.rest ELSE prev.rest ← list.rest;
EXIT
};
prev ← list;
REPEAT
FINISHED => ERROR;
ENDLOOP;
IF ct.copies[ci].copies = NIL THEN -- last copy of parent has been UnNewed
SetCopyIndex[local, ct.copies[ci].parent, nullCopyIndex];
RemoveEntry[local, copy];
};
entry: Entry;
Acquire[local, exclusive];
entry ← FindEntry[local, frame];
WITH e: entry SELECT FROM
original => {Release[loadState: local, commit: FALSE]; ERROR CallerBug};
copy => {
codebase: PrincOps.PrefixHandle = PrincOpsUtils.Codebase[frame];
Align: PROC [POINTER, WORD] RETURNS [POINTER] = LOOPHOLE[PrincOpsUtils.BITAND];
IF ~frame.alloced THEN ERROR;
RemoveGlobalFrame[frame];
RemoveCopy[frame];
PrincOpsUtils.Free[Align[frame - codebase.header.info.nlinks, 177774B]]
};
ENDCASE => ERROR;
Release[loadState: local, commit: TRUE];
};
GFT Maintenance
gftRover: [1..LAST[PrincOps.GFTIndex]+1] ← 1;
for creation of new GFT entries. 0 is reserved, and we must be able to handle gftRover = LAST[PrincOps.GFTIndex]+1!
EnterGlobalFrame: PROC [frame: GlobalFrameHandle, nSlots: [1..PrincOps.MaxNGfi]] = {
kMax: PrincOps.GFTIndex = PrincOps.SD[PrincOps.sGFTLength] - nSlots;
start: PrincOps.GFTIndex ← MIN[kMax, gftRover];
DO
FOR gfi: PrincOps.GFTIndex IN [start..kMax] DO
IF PrincOps.GFT[gfi] = PrincOps.EmptyGFTItem THEN {
We have one free slot so far, try for more (if needed)
IF nSlots # 1 THEN
FOR k: PrincOps.GFTIndex IN [gfi+1..gfi+nSlots) DO
IF PrincOps.GFT[k] # PrincOps.EmptyGFTItem THEN GO TO fail;
ENDLOOP;
We now have enough free slots (starting at gfi)
frame.gfi ← gfi;
FOR epOffset: PrincOps.EPBias IN [0..nSlots) DO
PrincOps.GFT[gfi + epOffset].framePtr ← frame;
PrincOps.GFT[gfi + epOffset].epbias ← epOffset;
ENDLOOP;
gftRover ← gfi+nSlots;
RETURN;
EXITS fail => {};
};
ENDLOOP;
IF start = 1 THEN EXIT;
start ← 1;
ENDLOOP;
ERROR LoadStateFull;
};
RemoveGlobalFrame: PROC [frame: GlobalFrameHandle] = {
gfi: PrincOps.GFTIndex = frame.gfi;
data: [0..37777B] = PrincOps.GFT[gfi].data;
IF gfi < gftRover THEN gftRover ← gfi;
FOR k: PrincOps.GFTIndex IN [gfi..PrincOps.SD[PrincOps.sGFTLength]) DO
IF PrincOps.GFT[k].data # data THEN EXIT;
PrincOps.GFT[k] ← PrincOps.EmptyGFTItem;
ENDLOOP;
};
Debugger LoadState
debuggerLoadState: BasicLoadStateFormat.LoadState;
InitializeLoadState: PROC = {
debuggerLSInterval: VM.Interval;
InitializeDebuggerLoadstate: PROC = {
maxConfigs: NAT = BasicLoadStateFormat.ConfigIndex.LAST;
debuggerLSInterval ← VM.SimpleAllocate[
count: VM.PagesForWords[SIZE[BasicLoadStateFormat.LoadStateObject[maxConfigs]]]];
debuggerLoadState ← VM.AddressForPageNumber[debuggerLSInterval.page];
[] ← PrincOpsUtils.LongZero[debuggerLoadState, VM.WordsForPages[debuggerLSInterval.count]];
debuggerLoadState.versionident ← BasicLoadStateFormat.versionID;
FOR gfi: PrincOps.GFTIndex IN PrincOps.GFTIndex DO
debuggerLoadState.gft[gfi] ← BasicLoadStateFormat.nullModule;
ENDLOOP;
LOOPHOLE[@debuggerLoadState.bcds, LONG POINTER TO CARDINAL]^ ← maxConfigs;
};
nConfigs: ConfigID ← 0;
CountConfigs: PROC [bc: BasicLoadState.ConfigID] RETURNS [BOOLFALSE] = {
nConfigs ← nConfigs.SUCC;
};
DoOneConfig: PROC [bc: BasicLoadState.ConfigID] RETURNS [BOOLFALSE] = {
configID: ConfigID = NewConfig[local, BasicLoadState.ConfigInfo[bc].bcd, NIL];
c: Config = local.configTable.configs[configID];
mtb: BcdDefs.Base = LOOPHOLE[c.bcd + c.bcd.mtOffset];
DoOneModule: PROC [bm: BasicLoadState.ModuleIndex]
RETURNS [BOOLFALSE] = {
gfh: GlobalFrameHandle = BasicLoadState.ModuleToGlobalFrame[bc, bm];
IF c.modules[bm].gfh ~= NIL OR c.modules[bm].mti = BcdDefs.MTNull THEN ERROR;
FOR i: NAT IN [0..mtb[c.modules[bm].mti].ngfi) DO
c.modules[bm+i].gfh ← gfh;
ENDLOOP;
AddEntry[
local, gfh, [original[copyIndex: nullCopyIndex, configID: configID, module: bm]]];
SetType[local, gfh, LOOPHOLE[BasicLoadState.GlobalFrameToType[gfh]]];
};
[] ← BasicLoadState.EnumerateModulesInConfig[bc, DoOneModule];
The configuration will be entered in the debugger loadstate at commit time.
};
InitializeDebuggerLoadstate[];
[] ← BasicLoadState.EnumerateConfigs[CountConfigs];
local ← NEW[Object ← [
useCount: 0,
configTable: NEW[ConfigTable[MIN[MAX[3*nConfigs/2, 2], ConfigID.LAST]]],
copyTable: NEW[CopyTable[5]],
collisionTable: NEW[CollisionTable[10]],
gfht: ALL[[empty[]]]
]];
local.configTable.nConfigs ← local.collisionTable.nCollisions ← 0;
local.copyTable.nCopies ← 1; -- because slot 0 isn't used
Acquire[local, exclusive];
[] ← BasicLoadState.EnumerateConfigs[DoOneConfig];
Release[loadState: local, commit: TRUE]; -- enters all configurations in the debugger loadstate
DebuggerSwap.NoteLoadstate[debuggerLSInterval.page];
DebuggerSwap.NoteRealLoadState[LOOPHOLE[local, LONG POINTER]];
BasicLoadState.Destroy[];
};
UpdateDebuggerLoadState: PROC [configID: ConfigID] = {
bcd: BcdBase = ConfigInfo[local, configID].bcd;
mtb: BcdDefs.Base = LOOPHOLE[bcd + bcd.mtOffset];
DoOneModule: PROC [module: ModuleIndex] RETURNS [BOOLFALSE] = {
gfh: GlobalFrameHandle;
mti: BcdDefs.MTIndex;
[gfh: gfh, mti: mti] ← ModuleInfo[local, configID, module];
FOR i: NAT IN [0..mtb[mti].ngfi) DO
debuggerLoadState.gft[gfh.gfi+i] ← [resolved: FALSE, config: configID, module: module];
ENDLOOP;
};
IF configID ~= debuggerLoadState.nBcds THEN ERROR;
debuggerLoadState.bcds[configID] ← [ptr[bcd]];
debuggerLoadState.nBcds ← debuggerLoadState.nBcds.SUCC;
[] ← EnumerateModulesInConfig[local, configID, DoOneModule];
};
Initialization
InitializeLoadState[];
PrincOps.SD[PrincOps.sCopy] ← LOOPHOLE[LocalCopyNew];
END.