YggFileTableImpl.mesa - global table of open files (to cause sharing of handles)
Copyright Ó 1985, 1986, 1988 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
Bob Hagmann March 17, 1989 2:36:44 pm PST
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: BOOLFALSE
];
Table: TYPE = ARRAY HashValue OF TableEntry;
table: REF Table = NEW[Table ← ALL[NIL]];
lastInsert: CARD ← 0;
lastRemove: CARD ← 0;
contents: INT ← 0;
debug: BOOLFALSE;
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] = {
Called by Lookup and by Insert
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] = {
Called only by InternalCreate and FileTableFinalizationProcess
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] = {
Called by Lookup and by AllocForCreate
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 {
fileList.first.reissued ← TRUE;
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;
Uncomment the next line for non-PrincOps testing
IF fh = NIL THEN ERROR;
};
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[] ];};
}.