Reference counts on CORBA objects support local memory management of space for objects in servers, and for proxies in clients. If the memory management rules are obeyed, the reference count management will ensure that the proxy exists as long as the client retains any reference to it.
Because the aim is to support local memory management, reference counts are maintained separately on a real object and on the (possibly many) proxies for it in different client address spaces. A call to _duplicate( )
in a server or client only affects the real object's reference count. Similarly, if a client calls _duplicate( )
, the only change will be to the reference count of the proxy in its address space.
There are two reasons for locally maintaining reference counts. First, you do not want a rogue client to affect the reference counts of a server's objects, setting them to artificially high values that would prevent or delay subsequent deletion of these objects. Additionally, you do not want a rogue client to call CORBA::release( )
often enough to delete some of the server's objects. Servers typically need to retain full control over when an object is deleted. Second, local management is less expensive than maintaining global reference counts, typically requiring a remote call for each duplication and release in any client.
Maintaining reference counts using _duplicate( )
and CORBA::release( )
is an extra burden that you must manage. The IDL compiler generates _var
types that handle reference counts automatically. So, for interface StockPortfolio
, the IDL compiler also produces a helper class called StockPortfolio_var
. An instance of StockPortfolio_var
automatically handles the reference count of the object it references. Therefore, the following code correctly maintains reference counts:
{ StockPortfolio_var sp1 StockPortfolio_var sp2; sp2= sp1 // _var types get automatic reference count increment. ... }
The reference count of the target object is decremented automatically when sp1
goes out of scope.
Each _var
variable owns the object that it points to (it shares ownership with other local references to the same object). This means that the destructor of a _var
variable decrements the reference count of the object it points to.
Note that StockPortfolio_var
defines operator->( )
to allow the operations and attributes of the target object to be used. Naturally, you should not treat these as real pointers (and perform address arithmetic and other low-level functions using them). Use of _var
types is optional, as is the case for CORBA::String_var
.
Note:
A _var
variable expects to own its target object. Its destructor expects to decrement the reference count of the object. Be careful not to assign a pointer to a _var
variable if it is not safe for that variable to own that target object.
When an object reference is passed as a parameter to an operation or appears as the return type, the corresponding C++ formal parameter types are as shown in Table 1.
IDL type | in | out | inout | return |
---|---|---|---|---|
Interface S |
S_ptr |
S_Ptr& |
S_Ptr& |
S_Ptr |
You must also be careful when passing or returning a null object reference. For example, if operation StockPortfolio::sellStock( )
wants to return a null object reference, it should execute the following code:
... return StockPortfolio::_nil( );
It is not CORBA compliant to pass or return a zero value. Equating a zero pointer with a null reference is too specific to C++ to be accepted generally in CORBA (which does not even have a notion of pointer).
Because an object reference maps to a pointer type (either to a _ptr
or a _var
type, both of which behave like pointers), memory management rules must be defined to prevent memory from being corrupted on the client or server side. Memory corruption means that an object or proxy has the wrong reference count, which will cause it either to stay in existence when there are no references to it or it will be deleted prematurely, causing subsequent use of an object reference to fail. As is the case for strings, CORBA defines the memory management rules for object references.
A C++ function that implements an IDL operation can be called in two ways:
The aim is to keep the reference count of each object consistent at all times.
Table 2 summarizes the memory management rules for object references.
Parameter | Rules |
---|---|
in parameters |
The caller retains ownership of the object reference passed in. The target object is given temporary access to the object pointed to by the object reference, but it is not given ownership. |
out parameters, return values, attribute values |
The target object gives up ownership of the returned object reference, and the caller is given ownership (hence it should call CORBA::release( ) on that object reference). Because the target object gives up ownership, typically it will call the relevant _duplicate( ) static function on the object reference that it returns. That is, it makes a "copy" of the object references and passes back the copy.
|
inout parameters |
The ownership of the object reference is temporarily passed to the target object. After the call, the caller retains ownership of the object reference (but it could point to a different object or proxy). If the target object need not change the object reference, it should not call CORBA::release( ) on it. If it needs to change the object reference, it should call CORBA::release( ) on the old value, and normally, it should call the relevant _duplicate static function on the new value. |
The client-side rules can be summarized as follows:
If a client receives an object reference from a call, it must release that object reference when it no longer needs it.
Most of the server-side rules can be summarized as follows:
A server gives up ownership of a reference it passes to a client. The reference count is decremented by 1, and it normally calls the relevant _duplicate( )
function before returning the reference.
This section describes memory management of in
, out
, and inout
parameters.
If a target object receives an object reference as an in
parameter, it must not call CORBA::release( )
on it. Instead, the caller retains ownership and will release the object reference when it wants. If the client and target object are in the same address space, the client will pass an object reference that it retains ownership of, and it will call CORBA::release( )
on it when it wants.
If the client and target object are in different address spaces, the ORB on the client side will pass a copy of the object reference to the ORB on the server side. The object's reference count is incremented automatically when the object reference reaches the server (if the object was not previously known to the server, a proxy for the object is created, and its reference count is set to 1), and the copy of the object reference is made available to the target object for the duration of the call. At the end of the call, the ORB on the server side must call CORBA::release( )
on this reference, not the target object.
Consider the following interface:
interface S { void sell(in StockPortfolio a);
A C++ client can use this interface as follows:
S_var s = . . . ; StockPortfolio_ptr a1 s->sell(a1); ... CORBA::release(a1) // release when required
If a1
is defined to be of type StockPortfolio_var
, CORBA::release( )
is called automatically when a1
goes out of scope, and the risk of a memory leak due to programmer error is reduced.
The implementation of operation S::sell( )
can be coded as follows:
void S_i::sell(StockPortfolio_ptr s1) throw (CORBA::SystemException) { ... // use reference s1: cout << s1->name( ); ... // Do not call CORBA::release( ); this is done by the caller. }
Note that the function does not call CORBA::release( )
on the parameter. The caller will do this.
If the implementation of the function needs to retain the object reference passed to it, it must duplicate the object reference (by using _duplicate( )
and retaining the returned pointer).
Because the called function is not given ownership of the object reference passed as an in
parameter, an error occurs if the implementation of sell( )
assigns the parameter s1
to a StockPortfolio_var
automatic variable (without calling _duplicate( )
on it). At the end of the call, the automatic variable is deleted, thereby calling CORBA::release( )
on the object or on the proxy it references.
However, there would be a second attempt to call CORBA::release( )
after the call completes (either in the NonStop DOM ORB if the client and target object are separated or in the client if it is in the same address space as the target object). No error occurs if the assignment to the StockPortfolio_var
automatic variable is done using _duplicate( )
.
If a client receives an object reference as an out
parameter or return value, it is the client's responsibility to call CORBA::release( )
when it no longer needs that object reference. The client is given ownership of the object reference.
If the client and target object are in the same address space, the target object must return an object reference that it expects the client to call CORBA::release( )
on. This normally means that the target object should call _duplicate( )
on the object reference before returning it to the caller. The same rule holds if the client and target objects are in different address spaces.
Consider the following interface:
interface S { StockPortfolio getPortfolio(out StockPortfolio a); }
A C++ client of this interface can use it as follows:
S_var s = . . ; StockPortfolio_ptr s1, s2; s2= s->getPortfolio(s1); ... CORBA::release(s1); // when finished with s1 CORBA::release(s2); // when finished with s2
If the variable s1
or s2
originally pointed to an object, memory would be leaked at the time of the call to operation getPortfolio( )
.
If s1
and s2
is defined to be of type StockPortfolio_var
, CORBA::release( )
is called automatically on the returned object references when s1
and s2
are destroyed, which reduces the risk of memory leaks due to programming errors. Further, if s1
or s2
previously referenced an object or proxy, CORBA::release( )
is called automatically when these variables are changed. This happens because of the call to getPortfolio( )
(shared ownership of the previous target objects or proxies would be given up).
The implementation of operation S::getPortfolio( )
can be coded as follows,
assuming that someStockPortfolio1
and someFront0ffice2
are attributes or global variables of type StockPortfolio_ptr
or StockPortfolio_var
:
StockPortfolio_ptr S_i::getPortfolio(StockPortfolio_ptr& s1) throw (CORBA::SystemException) { s1 = StockPortfolio_ptr::_duplicate(someStockPortfolio1); return StockPortfolio_ptr::_duplicate(someStockPortfolio2); }
Note: The caller should call CORBA::release( )
on the out
and return references.
Because CORBA::release( )
is called on the two object references by the caller of the function, the method's implementer must be careful not to return (as an out
parameter or return value) an object reference that it wants to keep without calling _duplicate( )
, as shown above. It is very important to note the need for these calls to _duplicate( )
.
While it is true to say that CORBA::release( )
will be called on the two object references, more detail can be added to this statement. If the client and target object are in different address spaces, the server-side ORB will send the two object references to the client side and then call CORBA::release( )
on the two object references returned by the getPortfolio( )
function. The calling code on the client is given references to local objects or proxies, and it can call CORBA::release( )
on the two object references when necessary.
If a returned reference is to an object or existing proxy in the client, its reference count is incremented automatically by the client-side ORB. If a returned reference is to an object previously unknown to the client, a proxy is created with a reference count of 1. If the client and target object are in the same address space, object references are passed directly to the client, and the client can call CORBA::release( )
on these when necessary, as before.
If a target object receives an inout
object reference parameter, it can change that parameter to refer to another object. If it does so, it claims (shared) ownership of the object or proxy that the parameter previously pointed to, and the caller is given (shared) ownership of the object or proxy that the parameter is changed to point to.
In other words, the target object is responsible for calling CORBA::release( )
on the object that was previously pointed to, and the target object must ensure that it is safe for the caller to call CORBA::release( )
on the object that the inout
parameter now points to. As explained before, this normally means that the target object calls _duplicate( )
on the object reference before returning it.
On the client side, if the object reference is unchanged, the client retains ownership without any change in reference count of the object or proxy pointed to. If the object reference is changed, the reference count of the object or proxy previously pointed to is decremented, and that of the object now pointed to is incremented.
Consider the following interface:
interface StockPortfolio { void update(inout Stock s1); }
A C++ client of this interface passes an object reference that may be updated by the target object:
StockPortfolio_var sp; Stock_ptr s1; sp->update (s1); // s1 may be different after this call ... CORBA::release(s1); // be sure to release when done
If the object reference is changed, the reference count of the previously referenced object or proxy is decremented because the object reference is changed. The reference count of the new object pointed to is incremented. If the value of the actual parameter, s1
, is unchanged by the call to update, the value of the reference count of the object it points to is also unchanged.
If the variable s1
is defined as a Stock_var
variable (rather than Stock_ptr)
, it can be updated by the call in the same way as shown in the previous code fragment. If it is updated, the reference count of the object previously pointed to is decremented, and that of the new object pointed to is incremented.
The implementation of operation StockPortfolio:: update( )
can be coded as follows:
void StockPortfolio_i::update(Stock_ptr& st1) throw (CORBA::SystemException){ ... // use st1 ... cout << st1->symbol( ); CORBA::release(st1); // The release decrements the // reference count of the current object: st1 = Stock_Ptr::_duplicate(someOtherStock); // assign a new stock object reference }
Instead of explicitly calling CORBA::release( )
on the parameter, the implementation of update( )
can assign it to a Stock_var
variable. For example:
void StockPortfolio_i::update(Stock_ptr& st1) throw (CORBA::SystemException) { // use st1 ... cout << st1->symbol; Stock_var newStock = st1; // then assign it to reference a new object: st1 = Stock_Ptr::_duplicate(someOtherStock); }
Note:
It is not correct to make this assignment to the automatic variable, newStock
, if the
inout
parameter is not changed by a particular call to the update( )
.