Object References, Memory Management, and Parameter Passing in C++

Middleware News Brief (MNB) features news and technical information about Open Source middleware technologies.

Introduction

Correct use of object references is an essential skill for CORBA developers. Object references are largely a client-side issue but there are server-side implications arising from the C++ language mapping that defines how object references are exchanged during CORBA request invocations. This is certainly not "news" but a periodic review of this topic is useful for most CORBA developers.

This Middleware News Brief article discusses the C++ language mapping for object references, addresses memory management issues related to object references, and demonstrates the correct means of exchanging object references during CORBA request invocations. The article focuses on object references, the basic pointer (_ptr) type, and rules for exchanging object references during CORBA requests. 

Object Reference Defined

An object reference is a handle to a CORBA object.

A CORBA object is a vague concept, but two aspects of CORBA objects are clear:

  1. Every CORBA object realizes a specific interface. An object's interface defines the operations and attributes it offers to its clients.
  2. Every CORBA object has a distinct identity. An object's identity informs an ORB where and how to dispatch its client's requests. 

An object reference embodies the corresponding CORBA object's identity and interface, and constitutes the primary means for invoking a CORBA object's operations. (Attributes in effect define operations, so hereafter an object's interface will be described only in terms of operations.)

An object reference can take many forms. Some forms are language independent and some are language specific.

A stringified IOR is a language-independent form of object reference.

The following is a stringified reference to a CORBA object incarnated on the author's laptop computer:

IOR:010000001900000049444c3a6f63697765622e636f6d2f48656c6c6f
3a312e3000000000010000000000000060000000010102cd050000006469
6e6f00cde12e1b00000014010f005253547fb8d63dd48809000000000001
00000001000000cd02000000000000000800000001cdcdcd004f41540100
00001400000001cdcdcd01000100000000000901010000000000

The CORBA object it refers to realizes the following interface:

  1. #include "example_exceptions.idl"
  2.  
  3. interface Hello
  4. {
  5. void say_hello();
  6. string ior_please();
  7. void take_exception() raises (ExampleExceptions::SimpleException);
  8. void shutdown();
  9. };

A corbaloc URL is another language-independent form of object reference.

The following corbaloc URL is another reference to the object denoted by the preceding stringified IOR:

corbaloc:iiop:dino:12001/Hello

Language-independent object references are essential to interoperability. They allow the exchange of object references between clients and servers deployed on heterogeneous platforms and implemented in different languages.

Language-specific forms are defined by formal language mappings to enable concrete development in the various languages supported by CORBA.

An object reference in the C++ language is a pointer to an object that proxies for the target CORBA object and has the target object's interface.

  1. A client invokes a request by dereferencing the pointer using native C++ syntax.
  2. When a client invokes a CORBA request, the proxy forwards the request to the local ORB.
  3. The local ORB uses identity information held by the object reference to locate the target object and dispatches the request accordingly.
  4. A synchronous request's reply is returned to the proxy by the local ORB and the proxy returns the reply to the client.

For each interface, the IDL compiler generates all of the following:

  • The servant base class
  • The proxy object class
  • Several helper types
  • Two pointer types: a basic pointer and a smart pointer
    • The basic pointer is analogous to a native C++ pointer; the smart pointer has the same behavior as the basic pointer but provides additional features that simplify memory management and parameter passing.

For the Hello interface presented above:

  • The servant based class is named POA_Hello
  • The proxy object class is named Hello
  • The basic pointer type is named Hello_ptr; the smart pointer type is named Hello_var

One of the generated helper classes assists with passing object references as out mode parameters. For the Hello interface, this helper type is named Hello_out.

It is common practice to refer to these types in a general sense, for example, the "pointer type," the "var type," and the "out type." These phrases refer respectively to generated _ptr, _var, and _out types.

Creating and Obtaining Object References

An object reference is created when the corresponding CORBA object is created.

CORBA objects are commonly created when servants are activated via PortableServer::POA::activate_object(). However, CORBA objects can be created without activating servants via other operations.

CORBA servers often export object references in different forms using a variety of mechanisms to provide clients with access to their CORBA objects. However, object references can be exposed to clients without the server's direct participation using forms such as corbaloc URLs.

An object reference can be introduced into a client's address space in many ways.

  • Clients frequently employ ORB operations such as CORBA::ORB::resolve_initial_references() and CORBA::ORB::string_to_object() to obtain object references.
  • Clients often use a Naming Service and obtain object references via CosNaming::NamingContext::resolve().
  • Clients may also obtain object references by invoking requests on known CORBA objects, i.e., objects already accessible in their address space that act as factories for other CORBA objects.

Regardless of the mechanism, when an object reference is introduced into a client's address space, the local ORB creates a proxy object and returns a basic pointer to that proxy.

An ORB chooses a proxy object class based on the interface type information at hand when a reference is introduced. This allows ORBs to produce object references in a type-safe manner.

Often, the available type information is sufficient only to return a CORBA::Object reference. This is the case with operations such as CORBA::ORB::resolve_initial_references(), CORBA::ORB::string_to_object(), and CosNaming::NamingContext::resolve(). When the ORB returns a CORBA::Object reference, the client is then obligated to narrow the reference to a more specific type. Factory object interfaces typically return specific object types, which allow an ORB to create a type specific reference in a safe manner.

Clients are specifically prohibited from directly creating object references with the exception of a nil reference.

Clients are permitted to copy object references, to narrow and widen object references to other related types, and to pass object references as parameters during CORBA request invocations.

Clients are also obligated to collaborate with the ORB in managing resources allocated to object references. This includes informing the ORB when object references are copied (duplicated) and destroyed (released).

Most ORBs employ reference counting as a memory management technique, but this is not required. The memory management interfaces and a client's obligations are independent of the ORB's implementation.

BASIC POINTER: THE _ptr TYPE

This section demonstrates use of the basic pointer, the _ptr type. The examples are based in part on the Hello interface shown previously. All references are initially introduced into the client's address space using CORBA::ORB::string_to_object(), but there are some examples that demonstrate factory object behavior.

CORBA::ORB::string_to_object() returns CORBA::Object_ptr types, i.e., CORBA::Object references because there isn't sufficient type information to return a more specific type, e.g.,:

CORBA::Object_ptr obj = orb->string_to_object("file://server.ior");

The caller should always verify that the object reference returned is not nil:

  1. if(CORBA::is_nil (obj))
  2. {
  3. // error: nil object reference returned
  4. }

The client now has a reference to a CORBA::Object, a basic pointer to a proxy having the CORBA::Object interface:

Figure 1. CORBA Object Reference

Figure 1. CORBA Object Reference

To gain access to something more useful than a CORBA::Object, the client must narrow the object reference:

Hello_ptr Hello_obj = Hello::_narrow(obj);

This operation returns an object reference having the Hotel interface or a nil reference. A nil reference will be returned if the target object's interface is not Hotel. Consequently, clients should always verify that the object reference returned from a _narrow() operation is not nil:

  1. if(CORBA::is_nil (Hello_obj))
  2. {
  3. // error: nil object reference returned
  4. }

The client now has a reference to a Hello object:

Hello Object Reference

Figure 2. Hello Object Reference

(For the remainder of this article, checks for nil object references are omitted for brevity.)

A consequence of the preceding two operations is that the ORB has allocated resources for two distinct proxy objects:

  1. A proxy for a CORBA::Object
  2. A proxy for a Hello object

Although both proxies are associated with the same target object, their interfaces are different and therefore they are separate proxy objects. The client has access to these objects via pointers provided by the ORB, but the ORB retains ownership, so it can manage resources in a coherent manner.

Because clients have access to resources owned by the ORB, clients must collaborate with the ORB to protect their interests in the use object references and to assist the ORB in the recovery of its resources.

To inform the ORB that an object reference is no longer in use and may be destroyed, a client calls CORBA::release():

CORBA::release(obj);

The ORB may destroy the obj reference and recover its resources upon the call to CORBA::release() or anytime thereafter. The client must assume, however, that the object reference is destroyed immediately upon the call to CORBA::release() and treat it accordingly:

Figure 3. Destroyed Object Reference

Figure 3. Destroyed Object Reference

The obj variable can be re-used to refer to other CORBA::Objects, but it must not be used to invoke requests until it has been associated with another CORBA object.

The release of obj has no effect on Hello_obj because they are distinct references so the following sequence is valid:

CORBA::Object_ptr obj = orb->string_to_object("file://server.ior");
Hello_ptr Hello_obj   = Hello::_narrow(obj);
CORBA::release(obj);
Hello_obj->say_hello();

Similarly, the release of Hello_obj has no effect on obj.

The following sequence, although nonsensical, demonstrates the point:

CORBA::Object_ptr obj = orb->string_to_object("file://server.ior");
for(int i = 0; i < 10; ++i)
{
  Hello_ptr Hello_obj = Hello::_narrow(obj);
  Hello_obj->say_hello();
  CORBA::release(Hello_obj);
}

To copy an object reference, a client calls _duplicate() using the appropriate proxy class. For example, to copy a Hello object reference:

Hello_ptr Hello_obj2 = Hello::_duplicate(Hello_obj);

Hello_obj2 is a reference to target object referred to by Hello_obj.

Figure 4. Duplicated Hello Object Reference

Figure 4. Duplicated Hello Object Reference

Other object references, including CORBA::Objects are also copied in this manner:

CORBA::Object_ptr obj2 = CORBA::Object::_duplicate(obj);
Figure 5. Duplicated CORBA Object Reference

Figure 5. Duplicated CORBA Object Reference

Conceptually, the ORB performs a deep copy when object references are duplicated. Most ORBs employ reference counting, so this operation usually amounts to incrementing a proxy object's reference count.

Similarly, a release operation usually amounts to decrementing a proxy object's reference count. A proxy object continues to exist until its reference count reaches 0, at which time the object is destroyed and associated resources recovered. However, it is important to treat different object references, e.g., Hello_obj and Hello_obj2, as distinct references each having dedicated resources managed by the ORB.

EXCHANGING OBJECT REFERENCES USING _ptr TYPES

This section discusses the exchange of object references using the _ptr type.

From the client side, object references are exchanged using just the _ptr types. Server implementations also employ _ptr types, except for object references passed as out mode parameters where the _out type is used.

The examples that follow are based on the Hello interface seen previously and the following:

  1. #include "Hello.idl"
  2.  
  3. interface Hello_Utils
  4. {
  5. Object object_from_ior(in string ior);
  6. Hello hello_from_object(in Object obj);
  7. void save_hello(in short which, in Hello hello);
  8. void get_hello(in short which, out Hello hello);
  9. void change_to(in short which, inout Hello hello);
  10. };

This interface was designed to demonstrate passing and returning object references to and from CORBA requests. 

  • Hello_Utils::object_from_ior() accepts a string form of object reference, a stringified IOR, a corbloc URL, etc., and returns a CORBA::Object reference. 
  • Hello_Utils::hello_from_object() accepts a CORBA::Object reference and returns a Hello object reference. 
  • Hello_Utils::save_hello() accepts a short integer, which serves as a key, and a Hello object reference. The object reference is retained for subsequent retrieval using the given key value. 
  • Hello_Utils::get_hello() returns, via an out parameter, the Hello object reference associated with a given key value. 
  • Hello_Utils::change_to() performs a similar function using an inout parameter instead of an out parameter.

In the subsequent examples, Utils_obj is a Hello_Utils object reference.

The first example, Hello_Utils::object_from_ior() demonstrates returning an object reference from a CORBA request. The operation definition:

Object object_from_ior(in string ior);

maps to the following proxy class method signature:

CORBA::Object_ptr Hello_Utils::object_from_ior(const char * ior);

An IOR is passed as a C string and the operation returns the corresponding CORBA::Object reference:

CORBA::Object_ptr obj = Utils_obj->object_from_ior("corbaloc:iiop:dino:12001/Hello");

This operation is implemented simply as (by convention, _i is appended to Hello_Utils to denote the Hello_Util's servant implementation.):

CORBA::Object_ptr 
Hello_Utils_i::object_from_ior(const char *ior)
   throw ( CORBA::SystemException )
{
  CORBA::Object_ptr obj = orb_->string_to_object(ior);
  return obj;
}

This is not a particularly interesting implementation. It serves only to demonstrate the following:

  • Object references are returned as _ptr types.
  • Object references returned from CORBA requests are released upon the return.
  • Clients assume responsibility for object references returned by CORBA requests.

The servant implementation does not explicitly release the reference; this occurs automatically upon the operation's return. A client that receives an object reference via a CORBA request must release the reference when it is no longer required.

The client's behavior is demonstrated by the following:

CORBA::Object_ptr obj = orb->string_to_object ("file://utils.ior");
Hello_Utils_ptr Utils_obj = Hello_Utils::_narrow (obj);
CORBA::release(obj);
obj = Utils_obj->object_from_ior("corbaloc:iiop:dino:12001/Hello");
Hello_ptr Hello_obj = Hello::_narrow(obj);
CORBA::release(obj);
//
// do useful stuff with Hello_obj;
//
CORBA::release(Hello_obj);

Each object reference, obj and Hello_obj, is released prior to its re-use or when it is no longer needed.

Object references passed to CORBA requests are passed as _ptr types.

The operation:

Hello hello_from_object(in Object obj);

maps to the following Hello_Utils proxy method signature:

Hello_ptr Hello_Utils::hello_from_object(CORBA::Object_ptr obj);

A server can use an object reference passed as an in parameter for the duration of the request. However, the server isn't responsible for the resources allocated to the object reference and therefore shouldn't release it.

A simple implementation of this method is:

Hello_ptr 
Hello_Utils_i::hello_from_object(CORBA::Object_ptr obj)
   throw (CORBA::SystemException)
{
  Hello_ptr hello_obj = Hello::_narrow(obj);
  return hello_obj;
}

The server has use of the object reference obj but doesn't have responsibility for it and so doesn't release it. Moreover, the reference is automatically released upon the operation's return, so the server must copy the reference to retain it.

Retaining an object reference beyond the lifetime of a single CORBA request is demonstrated by the following operation:

void save_hello(in short which, in Hello hello);

A simple implementation of this operation is:

void 
Hello_Utils_i::save_hello(CORBA::Short which, Hello_ptr hello)
   throw (CORBA::SystemException)
{
  if(1 <= which && which <= 2)
  {
    if(!(CORBA::is_nil(hello_objs_[which - 1])))
    {
      CORBA::release(hello_objs_[which - 1]);
    }
    hello_objs_[which - 1] = Hello::_duplicate(hello);
  }
}

The servant's implementation retains references to Hello objects in a small array. The value which, serving as a key, is used as the array index.

This operation:

  1. Receives a Hello object reference along with its key
  2. Releases the reference previously associated with the given key, if any
  3. Then duplicates the new reference using the appropriate proxy class method

Using the operations seen so far, a client can first obtain and then preserve for later use a Hello object reference as follows:

CORBA::Object_ptr obj = orb->string_to_object ("file://utils.ior");
Hello_Utils_ptr Utils_obj = Hello_Utils::_narrow (obj);
CORBA::release(obj);
obj = Utils_obj->object_from_ior("corbaloc:iiop:dino:12001/Hello");
Hello_obj = Utils_obj->hello_from_object(obj);
CORBA::release(obj);
//
// do stuff with Hello_obj
//
Utils_obj->save_hello(1, Hello_obj);
CORBA::release(Hello_obj);

Note that the client's release of the Hello reference has no effect on the Hello_Util server's reference to the same target object. These references are distinct and independent references. It is likely that they exist in separate address spaces, but even if that were not the case, the references are independent of one another.

The next operation demonstrates passing an object reference to a CORBA request as an out mode parameter. Conceptually, the client passes to the server a reference to a _ptr and the server populates the reference. The client can use the reference after the request returns if no exceptions occur. If an exception occurs, the condition of the object reference is undefined, and the client should not attempt to access it.

By definition, a reference passed as an out mode parameter should be nil when the operation is invoked. The client continues to employ _ptr types as with the other modes. The server, however, employs the _out helper type generated by the IDL compiler in its implementation.

The following operation:

void get_hello(in short which, out Hello hello);

maps to the following proxy class method:

void Hello_Utils::get_hello(CORBA::Short which, Hello_out hello)
throw (CORBA::SystemException)

A client invokes this operation as follows:

Hello_ptr Hello_obj = Hello::_nil();
Utils_obj->get_hello(1, Hello_obj);
Hello_obj->say_hello();

This example illustrates the point that object references exchanged via out parameters are nil upon invocation and populated during the operation. This is enforced by the _out type, which sets the reference to _nil in its constructor.

A simple implementation of this operation consistent with the preceding void Hello_Utils_i::save_hello() is:

void 
Hello_Utils_i::get_hello(CORBA::Short which, Hello_out hello)
    throw (CORBA::SystemException)
{
  if(1 <= which && which <= 2)
  {
    hello = Hello::_duplicate(hello_objs_[which - 1]);
  }
}

The server populates the hello object reference by duplicating a reference previously retained by the save_hello() operation. Duplicating the reference is essential because responsibility for references exchanged via an out parameter passes from the server to the client upon the operation's return.

The reference passed by the server is released from the server's address space so it can be transferred to the client's address space. If the server's source reference was not duplicated, the server would be left with a dangling reference.

Of course, releasing the reference is not necessary if the server doesn't wish to retain a copy for subsequent use. However, it is necessary in this example because the server retains copies of the references obtained via save_hello.

The role of the _out class, in this example a Hello_out, is to simplify handling of references exchanged via an out parameter and ensure consistency with the parameter passing rules.

An object reference passed to a CORBA request as inout mode parameter is passed as a reference to a _ptr.

The operation:

void change_to(in short which, inout Hello hello);

maps to the following proxy class method:

void Hello_Utils::change_to(CORBA::Short which, Hello_ptr & hello)
throw (CORBA::SystemException)

The server obtains responsibility for an object reference passed as inout mode parameter. It has access to the reference upon the invocation and must release the incoming reference if it is modified, which is the typical case.

Following is a simple implementation that demonstrates the rule:

void 
Hello_Utils_i::change_to(CORBA::Short which, Hello_ptr & hello)
    throw (CORBA::SystemException)
{
  if(1 <= which && which <= 2)
  {
    if(!CORBA::is_nil(hello_objs_[which - 1]))
    {
      CORBA::release(hello);
      hello = Hello::_duplicate(hello_objs_[which - 1]);
    }
  }
}

The important difference between this operation's implementation and the preceding example is that the server must release the incoming reference before assigning the new value. As with the preceding example, the reference assigned to the parameter hello is first duplicated in accordance with memory management policy.

The client passes a valid reference to the request as follows:

Utils_obj->get_hello(2, Hello_obj);
Hello_obj->say_hello();
//
// Change to the first reference.
//
Utils_obj->change_to(1, Hello_obj);
Hello_obj->say_hello();

The client does not release the Hello_obj reference prior to invoking Hello_Utils::change_to() because this is the server's responsibility.

Summary

This article described object references in general and the C++ language mapping for object references. It also discussed a client's responsibilities regarding memory management related to object references and use of the generated _ptr type to exchange object references between clients and servers during CORBA requests.