CORBA - More Reference Counting
Although we managed to develop a working solution in the first CORBA reference counting article the results were ugly and fragile. In this article we attempt to clean things up a little and, in doing so, get intimate with the Portable Object Adapter and its Servant Managers.
The problem
In the previous article we started out with the goal of adding COM style reference counting to a CORBA object. We found that the obvious COM way to do this didn’t work due to the way that the default behavior for the POA prevents us from deleting a servant object from within a method call on that servant object. Although the article was written from the point of view of reference counting the servant object, it is equally valid for any object where you want the client of the object to have some say over when the servant is deleted. We worked our way towards a solution, but it was fragile and intrusive. This article attempts to remedy the problems we encountered in the last article and provide a robust and easy to use reference counting base class for servant objects. Once we appear to achieve that goal the next article will expose the flaws that mean that allowing the client control over server object life-time in CORBA is not usually a good idea.
My own POA
The servant object that we finally ended up with in RefCounted4.zip required that you passed in the POA that you wanted the servant to be activated in. This cluttered the servant object’s constructor somewhat and made it possible to pass in the wrong kind of POA which would cause the servant to fail. Since we know the type of POA that we require (the POA must have the RETAIN
policy set and it shouldn’t have a custom servant locator or servant activator assigned to it) we could just as easily have the object itself manage the association with its POA and keep all of the details internal to the object.
In RefCounted5.zip we add a static data member to the reference counted class. This holds the POA that’s to be used by all instances of the servant object. The POA static data member is accessed via a private member function, GetPOA(). The job of this function is to create the correct type of POA the first time it’s called, store a reference to that POA in the static data member and from then on hand out that reference when asked. This allows all of the servant objects to share the same POA and removes the need to have the correct type of POA supplied when you create the servant objects.
This change makes the code slightly more robust and less complicated as it’s no longer possible to pass in an inappropriately configured POA. The code is still more complex than it needs to be. It still has to jump through hoops to avoid the way that the POA we’re using wants to fiddle with our servant objects after we’ve decided they’re no longer needed.
Servant Locators
CORBA lets you configure how your servant objects are hooked up to your CORBA Objects. You can customise a POA and inject your own code into the object activation and deactivation phases. If we could provide our own implementation of the activation, deactivation and object ID to servant lookup code then we would be free of the problems posed by the default POA behavior as we wouldn’t require the POA to maintain an Active Object Map for us.
To do this we need to write a Servant Locator. This is a class derived from POA_PortableServer::ServantLocator
that has a method called preinvoke
called before every method call dispatched onto the object and a method called postinvoke
called after each call completes. The call to previnvoke
is supplied with an object ID and it’s expected to find, or create, the appropriate servant object for the corresponding CORBA object and return it. The POA then dispatches the method call onto the servant and calls the servant locator’s postinvoke
method. The postinvoke
method is passed the object ID and the servant and it can do what it likes with them.
Our implementation of a servant locator maintains a vector of pointers to servant objects where the object ID of the associated CORBA object corresponds to the position of the servant object in the vector. The locator also maintains a list of free slots in the servant object vector so that it can reuse spaces in the vector as servants are deleted. When a servant object is created it adds itself into the servant locator that’s associated with its POA and when it’s deleted it removes itself from the associated locator.
Configuring the POA
To use a servant locator in a POA we need to create the POA with the following policies: A request processing policy of USE_SERVANT_MANAGER
- which means that the POA will call our servant manager, in this case a servant locator. A servant retention policy of NON_RETAIN
- which prevents the POA from maintaining an Active Object Map on our behalf. And an ID assignment policy of USER_ID
- which allows us to manage the generation of object IDs for the CORBA objects that are activated in the POA.
Because of the way that we arranged the code in RefCounted5.zip we simply need to adjust the GetPOA()
call so that it creates an appropriately configured POA and adds our servant locator to it.
This code is presented in RefCounted6.zip. Notice how the reference counting methods inside the reference counted object are now much simpler, we’re effectively hijacking the code that’s used for server side reference counting and using for client side references too.
Reusing the code
All of the code required for adding reference counting using a servant locator and appropriately configured POA is common to all servant objects that need to be reference counted. In RefCounted7.zip we factor all of this common code out into a reusable template base class that handles all of the nitty gritty details for us. All that is required is for our class that requires reference counting to derive from the template base class. In addition, we factor out the reference counting members of the interface into their own interface so that we can simply derive any interfaces that require reference counting from this base interface.
By now, it seems that the one thing missing from our template is a guarantee of thread safety. We fix that with RefCounted8.zip by adding a mutex to the base class and locking and unlocking the mutex around each access to the servant locator’s shared resources - the vector and the list.
The result is a solution which is neither fragile nor invasive. The servant object needs no changes made to incorporate reference counting except for the inclusion of the correct base class in its inheritance graph.
The end?
So, we’ve achieved our aim, or at least it looks that way. Unfortunately although this solution looks pretty good it’s likely to result in references being leaked by misbehaved clients and server side objects existing well beyond their use by date… The next article explains the problems that still exist and why, in CORBA, it’s very difficult to fix them.
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.
RefCounted5.zip - manage our own POA
RefCounted6.zip - use a servant locator
RefCounted7.zip - factor all the code into a reusable template
RefCounted8.zip - make the servant locator thread safe
Revision history
- 6th February 2001 - Initial revision at www.jetbyte.com.
- 15th March 2001 - adjusted the project files so that they use the OMNI_HOME environment variable to pick up the idl compiler, headers and libraries.
- 17th August 2005 - reprinted at www.lenholgate.com.