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 = BEGIN HashValue: TYPE = [0..256); Table: TYPE = ARRAY HashValue OF FileInternal.Handle; table: REF Table = NEW[Table _ ALL[NIL]]; Hash: PROC[id: File.FileID] RETURNS[HashValue] = { l: Basics.LongNumber = [li[LOOPHOLE[id]]]; RETURN[l.ll] }; checkpointing: BOOL _ FALSE; notCheckpointing: CONDITION; lastInsert: LONG CARDINAL _ 0; lastRemove: LONG CARDINAL _ 0; contents: INT _ 0; debug: BOOL _ FALSE; CountContents: INTERNAL PROC RETURNS[total: INT _ 0] = BEGIN FOR h: HashValue IN HashValue DO FOR file: FileInternal.Handle _ table[h], file.rest UNTIL file = NIL DO total _ total+1 ENDLOOP; ENDLOOP; END; CheckContents: INTERNAL PROC = BEGIN found: INT = CountContents[]; IF found # contents THEN ERROR ConfusedHash[]; END; Lookup: PUBLIC ENTRY PROC[volume: File.Volume, fp: File.FP] RETURNS [file: FileInternal.Handle] = BEGIN 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 => BEGIN file _ InternalCreate[]; file.volume _ volume; file.fp _ fp; InternalInsert[file]; END ENDLOOP; END; ConfusedHash: ERROR = CODE; allocNotInserted: INT _ 0; insertion: CONDITION; AllocForCreate: PUBLIC ENTRY PROC RETURNS[FileInternal.Handle] = BEGIN ENABLE UNWIND => NULL; WHILE checkpointing DO WAIT notCheckpointing ENDLOOP; allocNotInserted _ allocNotInserted+1; RETURN[ InternalCreate[] ] END; Insert: PUBLIC ENTRY PROC[new: FileInternal.Handle] = BEGIN ENABLE UNWIND => NULL; allocNotInserted _ allocNotInserted-1; BROADCAST insertion; InternalInsert[new]; END; DontInsert: PUBLIC ENTRY PROC = BEGIN allocNotInserted _ allocNotInserted-1; BROADCAST insertion; END; InternalInsert: INTERNAL PROC[new: FileInternal.Handle] = BEGIN h: HashValue = Hash[new.fp.id]; prev: FileInternal.Handle _ NIL; file: FileInternal.Handle _ table[h]; IF debug THEN CheckContents[]; DO IF file = NIL THEN BEGIN -- 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 END; 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[]; END; MyQueue: SafeStorage.FinalizationQueue = SafeStorage.NewFQ[length: 50]; InternalCreate: INTERNAL PROC RETURNS[FileInternal.Handle] = BEGIN 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 BEGIN Remove[file]; FileInternal.FreeHeaderVM[file]; END; ENDLOOP; RETURN[NEW[FileInternal.Object _ []]] END; Remove: INTERNAL PROC[old: FileInternal.Handle] = BEGIN 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 BEGIN contents _ contents-1; IF prev = NIL THEN table[h] _ file.rest ELSE prev.rest _ file.rest; EXIT END; prev _ file; file _ file.rest; ENDLOOP; lastRemove _ LOOPHOLE[old, LONG CARDINAL]; IF debug THEN CheckContents[]; END; DuplicateHashTableEntry: ERROR = CODE; LockAndCloseFiles: PUBLIC ENTRY PROC = BEGIN 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 BEGIN ENABLE File.Error => IF why = unknownFile AND file # NIL THEN CONTINUE; FileInternal.Lock[file, exclusive]; IF file.state = opened THEN { file.state _ none; FileInternal.FreeHeaderVM[file] }; END; ENDLOOP; ENDLOOP; END; UnlockFiles: PUBLIC ENTRY PROC = BEGIN 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; END; SafeStorage.EstablishFinalization[type: CODE[FileInternal.Object], npr: 1, fq: MyQueue]; END. öCedar Nucleus (Files): global table of open files (to cause sharing of handles) FileTableImpl.mesa Andrew Birrell November 15, 1983 3:05 pm Last Edited by: Levin, August 8, 1983 6:00 pm Last Edited by: Schroeder, June 10, 1983 5:20 pm Last Edited by: Paul Rovner, November 9, 1983 11:19 am 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; ʘJšœP™PJšœ™Jšœ)™)J™-J™0J™7šÏk ˜ Jšœœ˜Jšœœœ ˜(Jšœ œ.˜@Jšœ œY˜j—J˜šœœ˜Jšœ ˜'Jšœ ˜Jšœ˜ —J˜Jš˜J˜Jšœ œ ˜J˜Jšœœœ œ˜5J˜Jš œœ œ œœ˜)J˜šÏnœœœ ˜0JšœU™UJšœœœ ˜<—J˜Jšœœœ˜Jšœ œ˜J˜Jšœ œœ˜Jšœ œœ˜Jšœ œ˜Jšœœœ˜J˜š ž œœœœœ˜6Jš˜Jšœœ ˜šœœ1œ˜GJšœœ˜—Jšœ˜Jšœ˜—J˜šž œœœ˜Jš˜Jšœœ˜Jšœœœ˜.Jšœ˜—J˜š žœœœœœ˜;Jšœ˜%Jš œœœ˜J˜Jšœœœœ˜5Jšœœ˜/Jš œœœœœœ˜Všœœ˜Jš˜Jšœ˜J˜J˜ J˜Jš˜—Jšœ˜Jšœ˜—J˜Jšœœœ˜J˜Jšœœ˜Jšœ œ˜J˜š žœœœœœ˜@Jš˜Jšœœœ˜Jšœœœœ˜5Jšœ&˜&Jšœ˜Jšœ˜—J˜šžœœœœ˜5Jš˜Jšœœœ˜Jšœ' œ ˜;Jšœ˜Jšœ˜—J˜šž œœœœ˜Jš˜Jšœ' œ ˜;Jšœ˜—J˜Jšœ™šžœœœ˜9Jš˜Jšœ˜Jšœœ˜ Jšœ%˜%Jšœœ˜š˜Jšœ˜ šœœÏc*˜6Jšœ œœœŸ˜