VisiBroker for C++ Developer’s Guide : Real-time CORBA extensions

Real-time CORBA extensions
This section describes the Real-Time CORBA extensions, as defined in the Real-Time CORBA 1.0 Specification, supported by VisiBroker for C++. It also explains how to apply these Real-Time CORBA extensions in application code.
Note
Real-Time CORBA extensions support is available only on the following platforms:
Overview
VisiBroker for C++ provides the following Real-Time CORBA extensions:
Used to manage the creation and destruction of other Real-Time CORBA entities, such as Threadpools and Mutexes.
Enhanced Portable Object Adapters (POAs), which work with Threadpools and have a number of configurable Real-Time CORBA properties.
A platform-independent priority scheme that is used to control the priority of threads related to the VisiBroker application. Specifying priorities in terms of the Real-Time CORBA priority scheme, instead of the priority scheme of a particular OS, allows applications to be developed that schedule real-time activities consistently across machines running different Operating Systems, both Real-Time and non-Real-Time. It also aids the porting and/or extension of applications to different Operating Systems at a later date.
The means by which the Real-Time CORBA Priority scheme is ‘mapped’ onto the priority scheme of the underlying OS. You can install a Priority Mapping to control the way the priorities are mapped or you can use the ‘default mapping’ that is provided by the ORB.
Real-Time CORBA entities that allow an application to control the threads used by the ORB to execute CORBA invocations.
An extension of the CORBA::Current interface that allows Real-Time CORBA priority values to be assigned to application threads.
Two alternate models for deciding the priority at which CORBA invocations are executed.
An IDL-defined mutex interface, which gives applications access to the same mutex implementation as that used internally by the ORB. This guarantees consistent priority inheritance behavior, as well as improving application portability.
Mechanisms to allow range limitation and explicit control of the priorities of additional threads used internally within the ORB.
Note
Depending on the underlying Operating System, the control of Thread Priorities might require superuser (root) privileges.
Using the Real-Time CORBA extensions
Applications that want to make use of the Real-Time CORBA extensions must include the C++ header file rtcorba.h that is provided in the VisiBroker include directory.
Many of the Real-Time CORBA features have interfaces that are defined in IDL. The IDL for these features is specified in a new RTCORBA module. This IDL is available for inspection in the file RTCORBA.idl, which can be found in the idl directory of the VisiBroker installation.
However, there is no need to compile the IDL in RTCORBA.idl to make use of the Real-Time CORBA features. Applications need only to include the rtcorba.h header file that is provided with the other VisiBroker header files.
This is because all of the interfaces in the module are specified as ‘locality constrained’. That is, their object references cannot be passed off-node or used to invoke operations on instances remotely. All manipulation of Real-Time CORBA interfaces must be performed locally, as is the case with other CORBA entities such as CORBA::ORB and PortableServer::POA.
Real-Time CORBA ORB
The Real-Time CORBA extensions include a Real-Time ORB interface, which is used to manage other Real-Time CORBA entities. The interface is named RTCORBA::RTORB, and has the following definition:
module RTCORBA {
// locality constrained interface
interface RTORB {
 
Mutex create_mutex();
void destroy_mutex( in Mutex the_mutex );
exception InvalidThreadpool {};
 
ThreadpoolId create_threadpool(
in unsigned long stacksize,
in unsigned long static_threads,
in unsigned long dynamic_threads,
in Priority default_priority,
in boolean allow_request_buffering,
in unsigned long max_buffered_requests,
in unsigned long max_request_buffer_size );
 
void destroy_threadpool( in ThreadpoolId threadpool )
raises (InvalidThreadpool);
 
void threadpool_idle_time(
in ThreadpoolId threadpool,
in unsigned long seconds )
raises (InvalidThreadpool);
};
};
 
The operations shown in the IDL are described in “Threadpools” and “Real-Time CORBA Mutex API”.
The Real-Time ORB does not need to be explicitly initialized—it is initialized implicitly as part of the regular CORBA::ORB_init call. Any Real-Time ORB initialization arguments are passed in to the call to CORBA::ORB_init, along with non-Real-Time arguments. If any Real-Time initialization argument is invalid, the ORB_init call fails, and a system exception is raised.
To use the Real-Time ORB operations, the application must have a reference to the RTCORBA::RTORB instance. This reference can be obtained any time after the call to ORB_init, and is obtained by calling the resolve_initial_references operation on CORBA::ORB, with the object id "RTORB" as the parameter. Because resolve_initial_references returns the reference as a CORBA::Object_ptr, it must then be narrowed to a RTCORBA::RTORB_ptr before it can be used.
Note
To support Real-Time CORBA Extensions, the VisiBroker for C++ ORB has to operate in a special 'real-time compatible' mode, the behavior and semantics of which differ from the regular mode of operation. Since obtaining an "RTORB" reference automatically puts the ORB in this special mode, you should obtain an "RTORB" reference as early as possible in your application code to avoid any possible inconsistency in behavior.
The code example below shows how to obtain the RTCORBA::RTORB reference. Similar code can be found in the Real-Time CORBA examples included with the VisiBroker release: priority_model, threadpool, visthread and rtmutex.
#include "corba.h"
#include "rtcorba.h"
 
