DIRECTORY PrincOps: TYPE USING [wordsPerPage], FS: TYPE USING [Read, OpenFile, SameFile], FileSegment: TYPE USING [Span], Symbols: TYPE USING [HTIndex, HTRecord, MDIndex], SymbolPack: TYPE USING [ bb, cacheInfo, ctxb, extb, extLimit, fgTable, hashVec, ht, link, ltb, mainCtx, mdb, mdLimit, notifier, NullNotifier, seb, sourceFile, ssb, stHandle, tb], SymbolSegment: TYPE USING [Base, ExtIndex, FGHeader, STHeader], SymbolTable: TYPE USING [Base, Handle, anySpan], VM: TYPE USING [Free, Allocate, AddressForPageNumber, Interval, CantAllocate]; SymbolCache: MONITOR -- protects the cache of global frames for symbol tables IMPORTS initial: SymbolPack, FS, VM EXPORTS SymbolTable = { Handle: TYPE = SymbolTable.Handle; Missing: PUBLIC ERROR [Handle] = CODE; IllegalBase: PUBLIC ERROR [base: SymbolTable.Base] = CODE; Acquire: PUBLIC ENTRY PROC [handle: Handle] RETURNS [base: SymbolTable.Base] = { ENABLE UNWIND => NULL; node: CachePointer; interval: VM.Interval; { node _ MakeCacheEntry[handle ! VM.CantAllocate => {interval _ bestInterval; GOTO cantAllocate}]; IF freeTables = NIL THEN {base _ NEW initial; START base; Bump[@stats.nNew]} ELSE {base _ freeTables; freeTables _ freeTables.link}; base.link _ busyTables; busyTables _ base; InstallTable[base, node]; EXITS cantAllocate => RETURN WITH ERROR VM.CantAllocate[interval]; }; Bump[@stats.nAcquire]; }; Release: PUBLIC ENTRY PROC [base: SymbolTable.Base] = { ENABLE UNWIND => {NULL}; cacheNode: CachePointer = LOOPHOLE[base.cacheInfo, CachePointer]; -- XXX BEWARE prev: SymbolTable.Base; FOR node: CachePointer _ header.next, node.next UNTIL node = cacheNode DO IF node = free THEN RETURN WITH ERROR IllegalBase[base] ENDLOOP; prev _ NIL; FOR table: SymbolTable.Base _ busyTables, table.link UNTIL table = NIL DO IF table = base THEN { IF prev # NIL THEN prev.link _ table.link ELSE busyTables _ table.link; EXIT}; prev _ table; REPEAT FINISHED => RETURN WITH ERROR IllegalBase[base]; ENDLOOP; FreeCacheEntry[cacheNode]; base.link _ freeTables; freeTables _ base; Bump[@stats.nRelease]}; Locked: PUBLIC ERROR [handle: SymbolTable.Handle, nLocks: NAT] = CODE; Forget: PUBLIC ENTRY PROC [handle: SymbolTable.Handle] = {ENABLE UNWIND => NULL; FID: PROC [h: SymbolTable.Handle] RETURNS [FS.OpenFile] = INLINE {RETURN [[h.file]]}; OverLap: PROC [s1, s2: FileSegment.Span] RETURNS [BOOL] = INLINE { RETURN [IF s1.base <= s2.base THEN s1.base + s1.pages > s2.base ELSE s1.base < s2.base + s2.pages]; }; nLocks: INT _ 0; prev: CachePointer; FOR node: CachePointer _ unused.prev, prev UNTIL node = header DO prev _ node.prev; IF FS.SameFile[FID[handle], FID[node.table]] AND (handle.span = SymbolTable.anySpan OR OverLap[handle.span, node.table.span]) THEN { IF node.refCount # 0 THEN nLocks _ nLocks + node.refCount ELSE ReleaseCacheEntry[node]; }; ENDLOOP; IF nLocks # 0 THEN RETURN WITH ERROR Locked[handle, nLocks]; }; BaseToHandle: PUBLIC ENTRY PROC [base: SymbolTable.Base] RETURNS [SymbolTable.Handle] = { ENABLE UNWIND => NULL; cacheNode: CachePointer = LOOPHOLE[base.cacheInfo, CachePointer]; -- XXX BEWARE RETURN[cacheNode.table]; }; busyTables: SymbolTable.Base _ NIL; freeTables: SymbolTable.Base _ initial; cachePageLimit: INT _ 0; -- number of pages for symbol tables CacheSize: PUBLIC PROC RETURNS [pages: CARDINAL] = {RETURN [cachePageLimit]}; SetCacheSize: 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}; initialNodes: NAT = 15; maxNodes: NAT = 255; CacheNode: TYPE = RECORD [ prev, next: CachePointer, table: Handle, space: VM.Interval _ [0, 0], refCount: INT _ 0, nUses: INT _ 0]; CachePointer: TYPE = REF CacheNode; header, free, unused: CachePointer; nNodes: [0..maxNodes]; mappedPages: INT; MakeCacheEntry: INTERNAL PROC [handle: Handle] RETURNS [node: CachePointer] = { FOR node _ header.next, node.next UNTIL node = free DO IF node.table = handle THEN GOTO allocated; REPEAT allocated => NULL; FINISHED => { FOR node _ free, node.next UNTIL node = unused DO IF node.table = handle THEN GOTO unflushed; REPEAT unflushed => {IF node = free THEN free _ node.next; Detach[node]}; FINISHED => {node _ GetEmptyNode[]; node.table _ handle}; ENDLOOP; IF node.space = [0, 0] THEN { pages: INT = handle.span.pages; node.space _ TrimCache[pages]; FS.Read[ file: [node.table.file], from: node.table.span.base, nPages: pages, to: VM.AddressForPageNumber[node.space.page]]; }; Insert[node, free]; }; ENDLOOP; node.refCount _ node.refCount+1; node.nUses _ node.nUses + 1; RETURN}; FreeCacheEntry: INTERNAL PROC [node: CachePointer] = { IF (node.refCount _ node.refCount-1) = 0 THEN { position: CachePointer; Detach[node]; FOR position _ free, position.next UNTIL position = unused DO IF node.nUses >= position.nUses THEN EXIT; ENDLOOP; Insert[node, position]; IF position = free THEN free _ node}}; ReleaseCacheEntry: INTERNAL PROC [node: CachePointer] = { IF node = free THEN free _ node.next; ClearNode[node]; Detach[node]; Insert[node, unused]; unused _ node}; GetEmptyNode: INTERNAL PROC RETURNS [node: CachePointer] = { IF free = header OR (unused = header AND nNodes < maxNodes) THEN { node _ NEW[CacheNode]; nNodes _ nNodes + 1} ELSE { node _ unused; IF node = header THEN { IF node = free THEN free _ node.prev; unused _ node.prev; ClearNode[node]}; unused _ node.next; IF node = free THEN free _ node.next; Detach[node]}; RETURN}; Detach: INTERNAL PROC [node: CachePointer] = { node.prev.next _ node.next; node.next.prev _ node.prev}; Insert: INTERNAL PROC [node, position: CachePointer] = { node.prev _ position.prev; node.prev.next _ node; node.next _ position; position.prev _ node}; TrimCache: INTERNAL PROC [pages: INT] RETURNS[space: VM.Interval _ [0, 0]] = { UNTIL mappedPages <= cachePageLimit DO IF unused = free THEN EXIT; ClearNode[unused _ unused.prev]; ENDLOOP; space _ VM.Allocate[count: pages ! VM.CantAllocate => CONTINUE]; IF space = [0, 0] THEN { UNTIL unused = free DO ClearNode[unused _ unused.prev] ENDLOOP; space _ VM.Allocate[count: pages]; }; mappedPages _ mappedPages + pages; }; ClearNode: INTERNAL PROC [node: CachePointer] = { IF node.space # [0, 0] THEN { mappedPages _ mappedPages - node.space.count; VM.Free[node.space]; node.space _ [0, 0]; }; node.refCount _ node.nUses _ 0; }; InstallTable: INTERNAL PROC [base: SymbolTable.Base, node: CachePointer] = { SetBases[base, node]; base.notifier _ base.NullNotifier}; SetBases: INTERNAL PROC [base: SymbolTable.Base, node: CachePointer] = { b: LONG POINTER = VM.AddressForPageNumber[node.space.page]; tB: SymbolSegment.Base = LOOPHOLE[b]; p: LONG POINTER TO SymbolSegment.STHeader = b; q: LONG POINTER TO SymbolSegment.FGHeader; base.cacheInfo _ LOOPHOLE[node, LONG POINTER]; -- XXX BEWARE base.hashVec _ b+p.hvBlock.offset; base.ht _ DESCRIPTOR[b+p.htBlock.offset, p.htBlock.size/SIZE[Symbols.HTRecord]]; base.ssb _ b + p.ssBlock.offset; base.seb _ tB + p.seBlock.offset; base.ctxb _ tB + p.ctxBlock.offset; base.mdb _ tB + p.mdBlock.offset; base.bb _ tB + p.bodyBlock.offset; base.tb _ tB + p.treeBlock.offset; base.ltb _ tB + p.litBlock.offset; base.extb _ tB + p.extBlock.offset; base.mdLimit _ FIRST[Symbols.MDIndex] + p.mdBlock.size; base.extLimit _ FIRST[SymbolSegment.ExtIndex] + p.extBlock.size; base.mainCtx _ p.outerCtx; base.stHandle _ p; IF p.fgRelPgBase = 0 OR node.table.span.pages <= p.fgRelPgBase THEN { base.sourceFile _ NIL; base.fgTable _ NIL} ELSE { offset: CARDINAL = LOOPHOLE[@(NIL[POINTER TO SymbolSegment.FGHeader]).sourceFile]; q _ b + p.fgRelPgBase*PrincOps.wordsPerPage; base.sourceFile _ (b + p.fgRelPgBase*PrincOps.wordsPerPage) + offset; base.fgTable _ DESCRIPTOR[b + p.fgRelPgBase*PrincOps.wordsPerPage + q.offset, q.length]}}; Init: ENTRY PROC = { START initial; initial.link _ NIL; header _ NEW[CacheNode]; header.next _ header.prev _ header; nNodes _ 1; THROUGH [1..initialNodes] DO node: CachePointer _ NEW[CacheNode]; Insert[node, header]; nNodes _ nNodes + 1; ENDLOOP; free _ unused _ header.next; mappedPages _ 0}; Init[]; }. ¢file SymbolCache.mesa last edited by Satterthwaite, May 24, 1982 10:31 am last edited by Paul Rovner, November 17, 1983 2:23 pm public interface statistics internal cache management C A C H E O R G A N I Z A T I O N The cache keeps track of segments in CacheNodes. The CacheNodes are kept in a doubly-linked list and organized in three groups, separated by three pointers: header, free, and unused. Ordered by the link next, the list is organized as follows: (header .. free) nodes attached to frames, [free .. unused) nodes with segment but no frame, [unused .. header) empty and available, symbol table setup initialization Ê ¤˜J˜Jšœ™Jšœ3™3Jšœ5™5J˜šÏk ˜ Jšœ œœ˜$Jšœœœ˜*Jšœ œœ˜Jšœ œœ˜1šœ œœ˜J˜EJ˜DJ˜—Jšœœœ&˜?Jšœ œœ˜0Jšœœœ@˜NJ˜—šœ œÏc8˜NJšœœ˜#Jšœ˜J˜Jšœ™J˜Jšœœ˜"J˜Jšœ œœ œ˜&Jšœ œœœ˜:J˜J˜š Ïnœœœœœ˜PJšœœœ˜J˜Jšœ˜šœ˜šœ˜Jšœœ+œ˜C—šœ˜Jšœ œ œ˜8Jšœ3˜7—J˜+J˜š˜Jš œœœœœ˜<——Jšœ˜Jšœ˜Jšœ˜J˜—šŸœœœœ˜7Jšœœœ˜Jšœœ!ž ˜PJ˜šœ-œ˜IJš œ œœœœœ˜@—Jšœœ˜ šœ2œ œ˜Išœœ˜Jš œœœœœ˜N—J˜ š˜Jšœœœœ˜0—Jšœ˜—J˜J˜DJ˜J˜—Jš œœœ&œœ˜FJ˜šŸœœœœ!œœœ˜PJš œœœœ œœ ˜Uš Ÿœœœœœ˜Bšœœ˜Jšœ˜!Jšœ˜#J˜J˜——Jšœœ˜J˜šœ(œ˜AJ˜šœœ œ œ ˜,Jšœ$œ'˜Pšœ˜šœ˜Jšœ ˜$Jšœ˜—J˜——Jšœ˜—Jš œ œœœœ˜J˜Jš Ÿ œœœœ œœ˜MJ˜š Ÿ œœœœ œ˜5Jšœœœ˜J˜J˜J˜J˜——Jšœ ™ ˜Jšœœœ˜Jšœœœ˜Jšœœ(˜5J˜š Ÿœœœœœ˜=Jšœœ˜*J˜J˜——Jšœ™˜Jšœœ˜Jšœ œ˜J˜šœ œœ˜J˜J˜Jšœœ˜Jšœ œ˜Jšœœ˜J˜—Jšœœœ ˜#J˜J˜#J˜Jšœ œ˜J˜J˜Jšœ#™#J˜JšœD™DJšœE™EJšœA™AJšœ'™'Jšœ9™9Jšœ@™@Jšœ.™.J˜šŸœœœœ˜Ošœœ ˜6Jšœœœ ˜+š˜Jšœœ˜šœ˜ šœœ˜1Jšœœœ ˜+š˜Jšœœ œ"˜CJšœ2˜:—Jšœ˜—šœœ˜Jšœœ˜J˜šœ˜J˜J˜J˜Jšœœ(˜.—J˜—J˜J˜——Jšœ˜—J˜>Jšœ˜J˜—šŸœœœ˜6šœ'œ˜/J˜J˜ šœ œ˜=Jšœœœ˜*Jšœ˜—J˜Jšœœ˜&J˜——šŸœœœ˜9Jšœ œ˜%J˜J˜ J˜%J˜—šŸ œœœœ˜<šœœœœ˜BJšœœ"˜,—šœ˜J˜šœœ˜Jšœ œ˜%J˜&—J˜Jšœ œ˜%J˜—Jšœ˜J˜—šŸœœœ˜.J˜9J˜—šŸœœœ#˜8J˜2J˜-J˜—š Ÿ œœœ œœœ˜Pšœ˜&Jšœœœ˜J˜ —Jšœ˜Jšœœœœ˜@šœ˜šœ˜Jšœœ!œ˜?Jšœœ˜"Jšœ˜——Jšœ"˜"J˜J˜—šŸ œœœ˜1šœœ˜J˜-Jšœ˜J˜J˜—J˜J˜J˜J˜——Jšœ™˜šŸ œœœ1˜LJ˜:J˜—šŸœœœ1˜HJšœœœœ'˜;Jšœœ˜%Jšœœœœ˜.Jšœœœœ˜*Jšœœœœž ˜=J˜"Jšœ œ$œ˜PJ˜ J˜!J˜#J˜!J˜"J˜"J˜"J˜#Jšœœ#˜7Jšœœ+˜@J˜.šœœ(œ˜EJšœœœ˜*—šœ˜Jš œœœœœœ&˜RJ˜,J˜EJšœ œA˜ZJ˜———Jšœ™˜šŸœœœ˜Jšœœ˜#Jšœ œ ˜J˜#J˜ šœ˜Jšœœ ˜$J˜J˜Jšœ˜—J˜J˜J˜—J˜J˜J˜J˜J˜——…— ,-r