In the presence of correct management of reference counts, protocols may want to implement some sort of session caching. For example, many of the sessions created on the receiving end of a remote procedure call will have their reference counts go to zero after the reply has been sent, and without caching, all of those sessions will be destroyed. While caching may not be important for all protocols, it is vital to the performance of protocols that experience a high frequency of traffic from the same sources.
If a protocol does cache idle sessions, it is important to make the caching transparent to upper protocols (i.e., an upper protocol should not be able to distinguish a newly created lower session from a cached lower session that is being reused). One aspect of this requirement is that protocols that cache sessions must be sure to check for the presence of openEnables when reusing sessions which would otherwise be passively created, and to call xOpenDone when reusing such sessions from the cache.
Following are some examples of caching strategies and how a protocol might implement them. These are just examples; other strategies are certainly possible.
In this ``null caching'' strategy, the session's close routine destroys the session.
fooClose(s) xAssert(s->rcnt == 0); xDestroy(s);
A garbage collector event is started for each idle session. When the event expires, the session is collected.
fooDemux() if (active session does not exist) { if (openEnable exists) { if (cached session exists) { evCancel(sstate->gc); sstate->gc = 0; } else s = fooOpen(); xOpenDone(s, ...); } else { drop packet; return; } } xPop() fooOpen() ... if (active session does not exist) { if (cached session exists) { evCancel(sstate->gc); sstate->gc = 0; } } ... fooClose(s) sstate->gc = evRegister(foo_gc, s); mark session as cached foo_gc(s) xDestroy(s);
There is a single garbage collector for the protocol. It runs every so often and looks for idle sessions in the protocol's session map, calling a protocol-specific destroy function if it finds one.
The x-kernel has a default session garbage collector that a protocol can use for this purpose. See xkernel/include/gc.h for the interface.
foo_init() ... initSessionCollector(cacheMap, interval, fooDestroy); fooDemux() if (active session does not exist) { if (openEnable exists) { if (cached session exists) { move sessn from cached map to active map } else s = fooOpen(); xOpenDone(s, ...); } else { drop packet; return; } } xPop() fooOpen() ... if (active session does not exist) { if (cached session exists) { move sessn from cached map to active map } } ... fooClose(s) xAssert(s->rcnt == 0); move sessn from active map to cached map fooDestroy(s) xDestroy(s);