![]() |
![]() |
![]() |
CORBA ORBs are used in software systems that span a variety of problem domains including finance and banking, telecommunications network provisioning and maintenance, media collection and dissemination, aerospace, and defense [http://www.corba.org/success.htm]. Consequently, CORBA ORBs [formal/02-12-06] are subject to wide-ranging performance requirements, used in sequential as well as concurrent processing scenarios, and deployed on many different platforms. To be successful across such a broad range of applications, an ORB must provide some means of tailoring its behavior to a software application's specific requirements. Controlling how CORBA requests are processed is one way in which an ORB can be aligned to meet an application's needs.
Processing a CORBA request can be generalized as a two-step process:
To dispatch a request to a POA, an ORB selects a thread to process the request, identifies the target POA, and invokes the POA to process the request.
To dispatch a request to an application object, a POA applies a threading model to enforce constraints on concurrent processing and a request processing policy to identify and invoke the target object.
Modern, multi-threaded ORBs typically offer a variety of strategies for mapping requests to threads. Thus, much is also left to the application developer's discretion. Choosing appropriate strategies to control request processing behavior depends upon an application's characteristics:
This is the first of two articles that address request processing aspects of CORBA and TAO. [The ACE ORB (TAO) is a standard-compliant CORBA ORB developed and maintained by the Distributed Object Computing Group (DOC Group) and commercially supported by OCI.] This article focuses on an ORB's thread-related operations and the POA's threading models as set forth by the CORBA specification. TAO's implementation of the standard, its most frequently used threading and concurrency strategies, and other commonly-used strategies that govern its request dispatching behaviors are the subjects of the second article. TAO's implementation is largely based upon software patterns that have emerged from distributed system and distributed real-time embedded (DRE) system research in recent years. The Related Topics section contains references to material that presents several software patterns of particular interest to readers wanting to understand or examine TAO's implementation. Some readers, especially those involved in DRE system development, may find the Real-Time CORBA Specification's (formal/02-08-02) threading and concurrent processing capabilities useful. (TAO includes a RT CORBA implementation, but that is beyond the scope of these articles.)
The ORB's thread-related operations are used to:
module CORBA
{
// Portions of the CORBA module omitted.
interface ORB
{
// Portions of the ORB interface omitted.
boolean work_pending();
void perform_work();
void run();
void shutdown( in boolean wait_for_completion );
void destroy();
}
}
ORB::run() provides a thread to the ORB. In appropriate circumstances, this operation can be called from multiple threads to provide more than one thread to an ORB. This is a blocking operation from the application's perspective. Each thread that calls ORB::run() becomes dedicated to the ORB. ORB::run() does not return until the ORB has shut down.
ORB::perform_work() provides a thread to an ORB for a single unit of work. This is a non-blocking operation from the application's perspective; the application can perform tasks un-related to the ORB between calls to ORB::perform_work(). The definition for a unit of work is left to ORB implementers; often it is a single invocation. ORB::perform_work() is best used in conjunction with ORB::work_pending() to implement a polling loop that interleaves ORB processing with other activities, such as servicing another external interface. ORB::work_pending() indicates whether or not an ORB has an immediate need for a thread to perform ORB-related tasks. (The CORBA specification states that ORB::work_pending() and ORB::perform_work() are intended for use only with the main thread. In practice, it isn't clear the extent to which this restriction is implemented.)
ORB::shutdown() instructs an ORB to halt processing. This operation is typically invoked just prior to the ORB's destruction. If wait_for_completion is TRUE, this operation blocks until the ORB has concluded request processing and other object adapter related activities, and all object adapters have been destroyed. If wait_for_completion is FALSE, ORB::shutdown() may return before the ORB completes its shutdown process. Some implementations may require a call to ORB::run() or ORB::perform_work() after a call to ORB::shutdown() with wait_for_completion == FALSE so the ORB can finish shutting down.
An ORB continues its normal processing tasks while it is shutting down so there may be a significant delay, after ORB::shutdown() is called, before the ORB actually stops processing. For this reason, some implementations may allow an application to specify a time limit for the shutdown operation. If the ORB has not shut down gracefully within the allotted time, the ORB may then conduct a forced shutdown.
ORB::destroy() destroys an ORB and releases its resources so they can be reclaimed by the application. This operation will initiate the shutdown process when called on an ORB that has not been shut down. If ORB::destroy() initiates the shutdown process, it blocks until the ORB has shut down, as if ORB::shutdown() had been called with wait_for_completion == TRUE.
The following exceptions may be raised by thread-related operations or by circumstances arising from the use of thread-related operations:
ORB::work_pending() or ORB::perform_work called on an ORB after it has been shut down raises BAD_INV_ORDER.ORB::shutdown() with wait_for_completion == TRUE called from a thread processing a CORBA request raises BAD_INV_ORDER with an OMG minor code of 3 and a completion status of COMPLETED_NO.duplicate(), release(), or is_nil() invoked on an ORB after it has shut down, or invoked on an object reference obtained from an ORB that has since shut down raises BAD_INV_ORDER with an OMG minor code of 4.
Scenarios may arise in which operations other than those cited here may be called while an ORB is shutting down. Application developers should be prepared to handle this exception when such scenarios may occur because the time required to shut down an ORB is not predictable. (See the next section's discussion.)
OBJECT_NOT_EXISTORB::destroy() called from a thread processing an invocation raises BAD_INV_ORDER with an OMG minor code of 3. Server and hybrid applications (hybrid applications act as a CORBA client as well as a CORBA server) must make one or more threads available to the ORB via ORB::run() or ORB::perform_work(). A thread from which ORB::run() is called becomes dedicated to the ORB. A thread that calls ORB::perform_work() can be used to perform other tasks in addition to ORB-related tasks. Pure client applications do not need to use the thread-related operations. (Applications that behave largely as clients but employ a callback object or Asynchronous Message Invocation [AMI] to receive a server's replies are considered hybrid applications.)
Some common scenarios are:
ORB::run() from the main thread after the application is initialized.ORB::run() from each thread.ORB::work_pending() and ORB::perform_work(), to interleave CORBA invocations with other processing tasks.
A typical polling loop is similar to the following:
// Code fragment
for(;;)
{
if(orb->work_pending())
{
orb->perform_work();
}
// do other tasks
}
In most cases, ORB::perform_work() should be called only when ORB::work_pending() returns TRUE to prevent the application from blocking in the absence of CORBA invocations. However, some ORB implementations may allow time-bounded calls to ORB::perform_work() and thus remove the need to call ORB::work_pending().
ORB::run(), or to other tasks, using the appropriate mechanism.ORB::run() and use a polling loop to interleave ORB-related and other tasks on another thread or group of threads. The run time of ORB::perform_work() is not deterministic. Application developers should pay careful attention to an ORB's implementation and their application's design when interleaving CORBA invocations with other processing tasks via ORB::perform_work(). Numerous factors, including long-running CORBA requests, outbound CORBA requests that are issued during the processing of an inbound CORBA request, and the ORB's definition of a unit of work influence the run time of ORB::perform_work(). Some implementations extend ORB::perform_work() to permit specification of a maximum run time, but typically this is not sufficient to place an absolute limit on ORB::perform_work()'s run time. For example, when a synchronous outbound request is issued during the processing of an inbound request, the run time for the inbound request becomes a function of the outbound request's run time, which is beyond the scope of the local ORB.
Terminating a CORBA-based application gracefully is often a complicated matter because the ORB continues normal processing while it shuts down. An application dedicated to processing requests can be signaled to shut down via a CORBA request. An application that performs tasks un-related to CORBA requests can be signaled to shut down via a CORBA request as well as any other interfaces that are serviced outside the scope of CORBA requests.
If signaled to shut down via a CORBA interface, ORB::shutdown() can be invoked with wait_for_completion == FALSE from the thread that processes the request; invoking ORB::shutdown() with wait_for_completion == TRUE would raise an exception in this situation. If signaled to shut down outside the scope of a CORBA request, the responding thread can invoke ORB::shutdown() with wait_for_completion == TRUE or FALSE; wait_for_compltion == TRUE in this case will block the calling thread until the ORB has shut down.
Once ORB::shutdown() has been called, all calls to ORB::run() will return once the ORB has shut down. However, the time required to shut down an ORB depends upon the volume of client activity when shutdown is requested. In extreme cases, a complete cessation of client activity may be necessary to allow the ORB to shut down.
Here are some options for shutting down a CORBA-based application:
ORB::shutdown() can be called with wait_for_completion == FALSE from a thread processing a CORBA request that was invoked via some maintenance or adminstrative interface. All calls to ORB::run() will return once the ORB has shut down and thus release the threads dedicated to the ORB. Some mechanism should be used to prevent the main thread from exiting before the other threads exit.
The application may continue to run for a relatively long time. If this is unacceptable, another mechanism should be used to forcibly terminate the application.
ORB::shutdown() to terminate ORB-related processing. If ORB::shutdown() is called with wait_for_completion == FALSE, some other mechanism should be used to prevent the main thread from exiting before all other threads have exited.
This last option provides some additional flexibility. After the main thread initiates shutdown, it might then block until all other threads have exited or a specified time interval has elapsed. If the other threads exit before the time interval elapses, the main thread conducts any other cleanup activities and then exits. If the time period elapses, the main thread performs some, perhaps not all, of the usual cleanup activity and then exits causing a forced termination.
A CORBA request is processed in two stages:
Strategies employed by a multi-threaded ORB as it receives and dispatches requests determine the extent to which requests may be processed concurrently. These strategies are usually described as a ratio between threads and requests, e.g.:
The POA threading models establish concurrency constraints that are imposed during request processing in a multi-threaded environment. These constraints further qualify the extent to which CORBA requests may be processed concurrently.
A POA's threading model is determined by its ThreadPolicy value. There are three standard ThreadPolicy values:
An ORB-controlled POA places no constraints on concurrent requests; the POA effectively abdicates responsibility for threading and concurrent request processing to the ORB.

Figure 1: ORB Controlled Threading Model
Concurrent servant upcalls may occur at the POA level in multi-threaded environments if the ORB dispatches requests concurrently. Application objects activated by an ORB-controlled POA in a multi-threaded environment should be multi-thread safe.
A single-threaded POA constrains request processing such that concurrent upcalls shall not occur at the POA level, even in multi-threaded environments.

Figure 2: Single-Thread Threading Model
A multi-threaded ORB may dispatch concurrent requests to a single-threaded POA, but the subsequent servant upcalls will occur sequentially; thus an application object activated by a single-threaded POA will not be subject to concurrent requests. However, an application object activated by multiple single-threaded POAs may be subject to concurrent requests in a multi-threaded environment.

Figure 3: Servant Activated by Multiple Single-threaded POAs
Activating an application object with multiple POAs is not recommended.
Requests are dispatched to all main-threaded POAs sequentially. This effectively serializes requests dispatched by main-threaded POAs at the ORB level.

Figure 4: Main-Thread Model
An application object activated by a main-threaded POA will not be subject to concurrent requests. The main-threaded model is also applicable in processing environments where some code must be executed on the main thread. In such environments, the main-thread model insures that servant upcalls are processed on that thread. However, the application must make the main thread available to the ORB via ORB::run() or ORB::perform_work().
A POA's threading model is determined by its ThreadPolicy. The default ThreadPolicy value is ORB_CTRL_MODEL. A POA's ThreadPolicy, like all other POA policies, can be assigned only when the POA is created.
The relevant portions of the PortableServer module and the POA's interface are:
module PortableServer {
// Portions of the PortableServer module omitted.
const CORBA::PolicyType THREAD_POLICY_ID = 16;
enum ThreadPolicyValue {
ORB_CTRL_MODEL,
SINGLE_THREAD_MODEL,
MAIN_THREAD_MODEL
};
local interface ThreadPolicy : CORBA::Policy {
readonly attribute ThreadPolicyValue value;
};
local interface POA {
// Portions of interface POA omitted.
POA create_POA(
in string adapter_name;
in POAManager aPOAManager,
in CORBA::PolicyList policies
) raises (AdapterAlreadyExists, InvalidPolicy);
ThreadPolicy create_thread_policy(
in ThreadPolicyValue value);
};
};
The following code fragment demonstrates how to create a single-threaded POA:
// Portions of int main(int, char **argv) omitted.
// Init the ORB.
CORBA::ORB_var orb = CORBA::ORB_init (argc, argv);
// Get the Root POA.
CORBA::Object_var obj = orb->resolve_initial_references ("RootPOA");
PortableServer::POA_var poa = PortableServer::POA::_narrow (obj.in());
// Create and populate a policy list.
CORBA::PolicyList policies(1);
policies[0] = poa->create_thread_policy(PortableServer::SINGLE_THREAD_MODEL);
// Use the Root POA's POAManager.
PortableServer::POAManager_var mgr = poa->the_POAManager ();
// Create a child POA.
// Threading model is single-threaded.
// All other policies assume the default value.
PortableServer::POA_var st_poa = poa->create_POA("ST POA", mgr.in(), policies);
// Release memory allocated to the policy list.
policies[0]->destroy();
The ORB-controlled threading model (ORB_CTRL_MODEL) allows application developers to make the most of an ORB's concurrent processing capabilities and performance optimization strategies. An application using this threading model can also take advantage of fine-grained contention management techniques to resolve application objects' contention for shared resources and services. However, this model obligates application developers to assume concurrent requests will occur, and identify and resolve points of contention that may arise during concurrent CORBA requests.
The following scenarios might motivate use of the ORB-controlled threading model for a CORBA-based application that is subject to concurrent requests:
The single-threaded model (SINGLE_THREAD_MODEL) can simplify an application's design and implementation by guaranteeing that an application object will not be subject to concurrent invocations from a distinct POA. Applied within its constraints, this model shifts some responsibility for contention management away from the application. However, this mechanism can only resolve contention for distinct servants arising from concurrent requests; it can not resolve contention for services and resources shared by application objects. Resolving contention for commonly-used services and resources remains the application's responsibility.
The single-threaded model is appropriate when:
The main-thread model (MAIN_THREAD_MODEL) is applicable in two circumstances:
ORB::run() or ORB::perform_work() from the main thread. Chapter 21 of Advanced CORBA Programming with C++ [ISBN: 0201379279] by Michi Henning and Steve Vinoski discusses issues associated with multi-threaded applications. This text was written to be compliant with an earlier version of the specification (the current version is 3.0) but the authors' treatment of these issues is relevant to contemporary applications.
Pattern-Oriented Software Architecture, Volume 2 [ISBN: 0-471-60695-2] by Douglas Schmidt, Michael Stal, Hans Rohnert, and Frank Buschmann presents many architectural patterns that are relevant to the topics discussed in these articles. Although these patterns are not presented in the context of a CORBA ORB, they respond to the forces that affect an ORB and are therefore applicable to ORB implementations. In many cases, an ORB is cited among the known uses of these patterns. The Reactor pattern (pg. 179) defines an architecture that is used for demultiplexing and dispatching network events. The Half-Sync/Half-Async pattern (pg. 423) defines a competing architecture for demultiplexing and dispatching network events. The Leader/Follower pattern (pg. 447) defines a means of managing a thread pool suitable for use within an ORB. The Active Object pattern (pg. 369) defines a technique suitable for implementing a thread-per-connection strategy.
Readers interested in additional material related to threading and concurrent processing may find the following research papers useful:
This article examined aspects of CORBA request processing related to threading and concurrent processing as set forth by the CORBA specification. The material presented here discusses the ORB's thread-related operations and their use as well as the definition and use of the POA's threading models. Guidelines are offered to help developers consider an application's requirements and choose strategies that will fulfill those requirements. The followup article describes TAO's request-processing strategies and its implementation of the ORB's thread-related operations.
Object Computing, Inc (OCI) has been providing educational services to clients, industries and universities since 1993. We offer one of the most comprehensive distributed Object Oriented training curricula in the country. These curricula focus on the fundamentals of OO technology; with close to 40 workshops in OOAD, Java, XML, C++/CORBA and Unix/Linux.
For further information regarding OCI's Educational Services programs, please visit our Educational Services section on the web or contact us at training.
The OCI CORBA News Brief is intended to promote CORBA and object technology in the development of distributed computing applications. Each issue of the CORBA News Brief will feature news and technical information about OCI's supported open-source ORBs (TAO and JacORB), case studies, and examples using CORBA, as well as information about OCI's educational offerings.
The OCI CORBA News Brief is published on a monthly basis. Send ideas for articles of interest to corba@ociweb.com.
To subscribe or unsubscribe from the CNB mailing list, send mail to majordomo@ociweb.com with the line "subscribe cnb" or "unsubscribe cnb" in the body of the message.
|
|
|