Managing Data Instances (Working with Keyed Data Types)

This section applies only to data types that use keys, see DDS Samples, Instances, and Keys. Using the following operations for non-keyed types has no effect.

Topics come in two flavors: those whose associated data type has specified some fields as defining the ‘key,’ and those whose associated data type has not. An example of a data-type that specifies key fields is shown in Figure: Data Type with a Key.

Figure: Data Type with a Key

typedef struct Flight {
    long   flightId; //@key
    string departureAirport;
    string arrivalAirport;
    Time_t departureTime;
    Time_t estimatedArrivalTime;
    Location_t currentPosition;
};

If the data type has some fields that act as a ‘key,’ the Topic essentially defines a collection of data-instances whose values can be independently maintained. In Figure: Data Type with a Key, the flightId is the ‘key’. Different flights will have different values for the key. Each flight is an instance of the Topic. Each write() will update the information about a single flight. DataReaders can be informed when new flights appear or old ones disappear.

Since the key fields are contained within the data structure, Connext DDS could examine the key fields each time it needs to determine which data-instance is being modified. However, for performance and semantic reasons, it is better for your application to declare all the data-instances it intends to modify—prior to actually writing any DDS samples. This is known as registration, described below in Registering and Unregistering Instances.

The register_instance() operation provides a handle to the instance (of type DDS_InstanceHandle_t) that can be used later to refer to the instance.

Registering and Unregistering Instances

If your data type has a key, you may improve performance by registering an instance (data associated with a particular value of the key) before you write data for the instance. You can do this for any number of instances up the maximum number of instances configured in the DataWriter’s RESOURCE_LIMITS QosPolicy. Instance registration is completely optional.

Registration tells Connext DDS that you are about to modify (write or dispose of) a specific instance. This allows Connext DDS to pre-configure itself to process that particular instance, which can improve performance.

If you write without registering, you can pass the NIL instance handle as part of the write() call.

If you register the instance first, Connext DDS can look up the instance beforehand and return a handle to that instance. Then when you pass this handle to the write() operation, Connext DDS no longer needs to analyze the data to check what instance it is for. Instead, it can directly update the instance pointed to by the instance handle.

In summary, by registering an instance, all subsequent write() calls to that instance become more efficient. If you only plan to write once to a particular instance, registration does not ‘buy’ you much in performance, but in general, it is good practice.

To register an instance, use the DataWriter’s register_instance() operation. For best performance, it should be invoked prior to calling any operation that modifies the instance, such as write(), write_w_timestamp(), dispose(), or dispose_w_timestamp().

When you are done using that instance, you can unregister it. To unregister an instance, use the DataWriter’s unregister_instance() operation. Unregistering tells Connext DDS that the DataWriter does not intend to modify that data-instance anymore, allowing Connext DDS to recover any resources it allocated for the instance. It does not delete the instance; that is done with the dispose_instance() operation, see Disposing of Data. autodispose_unregistered_instances in the WRITER_DATA_LIFECYCLE QoS Policy controls whether instances are automatically disposed of when they are unregistered.

unregister_instance() should only be used on instances that have been previously registered. The use of these operations is illustrated in Figure: Registering an Instance.

Figure: Registering an Instance

Flight myFlight;
// writer is a previously-created FlightDataWriter
myFlight.flightId = 265;
DDS_InstanceHandle_t fl265Handle =
writer->register_instance(myFlight);
...
// Each time we update the flight, we can pass the handle
myFlight.departureAirport     = “SJC”;
myFlight.arrivalAirport       = “LAX”;
myFlight.departureTime        = {120000, 0};
myFlight.estimatedArrivalTime = {130200, 0};
myFlight.currentPosition      = { {37, 20}, {121, 53} };
if (writer->write(myFlight, fl265Handle) != DDS_RETCODE_OK) {
// ... handle error
}
// After updating the flight, it can be unregistered
if (writer->unregister_instance(myFlight, fl265Handle) !=
DDS_RETCODE_OK) {
// ... handle error
}

Once an instance has been unregistered, and assuming that no other DataWriters are writing values for the instance, the matched DataReaders will eventually get an indication that the instance no longer has any DataWriters. This is communicated to the DataReaders by means of the DDS_SampleInfo that accompanies each DDS data-sample (see The SampleInfo Structure). Once there are no DataWriters for the instance, the DataReader will see the value of DDS_InstanceStateKind for that instance to be NOT_ALIVE_NO_WRITERS.

The unregister_instance() operation may affect the ownership of the data instance (see the OWNERSHIP QosPolicy). If the DataWriter was the exclusive owner of the instance, then calling unregister_instance() relinquishes that ownership, and another DataWriter can become the exclusive owner of the instance.

The unregister_instance() operation indicates only that a particular DataWriter no longer has anything to say about the instance.

Note that this is different than the dispose() operation discussed in the next section, which informs DataReaders that the data-instance is no longer “alive.” The state of an instance is stored in the DDS_SampleInfo structure that accompanies each DDS sample of data that is received by a DataReader. User code can access the instance state to see if an instance is “alive”—meaning there is at least one DataWriter that is publishing DDS samples for the instance, see Instance States.

See also:

Disposing of Data

The dispose() operation informs DataReaders that, as far as the DataWriter knows, the data-instance no longer exists and can be considered “not alive.” When the dispose() operation is called, the instance state stored in the DDS_SampleInfo structure, accessed through DataReaders, will change to NOT_ALIVE_DISPOSED for that particular instance.

See Unregistering vs. Disposing: .

By default, instances are automatically disposed when they are unregistered. This behavior is controlled by the autodispose_unregistered_instances field in the WRITER_DATA_LIFECYCLE QoS Policy.

For example, in a flight tracking system, when a flight lands, a DataWriter may dispose of the data-instance corresponding to the flight. In that case, all DataReaders who are monitoring the flight will see the instance state change to NOT_ALIVE_DISPOSED, indicating that the flight has landed.

If a particular instance is never disposed of, its instance state will eventually change from ALIVE to NOT_ALIVE_NO_WRITERS once all the DataWriters that were writing that instance unregister the instance or lose their liveliness. For more information on DataWriter liveliness, see the LIVELINESS QosPolicy.

See also:

Looking Up an Instance Handle

Some operations, such as write(), require an instance_handle parameter. If you need to get such as handle, you can call the FooDataWriter’s lookup_instance() operation, which takes an instance as a parameter and returns a handle to that instance. This is useful for keyed data types.

DDS_InstanceHandle_t lookup_instance (const Foo & key_holder)

The instance must have already been registered (see Registering and Unregistering Instances). If the instance is not registered, this operation returns DDS_HANDLE_NIL.

Getting the Key Value for an Instance

Once you have an instance handle (using register_instance() or lookup_instance()), you can use the DataWriter’s get_key_value() operation to retrieve the value of the key of the corresponding instance. The key fields of the data structure passed into get_key_value() will be filled out with the original values used to generate the instance handle. The key fields are defined when the data type is defined, see DDS Samples, Instances, and Keys for more information.

Following our example in Figure: Registering an Instance, register_instance() returns a DDS_InstanceHandle_t (fl265Handle) that can be used in the call to the FlightDataWriter’s get_key_value() operation. The value of the key is returned in a structure of type Flight with the flightId field filled in with the integer 265.

See also: Propagating Serialized Keys with Disposed-Instance Notifications.

© 2018 RTI