<<>> <> <> <> <> <> <> DIRECTORY ConvertUnsafe USING [EqualSubStrings, SubString], FileParmOps USING [], FileParms USING [AcquireOp, ActualId, BindingOp, BindingProc, ForgetOp, Name, nullActual, Ops, ReleaseOp, SymbolSpaceRep], FileParmsPrivate USING [FileRecord, SymbolSpaceRep], Host: TYPE MachineParms USING [bitsPerAU, bitsPerChar], MimCommandUtil USING [KeyValue, PairList], MimSysOps USING [Map, MapBase, MapLength, MappedFile, UnMap], MimZones USING [permZone, RegisterForReset, tempZone], MobDefs USING [Base, FTSelf, Mob, MobBase, MobOffset, MTIndex, MTRecord, NameRecord, NameString, NullVersion, SGIndex, SGNull, VersionStamp], MobMapper USING [AlterMob], Rope USING [Concat, Equal, Find, Flatten, Length, ROPE], Symbols USING [HTIndex, MDFirst, MDIndex], SymbolSegment USING [Base, biases, bodyType, ctxType, ExtFirst, extType, FGHeader, htType, ltType, mdType, seType, ssType, STHeader, stType, treeType], SymbolTable USING [Handle], SymbolTablePrivate USING [SymbolTableBaseRep]; FileParmsImpl: MONITOR IMPORTS ConvertUnsafe, MimCommandUtil, MimSysOps, MimZones, MobMapper, Rope EXPORTS FileParms, FileParmOps, SymbolTable = { ROPE: TYPE = Rope.ROPE; MappedFile: TYPE = MimSysOps.MappedFile; Name: TYPE = FileParms.Name; ActualId: TYPE = FileParms.ActualId; BindingProc: TYPE = FileParms.BindingProc; nullActual: ActualId = FileParms.nullActual; VersionStamp: TYPE = MobDefs.VersionStamp; SymbolSpace: TYPE = REF SymbolSpaceRep; SymbolSpaceRep: PUBLIC TYPE = FileParmsPrivate.SymbolSpaceRep; <> FileIndex: TYPE = NAT; nullFileIndex: FileIndex = FileIndex.LAST; bytesPerUnit: NAT = Host.bitsPerAU / Host.bitsPerChar; <> BindingOpImpl: FileParms.BindingOp = { i: FileIndex ¬ nullFileIndex; name: ROPE ¬ FileName[formalId, defaultLocator]; IF name # NIL THEN { file: MappedFile ¬ NIL; version: MobDefs.VersionStamp; start: INT; length: INT; FOR i IN [0 .. nextFile) DO space: SymbolSpace ¬ fileTable[i].space; IF space # NIL AND Rope.Equal[name, fileTable[i].name, FALSE] THEN IF Rope.Equal[formalType, fileTable[i].type, FALSE] THEN GO TO found; ENDLOOP; file ¬ MimSysOps.Map[name].mf; <> IF file = NIL THEN GO TO empty; <> [version, start, length] ¬ ReadHeader[file, formalType]; IF version = nullActual.version THEN { <> MimSysOps.UnMap[file]; GO TO empty; }; i ¬ SearchCache[version]; <> IF i = nullFileIndex OR NOT Rope.Equal[formalId, fileTable[i].formal] THEN { <> space: SymbolSpace ¬ NEW[SymbolSpaceRep ¬ [ file: file, start: start, length: length]]; i ¬ NewCacheEntry[version: version, space: space, name: name, formal: formalId, type: formalType]; GO TO found; }; <> MimSysOps.UnMap[file]; GO TO found; EXITS found => {binder[[fileTable[i].version, fileTable[i].name]]; RETURN}; empty => {}; }; binder[nullActual]; }; AcquireOpImpl: FileParms.AcquireOp = { i: FileIndex ¬ SearchCache[actual.version]; space: SymbolSpace ¬ NIL; IF i = nullFileIndex THEN i ¬ NewCacheEntry[version: actual.version, space: NIL, name: actual.locator, formal: actual.locator, type: name]; space ¬ fileTable[i].space; IF space = NIL THEN { file: MappedFile ¬ MimSysOps.Map[fileTable[i].name].mf; IF file # NIL THEN { version: MobDefs.VersionStamp; start: INT; length: INT; [version, start, length] ¬ ReadHeader[file, fileTable[i].type]; IF version # fileTable[i].version THEN ClearCacheEntry[i] ELSE space ¬ fileTable[i].space ¬ NEW[SymbolSpaceRep ¬ [ file: file, start: start, length: length]]; }; }; RETURN [space]; }; ReleaseOpImpl: FileParms.ReleaseOp = { NULL; -- ref counts? }; ForgetOpImpl: FileParms.ForgetOp = { i: NAT ¬ 0; WHILE i < nextFile DO name: ROPE ¬ fileTable[i].name; IF fileTable[i].version = actual.version OR (name # NIL AND Rope.Equal[name, actual.locator, FALSE]) THEN { ClearCacheEntry[i]; nextFile ¬ nextFile - 1; IF i = nextFile THEN EXIT; fileTable[i] ¬ fileTable[nextFile]; fileTable[nextFile] ¬ []; LOOP; }; i ¬ i + 1; ENDLOOP; }; <> aList: MimCommandUtil.PairList; SetAList: PUBLIC PROC [map: MimCommandUtil.PairList] = {aList ¬ map}; ClearAList: PUBLIC PROC = {aList ¬ NIL}; <> Initialize: PUBLIC PROC RETURNS [FileParms.Ops] = { Finalize[]; AdjustFileTable[16]; nextFile ¬ 0; RETURN [[BindingOpImpl, AcquireOpImpl, ReleaseOpImpl, ForgetOpImpl]]; }; Finalize: PUBLIC PROC = { IF fileTable # NIL THEN { FOR i: NAT IN [0..nextFile) DO ClearCacheEntry[i] ENDLOOP; MimZones.tempZone.FREE[@fileTable]; }; Cleanup[]; }; <> FileName: PROC [key: Name, default: Name] RETURNS [ROPE] = { t: ROPE ¬ MimCommandUtil.KeyValue[key, aList]; IF t = NIL THEN t ¬ default; IF t = NIL THEN t ¬ key; RETURN [NormalizeFileName[t]]; }; NormalizeFileName: PROC [formal: Name] RETURNS [s: ROPE ¬ NIL] = INLINE { IF NOT Rope.Equal[formal, "$"] THEN { dotIndex: INT ¬ Rope.Find[formal, "."]; s ¬ IF dotIndex < 0 THEN Rope.Concat[formal, ".mob"] ELSE formal; }; }; <> FileRecord: TYPE = FileParmsPrivate.FileRecord; FileTable: TYPE = RECORD [ SEQUENCE length: FileIndex OF FileRecord ]; fileTable: REF FileTable ¬ NIL; nextFile: NAT ¬ 0; <> SearchCache: PROC [version: MobDefs.VersionStamp] RETURNS [FileIndex] = { FOR i: FileIndex IN [0 .. nextFile) DO IF fileTable[i].version = version THEN RETURN [i]; ENDLOOP; RETURN [nullFileIndex]; }; NewCacheEntry: PROC [version: VersionStamp, space: SymbolSpace, name: Name, formal: Name, type: Name] RETURNS [i: FileIndex] = { WHILE nextFile >= fileTable.length DO AdjustFileTable[fileTable.length + 16] ENDLOOP; i ¬ nextFile; fileTable[i] ¬ [version: version, space: space, name: name, formal: formal, type: type]; nextFile ¬ nextFile + 1; }; AdjustFileTable: PROC [newSize: NAT] = { newTable: REF FileTable ¬ NIL; oldSize: NAT = IF fileTable = NIL THEN 0 ELSE fileTable.length; IF newSize = 0 THEN newTable ¬ NIL ELSE { i: FileIndex; newTable ¬ MimZones.tempZone.NEW[FileTable[newSize]]; FOR i IN [0..MIN[oldSize, newSize]) DO newTable[i] ¬ fileTable[i] ENDLOOP; FOR i IN [oldSize..newSize) DO newTable[i] ¬ [version: nullActual.version] ENDLOOP; }; MimZones.tempZone.FREE[@fileTable]; fileTable ¬ newTable; }; ClearCacheEntry: PROC [i: FileIndex] = { space: SymbolSpace ¬ fileTable[i].space; fileTable[i] ¬ []; IF space # NIL THEN { file: MappedFile = space.file; IF file # NIL THEN MimSysOps.UnMap[file]; }; }; <> ReadHeader: PROC [file: MappedFile, formalId: Name] RETURNS [version: MobDefs.VersionStamp, start: INT, length: INT] = { IF file # NIL THEN { MakeBase: PROC [p: LONG POINTER, units: INT] RETURNS [MobDefs.Base] = INLINE { RETURN [LOOPHOLE[p+units, MobDefs.Base]]; }; mob: MobDefs.MobBase = LOOPHOLE[MimSysOps.MapBase[file]]; IF mob # NIL AND mob.nConfigs = 0 THEN { limit: CARD = MimSysOps.MapLength[file] / BYTES[UNIT]; IF MobMapper.AlterMob[mob, LOOPHOLE[mob], limit] # badVersion THEN { nString: MobDefs.NameString ¬ LOOPHOLE[mob + mob.ssOffset.units]; d: ConvertUnsafe.SubString ¬ [base: LOOPHOLE[nString], offset: 0, length: 0]; typeId: ConvertUnsafe.SubString ¬ [ base: LOOPHOLE[formalId ¬ Rope.Flatten[formalId]], offset: 0, length: Rope.Length[formalId]]; ftb: MobDefs.Base ¬ MakeBase[mob, mob.ftOffset.units]; mtb: MobDefs.Base ¬ MakeBase[mob, mob.mtOffset.units]; sgb: MobDefs.Base ¬ MakeBase[mob, mob.sgOffset.units]; mti: MobDefs.MTIndex ¬ MobDefs.MTIndex.FIRST; sSeg: MobDefs.SGIndex; UNTIL mti = mob.mtLimit DO name: MobDefs.NameRecord ¬ mtb[mti].name; d.length ¬ nString[name].ORD; d.offset ¬ name+1; IF ConvertUnsafe.EqualSubStrings[typeId, d] THEN EXIT; mti ¬ mti + MobDefs.MTRecord.SIZE; REPEAT FINISHED => IF mob.nModules = 1 THEN mti ¬ MobDefs.MTIndex.FIRST ELSE GO TO badFile; ENDLOOP; version ¬ IF mtb[mti].file = MobDefs.FTSelf THEN mob.version ELSE ftb[mtb[mti].file].version; sSeg ¬ mtb[mti].sseg; IF sSeg = MobDefs.SGNull OR sgb[sSeg].units.units = 0 THEN GO TO badFile; start ¬ LOOPHOLE[sgb[sSeg].base]; length ¬ LOOPHOLE[sgb[sSeg].units]; RETURN; }; }; EXITS badFile => {} }; version ¬ MobDefs.NullVersion; start ¬ 0; length ¬ 0; }; <> STB: TYPE = REF SymbolTableBaseRep; SymbolTableBaseRep: PUBLIC TYPE = SymbolTablePrivate.SymbolTableBaseRep; <> Handle: TYPE = SymbolTable.Handle; STBMissing: PUBLIC ERROR [Handle] = CODE; IllegalSTB: PUBLIC ERROR [base: STB] = CODE; Cleanup: PUBLIC ENTRY PROC = { ENABLE UNWIND => NULL; WHILE header # NIL DO node: CachePointer ¬ header; header ¬ node.next; ReleaseCacheEntry[node]; ENDLOOP; WHILE free # NIL DO node: CachePointer ¬ free; free ¬ free.next; ReleaseCacheEntry[node]; ENDLOOP; }; AcquireSTB: PUBLIC ENTRY PROC [handle: Handle] RETURNS [base: STB] = { ENABLE UNWIND => NULL; node: CachePointer ¬ MakeCacheEntry[handle]; IF freeTables = NIL THEN {base ¬ MimZones.permZone.NEW[SymbolTableBaseRep]; Bump[@stats.nNew]} ELSE {base ¬ freeTables; freeTables ¬ freeTables.link}; base.link ¬ busyTables; busyTables ¬ base; InstallTable[base, node]; Bump[@stats.nAcquire]; }; ReleaseSTB: PUBLIC ENTRY PROC [base: STB] = { ENABLE UNWIND => {NULL}; cacheNode: CachePointer = LOOPHOLE[base.cacheInfo]; FOR node: CachePointer ¬ header, node.next UNTIL node = NIL DO IF node = cacheNode THEN { prev: STB ¬ NIL; FOR table: STB ¬ busyTables, table.link UNTIL table = NIL DO IF table = base THEN { IF prev # NIL THEN prev.link ¬ table.link ELSE busyTables ¬ table.link; FreeCacheEntry[node]; base.link ¬ freeTables; freeTables ¬ base; Bump[@stats.nRelease]; RETURN; }; prev ¬ table; ENDLOOP; RETURN WITH ERROR IllegalSTB[base]; }; ENDLOOP; RETURN WITH ERROR IllegalSTB[base]; }; LockedSTB: PUBLIC ERROR [handle: SymbolTable.Handle, nLocks: NAT] = CODE; ForgetSTB: PUBLIC ENTRY PROC [handle: SymbolTable.Handle] = { ENABLE UNWIND => {NULL}; space: SymbolSpace ¬ NARROW[handle.space]; nLocks: INT ¬ 0; prev: CachePointer ¬ NIL; node: CachePointer ¬ header; WHILE node # NIL DO next: CachePointer ¬ node.next; IF space = node.space THEN { IF node.refCount # 0 THEN nLocks ¬ nLocks + node.refCount ELSE { IF prev = NIL THEN header ¬ next ELSE prev.next ¬ header; ReleaseCacheEntry[node]; node ¬ prev; }; }; prev ¬ node; node ¬ next; ENDLOOP; node ¬ free; prev ¬ NIL; WHILE node # NIL DO next: CachePointer ¬ node.next; IF space = node.space THEN { IF node.refCount # 0 THEN nLocks ¬ nLocks + node.refCount ELSE { IF prev = NIL THEN free ¬ next ELSE prev.next ¬ free; ReleaseCacheEntry[node]; node ¬ prev; }; }; prev ¬ node; node ¬ next; ENDLOOP; IF nLocks # 0 THEN RETURN WITH ERROR LockedSTB[handle, nLocks]; }; STBToHandle: PUBLIC ENTRY PROC [base: STB] RETURNS [SymbolTable.Handle] = { ENABLE UNWIND => NULL; cacheNode: CachePointer = LOOPHOLE[base.cacheInfo]; RETURN [[cacheNode.space, FALSE]]; }; busyTables: STB ¬ NIL; freeTables: STB ¬ NIL; cachePageLimit: INT ¬ 0; -- number of pages for symbol tables STBCacheSize: PUBLIC PROC RETURNS [pages: CARDINAL] = { RETURN [cachePageLimit]; }; SetSTBCacheSize: PUBLIC ENTRY PROC [pages: CARDINAL] = { ENABLE UNWIND => NULL; cachePageLimit ¬ pages; }; <> takingStatistics: BOOL = TRUE; Count: TYPE = INT ¬ 0; stats: RECORD [nAcquire, nNew, nRelease: Count] ¬ []; Bump: PROC [p: POINTER TO Count, delta: Count ¬ 1] = INLINE { IF takingStatistics THEN p­ ¬ p­ + delta; }; <> CacheNode: TYPE = RECORD [ next: CachePointer ¬ NIL, space: SymbolSpace ¬ NIL, refCount: INT ¬ 0, nUses: INT ¬ 0]; CachePointer: TYPE = REF CacheNode; header: CachePointer ¬ NIL; free: CachePointer ¬ NIL; unused: CachePointer ¬ NIL; nNodes: NAT ¬ 0; mappedPages: INT ¬ 0; <> <> <
cache nodes that are being used>> < cache nodes with spaces but not in use>> < cache nodes without spaces>> <<>> MakeCacheEntry: INTERNAL PROC [handle: Handle] RETURNS [node: CachePointer ¬ NIL] = { space: SymbolSpace ¬ NARROW[handle.space]; prev: CachePointer ¬ NIL; node ¬ header; <> WHILE node # NIL DO next: CachePointer ¬ node.next; IF node.space = space THEN GO TO found; node ¬ next; ENDLOOP; <> node ¬ free; WHILE node # NIL DO next: CachePointer ¬ node.next; IF node.space = space THEN { IF prev = NIL THEN free ¬ next ELSE prev.next ¬ NIL; GO TO first; }; prev ¬ node; node ¬ next; ENDLOOP; <> node ¬ GetEmptyNode[]; node.space ¬ space; GO TO first; EXITS first => { node.next ¬ header; header ¬ node; node.refCount ¬ 1; node.nUses ¬ node.nUses + 1; }; found => { node.refCount ¬ node.refCount+1; node.nUses ¬ node.nUses + 1; }; }; FreeCacheEntry: INTERNAL PROC [node: CachePointer] = { IF (node.refCount ¬ node.refCount-1) = 0 THEN { each: CachePointer ¬ header; lag: CachePointer ¬ NIL; WHILE each # NIL DO next: CachePointer ¬ each.next; IF each = node THEN { IF lag = NIL THEN header ¬ next ELSE lag.next ¬ next; node.next ¬ free; free ¬ node; RETURN; }; lag ¬ each; each ¬ next; ENDLOOP; ERROR; }; }; ReleaseCacheEntry: INTERNAL PROC [node: CachePointer] = { space: SymbolSpace ¬ node.space; IF space # NIL THEN node.space ¬ NIL; node.refCount ¬ node.nUses ¬ 0; node.next ¬ unused; unused ¬ node; }; GetEmptyNode: INTERNAL PROC RETURNS [node: CachePointer] = { IF unused = NIL THEN {node ¬ MimZones.permZone.NEW[CacheNode]; nNodes ¬ nNodes + 1} ELSE {node ¬ unused; unused ¬ unused.next}; node.refCount ¬ node.nUses ¬ 0; node.next ¬ NIL; }; <> InstallTable: INTERNAL PROC [base: STB, node: CachePointer] = { b: LONG POINTER = MimSysOps.MapBase[node.space.file]+node.space.start; tB: SymbolSegment.Base = LOOPHOLE[b]; p: LONG POINTER TO SymbolSegment.STHeader = b; base.cacheInfo ¬ LOOPHOLE[node]; base.hashVec ¬ b+p.hvBlock.offset; base.htb ¬ tB + p.htBlock.offset - SymbolSegment.biases[SymbolSegment.htType]; base.ssb ¬ b + p.ssBlock.offset - SymbolSegment.biases[SymbolSegment.ssType]; base.seb ¬ tB + p.seBlock.offset - SymbolSegment.biases[SymbolSegment.seType]; base.ctxb ¬ tB + p.ctxBlock.offset - SymbolSegment.biases[SymbolSegment.ctxType]; base.mdb ¬ tB + p.mdBlock.offset - SymbolSegment.biases[SymbolSegment.mdType]; base.bb ¬ tB + p.bodyBlock.offset - SymbolSegment.biases[SymbolSegment.bodyType]; base.tb ¬ tB + p.treeBlock.offset - SymbolSegment.biases[SymbolSegment.treeType]; base.ltb ¬ tB + p.litBlock.offset - SymbolSegment.biases[SymbolSegment.ltType]; base.stb ¬ tB + p.sLitBlock.offset - SymbolSegment.biases[SymbolSegment.stType]; base.extb ¬ tB + p.extBlock.offset - SymbolSegment.biases[SymbolSegment.extType]; base.mdLimit ¬ Symbols.MDFirst + p.mdBlock.size; base.extLimit ¬ SymbolSegment.ExtFirst + p.extBlock.size; base.mainCtx ¬ p.outerCtx; base.stHandle ¬ p; IF p.fgRelBase = 0 OR p.fgCount = 0 THEN { base.sourceFile ¬ NIL; base.fgTable ¬ NIL; } ELSE { q: LONG POINTER TO SymbolSegment.FGHeader = LOOPHOLE[b + p.fgRelBase]; source: LONG STRING = LOOPHOLE[q + SIZE[SymbolSegment.FGHeader[0]] - SIZE[StringBody[0]]]; base.sourceFile ¬ source; base.fgTable ¬ DESCRIPTOR[q + q.offset, q.length]; }; }; bytesPerAU: NAT = BYTES[UNIT]; <<>> MimZones.RegisterForReset[Finalize]; }.