omniORBpy 3 supports objects by value, declared with the
valuetype keyword in IDL. This chapter outlines some issues to
do with using valuetypes in omniORB. You are assumed to have read the
relevant parts of the CORBA specification, specifically chapters 4 and
5 of the CORBA 2.6 specification, and section 1.3.10 of the Python
language mapping, version 1.2.
10.1 Features
omniORB supports the complete objects by value specification, with the
exception of custom valuetypes. All other features including value
boxes, value sharing semantics, abstract valuetypes, and abstract
interfaces are supported.
10.2 Value sharing and local calls
When valuetypes are passed as parameters in CORBA calls (i.e. calls
on CORBA objects declared with interface in IDL), the structure
of related values is maintained. Consider, for example, the following
IDL definitions (which are from the example code in
src/examples/valuetype/simple:
module ValueTest {
valuetype One {
public string s;
public long l;
};
interface Test {
One op1(in One a, in One b);
};
};
If the client to the Test object passes the same value in both
parameters, just one value is transmitted, and the object
implementation receives a copy of the single value, with references to
it in both parameters.
In the case that the object is remote from the client, there is
obviously a copying step involved. In the case that the object is in
the same address space as the client, the same copying semantics must
be maintained so that the object implementation can modify the values
it receives without the client seeing the modifications. To support
that, omniORB must copy the entire parameter list in one operation, in
case there is sharing between different parameters. Such copying is a
rather more time-consuming process than the parameter-by-parameter
copy that takes place in calls not involving valuetypes.
To avoid the overhead of copying parameters in this way, applications
can choose to relax the semantics of value copying in local calls, so
values are not copied at all, but are passed by reference. In that
case, the client to a call will see any modifications to the
values it passes as parameters (and similarly, the object
implementation will see any changes the client makes to returned
values). To choose this option, set the copyValuesInLocalCalls
configuration parameter to zero.
10.3 Value factories
As specified in section 1.3.10 of the Python language mapping (version
1.2), factories are automatically registered for values with no
operations. This means that in common usage where values are just used
to hold state, the application code does not need to implement and
register factories. The application may still register different
factories if it requires.
If the IDL definitions specify operations on values, the application
is supposed to provide implementations of the operations, meaning that
it must register suitable factories. If the application chooses to
ignore the operations and just manipulate the data inside the values,
omniidl can be asked to register factories for all values, not
just ones with no operations, using the -Wbfactories option.
The Python language mapping says a value factory should be “a class
instance with a __call__ method taking no arguments”.
omniORBpy is less restrictive than that, and permits the use of
any callable object, in particular the value implementation
class itself.
10.4 Standard value boxes
The standard CORBA.StringValue and CORBA.WStringValue
value boxes are available to application code. To make the definitions
available in IDL, #include the standard orb.idl.
10.5 Values inside Anys
Valuetypes inserted into Anys cause a number of interesting issues.
Even when inside Anys, values are required to support complete sharing
semantics. Take this IDL for example:
module ValueTest {
valuetype One {
public string s;
public long l;
};
interface AnyTest {
void op1(in One v, in Any a);
};
};
Now, suppose the client behaves as follows:
v = One_impl("hello", 123)
a = CORBA.Any(ValueTest._tc_One, v)
obj.op1(v, a)
then on the server side:
class AnyTest_impl:
...
def op1(self, v, a):
v2 = a.value()
assert v2 == v
This is all very well in this kind of simple situation, but problems
can arise if truncatable valuetypes are used. Imagine this derived
value:
module ValueTest {
valuetype Two : truncatable One {
public double d;
};
};
Now, suppose that the client shown above sends an instance of
valuetype Two in both parameters, and suppose that the server
has not seen the definition of valuetype Two. In this
situation, as the first parameter is unmarshalled, it will be
truncated to valuetype One, as required. Now, when the Any is
unmarshalled, it refers to the same value, which has been truncated.
So, even though the TypeCode in the Any indicates that the value has
type Two, the stored value actually has type One. If the
receiver of the Any tries to pass it on, transmission will fail
because the Any's value does not match its TypeCode.
In the opposite situation, where an Any parameter comes before a
valuetype parameter, a different problem occurs. In that case, as the
Any is unmarshalled, there is no type information available for
valuetype Two, so omniORBpy constructs a suitable type based on
the transmitted TypeCode. Because omniORBpy is unable to know how (and
indeed if) the application has implemented valuetype One, the
generated class for valuetype Two is not derived from the
application's One class. When the second parameter is
unmarshalled, it is given as an indirection to the
previously-marshalled value inside the Any. The parameter is therefore
set to the constructed Two type, rather than being truncated to
an instance of the application's registered One type.
Because of these issues, it is best to avoid defining interfaces that
mix valuetypes and Anys in a single operation, and certainly to avoid
trying to share plain values with values inside Anys.