// First initialize the ORB
CORBA::ORB_ptr orb;
 
VISTRY
{
orb = ORB_init(argc, argv);
}
VISCATCH(CORBA::Exception, e)
{
cerr << "Exception initializing ORB" << endl << e << endl;
// handle error here
}
VISEND_CATCH
 
// Then obtain the RTORB reference
CORBA::Object_var ref;
 
// Note use of _var, so ref will be automatically released
VISTRY
{
ref = orb->resolve_initial_references("RTORB");
}
VISCATCH
{
cerr << "Exception obtaining RTORB reference" << endl << e << endl;
// handle error here
}
VISEND_CATCH
 
// Finally, narrow the RTORB reference
RTCORBA::RTORB_ptr rtorb;
VISTRY
{
rtorb = RTCORBA::RTORB::_narrow(ref);
// ref is no longer needed. Will be automatically released as it is a _var
}
VISCATCH(CORBA::Exception, e)
{
cerr << "Error narrowing RTORB reference" << endl << e << endl;
// Handle error here
}
VISEND_CATCH
Real-Time object adapters
In Real-Time CORBA, all Object Adapters are Real-Time Object Adapters. This means that all Object Adapters are aware of priorities and handle CORBA invocations according to rules defined by Real-Time CORBA. It is necessary for all Object Adapters on a node to be Real-Time. If some Object Adapters in the CORBA application were non-Real-Time, their operation would interfere with the behavior of the Real-Time Object Adapters (because threads associated with all Object Adapters must be scheduled together by the OS.)
As all Object Adapters are Real-Time, the normal Portable Object Adapter (POA) interface is used to manage them.
Real-Time Object Adapters are created in the normal way, through a call to create_POA. Configuration of the extra, Real-Time properties is achieved through the passing of new Real-Time policies in the policy list parameter. An example of POA creation specifying one such new policy (and its associated value) is shown below:
// Create Real-Time CORBA Priority Model Policy
// (Already obtained RTORB reference)
RTCORBA::PriorityModelPolicy_ptr priority_model_policy =
rtorb->create_priority_model_policy(
RTCORBA::SERVER_DECLARED, 25 );
 
// Create Policy List containing this RT CORBA Policy
// (Include any required non-Real-Time policies in the same list)
CORBA::PolicyList policies;
policies.length(1);
policies[0] = priority_model_policy;
 
