DIRECTORY Ascii USING [Lower], BTree USING [Compare, CompareEntries, DeleteKey, Entry, EntryFromRecord, EntrySize, EnumerateEntries, GetState, Key, New, NewPathStk, NewRecordFromEntry, Open, PageNumber, PagePtr, PageStorage, PathStk, ReadEntry, ReferencePage, Relation, ReleasePage, SalvageEntries, SetState, SetUpdateInProgress, State, statePage, Tree, UpdateEntry, UpdateType, Validate], BTreeSimple, BTreeVM USING [CacheSize, FilePagesPerPage], FS USING [BytesForPages, GetInfo, OpenFile, Read, SetByteCountAndCreatedTime, SetPageCount, WordsForPages, Write], PrincOpsUtils USING [GetClockPulses, LongCopy], Process USING [Detach], Rope USING [InlineFlatten, TextRep], SafeStorage USING [CantEstablishFinalization, EnableFinalization, EstablishFinalization, FinalizationQueue, FQNext, NewFQ, ReEstablishFinalization], VM USING [AddressForPageNumber, Allocate, Free, Interval, PagesForWords, PageNumberForAddress]; BTreeSimpleImpl: CEDAR MONITOR -- the monitor protects the BTreeVM cache only LOCKS h USING h: Handle IMPORTS Ascii, BTree, FS, PrincOpsUtils, Process, Rope, SafeStorage, VM EXPORTS BTreeSimple SHARES Rope = { OPEN BTreeSimple; KeyFromEntry: PUBLIC UNSAFE PROC [entryKey: EntryKey] RETURNS [key: InternalKey] = UNCHECKED { IF entryKey=NIL THEN RETURN[NIL]; key _ NEW[Rope.TextRep[entryKey.length] _ [text[length: entryKey.length, text: ]]]; PrincOpsUtils.LongCopy[to: BASE[DESCRIPTOR[key.text]], from: BASE[DESCRIPTOR[entryKey.text]], nwords: (entryKey.length+1)/2]; }; ValueFromEntry: PUBLIC UNSAFE PROC [entryValue: EntryValue] RETURNS [value: Value] = UNCHECKED { IF entryValue=NIL THEN RETURN[NIL]; value _ NEW[ValueObject[entryValue.length]]; PrincOpsUtils.LongCopy[to: BASE[DESCRIPTOR[value.words]], from: BASE[DESCRIPTOR[entryValue.words]], nwords: entryValue.length]; }; DefaultCompare: Compare --[key: InternalKey, entryKey: EntryKey] RETURNS [Comparison]-- = UNCHECKED { FOR i: CARDINAL IN [0 .. MIN[key.length, entryKey.length]) DO kc: CHAR _ Ascii.Lower[key[i]]; ec: CHAR _ Ascii.Lower[entryKey[i]]; IF kc#ec THEN RETURN[IF kc { -- not found -- h.misses _ h.misses+1; i _ GetEmptyCacheEntry[h]; h.cache[i].pNum _ number; h.cache[i].empty _ FALSE; IF type # new THEN LoadCachePage[h, i]; h.cache[i].hash _ h.hashTable[hash]; h.hashTable[hash] _ i; }; ENDLOOP; h.cache[i].recentlyUsed _ TRUE; h.cache[i].referenceCount _ h.cache[i].referenceCount + 1; IF type # read THEN h.cache[i].dirty _ TRUE; IF type = new THEN ExtendFile[h, number]; ptr _ h.cache[i].ptr; }; InnerReference[NARROW[storage]]; }; ReleasePage: BTree.ReleasePage --[storage: PageStorage, number: PageNumber, update: UpdateState]-- = { InnerRelease: ENTRY PROC [h: Handle] = INLINE { i: CacheIndex; FOR i _ h.hashTable[HashIndex[h, number]], h.cache[i].hash UNTIL i = noIndex DO IF h.cache[i].pNum = number THEN EXIT; REPEAT FINISHED => ERROR Bug[pageNotInUse]; ENDLOOP; IF h.cache[i].referenceCount = 0 THEN ERROR Bug[pageNotInUse] ELSE h.cache[i].referenceCount _ h.cache[i].referenceCount - 1; IF update # unchanged THEN { IF number = BTree.statePage THEN { -- is the statePage; can be either startOfUpdate or endOfUpdate IF update = startOfUpdate THEN StoreCachePage[h, i] -- only this page is dirty, so just write it ELSE WriteDirtyPagesWithStatePageLast[h]; -- many pages possibly dirty } ELSE StoreCachePage[h, i]; -- is not the statePage; must be endOfUpdate; only page i is dirty }; }; InnerRelease[NARROW[storage]]; }; GetCacheStats: ENTRY PROC [h: Handle] RETURNS [hits, misses, reads, writes, cumChainLength, cumReadWriteTime: LONG CARDINAL] = { RETURN [hits: h.hits, misses: h.misses, reads: h.reads, writes: h.writes, cumChainLength: h.cumChainLength, cumReadWriteTime: h.cumReadWriteTime]; }; FreeBuffers: ENTRY PROC [h: Handle] = { FOR i: CacheIndex IN [0 .. h.cache.count) DO IF h.cache[i].ptr#NIL THEN {ReleaseSpace[h, h.cache[i].ptr]; h.cache[i].ptr _ NIL}; h.cache[i].empty _ TRUE; ENDLOOP; FOR c: CARDINAL IN [0..h.count) DO h.hashTable[c] _ noIndex; ENDLOOP; }; HashIndex: INTERNAL PROC [h: Handle, pNum: BTree.PageNumber] RETURNS [CARDINAL] = INLINE { RETURN [pNum MOD h.count]; }; WriteDirtyPagesWithStatePageLast: INTERNAL PROC [h: Handle] = { statePageIndex: CacheIndex _ noIndex; FOR i: CacheIndex IN [0 .. h.cache.count) DO IF NOT h.cache[i].empty AND h.cache[i].dirty THEN { IF h.cache[i].pNum = BTree.statePage THEN statePageIndex _ i ELSE StoreCachePage[h, i]; }; ENDLOOP; IF statePageIndex # noIndex THEN StoreCachePage[h, statePageIndex]; }; GetEmptyCacheEntry: INTERNAL PROC [h: Handle] RETURNS [i: CacheIndex] = { FOR cntr: CARDINAL IN [0..2*h.cache.count) DO i _ (h.rover + cntr) MOD h.cache.count; SELECT TRUE FROM h.cache[i].empty => { IF h.cache[i].ptr = NIL THEN h.cache[i].ptr _ GetSpace[h]; h.cache[i].recentlyUsed _ FALSE; h.cache[i].dirty _ FALSE; h.cache[i].referenceCount _ 0; EXIT; }; h.cache[i].referenceCount = 0 => { IF h.cache[i].recentlyUsed THEN h.cache[i].recentlyUsed _ FALSE ELSE { -- not even recently used IF h.cache[i].dirty THEN IF h.cache[i].pNum = BTree.statePage THEN LOOP -- never force out the statePage ELSE StoreCachePage[h, i]; EXIT; }; }; ENDCASE; REPEAT FINISHED => ERROR Bug[noAvailableCacheEntries]; ENDLOOP; IF NOT h.cache[i].empty THEN { hx: CARDINAL = HashIndex[h, h.cache[i].pNum]; IF h.hashTable[hx] = i THEN h.hashTable[hx] _ h.cache[i].hash ELSE FOR j: CacheIndex _ h.hashTable[hx], h.cache[j].hash UNTIL j = noIndex DO IF h.cache[j].hash = i THEN {h.cache[j].hash _ h.cache[i].hash; EXIT}; REPEAT FINISHED => ERROR Bug[cacheEntryNotInHashTable]; ENDLOOP; h.cache[i].empty _ TRUE; }; h.rover _ (i+1) MOD h.cache.count; }; ExtendFile: INTERNAL PROC [h: Handle, n: BTree.PageNumber] = { fileSizeForNPlus1: INT = (n+1)*h.filePagesPerPage; IF h.filePages = 0 THEN h.filePages _ h.file.GetInfo[].pages; IF h.filePages < fileSizeForNPlus1 THEN { h.filePages _ fileSizeForNPlus1 + 8*h.filePagesPerPage; h.file.SetPageCount[h.filePages]; h.file.SetByteCountAndCreatedTime[bytes: FS.BytesForPages[h.filePages]]; }; }; LoadCachePage: INTERNAL PROC [h: Handle, i: CacheIndex] = { then: LONG CARDINAL = PrincOpsUtils.GetClockPulses[]; TRUSTED {h.file.Read[from: h.cache[i].pNum*h.filePagesPerPage, nPages: h.filePagesPerPage, to: h.cache[i].ptr]}; h.cumReadWriteTime _ h.cumReadWriteTime + (PrincOpsUtils.GetClockPulses[]-then); h.reads _ h.reads+1; }; StoreCachePage: INTERNAL PROC [h: Handle, i: CacheIndex] = { then: LONG CARDINAL = PrincOpsUtils.GetClockPulses[]; TRUSTED {h.file.Write[to: h.cache[i].pNum*h.filePagesPerPage, nPages: h.filePagesPerPage, from: h.cache[i].ptr]}; h.cumReadWriteTime _ h.cumReadWriteTime + (PrincOpsUtils.GetClockPulses[]-then); h.writes _ h.writes+1; h.cache[i].dirty _ FALSE; }; GetSpace: INTERNAL PROC [h: Handle] RETURNS [BTree.PagePtr] = TRUSTED { interval: VM.Interval = VM.Allocate[count: h.vmPagesPerPage]; RETURN [VM.AddressForPageNumber[interval.page]]; }; ReleaseSpace: INTERNAL PROC [h: Handle, ptr: BTree.PagePtr] = TRUSTED { VM.Free[[page: VM.PageNumberForAddress[ptr], count: h.vmPagesPerPage]]; }; Bug: ERROR [type: BugType] = CODE; BugType: TYPE = {cacheEntryNotInHashTable, cantGetHere, noAvailableCacheEntries, pageNotInUse}; Initialize: PROC = { finalizationQueue _ SafeStorage.NewFQ[]; SafeStorage.EstablishFinalization[type: VMObject.CODE, npr: 0, fq: finalizationQueue ! SafeStorage.CantEstablishFinalization => SafeStorage.ReEstablishFinalization[type: VMObject.CODE, npr: 0, fq: finalizationQueue]]; TRUSTED {Process.Detach[FORK FinalizationProcess]}; }; FinalizationProcess: PROC = { DO h: Handle _ NARROW [SafeStorage.FQNext[finalizationQueue]]; FreeBuffers[h]; h _ NIL; ENDLOOP; }; finalizationQueue: SafeStorage.FinalizationQueue; Initialize[]; }. ¼BTreeSimpleImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Taft, January 21, 1984 5:00:25 pm PST Russ Atkinson (RRA) March 11, 1985 9:48:59 pm PST BTree representation BTree global operations Higher-level BTree access Lower-level BTree access PathStk hints Private The following are the procedures passed to the BTree package as the RepresentationPrimitives (in addition to the client's Compare and CompareEntries procedures). Storage primitives and BTree cache This is almost identical to BTreeVMImpl, and perhaps someday most of the code should be shared (in particular, everything to do with VM cache management as opposed to file I/O). The main difference is that the BTree is designated by an FS.OpenFile rather than a File.Handle. FlushCache: ENTRY PROC [h: Handle] = { WriteDirtyPagesWithStatePageLast[h]; FOR i: CacheIndex IN [0 .. h.cache.count) DO h.cache[i].empty _ TRUE; ENDLOOP; FOR c: CARDINAL IN [0..h.count) DO h.hashTable[c] _ noIndex; ENDLOOP; }; The cache entry that is returned is empty; it has a valid space and a useCount of zero. It may be assocaited with a page by setting pNum. empty entry not currently being used Errors Initialization and finalization ÊÝ– "Cedar" style˜head™Icodešœ Ïmœ1™Lšœžœ"žœ˜KLšœ˜L˜—š¡œ ¢ œž œ˜lLšžœ =˜ULšœ˜L˜—š¡œ ¢ œž œ˜rLšžœ =˜ULšœ˜——™"L™’Lšœ žœ žœ˜2Lšœžœ ˜'Lšœžœžœ ˜šœ žœž œžœ˜#Lšœ˜Lšœ žœ˜Lšœ+˜+Lšœžœ˜Lšœ˜Lšœžœ˜Lšœ?žœžœ˜QLš œ žœžœžœžœ ˜8Lšœ˜—Lš œžœžœžœžœžœ ˜Gšœ žœžœ˜Lšœ˜Lšœ˜Lšœ˜Lšœ˜Lšœž˜#Lšœ˜—š¡ œžœ_žœ ˜‚Lšœ žœ˜)Lšœ˜Lšœ "˜3Lšœ&˜&LšœH˜HL˜ Lšœ žœ˜ šžœžœž˜,Lšœžœ˜Lšœžœ˜Lšžœ˜—šžœžœžœž˜"Lšœ˜Lšžœ˜—L˜"Lšžœ˜ Lšœ˜L˜—š¡ œ B¢ œ˜š¡œžœžœžœ˜1Lšœžœ˜&Lšœ˜šžœ(žœ ž˜?L˜&Lšžœžœžœ˜=šžœžœ ˜$L˜Lšœ˜Lšœ˜Lšœžœ˜Lšžœ žœ˜'Lšœ$˜$Lšœ˜Lšœ˜—Lšžœ˜—Lšœžœ˜Lšœ:˜:Lšžœ žœžœ˜,Lšžœ žœ˜)Lšœ˜Lšœ˜—Lšœžœ ˜ Lšœ˜L˜—š¡ œ Cœ˜fš¡ œžœžœžœ˜/Lšœ˜šžœ8žœ ž˜OLšžœžœžœ˜&Lšžœžœžœ˜+Lšžœ˜—Lšžœ˜ Lšžœžœ˜Lšžœ;˜?šžœžœ˜šžœ˜šžœ ?˜FLšžœ˜Lšžœ ,˜FLšžœ& ˜FLšœ˜—Lšžœ B˜]—Lšœ˜—Lšœ˜—Lšœ žœ ˜Lšœ˜L˜—š ¡ œžœžœ žœAžœžœ˜€LšžœŒ˜’Lšœ˜L˜—š¡ œžœžœ™&Lšœ$™$šžœžœž™,Lšœžœ™Lšžœ™—šžœžœžœž™"Lšœ™Lšžœ™—Lšœ™L™—š¡ œžœžœ˜'šžœžœž˜,Lšžœžœžœ4žœ˜SLšœžœ˜Lšžœ˜—šžœžœžœž˜"Lšœ˜Lšžœ˜—Lšœ˜L˜—š ¡ œžœžœ&žœžœžœ˜[Lšžœžœ ˜Lšœ˜—L˜š¡ œžœžœ˜?Lšœ%˜%šžœžœž˜,Lšžœžœžœ˜,šžœ˜Lšžœ"˜$Lšžœ˜Lšžœ˜Lšœ˜—Lšžœ˜—Lšžœžœ#˜CLšœ˜L˜—š¡œžœžœ žœ˜ILšœ) œ Fœ ™Ššžœžœžœž˜-Lšœžœ˜'šžœžœž˜šœ˜Lšœ ™ Lšžœž˜Lšžœ˜"Lšœžœ˜ Lšœžœ˜Lšœ˜Lšžœ˜Lšœ˜—šœ"˜"Lšœ™Lšžœ˜Lšžœž˜$šžœ ˜!šžœž˜Lšžœ"˜$Lšžœžœ  ˜*Lšžœ˜—Lšžœ˜Lšœ˜—Lšœ˜—Lšžœ˜—Lšžœžœžœ˜6Lšžœ˜—šžœžœžœ˜Lšœžœ!˜-Lšžœ˜Lšžœ"˜&šžœžœ2žœ ž˜NLšžœžœ%žœ˜FLšžœžœžœ˜7Lšžœ˜—Lšœžœ˜Lšœ˜—Lšœžœ˜"Lšœ˜L˜—š¡ œžœžœ%˜>Lšœžœ˜2Lšžœžœ&˜=Lšžœ ˜"šžœ˜Lšœ7˜7Lšœ!˜!LšœH˜HLšœ˜—Lšœ˜L˜—š¡ œžœžœ˜;Lšœžœžœ"˜5Lšžœi˜pLšœP˜PL˜Lšœ˜L˜—š¡œžœžœ ˜=Lšœžœžœ"˜5Lšžœj˜qLšœP˜PL˜Lšœžœ˜Lšœ˜L˜—š ¡œžœžœ žœžœ˜GLšœ=˜=Lšžœ*˜0Lšœ˜L˜—š¡ œžœžœ#žœ˜GLšœG˜GLšœ˜——™Lšœžœžœ˜"Lšœ žœR˜_—™š¡ œžœ˜Lšœ(˜(šœ1žœ˜Tšœ*˜*Lšœ3žœ"˜Y——Lšžœžœ˜3Lšœ˜L˜—š¡œžœ˜šž˜Lšœ žœ)˜;L˜Lšœžœ˜Lšžœ˜—Lšœ˜L˜—Lšœ1˜1L˜ Lšœ˜——…—Dš\3