Modern C++ API: why doesn't my DomainParticipant (or other entity) get deleted?

In the Connext DDS modern C++ API, many entities are "reference types" with an automatic lifecycle management based on a shared count (see API documentation). Most times you don't need to worry about deleting entities as long as their scope is well defined.

However, some times you can forget to account for a reference that prevents the deletion of an entity, such as a DomainParticipant. This solution explores a few methods to track down the references that prevent an entity from being deleted.

For example, at the end of your application you may see this exception ( PreconditionNotMetError) if you finalize the DomainParticipantFactory:

DDS_DomainParticipantFactory_deleteI:!delete factory instance: outstanding participant(s)

To debug that, you can first check how many DomainParticipants are still alive (see find_participants()) and how many references point to them. In the following example we also use the EntityName policy to identify each DomainParticipant. If you create your entities with an EntityName, it is easier to identify them later.

std::vector<dds::domain::DomainParticipant> participants;
rti::domain::find_participants(std::back_inserter(participants));

for (auto participant : participants) {
    auto participant_name = participant.qos().policy<rti::core::policy::EntityName>().name();
    std::cout << (participant_name.is_set() ? participant_name.get() : "a participant")
              << " has " << participant.delegate().use_count()
              << " references"
              << std::endl;   
} 

 The example output looks like this:

MyParticipant1 has 3 references
MyParticipant2 has 2 references

The use_count() API accounts for references you may be holding directly (including the one you just looked up) and references from other entities created from that DomainParticipant, such as Topics, Publishers, Subscribers, or Flowcontrollers. Note that the above code will always print at least 2 references: at least one that already existed (otherwise the entity would have been destroyed) and the reference in the participants vector.

Once you review your code and verify that you are not directly keeping references, you can check if there are Topics, Publishers, Subscribers, DataReaders, DataWriters, FlowControllers, etc. that directly or indirectly retain the DomainParticipant. The following table indicates what can retain each entity (that is, hold a reference that prevents it from being destroyed):

EntityWhat can retain it
DomainParticipantTopic, Publisher, Subscriber, FlowController
PublisherDataWriter
SubscriberDataReader
TopicDataWriter, DataReader
DataWriter 
DataReaderLoanedSamples (from calling read/take)

(Note: before Connext DDS 6.1.0 listeners also retained their entities.)

To figure out what entities are still alive, several other "find" functions allow you to navigate to them from a DomainParticipant. See the "related functions" section in the API Reference HTML documentation for each entity. For example, you can use find_subscribers, then find_datareaders.

There is a different option altogether. You can enable RTI monitoring in the application and use RTI Admin Console to visualize the DomainParticipants, DataReaders, and DataWriters that are still running. If you set their EntityName policy, you will see that too.

Programming Language:
Keywords: