-- Copyright (C) 1984 by Xerox Corporation. All rights reserved. -- NetDirCache.mesa, HGM, 22-Jan-84 1:18:38 DIRECTORY String USING [Equivalent], Stats USING [StatCounterIndex, StatIncr], NameServerDefs USING [ CacheEntry, CacheEntryObject, SearchNetDirForName, SearchNetDirForAddress], PupTypes USING [PupAddress], ServerHeap USING [Create, Free, Node]; NetDirCache: MONITOR IMPORTS String, Stats, NameServerDefs, ServerHeap EXPORTS NameServerDefs = BEGIN OPEN Stats, NameServerDefs; PupAddress: TYPE = PupTypes.PupAddress; CacheEntry: TYPE = NameServerDefs.CacheEntry; CacheEntryObject: TYPE = NameServerDefs.CacheEntryObject; firstCacheEntry: CacheEntry ← NIL; cacheLimit: CARDINAL ← 400; cacheWordsInUse: CARDINAL ← 0; sequenceNumber: CARDINAL ← 0; statHits, statMisses, statNone, statFile: PUBLIC StatCounterIndex; emptyCacheEntryObject: CacheEntryObject ← [ next: NIL, size: SIZE[CacheEntryObject], count: 1, sequence: 0, names: DESCRIPTOR[NIL, 0], addrs: DESCRIPTOR[NIL, 0]]; SetCacheSize: PUBLIC PROCEDURE [n: CARDINAL] = BEGIN cacheLimit ← n; END; BumpCacheSize: PUBLIC PROCEDURE [n: INTEGER] = BEGIN cacheLimit ← cacheLimit + n; END; FlushWholeCache: PUBLIC ENTRY PROCEDURE = BEGIN WHILE firstCacheEntry # NIL DO FlushOneEntry[]; ENDLOOP; IF cacheWordsInUse # 0 THEN ERROR CacheShouldBeEmptyNow; END; EnumerateCache: PUBLIC ENTRY PROCEDURE [proc: PROCEDURE [CacheEntry]] = BEGIN FOR ce: CacheEntry ← firstCacheEntry, ce.next UNTIL ce = NIL DO proc[ce]; ENDLOOP; END; SearchCacheForName: PUBLIC ENTRY PROCEDURE [key: LONG STRING, proc: PROCEDURE [CacheEntry]] RETURNS [hit: BOOLEAN] = BEGIN previous: CacheEntry; FOR ce: CacheEntry ← firstCacheEntry, ce.next UNTIL ce = NIL DO FOR i: CARDINAL IN [0..LENGTH[ce.names]) DO IF String.Equivalent[key, ce.names[i]] THEN BEGIN StatIncr[statHits]; IF LENGTH[ce.addrs] = 0 THEN StatIncr[statMisses]; ce.count ← ce.count + 1; IF firstCacheEntry # ce THEN BEGIN -- move this entry to the head of the list previous.next ← ce.next; ce.next ← firstCacheEntry; firstCacheEntry ← ce; END; proc[ce]; RETURN[TRUE]; END; ENDLOOP; previous ← ce; ENDLOOP; StatIncr[statNone]; RETURN[FALSE]; END; SearchCacheForAddress: PUBLIC ENTRY PROCEDURE [key: PupAddress, proc: PROCEDURE [CacheEntry]] RETURNS [hit: BOOLEAN] = BEGIN previous: CacheEntry; FOR ce: CacheEntry ← firstCacheEntry, ce.next UNTIL ce = NIL DO FOR i: CARDINAL IN [0..LENGTH[ce.addrs]) DO IF key = ce.addrs[i] THEN BEGIN StatIncr[statHits]; IF LENGTH[ce.addrs] = 0 THEN StatIncr[statMisses]; ce.count ← ce.count + 1; IF firstCacheEntry # ce THEN BEGIN -- move this entry to the head of the list previous.next ← ce.next; ce.next ← firstCacheEntry; firstCacheEntry ← ce; END; proc[ce]; RETURN[TRUE]; END; ENDLOOP; previous ← ce; ENDLOOP; StatIncr[statNone]; RETURN[FALSE]; END; ForceNameIntoCache: PUBLIC ENTRY PROCEDURE [key: LONG STRING, proc: PROCEDURE [CacheEntry]] RETURNS [hit: BOOLEAN] = BEGIN mine: CacheEntryObject ← emptyCacheEntryObject; IF ~NameServerDefs.SearchNetDirForName[key, @mine] THEN RETURN[FALSE]; StatIncr[statFile]; proc[AddToCache[@mine]]; RETURN[TRUE] END; ForceAddressIntoCache: PUBLIC ENTRY PROCEDURE [key: PupAddress, proc: PROCEDURE [CacheEntry]] RETURNS [hit: BOOLEAN] = BEGIN mine: CacheEntryObject ← emptyCacheEntryObject; IF ~NameServerDefs.SearchNetDirForAddress[key, @mine] THEN RETURN[FALSE]; StatIncr[statFile]; proc[AddToCache[@mine]]; RETURN[TRUE] END; NothingInCacheToFlush: ERROR = CODE; CacheShouldBeEmptyNow: ERROR = CODE; FlushOneEntry: INTERNAL PROCEDURE = BEGIN previous, ce: CacheEntry; IF firstCacheEntry = NIL THEN ERROR NothingInCacheToFlush; ce ← firstCacheEntry; IF firstCacheEntry.next = NIL THEN firstCacheEntry ← NIL ELSE BEGIN WHILE ce.next # NIL DO previous ← ce; ce ← ce.next; ENDLOOP; previous.next ← NIL; END; cacheWordsInUse ← cacheWordsInUse - ce.size; FOR i: CARDINAL IN [0..LENGTH[ce.names]) DO ServerHeap.Free[ce.names[i]]; ENDLOOP; IF BASE[ce.names] # NIL THEN ServerHeap.Free[BASE[ce.names]]; IF BASE[ce.addrs] # NIL THEN ServerHeap.Free[BASE[ce.addrs]]; ServerHeap.Free[ce]; END; AddToCache: INTERNAL PROCEDURE [his: CacheEntry] RETURNS [ce: CacheEntry] = BEGIN WHILE his.size + cacheWordsInUse > cacheLimit DO FlushOneEntry[]; ENDLOOP; ce ← ServerHeap.Node[SIZE[CacheEntryObject]]; ce↑ ← his↑; ce.next ← firstCacheEntry; ce.sequence ← (sequenceNumber ← sequenceNumber + 1); firstCacheEntry ← ce; cacheWordsInUse ← cacheWordsInUse + ce.size; END; ServerHeap.Create[]; END.