UFFileManagerImpl.mesa
Created January 7, 1983
Last edit by Michael Plass on December 28, 1983 2:58 pm
Last edit by Doug Wyatt on November 22, 1983 2:31 pm
DIRECTORY
FS USING [Close, Error, ErrorDesc, GetInfo, nullOpenFile, Open, OpenFile, Read],
Process USING [Detach, InitializeCondition, MsecToTicks, Ticks],
RefText USING [ObtainScratch, ReleaseScratch, TrustTextAsRope],
Rope USING [Fetch, FromRefText, Length, ROPE],
SafeStorage USING [EnableFinalization, EstablishFinalization, FinalizationQueue, FQEmpty, FQNext, NewFQ, ReclaimCollectibleObjects],
VM USING [AddressForPageNumber, Allocate, Free, Interval, nullInterval, WordsForPages],
UFFileManager;
UFFileManagerImpl: CEDAR MONITOR
IMPORTS FS, Process, RefText, Rope, SafeStorage, VM
EXPORTS UFFileManager =
BEGIN OPEN UFFileManager;
FontFile: PUBLIC TYPE = REF FontFilePermission;
FontFilePermission: PUBLIC TYPE = RECORD [
link: REF FontFilePermission,
key: Key
];
FontFileDescription: TYPE = RECORD [
permissions: REF FontFilePermission,
permissionHint: REF FontFilePermission,
clientData: REFNIL,
openFile: FS.OpenFile,
error: FS.ErrorDesc,
vm: VM.Interval,
origin: LONG POINTER,
size: INT -- in words
];
keyTable: REF KeyTableRec ← NEW[KeyTableRec[6]];
KeyTableRec: TYPE = RECORD [
numberOfEntries: NAT ← 0,
entries: SEQUENCE length: NAT OF NameFilePair
];
NameFilePair: TYPE = RECORD [fileName: ROPE, description: REF FontFileDescription];
permissionCount: INT ← 0;
permissionsSinceCollectionRequest: NAT ← 0;
permissionThreshold: NAT ← 96;
RequestCollection: PROC = {
permissionsSinceCollectionRequest ← 0;
SafeStorage.ReclaimCollectibleObjects[];
};
finalizationQueue: SafeStorage.FinalizationQueue ← SafeStorage.NewFQ[permissionThreshold];
FinalizerProcess: PROCEDURE [finalizationQueue: SafeStorage.FinalizationQueue] = {
DO
permission: REF FontFilePermission ← NARROW[SafeStorage.FQNext[finalizationQueue]];
key: Key ← permission.key;
closed: BOOLEAN ← Finalize[permission];
permission ← NIL;
IF closed THEN fontFileWatcherProc[key, close];
IF SafeStorage.FQEmpty[finalizationQueue] THEN RequestCollection[];
ENDLOOP;
};
Finalize: ENTRY PROCEDURE [permission: REF FontFilePermission] RETURNS [closed: BOOLEANFALSE] = {
ENABLE UNWIND => NULL;
key: Key ← permission.key;
description: REF FontFileDescription ← keyTable[key.index].description;
IF permission = description.permissions THEN {
description.permissions ← permission.link;
}
ELSE {
prev: REF FontFilePermission ← description.permissions;
FOR p: REF FontFilePermission ← description.permissions.link, p.link UNTIL p=permission DO
prev ← p;
ENDLOOP;
prev.link ← permission.link;
};
permission.link ← NIL;
IF description.permissions = NIL THEN {
IF description.openFile # FS.nullOpenFile THEN {
description.origin ← NIL;
description.size ← 0;
TRUSTED { VM.Free[description.vm] };
description.vm ← VM.nullInterval;
FS.Close[description.openFile];
description.openFile ← FS.nullOpenFile;
closed ← TRUE;
};
keyTable[key.index].description ← NIL;
};
permissionCount ← permissionCount-1;
};
Init: ENTRY PROCEDURE = {
ENABLE UNWIND => NULL;
SafeStorage.EstablishFinalization[CODE[FontFilePermission], 1, finalizationQueue];
TRUSTED {Process.Detach[FORK FinalizerProcess[finalizationQueue]]};
};
KeyOf: PUBLIC ENTRY PROCEDURE [fileName: ROPE] RETURNS [key: Key] = {
ENABLE UNWIND => NULL;
len: NAT ← fileName.Length[];
canonicalName: REF TEXT ← RefText.ObtainScratch[len];
i: NAT ← 0;
Getc: PROC RETURNS [ch: CHAR] = {IF i<len THEN {ch𡤏ileName.Fetch[i]; i←i+1} ELSE ch←' };
c: CHAR ← Getc[];
j: NAT ← 0;
Put: PROC [ch: CHAR] = {canonicalName[j] ← ch; j ← j + 1};
PreventTableOverflow: PROC = {
entries: NAT ← keyTable.numberOfEntries;
IF entries = keyTable.length THEN {
newKeyTable: REF KeyTableRec ← NEW[KeyTableRec[MIN[entries+entries/3+1, LAST[NAT]-1]]];
newKeyTable.numberOfEntries ← entries;
FOR l: NAT IN [0..entries) DO
newKeyTable[l] ← keyTable[l];
ENDLOOP;
keyTable ← newKeyTable;
};
};
Match: PROC [rope: Rope.ROPE] RETURNS [BOOLEAN] = {
IF rope.Length # j THEN RETURN[FALSE];
FOR i: NAT DECREASING IN [0..j) DO
IF rope.Fetch[i] # canonicalName[i] THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE]
};
lowerToUpper: INTEGER ← 'A - 'a;
key ← [index:LAST[NAT]];
WHILE c#' DO
SELECT c FROM
'[ => {Put['/]; c ← Getc[]};
'] => {Put['/]; IF (c ← Getc[]) = '< THEN c ← Getc[]};
'> => {Put['/]; c ← Getc[]};
IN ['a .. 'z] => {Put[c + lowerToUpper]; c ← Getc[]};
ENDCASE => {Put[c]; c ← Getc[]};
ENDLOOP;
canonicalName.length ← j;
FOR k: NAT IN [0..keyTable.numberOfEntries) DO
IF Match[keyTable[k].fileName] THEN {key ← [index: k]; EXIT};
ENDLOOP;
IF key.index = LAST[NAT] THEN {
PreventTableOverflow[];
keyTable[keyTable.numberOfEntries] ← [fileName: Rope.FromRefText[canonicalName], description: NIL];
key ← [index: keyTable.numberOfEntries];
keyTable.numberOfEntries ← keyTable.numberOfEntries+1;
};
RefText.ReleaseScratch[canonicalName];
};
KeyOfRefText: PUBLIC PROCEDURE [fileName: REF TEXT] RETURNS [key: Key] = {
key ← KeyOf[RefText.TrustTextAsRope[fileName]];
};
waitTime: Process.Ticks ← Process.MsecToTicks[5000];
fileHintTimeout: CONDITION;
fileHintTimeoutProcess: PROCESSNIL;
FileHintTimeoutProcess: ENTRY PROCEDURE = {
ENABLE UNWIND => NULL;
WAIT fileHintTimeout;
fileHintTimeoutProcess ← NIL;
FOR i: NAT IN [0..keyTable.numberOfEntries) DO
description: REF FontFileDescription ← keyTable[i].description;
IF description#NIL THEN description.permissionHint ← NIL;
ENDLOOP;
WAIT fileHintTimeout;
IF fileHintTimeoutProcess = NIL THEN SafeStorage.ReclaimCollectibleObjects[suspendMe: FALSE];
};
StartFileHintTimeout: ENTRY PROCEDURE = {
ENABLE UNWIND => NULL;
IF fileHintTimeoutProcess=NIL THEN TRUSTED {
Process.InitializeCondition[@fileHintTimeout, waitTime];
Process.Detach[fileHintTimeoutProcess ← FORK FileHintTimeoutProcess[]];
};
};
Open: PUBLIC PROCEDURE [key: Key, initProc: InitProc ← NIL] RETURNS [fontFile: FontFile] = {
description: REF FontFileDescription ← keyTable[key.index].description;
IF description#NIL THEN fontFile ← description.permissionHint;
IF fontFile=NIL THEN {
triedToOpen: BOOLEAN;
[fontFile, triedToOpen] ← MonitoredOpen[key, initProc];
IF triedToOpen THEN fontFileWatcherProc[key, IF Status[fontFile].ok THEN open ELSE openFailed];
IF permissionsSinceCollectionRequest>permissionThreshold THEN RequestCollection[];
};
StartFileHintTimeout[];
};
MonitoredOpen: ENTRY PROCEDURE [key: Key, initProc: InitProc ← NIL] RETURNS [fontFile: FontFile, triedToOpen: BOOLEANFALSE] = {
ENABLE UNWIND => NULL;
description: REF FontFileDescription ← keyTable[key.index].description;
IF description = NIL THEN {
openOk: BOOLEANTRUE;
description ← NEW[FontFileDescription];
description.vm ← VM.nullInterval;
description.openFile ← FS.nullOpenFile;
description.error ← [ok, NIL, NIL];
description.openFile ← FS.Open[
name: keyTable[key.index].fileName, lock: $read, remoteCheck: FALSE
! FS.Error => {openOk ← FALSE; description.error ← error; CONTINUE}];
triedToOpen ← TRUE;
IF openOk THEN TRUSTED {
pages: INT = FS.GetInfo[description.openFile].pages;
description.vm ← VM.Allocate[pages];
description.origin ← VM.AddressForPageNumber[description.vm.page];
FS.Read[file: description.openFile, from: 0, nPages: pages, to: description.origin];
description.size ← VM.WordsForPages[pages];
};
};
keyTable[key.index].description ← description;
fontFile ← NEW[FontFilePermission];
permissionCount ← permissionCount + 1;
permissionsSinceCollectionRequest ← permissionsSinceCollectionRequest + 1;
fontFile.link ← description.permissions;
description.permissions ← fontFile;
fontFile.key ← key;
IF description.clientData = NIL AND description.openFile # NIL AND initProc # NIL THEN
description.clientData ← initProc[key, fontFile];
description.permissionHint ← fontFile;
SafeStorage.EnableFinalization[fontFile];
};
GetData: PUBLIC PROCEDURE [fontFile: FontFile] RETURNS [REF] = {
RETURN[keyTable[fontFile.key.index].description.clientData];
};
Status: PUBLIC PROCEDURE [fontFile: FontFile] RETURNS [error: FS.ErrorDesc, ok: BOOLEAN] = {
description: REF FontFileDescription ← keyTable[fontFile.key.index].description;
error ← description.error;
ok ← error.group=$ok;
};
Pointer: PUBLIC UNSAFE PROCEDURE [fontFile: FontFile] RETURNS [origin: LONG POINTER] = {
origin ← keyTable[fontFile.key.index].description.origin;
};
NameOf: PUBLIC PROCEDURE [key: Key] RETURNS [fileName: ROPE] = {
IF key.index<keyTable.numberOfEntries THEN fileName ← keyTable[key.index].fileName;
};
Size: PUBLIC PROCEDURE [fontFile: FontFile] RETURNS [sizeInSixteenBitWords: INT] = {
sizeInSixteenBitWords ← keyTable[fontFile.key.index].description.size;
};
InBounds: PUBLIC UNSAFE PROCEDURE [fontFile: FontFile, blockAddress: LONG POINTER, sizeInWords: NAT] RETURNS [BOOLEAN] = UNCHECKED {
description: REF FontFileDescription ← keyTable[fontFile.key.index].description;
addr: LONG CARDINALLOOPHOLE[blockAddress];
org: LONG CARDINALLOOPHOLE[description.origin];
end: LONG CARDINAL ← org + description.size;
RETURN[
sizeInWords >= 0 AND
addr >= org AND
addr + sizeInWords <= end
];
};
fontFileWatcherProc: FontFileWatcher ← defaultFontFileWatcher;
defaultFontFileWatcher: FontFileWatcher = {};
RegisterFontFileWatcher: PUBLIC PROCEDURE [fontFileWatcher: FontFileWatcher] = {
fontFileWatcherProc ← fontFileWatcher;
};
PermissionCount: PUBLIC PROCEDURE RETURNS [INT] = {RETURN[permissionCount]};
EnumerateOpenFontFiles: PUBLIC PROCEDURE [visit: PROC[Rope.ROPE]] = {
This is written so it doesn't have to be an entry procedure, so it doesn't matter if the visit proc hangs.
i: NAT ← 0;
DO
table: REF KeyTableRec ← keyTable;
rope: Rope.ROPENIL;
IF i>= table.numberOfEntries THEN EXIT;
IF table[i].description # NIL THEN rope ← table[i].fileName;
table ← NIL;
IF rope # NIL THEN visit[rope];
i ← i+1;
ENDLOOP;
};
Init[];
END.