// Create POA, using the Policy List
// (Associate POA with the Root POA's POA manager, if none other)
// (Already obtained Root POA reference)
PortableServer::POAManager_var poa_manager =
rootPOA->the_POAManager();
VISTRY
{
poa = rootPOA->create_POA("myPOA", poa_manager, policies);
}
VISCATCH(CORBA::Exception, e)
{
// handle exceptions here
}
VISEND_CATCH
The Real-Time policies that can be configured at the time of POA creation are concerned with the Priority Model that the POA supports and which Threadpool it will be associated with.
If any of these Real-Time properties is not configured by the application at the time of POA creation, the ORB initializes that property with a default value. The default Priority Model behavior is for the POA to support the Server Declared Priority Model, and the default Threadpool behavior is for the POA to be associated with the General Threadpool.
The configuration of these properties and their default values are described in “Threadpools” and “Real-Time CORBA priority models”.
Real-Time CORBA priority
Real-Time CORBA defines a universal, platform independent priority scheme called Real-Time CORBA Priority. It allows Real-Time CORBA applications to make prioritized CORBA invocations in a consistent fashion between nodes running different Operating Systems. Even if all nodes in the existing system are running the same Operating System, its use aids the configuration of priorities in the system, improves application portability, and simplifies future extension to a mixed OS environment.
For consistency and portability, Real-Time CORBA applications must use Real-Time CORBA Priority to express the priorities in the CORBA part of the application, even if all nodes in a system use the same OS, and hence the same priority scheme.
The RTCORBA::Priority type is used to represent Real-Time CORBA Priority:
module RTCORBA {
typedef short Priority;
const Priority minPriority = 0;
const Priority maxPriority = 32767;
};
A signed short is used in order to accommodate the Java language mapping. However, only values in the range 0 (minPriority) to 32767 (maxPriority) are valid.
Note
As per the Real-Time CORBA Specification, numerically higher RTCORBA::Priority values are defined to be of higher priority.
In practice, an application does not need to use the entire range of valid RTCORBA::Priority values (0 to 32767.) A smaller range, which suits the needs of the application, can be defined as the only admissible range. This is achieved through control of the Priority Mapping. Priority Mappings are described in the next section.
By default, VisiBroker for C++ installs a Priority Mapping that only allows RTCORBA::Priority values in the range 0 to 31 (the POSIX threading range of priorities). See the next section for details.
Priority mappings
A given Real-Time Operating System has a particular priority scheme: the range and direction of priority values that it uses. For example, the Pthreads priority scheme used by some Operating Systems is POSIX compliant with priorities in the range 0 to 31. In Real-Time CORBA, this is referred to as the Native Thread Priority Scheme and the priority values are referred to as native priority values.
As the Real-Time CORBA application describes its priorities in terms of RTCORBA::Priority values, and the OS works in terms of native priority values, a mapping must be defined between these two priority schemes. The mapping is used by the ORB, to obtain the native priority corresponding to a given RTCORBA::Priority value, and vice versa, as is required. This is done, for example, when an application specifies that it wants a Threadpool to have threads that are created with a particular RTCORBA::Priority, and the ORB needs to know what native priority to tell the OS to use when it actually creates the threads.
The Priority Mapping can also be used directly by the application, but this should only occur in special circumstances. This is discussed further in “Using native priorities in VisiBroker application code”.
The ORB comes with a default Priority Mapping, which is sufficient for experimenting with the Real-Time CORBA features and might be sufficient for many Real-Time applications (since it is based on the POSIX priority scheme.) Therefore, when first becoming familiar with the Real-Time features of VisiBroker for C++, it might be appropriate to skip the rest of this section, and learn about the rest of the Real-Time CORBA features (beginning with “Threadpools”), before returning to this section to understand the details of Priority Mappings and the reasons for installing one that is different from the default.
Priority mapping types
To support Priority Mappings, a RTCORBA::NativePriority type and RTCORBA::PriorityMapping type are defined:
module RTCORBA {
typedef short NativePriority;
native PriorityMapping
};
RTCORBA::NativePriority values must be integers in the range –32768 to +32767. However, depending on the OS, the valid range is a subset of this range.
The RTCORBA::PriorityMapping type is defined as an IDL native interface. This means that the interface is defined directly in each implementation language, rather than being defined in IDL and mapped automatically to each language according to the rules of the particular CORBA language mapping. This is done for reasons of efficiency.
The C++ mapping of the RTCORBA::PriorityMapping interface is:
class PriorityMapping {
public:
virtual CORBA::Boolean to_native(
RTCORBA::Priority corba_priority,
RTCORBA::NativePriority &native_priority );
 
virtual CORBA::Boolean to_CORBA(
RTCORBA::NativePriority native_priority,
RTCORBA::Priority &corba_priority );
 
virtual RTCORBA::Priority max_priority();
 
PriorityMapping();
virtual ~PriorityMapping() {}
static RTCORBA::PriorityMapping * instance();
};
The methods that define the behavior of a particular Priority Mapping are to_native, to_CORBA and max_priority. Their purpose is as follows:
This method takes a RTCORBA::Priority value from the corba_priority parameter and either maps it to a RTCORBA::NativePriority value or fails to map it. If the value is mapped, the resulting native priority value is stored in the location referenced by the parameter native_priority (which is a C++ reference parameter) and a true value is returned to indicate that the mapping was successful. If the value is not mapped, the contents of the native_priority parameter are not altered, and a false value is returned to indicate that the mapping operation failed.
The converse of to_native, the to_CORBA method takes a RTCORBA::NativePriority value from the native_priority parameter, and either maps it to a RTCORBA::Priority value or fails to map it. If the value is mapped, the resulting RTCORBA::Priority value is stored in the location referenced by the corba_priority parameter (which is a C++ reference parameter) and a true value is returned to indicate that the mapping was successful. If the value is not mapped, the contents of the corba_priority parameter are not altered, and a false value is returned to indicate that the mapping operation failed.
This method just returns the highest RTCORBA::Priority value that is valid in this mapping. The ORB needs to be explicitly told the highest value as there is no efficient way for it to determine it by examining the behavior of the to_native and to_CORBA methods given different input values.
The implementation of these methods must conform to certain rules, which are described below.
Rules for priority mappings
Any Priority Mapping that is installed (including the default Priority Mapping) must conform to the following rules:
The to_native and to_CORBA methods should be able to handle all values of their input parameter, in the range –32768 to +32767.
to_native must definitely fail to map values outside the range 0 to 32767, and might fail to map values within that range as well. (For example the default Priority Mapping fails to map all values outside the range 0 to 31.)
to_CORBA must definitely fail to map values outside the range of the native priority scheme and might fail to map values within that range as well. (The default Priority Mapping chooses to map all values in the 0 to 31 range.)
Lower RTCORBA::Priority values should always map to lower importance native priority values, and higher to higher. Note that in the case of a Pthreads based operating system, this means mapping numerically lower RTCORBA::Priority values to/from numerically higher Native Priorities. This follows the convention used by the majority of Real-Time Operations Systems. The convention maintains consistency with Real-Time CORBA applications developed on other Real-Time Operations Systems. Otherwise future porting and interworking with other Real-Time applications will be greatly complicated.
RTCORBA::Priority 0 should always be mapped, and always be mapped to the value of lowest importance in the range of native priority values that is mapped to/ from.
max_priority must return the highest RTCORBA::Priority value that is mapped by the mapping. (That is, the highest value for which a native priority value is returned.)
The following are not mandated, but will often be the case, unless there is special reason to do otherwise:
to_native and to_CORBA usually return the same value (or fail to map) every time they are called with the same input value.
to_native and to_CORBA are usually reverse mappings of one another.
The ranges of RTCORBA::Priority and native priority values that are mapped are usually each a single contiguous range of priority values.
Default priority mapping
VisiBroker for C++ provides a default Priority Mapping. This is the Priority Mapping that will be used unless a different one is written by the application developer and installed using the process described in “Replacing the default priority mapping”.
Note
Only one Priority Mapping can be installed at any one time on a given VisiBroker application. The act of installing one Priority Mapping automatically un-installs the previously installed Priority Mapping (usually the default Priority Mapping.)
The default Priority Mapping has the following characteristics:
Valid RTCORBA::Priority range is 0 to 31 only. This follows the POSIX threading model. All priorities outside of this range are invalid, which means an exception is raised if an attempt is made to use them.
The valid RTCORBA::Priority values are mapped one-to-one onto a 32 priority sub-range of the native operating system—the "native priority range".
The valid RTCORBA::Priority values are mapped onto the native priority range in such a way that RTCORBA::Priority value 0 corresponds to the lowest-importance native priority in the sub-range used, and RTCORBA::Priority 31 corresponds to the highest-importance native priority in the sub-range used.
The following table shows the RTCORBA::Priority default mappings for the supported operating systems.
The default Priority Mapping is defined within the ORB, and hence the source code for it is not included in the VisiBroker release. The source code for the mapping is shown here, however, to show exactly how this mapping behaves:
VisiBroker for C++ Default 'to_corba' Priority Mapping CORBA::Boolean VISDefaultPriorityMapping::to_CORBA( RTCORBA::NativePriority native_priority,
 
RTCORBA::Priority &amp;corba_priority )
#if defined(SOLARIS)
native_priority = 10 + corba_priority; // 0 -> 10, 31 -> 41
 
