<> <> DIRECTORY Basics, BasicTime, IO, Process, RedBlackTree, Rope, STP, STPConnectionCache, UserCredentials; STPConnectionCacheImpl: CEDAR MONITOR IMPORTS BasicTime, IO, Process, RedBlackTree, Rope, STP, UserCredentials EXPORTS STPConnectionCache = {OPEN STPConnectionCache; Time: TYPE = BasicTime.GMT; PrivateStuff: TYPE = REF PrivateStuffRec; PrivateStuffRec: TYPE = RECORD [ client: ROPE _ NIL, change: CONDITION, dieAfter: Time _ BasicTime.earliestGMT]; cache: RedBlackTree.Table _ RedBlackTree.Create[GetCacheKey, CacheCompare]; Get: PUBLIC ENTRY PROC [host, client: ROPE] RETURNS [h: Handle] = { ENABLE UNWIND => NULL; p: PrivateStuff; h _ NARROW[cache.Lookup[host]]; IF client = NIL THEN { self: CARDINAL _ LOOPHOLE[Process.GetCurrent[]]; client _ IO.PutFR["Process %g", IO.card[self]]; }; IF h = NIL THEN { h _ NEW [HandleRec _ [ host: host, stpHandle: STP.Create[], private: p _ NEW [PrivateStuffRec _ []] ]]; TRUSTED { Process.InitializeCondition[@p.change, Process.SecondsToTicks[5]]; Process.EnableAborts[@p.change]; Process.Detach[FORK CloseIt[h, p]]; }; RedBlackTree.Insert[cache, h, host]; } ELSE p _ NARROW[h.private]; IF NOT h.stpHandle.IsOpen[] THEN { name, password: ROPE; [name, password] _ UserCredentials.Get[]; [] _ h.stpHandle.Open[host]; h.stpHandle.Login[name, password]; }; WHILE p.client # NIL DO WAIT p.change ENDLOOP; p.client _ client; BROADCAST p.change; }; Release: PUBLIC ENTRY PROC [h: Handle, timeout: INT--seconds--] = { ENABLE UNWIND => NULL; p: PrivateStuff _ NARROW[h.private]; expire: Time _ BasicTime.Update[BasicTime.Now[], timeout]; p.client _ NIL; IF BasicTime.Period[from: p.dieAfter, to: expire] > 0 THEN p.dieAfter _ expire; BROADCAST p.change; }; CloseIt: ENTRY PROC [h: Handle, p: PrivateStuff] = { ENABLE UNWIND => NULL; DO now: Time; delta: INT; WAIT p.change; now _ BasicTime.Now[]; delta _ BasicTime.Period[from: now, to: p.dieAfter]; IF delta <= 0 THEN {--time to be closed-- IF p.client = NIL THEN { IF h.stpHandle.IsOpen[] THEN h.stpHandle.Close[]; TRUSTED { Process.SetTimeout[@p.change, maxDelay]; }; } ELSE TRUSTED { Process.SetTimeout[@p.change, minDelay]; }; } ELSE { TRUSTED { Process.SetTimeout[@p.change, Process.SecondsToTicks[MIN[delta, 60]+1]]; }; }; ENDLOOP; }; minDelay: Process.Ticks _ Process.SecondsToTicks[6]; maxDelay: Process.Ticks _ Process.SecondsToTicks[600]; GetCacheKey: PROC [data: REF ANY] RETURNS [key: ROPE] --RedBlackTree.GetKey-- = { h: Handle _ NARROW[data]; key _ h.host}; CacheCompare: PROC [k, data: REF ANY] RETURNS [Basics.Comparison] --RedBlackTree.Compare-- = { k1: ROPE _ NARROW[k]; k2: ROPE _ GetCacheKey[data]; RETURN [k1.Compare[k2, FALSE]]; }; }.