<<>> <> <> <> <> <> DIRECTORY IO, PropList, Random, Rope, SymTab, SystemNames, Xl, XlPrivate, XlPrivateSplit, XlService; XlServiceImpl: CEDAR MONITOR IMPORTS IO, PropList, Random, Rope, SymTab, SystemNames, Xl EXPORTS Xl, XlPrivateSplit, XlService ~ BEGIN OPEN Xl, XlPrivate; ROPE: TYPE = Rope.ROPE; badService: ERROR = CODE; trustedServiceProp: REF ATOM = NEW[ATOM ¬ $trustedServiceProp]; <<--the service is put on a property of the connection so we are sure about decreasing the connection count only once >> serviceVersion: ROPE ~ "_Cedar_ServiceVersion"; --name of X atom to distinguish services serviceTab: SymTab.Ref ~ SymTab.Create[3]; <<--Problem: the removal of dead dead services is not completely guaranteed depending on how connections die. This is a minor problem since we don't intend to use big timesharing and use many many servers yet. >> <<--Possible solution: we might have list of connections anyway; service maintenance could be bound to that list. >> Service: TYPE = REF ServiceImplRep; ServiceImplRep: TYPE ~ RECORD [ connectionCount: INT ¬ 0, --maybe alive connections only propList: PropList.List ¬ NIL, --the real properties for clients serviceKey: ROPE ¬ NIL ]; ServiceRep: PUBLIC TYPE = ServiceImplRep; debuggingAssumeServicePerModuleInstance: BOOL ~ FALSE; --then everything is same service !!! --usefull for debugging the port of Xl to new machine debuggingHackKey: ROPE ¬ IO.PutFR1["hack serviceKey %g", IO.time[]]; InitServiceStuff: PUBLIC PROC [c: Connection] = { serviceKey: ROPE; atom: XAtom ¬ MakeAtom[c, serviceVersion]; rootWindow: Window ¬ FirstRoot[c]; ret: PropertyReturnRec; IF debuggingAssumeServicePerModuleInstance THEN serviceKey ¬ debuggingHackKey ELSE { <<--normal case>> ret ¬ GetProperty[c: c, w: rootWindow, property: atom, supposedFormat: 8]; IF ret.format#8 OR ret.type#atom OR ret.value=NIL THEN { <<<>>> tentativeKey: ROPE ¬ IO.PutFLR["%g %g %g", LIST[IO.time[], IO.rope[SystemNames.MachineName[]], IO.int[Random.NextInt[]]] ]; BEGIN --prevent X server bug [XlR2 of MIT] WHILE Rope.Length[tentativeKey] MOD 4 # 0 DO tentativeKey ¬ Rope.Concat[tentativeKey, " "]; ENDLOOP; END; GrabServer[c]; ret ¬ GetProperty[c: c, w: rootWindow, property: atom, supposedFormat: 8]; IF ret.format#8 OR ret.type#atom OR ret.value=NIL THEN { ChangeProperty[c, rootWindow, atom, atom, replace, serviceKey ¬ tentativeKey]; }; UngrabServer[c]; }; serviceKey ¬ NARROW[ret.value]; }; [] ¬ EntryDefineService[c, serviceKey]; }; EntryDefineService: ENTRY PROC [c: Connection, serviceKey: ROPE] RETURNS [service: Service] = { ENABLE UNWIND => NULL; service ¬ NEW[ServiceImplRep ¬ [connectionCount: 0, serviceKey: serviceKey, propList: PropList.NewPropList[]]]; IF ~SymTab.Insert[serviceTab, serviceKey, service] THEN service ¬ NARROW[SymTab.Fetch[serviceTab, serviceKey].val]; service.connectionCount ¬ service.connectionCount + 1; c.service ¬ service; c.serviceProperties ¬ service.propList; PutConnectionProp[c, trustedServiceProp, service]; }; ReleaseService: PUBLIC ENTRY PROC [c: Connection] = { ENABLE UNWIND => NULL; IF c#NIL THEN { service: Service ¬ c.service; IF service#NIL AND GetConnectionProp[c, trustedServiceProp]=service THEN { PutConnectionProp[c, trustedServiceProp, NIL]; --make sure service released once only service.connectionCount ¬ MAX[service.connectionCount - 1, 0]; IF service.connectionCount=0 THEN [] ¬ SymTab.Delete[serviceTab, service.serviceKey]; }; }; }; GetService: PUBLIC PROC [c: Connection] RETURNS [service: REF] = { service ¬ c.service }; PutProp: PUBLIC ENTRY PROC [service: REF, key: REF, val: REF ¬ NIL] = { WITH service SELECT FROM sr: REF ServiceImplRep => [] ¬ PropList.PutProp[sr.propList, key, val]; ENDCASE => RETURN WITH ERROR badService }; GetProp: PUBLIC ENTRY PROC [service: REF, key: REF] RETURNS [val: REF ¬ NIL] = { WITH service SELECT FROM sr: REF ServiceImplRep => RETURN [ PropList.GetProp[sr.propList, key] ]; ENDCASE => RETURN WITH ERROR badService }; PutServiceProp: PUBLIC PROC [c: Connection, key: REF, val: REF ¬ NIL] = { [] ¬ PropList.PutProp[c.serviceProperties, key, val] }; GetServiceProp: PUBLIC PROC [c: Connection, key: REF] RETURNS [val: REF] = { val ¬ PropList.GetProp[c.serviceProperties, key] }; GetServicePropAndInit: PUBLIC PROC [c: Connection, key: REF, init: InitializeProcType] RETURNS [val: REF] = { WrapInit: PropList.InitializeProcType = {val ¬ init[NARROW[data], key]}; val ¬ PropList.GetPropOrInit[c.serviceProperties, key, WrapInit, c].val }; END.