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
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] =
The LOOPHOLE below is "respectable", since all we want is a decent hash function (?).
{ l: Basics.LongNumber = [li[LOOPHOLE[id]]]; RETURN[l.ll] };
checkpointing: BOOLFALSE;
notCheckpointing: CONDITION;
lastInsert: LONG CARDINAL ← 0;
lastRemove: LONG CARDINAL ← 0;
contents: INT ← 0;
debug: BOOLFALSE;
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;
Called by Lookup and by Insert
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];
Called by Lookup and by AllocForCreate
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;
Called only by InternalCreate
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;
file.rest ← NIL;
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.