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: REF ← NIL,
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:
BOOLEAN ←
FALSE] = {
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: PROCESS ← NIL;
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:
BOOLEAN ←
FALSE] = {
ENABLE UNWIND => NULL;
description: REF FontFileDescription ← keyTable[key.index].description;
IF description =
NIL
THEN {
openOk: BOOLEAN ← TRUE;
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 CARDINAL ← LOOPHOLE[blockAddress];
org: LONG CARDINAL ← LOOPHOLE[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.ROPE ← NIL;
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.