The ‘Single Thread Model’ is useless.

Normally a CORBA application processes each request in its own thread. So the author of a CORBA servant must always be aware that their methods may be running concurrently in multiple threads. Critical sections must be identified and locked accordingly.

Multi-threaded programming can be quite a pain, and all that locking, unlocking and context-switching can be a drag on performance. The SINGLE_THREAD_MODEL seems to offer a way to escape all these worries. Sadly it doesn’t deliver on that promise.

SINGLE_THREAD_MODELRequests for a single-threaded POA are processed sequentially. In a multi-threaded environment, all upcalls made by this POA to implementation code (servants and servant managers) are made in a manner that is safe for code that is multi-thread-unaware. The POA will still allow reentrant calls from an object implementation to itself, or to another object implementation managed by the same POA.

from CORBA Spec. Section 11.3.7.1 ‘Thread Policy’

It uses more than one thread.

omniORB implements SINGLE_THREAD_MODEL by synchronising the normal upcalls from the POA to implementation code (the servant) with a recursive mutex. So, although the calls are never made concurrently, they can be processed by more than one thread.

Mutex locks must be released by the same thread that acquires them. Programmers who hope to use SINGLE_THREAD_MODEL as a simple way of making this possible will be disappointed.

Regardless of the POA’s threading policy, it’s a bad idea to hold a mutex for a remote client – if the client crashes or the network fails, then you are left with an unreleasable lock. The golden rule is to hold a mutex for the minimum possible time.

However, it’s often difficult to tell whether a third party library is acquiring mutexes for you. For example, Oracle’s OCI library has a ‘thread safe’ mode, which protects certain resources with mutexes. The documentation is a bit vague about exactly when locks are acquired and released.

So, how can you use OCI (or any similar third party library) from within a CORBA application? The only real solution is to create your own thread (or threads), and only call the library functions from there. CORBA servants must communicate with the library thread via a condition variable or similar.

There are hidden dangers.

omniORB’s SINGLE_THREAD_MODEL POA uses a recursive mutex to ensure that in-process calls can be made without deadlocking. However, the recursive mutex can only detect a re-entrant call sequence if it is confined to a single thread. If the servant calls out to another process, which then calls back to the original object, then a second thread will be used to handle the call-back. The original thread will still hold the mutex, and a deadlock will ensue.

Even more subtle problems can arise. If a SINGLE_THREAD_MODEL POA is used to hold a servant manager, then deadlocks can occur even without recursive call-backs.

The Alternatives.

Since SINGLE_THREAD_MODEL simply layers a simple locking policy on top of CORBA’s normal multi-threaded calls, the best alternative is simply to implement your own locking policy. This will be a little more work, but the final result will probably be a better solution. That’s because you can identify critical regions in your code, and limit your locking to protecting them. The SINGLE_THREAD_MODEL POA would lock every method call, even when it’s not necessary.

If you absolutely need a single threaded servant, then you can use the MAIN_THREAD_MODEL:

MAIN_THREAD_MODELRequests for all main-thread POAs are processed sequentially. In a multi-threaded environment, all upcalls made by all POAs with this policy to servants are made in a manner that is safe for code that is multi-thread-unaware. If the environment has special requirements that some code must run on a distinguished ‘main’ thread, servant upcalls will be processed on that thread.

from CORBA Spec. Section 11.3.7.1 ‘Thread Policy’

This uses the main thread to process all servant calls. The best thing about this option is that it can be easily added to an existing application, without too much disruption.

Finally, you can make your own thread or threads to do the work. This is the most flexible approach, but of course it’s the most tricky to implement.

Leave a Comment

Sponsors