-- Copyright (C) 1982, 1984 by Xerox Corporation. All rights reserved. -- Grapevine: Name Lookup Server cache -- -- [Juniper]<Grapevine>MS>NameLookup.mesa -- HGM, 17-Nov-84 20:27:08 -- Andrew Birrell, 4-Mar-82 15:37:12 DIRECTORY BTreeDefs USING [ BTreeHandle, CreateAndInitializeBTree, Insert, KeyNotFound, Lookup, ReleaseBTree, TestKeys], Buffer USING [ReturnBuffer], Inline USING [COPY], LogDefs USING [WriteChar, WriteLogEntry], Process USING [Detach, Pause, SecondsToTicks], PupDefs USING [ GetBufferParms, GetFreePupBuffer, GetPupContentsBytes, PupAddress, PupBuffer, PupRouterBroadcastThis, PupSocket, PupSocketDestroy, PupSocketMake, ReturnPup, SecondsToTocks, SetPupContentsWords], PupTypes USING [fillInSocketID, mailSoc, miscSrvSoc], String USING [AppendString, AppendDecimal], Time USING [Current, Packed], VMDefs USING [AbandonFile, FileHandle, GetFileLength, OpenFile, SetFileLength]; NameLookup: MONITOR IMPORTS BTreeDefs, Buffer, Inline, LogDefs, Process, PupDefs, String, Time, VMDefs = BEGIN LogChar: PROC [c: CHARACTER] = {LogDefs.WriteChar['n]; LogDefs.WriteChar[c]}; LowerCase: PROCEDURE [c: CHARACTER] RETURNS [CHARACTER] = INLINE { RETURN[IF c IN ['A..'Z] THEN c - 'A + 'a ELSE c]}; IsFirstGE: BTreeDefs.TestKeys = BEGIN -- parameters a,b: DESC FOR ARRAY OF WORD returns[ BOOLEAN]-- aC: POINTER TO PACKED ARRAY OF CHARACTER = LOOPHOLE[BASE[a]]; bC: POINTER TO PACKED ARRAY OF CHARACTER = LOOPHOLE[BASE[b]]; FOR i: CARDINAL IN [0..2 * MIN[LENGTH[a], LENGTH[b]]) DO IF LowerCase[aC[i]] < LowerCase[bC[i]] THEN RETURN[FALSE]; IF LowerCase[aC[i]] > LowerCase[bC[i]] THEN RETURN[TRUE]; ENDLOOP; RETURN[LENGTH[a] >= LENGTH[b]]; END; AreTheyEq: BTreeDefs.TestKeys = BEGIN aC: POINTER TO PACKED ARRAY OF CHARACTER = LOOPHOLE[BASE[a]]; bC: POINTER TO PACKED ARRAY OF CHARACTER = LOOPHOLE[BASE[b]]; IF LENGTH[a] = LENGTH[b] THEN FOR i: CARDINAL IN [0..2 * LENGTH[a]) DO IF LowerCase[aC[i]] # LowerCase[bC[i]] THEN EXIT; REPEAT FINISHED => RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; END; Key: TYPE = DESCRIPTOR FOR PACKED ARRAY OF CHARACTER; Desc: PROCEDURE [name: Key] RETURNS [DESCRIPTOR FOR ARRAY OF WORD] = INLINE BEGIN IF LENGTH[name] MOD 2 # 0 THEN name[LENGTH[name]] ← '@; RETURN[DESCRIPTOR[BASE[name], (1 + LENGTH[name]) / 2]] END; tree: BTreeDefs.BTreeHandle; treeFile: VMDefs.FileHandle; hitCount: LONG CARDINAL ← 0; missCount: LONG CARDINAL ← 0; entryCount: CARDINAL ← 0; roysBuggingFlag: BOOLEAN ← FALSE; -- TRUE to provoke a VM bug -- OpenTree: INTERNAL PROC = BEGIN treeFile ← VMDefs.OpenFile[ name: "NameLookup.BTree$"L, options: oldOrNew, cacheFraction: 5]; IF roysBuggingFlag THEN VMDefs.SetFileLength[treeFile, [0, 0]]; tree ← BTreeDefs.CreateAndInitializeBTree[ fileH: LOOPHOLE[treeFile], initializeFile: TRUE, useDefaultOrderingRoutines: FALSE, isFirstGreaterOrEqual: IsFirstGE, areTheyEqual: AreTheyEq]; entryCount ← 0; hitCount ← missCount ← 0; END; CloseTree: INTERNAL PROC = { VMDefs.AbandonFile[LOOPHOLE[BTreeDefs.ReleaseBTree[tree]]]}; maxDirVersion: CARDINAL ← 0; InitTree: ENTRY PROC = {OpenTree[]}; FlushTree: ENTRY PROC = BEGIN log: STRING = [40]; String.AppendString[log, "Found NLS version "L]; String.AppendDecimal[log, maxDirVersion]; LogDefs.WriteLogEntry[log]; LogDefs.WriteChar['V]; CloseTree[]; OpenTree[]; END; Insert: ENTRY PROC [key: Key, value: DESCRIPTOR FOR ARRAY OF WORD] = INLINE BEGIN keyDesc: DESCRIPTOR FOR ARRAY OF WORD = Desc[key]; IF LENGTH[keyDesc] < 32 AND (LENGTH[keyDesc] + LENGTH[value]) <= 80 THEN BEGIN BTreeDefs.Insert[tree, keyDesc, value]; entryCount ← entryCount + 1; END; END; Lookup: ENTRY PROC [key: Key, value: DESCRIPTOR FOR ARRAY OF WORD] RETURNS [length: CARDINAL] = INLINE BEGIN length ← BTreeDefs.Lookup[tree, Desc[key], value]; IF length = BTreeDefs.KeyNotFound THEN missCount ← missCount + 1 ELSE hitCount ← hitCount + 1; END; Interesting: PROC [ key: Key, local: PupDefs.PupAddress, value: DESCRIPTOR FOR ARRAY OF PupDefs.PupAddress] RETURNS [BOOLEAN] = BEGIN -- In order to limit the size of the cache (or, to avoid designing a -- replacement algorithm), not all names are cached. -- Names which are composite ones (with "+"), or are really just address -- constants are never cached. Names which have a socket number which -- is MTP or one of the Grapevine sockets, or map to this host are -- cached. Other names are cached if the BTree is small. -- I believe that in all normal environments an 8 page BTree will cache -- (almost) all names that occur. -- There is also a restriction on BTree key lengths FOR i: CARDINAL IN [0..LENGTH[key]) DO SELECT key[i] FROM IN ['a..'z], IN ['A..'Z], '- => NULL; ENDCASE => RETURN[FALSE]; ENDLOOP; IF VMDefs.GetFileLength[treeFile].page < 8 THEN RETURN[TRUE]; FOR j: CARDINAL IN [0..LENGTH[value]) DO SELECT value[j].socket.b FROM PupTypes.mailSoc.b, IN [50B..57B] => RETURN[TRUE]; ENDCASE => IF value[j].host = local.host AND (value[j].net = local.net OR value[j].net = 0) THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE] END; LoadCache: PROC [keyBuffer: PupDefs.PupBuffer, keyLength: CARDINAL] = BEGIN nlsSoc: PupDefs.PupSocket = PupDefs.PupSocketMake[ PupTypes.fillInSocketID, , PupDefs.SecondsToTocks[2]]; local: PupDefs.PupAddress = nlsSoc.getLocalAddress[]; b: PupDefs.PupBuffer ← NIL; newVersion: BOOLEAN ← FALSE; SameVersion: PROC [new: CARDINAL] RETURNS [BOOLEAN] = BEGIN IF new > maxDirVersion THEN BEGIN maxDirVersion ← new; newVersion ← TRUE; LogDefs.WriteLogEntry["Waiting for new NLS version"L]; RETURN[FALSE] END ELSE RETURN[TRUE]; END; -- The protocol is: -- us -> broadcast: NetDirVersion[0] -- nls -> us: NetDirVersion[his] -- us -> nls: NameLookup[key, pupID=hisVersion] -- nls -> us: NameIs[array of PupAddress, pupID=hisVersion] -- Because a particular nls has monotonic netDir versions, we know -- that the name value came from a netDir at least as recent as the -- version number he gave us. THROUGH [1..1] -- re-tries for lost packets -- DO b ← PupDefs.GetFreePupBuffer[]; b.pup.pupWords[0] ← 0; PupDefs.SetPupContentsWords[b, 1]; b.pup.source ← local; b.pup.dest.socket ← PupTypes.miscSrvSoc; b.pup.pupType ← netDirVersion; b.pup.pupID ← [0, 0]; PupDefs.PupRouterBroadcastThis[b]; UNTIL (b ← nlsSoc.get[]) = NIL -- UNTIL timeout -- DO SELECT b.pup.pupType FROM netDirVersion => IF PupDefs.GetPupContentsBytes[b] = 2 AND b.pup.pupWords[0] >= maxDirVersion THEN BEGIN b.pup.pupID ← [a: 0, b: b.pup.pupWords[0]]; Inline.COPY[ from: @(keyBuffer.pup.pupChars), to: @(b.pup.pupWords), nwords: (1 + keyLength) / 2]; PupDefs.ReturnPup[b, nameLookup, keyLength]; END ELSE Buffer.ReturnBuffer[b]; nameIs => IF b.pup.pupID.b >= maxDirVersion THEN BEGIN length: CARDINAL = PupDefs.GetPupContentsBytes[b] / 2; key: Key = DESCRIPTOR[@(keyBuffer.pup.pupChars), keyLength]; IF SameVersion[b.pup.pupID.b] AND keyBuffer # NIL AND Interesting[ key, local, DESCRIPTOR[ @(b.pup.pupWords), length / SIZE[PupDefs.PupAddress]]] THEN BEGIN Insert[key, DESCRIPTOR[@(b.pup.pupWords), length]]; LogChar['C]; END ELSE LogChar['I]; GOTO done END ELSE Buffer.ReturnBuffer[b]; nameError => IF b.pup.pupID.b >= maxDirVersion THEN BEGIN IF SameVersion[b.pup.pupID.b] AND keyBuffer # NIL THEN LogChar['E] --bad name-- ; GOTO done END ELSE Buffer.ReturnBuffer[b]; ENDCASE => Buffer.ReturnBuffer[b] -- ignore -- ; ENDLOOP; LogChar['?] --timeout: re-try-- ; REPEAT done => DO Buffer.ReturnBuffer[b]; -- be polite: swallow extra replies! -- IF (b ← nlsSoc.get[]) = NIL THEN EXIT; ENDLOOP; FINISHED => LogChar['X] --failed-- ; ENDLOOP; PupDefs.PupSocketDestroy[nlsSoc]; IF keyBuffer # NIL THEN Buffer.ReturnBuffer[keyBuffer]; IF newVersion -- don't flush until NLS has settled down again! -- THEN {Process.Pause[Process.SecondsToTicks[600]]; FlushTree[]}; FinderEnded[]; END; finderRunning: BOOLEAN ← FALSE; lastTime: Time.Packed ← [0]; -- time of last forced poll -- PossibleLoadCache: ENTRY PROC [b: PupDefs.PupBuffer, length: CARDINAL] = BEGIN IF NOT finderRunning THEN { finderRunning ← TRUE; Process.Detach[FORK LoadCache[b, length]]; lastTime ← Time.Current[]} ELSE IF b # NIL THEN Buffer.ReturnBuffer[b]; END; FinderEnded: ENTRY PROC = INLINE {finderRunning ← FALSE}; NameLookupMain: PROC = BEGIN soc: PupDefs.PupSocket = PupDefs.PupSocketMake[ PupTypes.miscSrvSoc, , PupDefs.SecondsToTocks[120]]; maxWords: CARDINAL = PupDefs.GetBufferParms[].bufferSize; InitTree[]; DO b: PupDefs.PupBuffer = soc.get[]; IF b # NIL THEN SELECT b.pup.pupType FROM nameLookup => BEGIN key: Key = DESCRIPTOR[@(b.pup.pupChars), PupDefs.GetPupContentsBytes[b]]; length: CARDINAL = Lookup[key, DESCRIPTOR[@(b.pup.pupWords), maxWords]]; IF length # BTreeDefs.KeyNotFound THEN { LogChar['H]; PupDefs.ReturnPup[b, nameIs, length * 2]} ELSE {LogChar['M]; PossibleLoadCache[b, LENGTH[key]]}; END; whereIsUser => PupDefs.ReturnPup[b, userIs, 0]; ENDCASE => PupDefs.ReturnPupBuffer[b]; IF lastTime + 120 < Time.Current[] THEN -- check current net directory version -- {PossibleLoadCache[NIL, 0]}; ENDLOOP; END; NameLookupProcess: PROCESS = FORK NameLookupMain[]; END.