#if defined(AIX)
native_priority = 28 + corba_priority; // 0 -> 28, 31 -> 59
#elif defined(HPUX_11)
native_priority = corba_priority; // 0 -> 0, 31 -> 31
#elif defined(__linux)
native_priority = 30 + corba_priority; // 0 -> 30, 31 -> 61
#else
# error Supported OS not detected
#endif
return (CORBA::Boolean) 1;
}
}
 
// VisiBroker for C++ Default 'to_corba' Priority Mapping
CORBA::Boolean
VISDefaultPriorityMapping::to_native( RTCORBA::Priority corba_priority,
 
RTCORBA::NativePriority &amp;native_priority )
#if defined(SOLARIS)
if ((native_priority < 10) || (native_priority > 41))
 
#elif defined(AIX)
if ((native_priority < 28) || (native_priority > 59))
#elif defined(HPUX_11)
if ((native_priority < 0) || (native_priority > 31))
#elif defined(__linux)
if ((native_priority < 30) || (native_priority > 61))
#else
# error Supported OS not detected
#endif
{
return (CORBA::Boolean) 0;
}
else
{
#if defined(SOLARIS)
corba_priority = native_priority - 10; // 10 -> 0, 41 -> 31
#elif defined(AIX)
 
corba_priority = native_priority - 28; // 28 -> 0, 59 -> 31
 
#elif defined(HPUX_11)
corba_priority = native_priority; // 0 -> 0, 31 -> 31
#elif defined(__linux)
corba_priority = native_priority - 30; // 30 -> 0, 61 -> 31
#else
# error Supported OS not detected
#endif
return (CORBA::Boolean) 1;
}
}
 
// Default max method : returns the max RTCORBA::Priority supported
// by the default priority mapping
RTCORBA::Priority VISDefaultPriorityMapping::max_priority()
{
return 31;
}
Replacing the default priority mapping
Note
Only one Priority Mapping can be installed at any one time on a particular system. The act of installing one Priority Mapping automatically uninstalls the previously installed Priority Mapping (usually the default Priority Mapping.)
The application might wish to replace the default Priority Mapping on some or all nodes in the system. Reasons for doing this include:
To have more or fewer RTCORBA::Priority values in the range of valid (mapped) values. For example, to only map RTCORBA::Priority values in the range 0 to 8 or to map values in the range 0 to 128.
Note that the relationship between the ranges of RTCORBA::Priority and native priority values that are valid in the mapping will determine whether the mapping is a one-to-one mapping or not. The mapping does not have to be a one-to-one mapping, but this can be convenient. The default Priority Mapping is a one-to-one mapping.
Note
Installed Priority Mappings should follow the convention, used in the default Priority Mapping, of making the RTCORBA::Priority 0 have the lowest importance. This means ensuring that RTCORBA::Priority 0 maps to the numerically smallest native priority value (of the sub-range that is being mapped to.) This maintains consistency with Real-Time CORBA applications developed across operating systems. Otherwise future porting and interworking with other Real-Time applications will be greatly complicated.
A new Priority Mapping is installed by defining a new class, which must inherit from the class RTCORBA::PriorityMapping, and creating one static instance of it in the application. When the static instance is initialized (during the execution of static constructors) the base RTCORBA::PriorityMapping class constructor will register the new mapping with the ORB.
For an example of writing and installing a new Priority Mapping, look at the files mapping.h and mapping.C in the threadpool example included in the VisiBroker installation. Note the single instance of the new class that is created in global scope in mapping.C. When the resulting mapping.o is built with a VisiBroker application and static constructor initialization takes place during the execution of the application, it is the initialization of this instance that installs the mapping.
Using native priorities in VisiBroker application code
Although applications must use Real-Time CORBA Priority to discern the priority of different parts of their CORBA application (and the priority of CORBA invocations between parts of the application), there are cases in which the application needs to discern Native Priority. Examples include configuring a sub-system outside of the CORBA application, which only knows about the native priority scheme, or using an OS call directly, which takes a native priority value as a parameter. In these cases, it might be necessary to translate between Real-Time CORBA and native priority in the application. To allow this, VisiBroker for C++ provides the static instance method on the class RTCORBA::PriorityMapping. This method returns a pointer to the currently installed Priority Mapping.
Using this method, it is guaranteed to the application code that any priority mapping method calls it makes are executed on the currently installed mapping, regardless of the internal implementation details of the mapping. This allows the code to continue to work even if the installed mapping is changed. The following example uses the installed Priority Mapping from application code.
RTCORBA::Priority corba_priority;
 
