<> <> <> <> <> <> <> <> <<>> DIRECTORY Basics USING [LongNumber], Process USING [Detach], SafeStorage USING [CantEstablishFinalization, EnableFinalization, EstablishFinalization, FinalizationQueue, FQNext, NewFQ], YggDID USING [DID, EqualDIDs, HashDID], YggDIDMap USING [RunList], YggFile USING [FileHandle], YggFileInternal USING [FileHandleRep], YggInternal USING [FileHandle]; YggFileTableImpl: CEDAR MONITOR IMPORTS Process, SafeStorage, YggDID EXPORTS YggInternal, YggFileInternal = { FileHandle: TYPE = YggInternal.FileHandle; FileHandleRep: PUBLIC TYPE = YggFileInternal.FileHandleRep; HashValue: TYPE = [0..256); TableEntry: TYPE = LIST OF RECORD[ fh: YggFile.FileHandle, reissued: BOOL _ FALSE ]; Table: TYPE = ARRAY HashValue OF TableEntry; table: REF Table = NEW[Table _ ALL[NIL]]; lastInsert: CARD _ 0; lastRemove: CARD _ 0; contents: INT _ 0; debug: BOOL _ FALSE; Hash: PROC [did: YggDID.DID, fileUse: ATOM] RETURNS [HashValue] = TRUSTED { atomAddress: CARD32 = LOOPHOLE[fileUse]; didHash: CARD32 = YggDID.HashDID[did]; ah: Basics.LongNumber; dh: Basics.LongNumber; bh: CARD32; l: Basics.LongNumber; ah _ LOOPHOLE[atomAddress, Basics.LongNumber]; dh _ LOOPHOLE[didHash, Basics.LongNumber]; bh _ ah.hi + dh.lo; l _ [li[LOOPHOLE[bh]]]; RETURN[l.ll]; }; CountContents: INTERNAL PROC RETURNS [total: INT _ 0] = { FOR h: HashValue IN HashValue DO FOR fileList: TableEntry _ table[h], fileList.rest UNTIL fileList = NIL DO total _ total+1; ENDLOOP; ENDLOOP; }; CheckContents: INTERNAL PROC = { found: INT = CountContents[]; IF found # contents THEN ERROR ConfusedHash[]; }; Lookup: PUBLIC ENTRY PROC [runList: YggDIDMap.RunList, did: YggDID.DID, fileUse: ATOM] RETURNS [file: YggFile.FileHandle] = { ENABLE UNWIND => NULL; fileList: TableEntry; h: HashValue = Hash[did, fileUse]; FOR fileList _ table[h], fileList.rest UNTIL fileList = NIL DO IF YggDID.EqualDIDs[fileList.first.fh.did, did] AND fileList.first.fh.fileUse = fileUse THEN { fileList.first.reissued _ TRUE; file _ fileList.first.fh; EXIT; }; REPEAT FINISHED => { file _ InternalCreate[]; file.did _ did; file.fileUse _ fileUse; file.runList _ runList; FOR rl: YggDIDMap.RunList _ runList, rl.rest UNTIL rl = NIL DO file.sizeInPages _ MAX [rl.first.firstPage+rl.first.pages, file.sizeInPages]; ENDLOOP; InternalInsert[file]; } ENDLOOP; }; ConfusedHash: ERROR = CODE; allocNotInserted: INT _ 0; insertion: CONDITION; AllocForCreate: PUBLIC ENTRY PROC RETURNS [YggFile.FileHandle] = { ENABLE UNWIND => NULL; allocNotInserted _ allocNotInserted+1; RETURN[ InternalCreate[] ] }; Insert: PUBLIC ENTRY PROC [new: YggFile.FileHandle] = { ENABLE UNWIND => NULL; allocNotInserted _ allocNotInserted-1; BROADCAST insertion; InternalInsert[new]; }; DontInsert: PUBLIC ENTRY PROC = { allocNotInserted _ allocNotInserted-1; BROADCAST insertion; }; InternalInsert: INTERNAL PROC [new: YggFile.FileHandle] = { <> h: HashValue _ Hash[new.did, new.fileUse]; prev: TableEntry _ NIL; fileList: TableEntry _ table[h]; IF debug THEN CheckContents[]; DO IF fileList = NIL THEN { -- stick new at the end of this hash chain lastInsert _ LOOPHOLE[new, LONG CARDINAL]; -- for debugging contents _ contents+1; IF prev = NIL THEN table[h] _ LIST[[new, FALSE]] ELSE prev.rest _ CONS[[new, FALSE], NIL]--prev.rest = NIL--; SafeStorage.EnableFinalization[new]; EXIT }; prev _ fileList; fileList _ fileList.rest; ENDLOOP; IF debug THEN CheckContents[]; }; MyQueue: SafeStorage.FinalizationQueue = SafeStorage.NewFQ[length: 50]; Remove: INTERNAL PROC [old: YggFile.FileHandle] = { <> h: HashValue = Hash[old.did, old.fileUse]; prev: TableEntry _ NIL; fileList: TableEntry _ table[h]; IF debug THEN CheckContents[]; DO IF fileList = NIL THEN EXIT --ERROR--; -- old was not found in table. IF fileList.first.fh = old THEN { contents _ contents-1; IF prev = NIL THEN table[h] _ fileList.rest ELSE prev.rest _ fileList.rest; EXIT }; prev _ fileList; fileList _ fileList.rest; ENDLOOP; lastRemove _ LOOPHOLE[old, LONG CARDINAL]; IF debug THEN CheckContents[]; }; DuplicateHashTableEntry: ERROR = CODE; InternalCreate: INTERNAL PROC RETURNS [YggFile.FileHandle] = { <> RETURN[NEW[YggFileInternal.FileHandleRep _ []]] }; FileTableFinalizationProcess: PROC = { DO innerFileTableFinalizationProcess: ENTRY PROC [] = { ENABLE UNWIND => NULL; fileList: TableEntry _ NIL; h: HashValue = Hash[file.did, file.fileUse]; FOR fileList _ table[h], fileList.rest UNTIL fileList = NIL DO IF YggDID.EqualDIDs[fileList.first.fh.did, file.did] AND fileList.first.fh.fileUse = file.fileUse THEN { <> IF file # fileList.first.fh THEN ERROR; EXIT; }; REPEAT FINISHED => { -- not found! OK while testing in PrincOps, but ERROR in PCedar }; ENDLOOP; IF fileList # NIL THEN { IF fileList.first.reissued THEN { IF debug THEN { fh: TableEntry _ table[Hash[fileList.first.fh.did, fileList.first.fh.fileUse]]; UNTIL fh = NIL DO IF fh = fileList THEN EXIT ELSE fh _ fh.rest ENDLOOP; <> <> }; fileList.first.reissued _ FALSE; SafeStorage.EnableFinalization[file]; } ELSE { Remove[fileList.first.fh]; }; }; }; file: REF FileHandleRep _ NIL; file _ NARROW[SafeStorage.FQNext[MyQueue]]; innerFileTableFinalizationProcess[]; file _ NIL; ENDLOOP; }; SafeStorage.EstablishFinalization[type: CODE[YggFileInternal.FileHandleRep], npr: 1, fq: MyQueue ! SafeStorage.CantEstablishFinalization => CONTINUE;]; TRUSTED {Process.Detach[FORK FileTableFinalizationProcess[] ];}; }.