DIRECTORY Basics USING [LongNumber], File USING [Error, FileID, FP, Volume], FileInternal USING [FreeHeaderVM, Handle, Lock, Object, Unlock], SafeStorage USING [EnableFinalization, EstablishFinalization, FinalizationQueue, FQEmpty, FQNext, NewFQ]; FileTableImpl: CEDAR MONITOR IMPORTS File, FileInternal, SafeStorage EXPORTS FileInternal SHARES File = { HashValue: TYPE = [0..256); Table: TYPE = ARRAY HashValue OF FileInternal.Handle; table: REF Table = NEW[Table _ ALL[NIL]]; checkpointing: BOOL _ FALSE; notCheckpointing: CONDITION; lastInsert: LONG CARDINAL _ 0; lastRemove: LONG CARDINAL _ 0; contents: INT _ 0; debug: BOOL _ FALSE; Hash: PROC [id: File.FileID] RETURNS [HashValue] = { l: Basics.LongNumber = [li[LOOPHOLE[id]]]; RETURN[l.ll]; }; CountContents: INTERNAL PROC RETURNS [total: INT _ 0] = { FOR h: HashValue IN HashValue DO FOR file: FileInternal.Handle _ table[h], file.rest UNTIL file = NIL DO total _ total+1 ENDLOOP; ENDLOOP; }; CheckContents: INTERNAL PROC = { found: INT = CountContents[]; IF found # contents THEN ERROR ConfusedHash[]; }; Lookup: PUBLIC ENTRY PROC [volume: File.Volume, fp: File.FP] RETURNS [file: FileInternal.Handle] = { ENABLE UNWIND => NULL; h: HashValue = Hash[fp.id]; WHILE checkpointing DO WAIT notCheckpointing ENDLOOP; FOR file _ table[h], file.rest UNTIL file = NIL DO IF file.fp.id = fp.id AND file.volume = volume THEN { file.reissued _ TRUE; EXIT }; REPEAT FINISHED => { file _ InternalCreate[]; file.volume _ volume; file.fp _ fp; InternalInsert[file]; } ENDLOOP; }; ConfusedHash: ERROR = CODE; allocNotInserted: INT _ 0; insertion: CONDITION; AllocForCreate: PUBLIC ENTRY PROC RETURNS [FileInternal.Handle] = { ENABLE UNWIND => NULL; WHILE checkpointing DO WAIT notCheckpointing ENDLOOP; allocNotInserted _ allocNotInserted+1; RETURN[ InternalCreate[] ] }; Insert: PUBLIC ENTRY PROC [new: FileInternal.Handle] = { ENABLE UNWIND => NULL; allocNotInserted _ allocNotInserted-1; BROADCAST insertion; InternalInsert[new]; }; DontInsert: PUBLIC ENTRY PROC = { allocNotInserted _ allocNotInserted-1; BROADCAST insertion; }; InternalInsert: INTERNAL PROC [new: FileInternal.Handle] = { h: HashValue = Hash[new.fp.id]; prev: FileInternal.Handle _ NIL; file: FileInternal.Handle _ table[h]; IF debug THEN CheckContents[]; DO IF file = 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] _ new ELSE prev.rest _ new--prev.rest = NIL--; SafeStorage.EnableFinalization[new]; EXIT }; IF file.fp.id = new.fp.id AND file.volume = new.volume THEN ERROR ConfusedHash[]; prev _ file; file _ file.rest; ENDLOOP; IF debug THEN CheckContents[]; }; MyQueue: SafeStorage.FinalizationQueue = SafeStorage.NewFQ[length: 50]; InternalCreate: INTERNAL PROC RETURNS [FileInternal.Handle] = { UNTIL SafeStorage.FQEmpty[MyQueue] DO file: FileInternal.Handle = NARROW[SafeStorage.FQNext[MyQueue]]; IF file.reissued THEN { IF debug THEN { fh: FileInternal.Handle _ table[Hash[file.fp.id]]; UNTIL fh = NIL DO IF fh = file THEN EXIT ELSE fh _ fh.rest ENDLOOP; IF fh = NIL THEN ERROR; }; file.reissued _ FALSE; SafeStorage.EnableFinalization[file]; } ELSE { Remove[file]; FileInternal.FreeHeaderVM[file]; }; ENDLOOP; RETURN[NEW[FileInternal.Object _ []]] }; Remove: INTERNAL PROC [old: FileInternal.Handle] = { h: HashValue = Hash[old.fp.id]; prev: FileInternal.Handle _ NIL; file: FileInternal.Handle _ table[h]; IF debug THEN CheckContents[]; DO IF file = NIL THEN EXIT--ERROR--; -- old was not found in table. IF file = old THEN { contents _ contents-1; IF prev = NIL THEN table[h] _ file.rest ELSE prev.rest _ file.rest; EXIT }; prev _ file; file _ file.rest; ENDLOOP; lastRemove _ LOOPHOLE[old, LONG CARDINAL]; IF debug THEN CheckContents[]; }; DuplicateHashTableEntry: ERROR = CODE; LockAndCloseFiles: PUBLIC ENTRY PROC = { checkpointing _ TRUE; WHILE allocNotInserted > 0 DO WAIT insertion ENDLOOP; FOR h: HashValue IN HashValue DO FOR file: FileInternal.Handle _ table[h], file.rest UNTIL file = NIL DO { ENABLE File.Error => IF why = unknownFile AND file # NIL THEN LOOP; FileInternal.Lock[file, exclusive]; IF file.state = opened THEN { file.state _ none; FileInternal.FreeHeaderVM[file] }; }; ENDLOOP; ENDLOOP; }; UnlockFiles: PUBLIC ENTRY PROC = { FOR h: HashValue IN HashValue DO FOR file: FileInternal.Handle _ table[h], file.rest UNTIL file = NIL DO IF file.state # deleted THEN FileInternal.Unlock[file] ENDLOOP; ENDLOOP; checkpointing _ FALSE; BROADCAST notCheckpointing; }; SafeStorage.EstablishFinalization[type: CODE[FileInternal.Object], npr: 1, fq: MyQueue]; }. FileTableImpl.mesa - global table of open files (to cause sharing of handles) Copyright c 1985 by Xerox Corporation. All rights reserved. Andrew Birrell November 15, 1983 3:05 pm Levin, August 8, 1983 6:00 pm Schroeder, June 10, 1983 5:20 pm Rovner, November 9, 1983 11:19 am Russ Atkinson (RRA) May 14, 1985 12:35:48 pm PDT The LOOPHOLE below is "respectable", since all we want is a decent hash function (?). Called by Lookup and by Insert Called by Lookup and by AllocForCreate Called only by InternalCreate file.rest _ NIL; Κ₯˜codešœM™MKšœ Οmœ1™