// Priority Mapping methods return boolean flag, rather than
// throwing exceptions
if
( !RTCORBA::PriorityMapping::instance()->to_CORBA(
100,corba_priority) )
{
// Handle failure to map native priority to RT CORBA priority
}
// Use corba_priority value here ...
Threadpools
VisiBroker for C++ uses Threadpools to manage the threads of execution on the server-side of the ORB. Threadpools offer the following features:
This helps guarantee Real-Time system behavior, by allowing the application programmer to ensure that there are enough thread resources to satisfy a certain number of concurrent invocations, and also helps reduce latency and increase predictability, by avoiding the destruction and recreation of threads between invocations.
Having multiple Threadpools, associated with different Object Adapters allows one part of the system to be isolated from the thread usage of another, possibly lower priority, part of the application system. This can again be used to help achieve Real-Time behavior of the system as a whole.
A Threadpool can be used to set a maximum limit on the number of threads that a POA or set of POAs might use. In systems where the total number of threads that can be used is constrained, this can be used in conjunction with Threadpool partitioning to avoid thread starvation in a critical part of the system.
Threadpool API
Threadpools are managed using the following operations of the RTCORBA::RTORB interface:
module RTCORBA {
typedef unsigned long ThreadpoolId;
 
// locality constrained object
interface RTORB {
exception InvalidThreadpool {};
 
ThreadpoolId create_threadpool(
in unsigned long stacksize,
in unsigned long static_threads,
in unsigned long dynamic_threads,
in Priority default_priority,
in boolean allow_request_buffering,
in unsigned long max_buffered_requests,
in unsigned long max_request_buffer_size );
 
void destroy_threadpool( in ThreadpoolId threadpool )
raises (InvalidThreadpool);
 
void threadpool_idle_time(
in ThreadpoolId threadpool,
in unsigned long seconds )
raises (InvalidThreadpool);
};
};
These operations are described in the sections that follow. Examples of Threadpool creation and their association with POAs can be found in the threadpool example included with the VisiBroker installation.
Threadpool creation and configuration
A Threadpool is created by invoking the create_threadpool operation on the Real-Time ORB. The arguments to create_threadpool have the following significance:
The stack size, in bytes, that each thread created for the Threadpool should have.
The number of threads that will be created and assigned to the pool at the time of Threadpool creation. These threads will not be destroyed until the Threadpool itself is destroyed. After they have been used to execute a CORBA invocation, they are returned to the Threadpool, and await another invocation to execute.
The number of threads that can be created dynamically, to execute CORBA invocations received when all the static threads are currently in use. The number can be zero, in which case no threads can be dynamically created after Threadpool creation. (In this case, the number of concurrently executing invocations is limited by the number of static threads.)
The RTCORBA::Priority at which idle threads should remain while in the pool waiting for a CORBA invocation to execute. The priority at which the invocation is executed depends on the Real-Time CORBA Priority Model in use. See “Real-Time CORBA Priority Models” for details. This parameter determines the priority of the threads when they are not handling invocations.
These arguments support the Request Buffering feature from the Real-Time CORBA specification, which allows for invocation requests to be queued once the static and dynamic thread limits of a Threadpool have been reached. This feature is not currently supported in VisiBroker for C++, and the value of these arguments is ignored.
If dynamic_threads is greater than zero, so that threads can be created dynamically, the threads are not immediately destroyed after they have completed executing the CORBA invocation that they were created to handle. They are returned to the Threadpool, in the same way that static threads are. However, dynamic threads that remain idle in the Threadpool might eventually be destroyed during garbage collection that occurs from time to time.
The amount of time a dynamically created thread must remain idle in a Threadpool before it is destroyed can be set using the threadpool_idle_time operation of RTCORBA::RTORB. If the idle time is not set using this operation, it defaults to 300 seconds.
If successful, create_threadpool returns an identifier for the new Threadpool. The identifier is of type RTCORBA::ThreadpoolId (an unsigned long), and is subsequently used to refer to that Threadpool.
Note
The semantics of dynamic_threads value here in the Real-Time context differs from that of the Dispatcher Thread pool threadMax value. (See “Thread pool dispatch policy”.) A dynamic_threads value of zero means that, even if needed, no additional threads are created for the RTCORBA Threadpool. In contrast, a threadMax value of zero for the dispatcher threadpool means that the VisiBroker ORB has the freedom to create additional threads, as and when required.
Association of an object adapter with a threadpool
Every POA created using VisiBroker for C++ is associated with a Threadpool. Each Threadpool, on the other hand, can be associated with any number of POAs. By configuring multiple POAs to use the same or different Threadpools, the application designer can control the use of threads by different sets of CORBA Objects.
Which Threadpool a POA is associated with is determined by passing the RTCORBA::ThreadpoolId of the desired Threadpool into the create_POA operation as the value of a RTCORBA::ThreadpoolPolicy policy. The following example associates a POA with a Threadpool at time of POA initialization.
// Obtain RTORB reference
CORBA::Object_var objref =
orb->resolve_initial_references("RTORB");
RTCORBA::RTORB_var rtorb = RTCORBA::RTORB::_narrow(objref);
 
