UFFileManagerImpl.mesa
Created January 7, 1983
Last edit by Michael Plass on October 21, 1983 5:16 pm
DIRECTORY CIFS, Environment, File, Process, RefText, Rope, RTTypesBasic, SafeStorage, Space, UFFileManager;
UFFileManagerImpl: CEDAR MONITOR
IMPORTS CIFS, File, Process, RefText, Rope, RTTypesBasic, SafeStorage, Space
EXPORTS UFFileManager =
BEGIN OPEN UFFileManager;
FontFile: PUBLIC TYPE = REF FontFilePermission;
FontFilePermission: PUBLIC TYPE = RECORD [
link: REF FontFilePermission,
key: Key
];
qZone: ZONE ← SafeStorage.NewZone[quantized];
Used only for FontFilePermissions.
FontFileDescription: TYPE = RECORD [
permissions: REF FontFilePermission,
permissionHint: REF FontFilePermission,
clientData: REFNIL,
openFile: CIFS.OpenFile,
errorCode: CIFS.ErrorCode,
errorMsg: Rope.ROPE,
space: Space.Handle,
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: RTTypesBasic.FinalizationQueue ← RTTypesBasic.NewFQ[permissionThreshold];
FinalizerProcess: PROCEDURE [finalizationQueue: RTTypesBasic.FinalizationQueue] = {
DO
permission: REF FontFilePermission ← NARROW[RTTypesBasic.FQNext[finalizationQueue]];
key: Key ← permission.key;
closed: BOOLEAN ← Finalize[permission];
permission ← NIL;
IF closed THEN fontFileWatcherProc[key, close];
IF RTTypesBasic.FQEmpty[finalizationQueue] THEN RequestCollection[];
ENDLOOP;
};
Finalize: ENTRY PROCEDURE [permission: REF FontFilePermission] RETURNS [closed: BOOLEANFALSE] = TRUSTED {
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 # NIL THEN {
Space.Delete[description.space];
CIFS.Close[description.openFile];
closed ← TRUE;
};
keyTable[key.index].description ← NIL;
};
permissionCount ← permissionCount-1;
};
Init: ENTRY PROCEDURE = TRUSTED {
ENABLE UNWIND => NULL;
RTTypesBasic.EstablishFinalization[CODE[FontFilePermission], 1, finalizationQueue];
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 = TRUSTED {
ENABLE UNWIND => NULL;
IF fileHintTimeoutProcess=NIL THEN {
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[];
};
IF fileHintTimeoutProcess=NIL THEN StartFileHintTimeout[];
};
MonitoredOpen: ENTRY PROCEDURE [key: Key, initProc: InitProc ← NIL] RETURNS [fontFile: FontFile, triedToOpen: BOOLEANFALSE] = {
ENABLE UNWIND => NULL;
description: REF FontFileDescription ← keyTable[key.index].description;
openOk: BOOLEANTRUE;
IF description = NIL THEN {
description ← NEW[FontFileDescription];
description.space ← Space.nullHandle;
description.openFile ← CIFS.Open[keyTable[key.index].fileName, CIFS.read + CIFS.dontCheck ! CIFS.Error => TRUSTED {openOk ← FALSE; description.errorCode ← code; description.errorMsg ← error; CONTINUE}];
triedToOpen ← TRUE;
IF openOk THEN TRUSTED {
capability: File.Capability ← CIFS.GetFC[description.openFile];
pages: File.PageCount ← File.GetSize[capability];
description.size ← INT[pages]*Environment.wordsPerPage;
description.space ← Space.Create[size: pages, parent: Space.virtualMemory];
Space.Map[space: description.space, window: [file: capability, base: 1]];
Space.CreateUniformSwapUnits[16, description.space];
description.origin ← Space.LongPointer[description.space];
}
ELSE description.openFile ← NIL;
};
keyTable[key.index].description ← description;
fontFile ← qZone.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;
};
GetData: PUBLIC PROCEDURE [fontFile: FontFile] RETURNS [REF] = {
RETURN[keyTable[fontFile.key.index].description.clientData];
};
Status: PUBLIC PROCEDURE [fontFile: FontFile] RETURNS [errorCode: CIFS.ErrorCode, errorMsg: Rope.ROPE, ok: BOOLEAN] = {
description: REF FontFileDescription ← keyTable[fontFile.key.index].description;
errorCode ← description.errorCode;
errorMsg ← description.errorMsg;
ok ← description.openFile # NIL
};
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.