FSRemoteFileExtrasImpl.mesa
Demers, September 19, 1987 3:40:59 pm PDT
DIRECTORY
BasicTime USING [earliestGMT, GMT, Now, Period],
FS USING [Error, ErrorDesc, FileType],
FSBackdoor USING [ErrorCode, ProduceError, Version],
FSPseudoServers USING [AvoidRemoteCheck, TranslateForRead, TranslateForWrite],
FSRemoteFileExtras USING [ConfirmProc, ConfirmRetrieveProc, GetServerProc, InfoProc, NameProc, ServerHandle, ServerObject, ServerProcs, ServerProcsObject, SweepProc, ValidateProc],
IO USING [STREAM],
Process USING [Abort, CheckForAbort, Detach, EnableAborts, MsecToTicks, PauseMsec, SetTimeout],
Rope USING [Cat, Flatten, Length, Match, ROPE],
SymTab USING [Create, Delete, EachPairAction, Fetch, Pairs, Ref, Store]
;
FSRemoteFileExtrasImpl: CEDAR MONITOR
IMPORTS BasicTime, FS, FSBackdoor, FSPseudoServers, Process, Rope, SymTab
EXPORTS FSRemoteFileExtras
~{
Copied Types
ConfirmProc: TYPE ~ FSRemoteFileExtras.ConfirmProc;
ConfirmRetrieveProc: TYPE ~ FSRemoteFileExtras.ConfirmRetrieveProc;
FileType: TYPE ~ FS.FileType;
GetServerProc: TYPE ~ FSRemoteFileExtras.GetServerProc;
GMT: TYPE ~ BasicTime.GMT;
InfoProc: TYPE ~ FSRemoteFileExtras.InfoProc;
NameProc: TYPE ~ FSRemoteFileExtras.NameProc;
ROPE: TYPE ~ Rope.ROPE;
ServerHandle: TYPE ~ FSRemoteFileExtras.ServerHandle;
Version: TYPE ~ FSBackdoor.Version;
Parameters
msecBetweenSweeps: CARD ← 60000;
nonexistentServerTTL: CARD ← 300;
waitForServerTimeout: CARD ← 20000;
Exported to FSRemoteFile
Delete: PUBLIC PROC [server, file: ROPE, wantedCreatedTime: GMT, proc: ConfirmProc] ~ {
uServer: ROPE ← UnbracketServer[server];
h: ServerHandle ← GetServer[FSPseudoServers.TranslateForWrite[uServer]];
h.procs.delete[h, file, wantedCreatedTime, proc];
};
EnumerateForInfo: PUBLIC PROC [server, pattern: ROPE, proc: InfoProc] ~ {
uServer: ROPE ← UnbracketServer[server];
servers: LIST OF ROPE ← FSPseudoServers.TranslateForRead[uServer];
replicated: BOOL = FSPseudoServers.AvoidRemoteCheck[server];
gotOne: BOOLFALSE;
savedErrorDesc: FS.ErrorDesc;
procInner: InfoProc ~ {
gotOne ← TRUE;
Process.CheckForAbort[];
RETURN [proc[fullFName, attachedTo, created, bytes, keep, fileType]];
};
FOR each: LIST OF ROPE ← servers, each.rest WHILE (each # NIL) DO
ENABLE FS.Error => {
IF gotOne OR ((error.code = $unknownFile) AND replicated) THEN REJECT;
IF each = servers THEN savedErrorDesc ← error;
LOOP;
};
h: ServerHandle ← GetServer[each.first];
h.procs.enumerateForInfo[h, pattern, procInner];
IF gotOne OR replicated THEN RETURN;
ENDLOOP;
IF savedErrorDesc.code # NIL THEN ERROR FS.Error[savedErrorDesc];
Should I raise FS.Error if NOT gotOne?
};
EnumerateForNames: PUBLIC PROC [server, pattern: ROPE, proc: NameProc] ~ {
uServer: ROPE ← UnbracketServer[server];
servers: LIST OF ROPE ← FSPseudoServers.TranslateForRead[uServer];
replicated: BOOL = FSPseudoServers.AvoidRemoteCheck[server];
gotOne: BOOLFALSE;
savedErrorDesc: FS.ErrorDesc;
procInner: NameProc ~ {
gotOne ← TRUE;
Process.CheckForAbort[];
RETURN [proc[fullFName]];
};
FOR each: LIST OF ROPE ← servers, each.rest WHILE (each # NIL) DO
ENABLE FS.Error => {
IF gotOne OR ((error.code = $unknownFile) AND replicated) THEN REJECT;
IF each = servers THEN savedErrorDesc ← error;
LOOP;
};
h: ServerHandle ← GetServer[each.first];
h.procs.enumerateForNames[h, pattern, procInner];
IF gotOne OR replicated THEN RETURN;
ENDLOOP;
IF savedErrorDesc.code # NIL THEN ERROR FS.Error[savedErrorDesc];
Should I raise FS.Error if NOT gotOne?
};
Info: PUBLIC PROC [server, file: ROPE, wantedCreatedTime: GMT]
RETURNS [version: Version, bytes: INT, created: GMT, fileType: FileType] ~ {
uServer: ROPE ← UnbracketServer[server];
servers: LIST OF ROPE ← FSPseudoServers.TranslateForRead[uServer];
replicated: BOOL = FSPseudoServers.AvoidRemoteCheck[server];
savedErrorDesc: FS.ErrorDesc;
FOR each: LIST OF ROPE ← servers, each.rest WHILE (each # NIL) DO
ENABLE FS.Error => {
IF (error.code = $unknownFile) AND replicated THEN REJECT;
IF each = servers THEN savedErrorDesc ← error;
LOOP;
};
h: ServerHandle ← GetServer[each.first];
[version, bytes, created, fileType] ← h.procs.getInfo[h, file, wantedCreatedTime];
RETURN;
ENDLOOP;
ERROR FS.Error[savedErrorDesc];
};
Rename: PUBLIC PROC [server, fromFile: ROPE, fromCreated: GMT, toFile: ROPE, proc: ConfirmProc] ~ {
uServer: ROPE ← UnbracketServer[server];
h: ServerHandle ← GetServer[FSPseudoServers.TranslateForWrite[uServer]];
h.procs.rename[h, fromFile, fromCreated, toFile, proc];
};
Retrieve: PUBLIC PROC [server, file: ROPE, wantedCreatedTime: GMT, proc: ConfirmRetrieveProc, checkFileType: BOOL, fileType: FileType] ~ {
uServer: ROPE ← UnbracketServer[server];
servers: LIST OF ROPE ← FSPseudoServers.TranslateForRead[uServer];
replicated: BOOL = FSPseudoServers.AvoidRemoteCheck[server];
savedErrorDesc: FS.ErrorDesc;
FOR each: LIST OF ROPE ← servers, each.rest WHILE (each # NIL) DO
ENABLE FS.Error => {
IF (error.code = $unknownFile) AND replicated THEN REJECT;
IF each = servers THEN savedErrorDesc ← error;
LOOP;
};
h: ServerHandle ← GetServer[each.first];
h.procs.retrieve[h, file, wantedCreatedTime, proc, checkFileType, fileType];
RETURN;
ENDLOOP;
ERROR FS.Error[savedErrorDesc];
};
Store: PUBLIC PROC [server, file: ROPE, str: IO.STREAM, created: GMT, proc: ConfirmProc] ~ {
uServer: ROPE ← UnbracketServer[server];
h: ServerHandle ← GetServer[FSPseudoServers.TranslateForWrite[uServer]];
h.procs.store[h, file, str, created, proc];
};
Registration
Registration: TYPE ~ REF RegistrationObject;
RegistrationObject: TYPE ~ RECORD [
next: Registration,
flavor: ATOM,
getServer: GetServerProc
];
registrations: Registration ← NIL;
Register: PUBLIC ENTRY PROC [flavor: ATOM, getServer: GetServerProc] ~ {
p, prev: Registration;
FOR p ← registrations, p.next DO
IF p.flavor = flavor THEN {
IF prev = NIL THEN registrations ← p.next ELSE prev.next ← p.next;
EXIT;
};
prev ← p;
ENDLOOP;
IF getServer # NIL
THEN registrations ← NEW[RegistrationObject ← [registrations, flavor, getServer]];
};
Server Handle Cache
serverTab: SymTab.Ref ← SymTab.Create[];
lastSweepTime: BasicTime.GMT ← BasicTime.earliestGMT;
Daemon: PROC ~ {
thisSweepTime: BasicTime.GMT;
seconds: INT;
CallSweep: SymTab.EachPairAction -- [key, val] RETURNS [quit] -- ~ {
h: ServerHandle ~ NARROW[val];
h.procs.sweep[h, seconds];
RETURN [FALSE];
};
DO
Process.PauseMsec[msecBetweenSweeps];
thisSweepTime ← BasicTime.Now[];
seconds ← BasicTime.Period[from~lastSweepTime, to~thisSweepTime];
[] ← SymTab.Pairs[serverTab, CallSweep];
lastSweepTime ← thisSweepTime;
ENDLOOP;
};
Finding a server
GetServerResponse: TYPE ~ REF GetServerResponseObject;
GetServerResponseObject: TYPE ~ RECORD [
gotServer: CONDITION,
handle: ServerHandle
];
GetServer: PROC [server: ROPE] RETURNS [h: ServerHandle] ~ {
h ← NARROW[SymTab.Fetch[serverTab, server].val];
IF (h = NIL) OR (NOT h.procs.validate[h]) THEN {
kids: LIST OF PROCESSNIL;
response: GetServerResponse;
response ← NEW [GetServerResponseObject];
FOR each: Registration ← registrations, each.next WHILE each # NIL DO
kids ← CONS[ (FORK DoGetServer[each, server, response]), kids ];
ENDLOOP;
h ← WaitForResponse[response];
FOR child: LIST OF PROCESS ← kids, child.rest WHILE child # NIL DO
TRUSTED { Process.Abort[child.first] };
TRUSTED { Process.Detach[child.first] };
ENDLOOP;
IF h = NIL THEN h ← MakeNonexistentServer[server];
[] ← SymTab.Store[serverTab, server, h];
};
IF h.flavor = $nonexistentServer THEN ErrorNonexistentServer[h];
};
DoGetServer: PROC [r: Registration, server: ROPE, response: GetServerResponse] ~ {
ENABLE FS.Error => CONTINUE;
h: ServerHandle;
NotifyGotHandle: ENTRY PROC ~ {
IF response.handle # NIL THEN RETURN;
response.handle ← h;
NOTIFY response.gotServer;
};
IF (h ← r.getServer[server]) # NIL THEN NotifyGotHandle[];
};
WaitForResponse: ENTRY PROC [response: GetServerResponse] RETURNS [ServerHandle] ~ {
IF response.handle = NIL THEN TRUSTED {
Process.EnableAborts[@response.gotServer];
Process.SetTimeout[@response.gotServer, Process.MsecToTicks[waitForServerTimeout]];
WAIT response.gotServer;
};
RETURN [response.handle];
};
Controlling the flavor of a cached server
SetCachedServer: PUBLIC PROC [server: ROPE, flavor: ATOM] ~ {
r: Registration;
h: ServerHandle;
ClearCachedServer[server];
FOR r ← registrations, r.next WHILE (r # NIL) AND (r.flavor # flavor) DO NULL ENDLOOP;
IF r = NIL THEN RETURN;
h ← r.getServer[server];
IF h = NIL THEN h ← MakeNonexistentServer[server];
[] ← SymTab.Store[serverTab, server, h];
};
ClearCachedServer: PUBLIC PROC [server: ROPE] ~ {
[] ← SymTab.Delete[serverTab, server];
};
Nonexistent Server Implementation
NonexistentServerData: TYPE ~ REF NonexistentServerDataObject;
NonexistentServerDataObject: TYPE ~ RECORD [
ttl: CARD ← nonexistentServerTTL
];
nonexistentServerProcs: FSRemoteFileExtras.ServerProcs ←
NEW[FSRemoteFileExtras.ServerProcsObject ← [
sweep~SweepNonexistentServer,
validate~ValidateNonexistentServer,
delete~NIL,
enumerateForInfo~NIL,
enumerateForNames~NIL,
getInfo~NIL,
rename~NIL,
retrieve~NIL,
store~NIL
]
];
MakeNonexistentServer: PROC [name: ROPE] RETURNS [h: ServerHandle] ~ {
d: NonexistentServerData ← NEW[NonexistentServerDataObject];
h ← NEW[FSRemoteFileExtras.ServerObject ← [$nonexistentServer, name, nonexistentServerProcs, d]];
};
SweepNonexistentServer: FSRemoteFileExtras.SweepProc -- [h: ServerHandle, seconds: INT] -- ~ {
d: NonexistentServerData ← NARROW[h.data];
IF d.ttl > CARD[seconds] THEN d.ttl ← d.ttl - seconds;
};
ValidateNonexistentServer: FSRemoteFileExtras.ValidateProc -- [h: ServerHandle] RETURNS [ok: BOOL] -- ~ {
d: NonexistentServerData ← NARROW[h.data];
RETURN[d.ttl > 0];
};
ErrorNonexistentServer: PROC [h: ServerHandle, file: ROPENIL] ~ {
FSBackdoor.ProduceError[FSBackdoor.ErrorCode.unknownServer, Rope.Cat["Couldn't find the server for \"", BracketServer[h.name], file, "\""]];
};
Name manipulation
BracketServer: PROC[server: ROPE] RETURNS [ROPE] = {
IF Rope.Match["[*", server]
THEN RETURN [server]
ELSE RETURN [ Rope.Cat[ "[", server, "]" ] ];
};
UnbracketServer: PROC[server: ROPE] RETURNS [ROPE] = {
IF Rope.Match["[*", server]
THEN RETURN [ Rope.Flatten[server, 1, Rope.Length[server]-2]]
ELSE RETURN [ server ];
};
Initialization
TRUSTED { Process.Detach[ FORK Daemon[] ] };
}...