// Create a Threadpool
RTCORBA::ThreadpoolId tpool_id =
rtorb->create_threadpool(
30000, // stacksize
5, // num static threads
0, // num dynamic threads
20, // default RT CORBA priority
0, 0, 0);
 
// Create Threadpool Policy object for use in POA initialization
RTCORBA::ThreadpoolPolicy_ptr tpool_policy =
rtorb->create_threadpool_policy( tpool_id );
 
// Create Policy List for POA initialization
// (Include any required non-Real-Time policies in the same list)
CORBA::PolicyList policies;
policies.length(1);
policies[0] = tpool_policy;
 
// Create POA, using the Policy List
// (Associate POA with the Root POA's POA manager, if none other)
// (Already obtained Root POA reference)
PortableServer::POAManager_var poa_manager =
rootPOA->the_POAManager();
VISTRY
{
poa = rootPOA->create_POA("myPOA", poa_manager, policies);
}
VISCATCH(CORBA::Exception, e)
{
// handle exceptions here
}
VISEND_CATCH
create_POA fails if any part of the Real-Time CORBA configuration is invalid. For example, if the ThreadpoolId is not for a currently existing Threadpool, a CORBA::BAD_PARAM system exception is raised.
The General threadpool
If a Threadpool is not specified at POA creation time, as described in the previous section, then the new POA that is created is associated with a special Threadpool, called the General Threadpool.
The General Threadpool does not have to be created by a call to RTCORBA::RTORB’s create_threadpool operation. Instead, the General Threadpool is created automatically by the ORB the first time it is required.
The General Threadpool is created with the following configuration:
If this configuration is not appropriate for the application, the General Threadpool should not be used, and the application should explicitly associate each POA with an appropriately configured Threadpool at POA creation time.
Threadpool destruction
A Threadpool can be destroyed by passing its ThreadpoolId as the argument to a call to the destroy_threadpool operation of RTCORBA::RTORB:
// RTORB reference and Threadpool id obtained previously
 
// Get RT ORB reference
CORBA::Object_var objref =
orb->resolve_initial_references("RTORB");
RTCORBA::RTORB_var rtorb = RTCORBA::RTORB::_narrow(objref);
 
VISTRY
{
rtorb->destroy_threadpool( pool_id );
}
VISCATCH(CORBA::Exception, e)
{
// handle error here
}
VISEND_CATCH
All POAs that have been associated with a particular Threadpool (that had this Threadpool specified as the Threadpool to use, at the time of POA creation) must have been destroyed before the destroy_threadpool operation will succeed.
If POAs still exist that are associated with the Threadpool, the call fails and a system exception is raised.
Real-Time CORBA Current interface
Real-Time CORBA defines a Real-Time CORBA Current interface to provide access to the CORBA priority of a thread. The following sample shows the RTCORBA::Current interface.
module RTCORBA {
interface Current : CORBA::Current {
attribute Priority base_priority;
};
};
A Real-Time CORBA Priority can be associated with the current thread, by setting the base_priority attribute of the RTCORBA::Current object. This has two effects:
The priority value stays in effect (for both of the above purposes) until a new value is set.
The current value can also be read, using the corresponding get attribute operation.
A CORBA::BAD_PARAM system exception is raised by the set attribute operation if an attempt is made to set a priority outside of the valid 0 to 32767 range. A CORBA::DATA_CONVERSION exception is raised if an attempt is made to set a priority that is in the 0 to 32767 range, but outside of the range supported by the currently installed Priority Mapping.
A CORBA::INITIALIZE system exception is raised if an attempt is made to get the priority value from a thread that has not yet had a Real-Time CORBA Priority value set on it. (The native priority of the current thread is not just mapped to a Real-Time CORBA Priority and returned.)
To use the RTCORBA::Current object, a reference to it must be obtained. This is achieved by calling the CORBA::ORB operation resolve_initial_references with the parameter RTCurrent, as shown in the following example:
// Obtain the RTCORBA::Current reference
CORBA::Object_var ref;
 
