FileTableImpl.mesa - global table of open files (to cause sharing of handles)
Copyright © 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
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: BOOLFALSE;
notCheckpointing: CONDITION;
lastInsert: LONG CARDINAL ← 0;
lastRemove: LONG CARDINAL ← 0;
contents: INT ← 0;
debug: BOOLFALSE;
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];
};
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] = {
Called by Lookup and by Insert
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] = {
Called by Lookup and by AllocForCreate
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] = {
Called only by InternalCreate
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;
file.rest ← NIL;
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];
}.