DIRECTORY
BasicLoadState
USING [
ConfigID, ConfigInfo, Destroy, EnumerateConfigs, EnumerateModulesInConfig, GlobalFrameToType, ModuleIndex, ModuleToGlobalFrame],
BasicLoadStateFormat USING [ConfigIndex, LoadState, LoadStateObject, nullModule, versionID],
BcdDefs USING [Base, MTHandle, MTIndex, MTNull],
BcdOps USING [ProcessModules],
DebuggerSwap USING [NoteLoadstate],
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, CSegPrefix, EmptyGFTItem, EPBias, EPIndex, FrameHandle, GFT, GFTIndex, MainBodyIndex, NullGlobalFrame, sCopy, SD, sGFTLength],
PrincOpsUtils
USING [
Alloc, BITAND, BITSHIFT, CodeBase, COPY, Free, GetReturnFrame, GlobalFrame, MakeFsi, SetReturnLink, ZERO],
SafeStorageOps USING [CopyTypesAndLiterals],
VM USING [Allocate, Interval, PageNumberToAddress, PagesToWords, WordsToPages];
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
ConfigInfo:
PUBLIC
SAFE PROC [loadState: Handle, config: ConfigID]
RETURNS [bcd: BcdBase] = TRUSTED {
CheckAccess[loadState];
RETURN[loadState.configTable.configs[config].bcd]
};
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;
};
};
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]]
]
};
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 => ERROR;
};
Modification Procedures
LoadStateFull: PUBLIC SAFE ERROR = CODE;
NewConfig:
PUBLIC SAFE PROC [loadState: Handle, bcd: BcdBase]
RETURNS [configID: ConfigID] = TRUSTED {
NewConfigObject:
PROC [bcd: BcdBase]
RETURNS [c: Config] = {
nModules: NAT ← 0;
ComputeSize:
PROC [mth: BcdDefs.MTHandle, mti: BcdDefs.MTIndex]
RETURNS [BOOL ← FALSE] = {
nModules ← MAX[nModules, mth.gfi+mth.ngfi];
};
FillInModules:
PROC [mth: BcdDefs.MTHandle, mti: BcdDefs.MTIndex]
RETURNS [BOOL ← FALSE] = {
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.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[LOOPHOLE[PrincOpsUtils.GlobalFrame[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, config, PassItOn].module ~= nullModule]
};
CheckAccess[loadState];
[] ← EnumerateConfigs[loadState, order, DoOneConfig];
};
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 [
BOOL ←
FALSE] = {
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] = {
entry: Entry = FindEntry[loadState, gfh];
WITH e: entry
SELECT
FROM
original => {
SELECT e.copyIndex
FROM
nullCopyIndex => RETURN[nullCopyIndex];
fuzzyCopyIndex =>
FOR ci: CopyIndex
IN [fuzzyCopyIndex..loadState.copyTable.nCopies)
DO
IF loadState.copyTable.copies[ci].parent = gfh THEN RETURN [ci];
REPEAT
FINISHED => ERROR;
ENDLOOP;
ENDCASE => RETURN[e.copyIndex]
};
copy => RETURN[e.copyIndex]
ENDCASE => ERROR;
};
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;
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[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 [
BOOL ←
FALSE] = {
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] =
--INLINE-- {
entry: Entry = FindEntry[local, old];
WITH e: entry
SELECT
FROM
original => RETURN[old];
copy => RETURN[local.copyTable.copies[e.copyIndex].parent];
ENDCASE => ERROR;
};
AllocGlobalFrame:
PROC [
old: GlobalFrameHandle, cp: LONG POINTER TO PrincOps.CSegPrefix]
RETURNS [frame: GlobalFrameHandle, linkspace: CARDINAL] = --INLINE-- {
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.ZERO[LONG[frame], nWords];
};
AddCopy:
PROC [original: GlobalFrameHandle, copy: GlobalFrameHandle] =
--INLINE-- {
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: LONG POINTER TO PrincOps.CSegPrefix;
entry: Entry;
ValidateGlobalFrame[local, old];
Acquire[local, exclusive];
old ← FindOriginal[old];
[new, linkspace] ← AllocGlobalFrame[old, codebase ← PrincOpsUtils.CodeBase[old]];
new ← new + linkspace;
new^ ← [
gfi: , alloced: TRUE, shared: TRUE, copied: TRUE, started: FALSE,
trapxfers: FALSE, codelinks: old.codelinks, code: old.code, global:];
new.code.out ← TRUE; -- cause start trap
new.global[0] ← 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] =
--INLINE-- {
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: LONG POINTER TO PrincOps.CSegPrefix = 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: CARDINAL ← 1; -- for creation of new GFT entries. (Note that 0 is reserved.)
EnterGlobalFrame:
PROC [frame: GlobalFrameHandle, nSlots: PrincOps.EPBias] = {
k: PrincOps.GFTIndex ← gftRover;
kMax: PrincOps.GFTIndex = PrincOps.SD[PrincOps.sGFTLength] - nSlots;
n: NAT ← 0;
DO
IF (k ← IF k >= kMax THEN 1 ELSE k + 1) = gftRover THEN ERROR LoadStateFull;
IF PrincOps.GFT[k] ~= PrincOps.EmptyGFTItem THEN n ← 0
ELSE IF (n ← n + 1) = nSlots THEN EXIT;
ENDLOOP;
frame.gfi ← (gftRover ← k) - nSlots + 1;
FOR epOffset: PrincOps.EPBias
IN [0..nSlots)
DO
PrincOps.GFT[frame.gfi + epOffset].framePtr ← frame;
PrincOps.GFT[frame.gfi + epOffset].epbias ← epOffset;
ENDLOOP;
};
RemoveGlobalFrame:
PROC [frame: GlobalFrameHandle] = {
codebase: LONG POINTER TO PrincOps.CSegPrefix = PrincOpsUtils.CodeBase[frame];
FOR k:
NAT
IN [0..codebase.header.info.ngfi)
DO
PrincOps.GFT[frame.gfi+k] ← PrincOps.EmptyGFTItem;
ENDLOOP;
};
Debugger LoadState (a cleverer worldswap debugger would render this unnecessary)
debuggerLoadState: BasicLoadStateFormat.LoadState;
InitializeLoadState:
PROC = {
debuggerLSInterval: VM.Interval;
InitializeDebuggerLoadstate:
PROC = {
maxConfigs: NAT = BasicLoadStateFormat.ConfigIndex.LAST;
debuggerLSInterval ←
VM.Allocate[
count: VM.WordsToPages[SIZE[BasicLoadStateFormat.LoadStateObject[maxConfigs]]]];
debuggerLoadState ← VM.PageNumberToAddress[debuggerLSInterval.page];
[] ← PrincOpsUtils.ZERO[debuggerLoadState, VM.PagesToWords[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 [
BOOL ←
FALSE] = {
nConfigs ← nConfigs.SUCC;
};
DoOneConfig:
PROC [bc: BasicLoadState.ConfigID]
RETURNS [
BOOL ←
FALSE] = {
configID: ConfigID = NewConfig[local, BasicLoadState.ConfigInfo[bc].bcd];
c: Config = local.configTable.configs[configID];
mtb: BcdDefs.Base = LOOPHOLE[c.bcd + c.bcd.mtOffset];
DoOneModule:
PROC [bm: BasicLoadState.ModuleIndex]
RETURNS [BOOL ← FALSE] = {
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];
BasicLoadState.Destroy[];
};
UpdateDebuggerLoadState:
PROC [configID: ConfigID] = {
bcd: BcdBase = ConfigInfo[local, configID];
mtb: BcdDefs.Base = LOOPHOLE[bcd + bcd.mtOffset];
DoOneModule:
PROC [module: ModuleIndex]
RETURNS [
BOOL ←
FALSE] = {
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];
};
PrincOps.SD[PrincOps.sCopy] ← LocalCopyNew;