VISTRY
{
ref = orb->resolve_initial_references("RTCurrent");
}
VISCATCH
{
// handle error here
}
VISEND_CATCH
 
// Narrow the RTCORBA::Current reference
RTCORBA::Current_ptr rtcurrent;
VISTRY
{
rtcurrent = RTCORBA::Current::_narrow(ref);
}
VISCATCH(CORBA::Exception, e)
{
// handle error here
}
VISEND_CATCH
Note that the RTCORBA::Current reference only needs to be obtained once. The same variable can be used by different threads, and will behave as if it is private to each of them (setting and getting their thread-specific priority value.) This behavior is inherited from the base CORBA::Current object.
Real-Time CORBA priority models
Real-Time CORBA supports two models for the coordination of priorities across a system. These two models provide two alternate answers to the question: where does the priority at which the CORBA invocation is executed come from? They are:
In this model, the Real-Time CORBA Priority associated with a client CORBA application thread, using RTCORBA::Current, is also used as the priority on the server-side of the invocation. The thread that executes the invocation (which is taken from a Threadpool) runs at a native priority that is mapped from the Real-Time CORBA priority set on the client side prior to making the invocation.
In this model the Real-Time CORBA Priority associated with a client CORBA application thread only affects the priority on the client-side of the invocation. The priority that the invocation is handled at on the server-side is determined by the configuration of the CORBA Object and the POA that created it.
Which Priority Model is used is a server-side issue, configured at the POA level. All CORBA Objects created from the same POA will have their invocations processed according to the Priority Model the POA is configured with.
The Priority Model is selected at POA initialization time, by including a RTCORBA::PriorityModelPolicy instance in the Policy List passed as a parameter to create_POA. The Policy is configured with one of the following values:
To select the Client Propagated Priority Model.
To select the Server Declared Priority Model.
In either case, a RTCORBA::Priority value is also specified as part of the Policy. The two models use this priority value differently:
The Server Declared Priority Model is the default model. If a POA is initialized without specifying which model to use, it will be configured to use the Server Declared Priority Model. However, in this case there is a subtle difference in behavior; because a priority has not been specified, the invocations run at the default priority of the Threadpool that the POA is associated with. (The default priority is a configurable property of Threadpools. It is the priority that threads remain at when idle in the pool. See “Threadpools” for details.)
The following code demonstrates the setting of the Priority Model Policy at the time of POA creation. In this case, the Client Propagated Priority Model is selected, with a default priority of 7 (for invocations from non-Real-Time Clients).
// Create Real-Time CORBA Priority Model Policy
 
RTCORBA::PriorityModelPolicy_ptr priority_model_policy =
rtorb->create_priority_model_policy(
RTCORBA::CLIENT_PROPAGATED, 7 );
 
// Create Policy List containing this RT CORBA Policy
// (Include any required non-Real-Time policies in the same list)
CORBA::PolicyList policies;
policies.length(1);
policies[0] = priority_model_policy;
 
