CORBA - Keep Alive

| 0 Comments
One way of making a reference counted implementation more robust is to run the keep-alive protocol yourself. We demonstrate this option here.

The problem
In the previous article we developed a strategy where the server would destroy objects if the client hadn't used them for a certain period of time. This protects the server from leaked objects and is necessary because CORBA doesn't provide a keep alive mechanism. If a client has a valid reason to hold onto an object and not do anything with it then the only way that they can keep the object active in the server is to poke it every now and then - even if they have no reason to make a method call on the object.

Keep yourself alive
Since clients may have valid reasons for needing to keep their objects active, but not in use, we may as well give them a method on the object that does nothing at the server end but that can be used to make sure that their objects don't get timed out.

interface KeepAlive
{
   void Ping();
};

The client can now call the Ping() method on the object which need not do anything on the server. The mere fact that the method is called and dispatched via the Servant Locator is enough to ensure that the servant is not evicted. Since the client has no way of knowing how often it needs to call the Ping() method to keep the object alive, we could provide a method to return the object's timeout value, this would allow a client to ensure that they call Ping() often enough to keep their object alive.

interface KeepAlive
{
   void Ping();
   long GetTimeout();
};

This approach means that we're effectively implementing a keep alive protocol on our objects that's driven by the client. Whilst the client keeps using the object, or keeps pinging it to keep it alive, the object stays around on the server. When the client finishes with the object they can destroy it, if they forget, or if they terminate unexpectedly then the object will time out and the server will clean it up. This creates more work for the client programmer, but is necessary as CORBA doesn't provide a keep alive protocol for us.

In KeepAlive1.zip we add the interface above to the iterator interfaces and add a client command "ping" to demonstrate them in action. Notice that if you run the server and then run a single client with the ping command the final ping will work correctly - the servant hasn't been evicted. This is intentional and is due to the fact that eviction only occurs when new objects are added to the Servant Locator. If you run one client with the ping command and, in another window, repeatedly run a client with the "it" command you'll see that the ping client is evicted before the final ping can occur. The ping client has timed out, due to lack of use and the servant is evicted from the server.

We could check for eviction on every access to the servant locator, but the eviction overhead would then occur on all method calls. Our current design only incurs the overhead of eviction when an object is added to the Servant Locator.

Back to reference counting
Now that we have a solution that allows the client programmer control over the lifetime of a server object, but still allows the server programmer to be certain that clients cant leak objects we can use it with the reference counted objects. Updating the reference counting base class is easy enough, TRefCountedBase now inherits from TEvictor<> and simply provides the additional AddRef() and Release() functionality. Unfortunately it doesn't work and exposes a problem in our evictor implementation, first I'll explain the problem and then we'll fix it.

The original reference counting implementation presented in the first two articles reused the reference count supplied by RefCountServantBase as this saved us storing a second counter with the reference counted object. When we evolved the code from being a Servant Locator for a reference counting implementation to being a Servant Locator for an evictor implementation we kept the use of the RefCountServantBase reference count. This was inappropriate as the evicting Servant Locator held only a single reference to each servant. What's more, if anyone else held a reference to the servant then the evictor wouldn't work as it relied on the object removing itself from the Servant Locator when it was destroyed but it initiated that destruction by removing a reference on the servant object. If the removal of the reference didn't cause the destruction of the servant then the object wasn't evicted ... This problem existed since our first evictor implementation but is made more visible when we expose the reference count to the clients through TRefCountedBase. Now whenever we need to evict a reference counted object due to lack of use we will be evicting a servant which has a reference count of more than 1. The Servant Locator will evict the servant and initiate destruction by removing its reference but as clients still hold references to the object the object doesn't destroy itself.

In KeepAlive2.zip we address this problem by removing the reliance on RefCountServantBase for objects that we store in the evictor. Now when we wish to evict a servant we simply call RemoveFromLocator() and the object is removed and deleted by the Servant Locator. TRefCountedBase must now have its own reference count for client side references and when the reference count falls to zero it can ask the Servant Locator to remove and destroy it.

Download
The following source was built using Visual Studio 6.0 SP3 and tested with OmniORB - the open source Corba ORB from AT&T Cambridge. You need to add OMNI_HOME to your environment so that the idl compiler, headers and libraries can be found.

To compile the IDL files you will need to change the path used in the build command for the idl files - well, you will unless you happen to have installed OMNI ORB into exactly the same location as I have... Select the IDL files in the project workspace, right click on them, select settings and edit the path to the OMNIIDL compiler.

Get OmniORB
KeepAlive1.zip
KeepAlive2.zip

Revision history

Leave a comment