// Create POA, using the Policy List
// (Associate POA with the Root POA's POA manager, if none other)
// (Already obtained Root POA reference)
PortableServer::POAManager_var poa_manager =
rootPOA->the_POAManager();
VISTRY
{
poa = rootPOA->create_POA("myPOA", poa_manager, policies);
}
VISCATCH(CORBA::Exception, e)
{
// handle exceptions here
}
VISEND_CATCH
See the priority_model example included with the VisiBroker installation for further examples of configuring the two different Priority Models.
Setting priority at the object level
When the Server Declared Priority Model is selected a priority value is supplied to determine the priority at which invocations will be executed on the server-side of the ORB. This priority value is used when handling invocations on behalf of any CORBA Object created by that POA.
However, this scope of control of priority is too coarse for some applications. To remedy this, Real-Time CORBA allows the priority that invocations will be executed at in the Server Declared model to be overridden on a per-Object basis.
The priority to run invocations at can be overridden for a given object by using either the operation activate_object_with_priority or activate_object_with_id_and_priority to activate the object in question. These operations work in the same way as activate_object and activate_object_with_id, but take a Real-Time CORBA Priority value as an additional parameter.
The Real-Time CORBA Specification defines these methods in the RTPortableServer module. However, VisiBroker for C++ defines these operations as part of the VisiBroker Extended POA interface, PortableServerExt::POA, which is accessed by narrowing a POA object reference using the static C++ method PortableServerExt::POA::_narrow.
For an example of setting the priority on a per-Object basis, see the file model_srvr.C in the priority_model example included with VisiBroker.
Real-Time CORBA Mutex API
VisiBroker for C++ implements the following Real-Time CORBA Mutex interface:
#include "timebase.idl"
module RTCORBA {
 
// locality constrained interface
interface Mutex {
void lock();
void unlock();
boolean try_lock(in TimeBase::TimeT max_wait);
// if max_wait = 0 then return immediately
};
 
interface RTORB {
...
Mutex create_mutex();
void destroy_mutex( in Mutex the_mutex );
...
};
};
A new RTCORBA::Mutex object is obtained using the create_mutex operation of RTCORBA::RTORB. A Mutex object has two states: locked and unlocked. Mutex objects are created in the unlocked state. When the Mutex object is in the unlocked state the first thread to call the lock() operation will cause the Mutex object to change to the locked state and the calling thread will be assigned ownership of the Mutex object.
Subsequent threads that call the lock() operation while the Mutex object is still in the locked state will block until the owner thread unlocks it by calling the unlock() operation.
The try_lock() operation works like the lock() operation except that if it does not get the lock within max_wait time it returns false. If the try_lock() operation does get the lock within the max_wait time period it returns true.
Control of internal ORB thread priorities
VisiBroker for C++ allows the application to control the priority of the threads that the ORB creates for internal use.
The internal ORB threads are:
A single DSUser thread is created the first time the ORB attempts to communicate with the VisiBroker Smart Agent (osagent). This will usually happen the first time either activate_object or a _bind method is called. This thread manages all communication between the ORB and the Smart Agent. The thread name is ‘VISDSUser’.
Listener Threads are created as part of the initialization of a Server Engine. (This occurs during POA initialization, whenever a POA wishes to use a Server Engine that has not been yet been used.) These threads wait for incoming CORBA invocations to be received from network connections. Listener Threads for IIOP communication have thread names of the form VISLis<N>, where <N> is an index number that starts from zero and indicates the order in which the listeners were created.
A single instance of this is created the first time a Threadpool is created. This will occur either when the application explicitly creates a Threadpool, or the first time the application creates a POA without specifying a Threadpool (in which case the General Threadpool is created so that it can be used.) Garbage Collection Threads have thread names of the form VISGC<N>, where <N> corresponds to the Threadpool Id of the threadpool they are associated with.
If the application does not configure the priority of these threads they all default to running at the highest RTCORBA::Priority in the installed priority mapping. That is the priority that is returned by the Priority Mapping’s max_priority method. Hence, with the Default Priority Mapping installed, they will all run at RTCORBA::Priority 31.
There are two ways of configuring the priority of the different types of internal ORB threads:
Configuring individual internal ORB thread priorities
The priority of different types (and in one case, different instances) of internal ORB threads can be controlled by specifying values for certain of VisiBroker properties.
In all cases, the priority value is specified as a Real-Time CORBA Priority value. The value must be a valid priority under the installed priority mapping:
Sets the default priority that Listener threads will run at. Can be changed at any time. The current value at the time of Server Engine creation (which occurs during POA creation) is the value used for any new Listeners that are created. Can be overridden, using the next property.
Where <SE name> is the name of a Server Engine and <SCM name> is the name of a Server Connection Manager. Sets the priority of the Listener thread associated with a specific SCM in a specific Server Engine. Can be set at any time prior to the creation of that Server Engine (which occurs during the creation of the first POA that uses that Server Engine.)
Sets the priority at which the ORB’s DSUser thread will run. Must be set no later than the first time that the ORB attempts to communicate with a VisiBroker Smart Agent (which is typically when a POA is created, an object is activated or a call to a _bind method is made.)
Sets the priority of all Garbage Collection threads. Can be changed at any time. The current value at the time of Threadpool creation is the value used.
Note
In earlier versions of the VisiBroker-RT product, the vbroker.agent.threadPriority property was called vbroker.dsuser.thread.priority. The name has been changed to keep it consistent with other VisiBroker Smart Agent property names. However, the old property name is still supported and you can continue to use it in existing deployments.
Limiting the internal ORB thread priority range
A range limit is set on internal ORB threads by passing the following argument to ORB_init: -ORBRTPriorityRange <min>,<max>
-ORBRTPriorityRange is given as one argument, and the two values are given together in another argument, separated by a comma, as shown in the following example.
// Prepare arguments for ORB_init
int argc = 3;
char * argv[] = { "app_name", "-ORBRTPriorityRange", "10,17" };
 
// Initialize ORB
CORBA::ORB_ptr = ORB_init(argc, argv);
The two values give the minimum RTCORBA::Priority followed by the maximum RTCORBA::Priority value that internal ORB threads are permitted to run at. If this argument is given, the VisiBroker internal ORB threads defaults to running at the maximum priority that is specified.
If the range is invalid, the ORB_init call fails and raises a CORBA system exception. If the range is invalid because one or both of the values is not a valid RTCORBA::Priority value, or because min is greater than max, then a CORBA::BAD_PARAM exception is raised. If the range is invalid because one or both of the values is outside of the range supported by the installed Priority Mapping, then a CORBA::DATA_CONVERSION exception is raised.