Fast-DDS-python icon indicating copy to clipboard operation
Fast-DDS-python copied to clipboard

Apparent memory leak in Discovery Server Listener

Open KeaneWong opened this issue 10 months ago • 0 comments

Hello, I am setting up a Discovery Server to connect a pair of clients (both local, in the same program) sending Request-Reply data to one another for a proof of concept. While setting up the server and binding a listener, I encountered the following message:

swig/python detected a memory leak of type 'eprosima::fastdds::rtps::ParticipantDiscoveryStatus *', no destructor found. swig/python detected a memory leak of type 'eprosima::fastdds::rtps::ReaderDiscoveryStatus *', no destructor found.

The same occurs if the participant was a DataWriter.

It kinda looks like when a participant is discovered, the callback on_participant_discovery is are called with Enums of type ParticipantDiscoverStatus, which isn't destroyed correctly. Is it possible the ParticipantDiscoveryStatus type might not be recognized by Swig and so no destructor is created for it?

Or if I'm setting up the server incorrectly, would I be able to get some guidance on the better way to do it? I have been attempting to set up based off the C++ examples in the documentation Any help would be greatly appreciated.

The minimal reproducing example is below

#!/usr/bin/env python3

import fastdds
from fastdds import (
    DiscoveryProtocol_SERVER,
    DiscoveryProtocol_CLIENT,

)

# Path to the python binding files
from FastDDS_API.hwbuild.HelloWorld import (
    HelloWorld,
    HelloWorldPubSubType
)


class ReaderListener(fastdds.DataReaderListener):
    def __init__(self):
        super().__init__()

    def on_data_available(self, reader):
        info = fastdds.SampleInfo()
        data = HelloWorld()
        reader.take_next_sample(data, info)
        message = data.message()
        print("Message was", message)

    def on_subscription_matched(self, datareader, info):
        if 0 < info.current_count_change:
            print(
                "Subscriber matched publisher {}".format(
                    info.last_publication_handle
                )
            )
        else:
            print(
                "Subscriber unmatched publisher {}".format(
                    info.last_publication_handle
                )
            )


class Reader:
    def __init__(self, domain):
        # Initialize factory qos
        factory = fastdds.DomainParticipantFactory.get_instance()
        self.participant_qos = fastdds.DomainParticipantQos()
        (self.participant_qos.wire_protocol()
         .builtin.discovery_config).discoveryProtocol = DiscoveryProtocol_CLIENT

        # Locator configuration
        server_locator = fastdds.Locator_t()
        server_locator.kind = fastdds.LOCATOR_KIND_UDPv6
        server_locator.port = 5000
        server_locator.address = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)
        (self.participant_qos.wire_protocol().builtin.discovery_config
         .m_DiscoveryServers.push_back(server_locator))

        factory.get_default_participant_qos(self.participant_qos)
        self.participant = factory.create_participant(
            domain, self.participant_qos
        )

        self.topic_data_type = HelloWorldPubSubType()
        self.topic_data_type.set_name("HelloWorld")
        self.type_support = fastdds.TypeSupport(self.topic_data_type)
        self.participant.register_type(self.type_support)

        self.topic_qos = fastdds.TopicQos()
        self.participant.get_default_topic_qos(self.topic_qos)
        self.topic = self.participant.create_topic(
            "HelloWorldTopic",
            self.topic_data_type.get_name(),
            self.topic_qos,
        )

        self.subscriber_qos = fastdds.SubscriberQos()
        self.participant.get_default_subscriber_qos(self.subscriber_qos)
        self.subscriber = self.participant.create_subscriber(
            self.subscriber_qos
        )

        self.listener = ReaderListener()
        self.reader_qos = fastdds.DataReaderQos()
        self.subscriber.get_default_datareader_qos(self.reader_qos)
        self.reader = self.subscriber.create_datareader(
            self.topic, self.reader_qos, self.listener
        )

    def __del__(self):
        factory = fastdds.DomainParticipantFactory.get_instance()
        self.participant.delete_contained_entities()
        factory.delete_participant(self.participant)


class ServerListener(fastdds.DomainParticipantListener):
    def __init__(self):
        super().__init__()

    def on_participant_discovery(self, participant, reason, info, should_be_ignored):
        print("Participant discovered")


class Server:
    def __init__(self, domain):

        # Initialize factory_qos
        factory = fastdds.DomainParticipantFactory.get_instance()
        self.participant_qos = fastdds.DomainParticipantQos()
        self.participant_qos.name("DS-Server")
        (self.participant_qos.wire_protocol()
         .builtin.discovery_config).discoveryProtocol = DiscoveryProtocol_SERVER

        # Locator configuration
        listening_locator = fastdds.Locator_t()
        listening_locator.kind = fastdds.LOCATOR_KIND_UDPv6
        listening_locator.port = 5000
        listening_locator.address = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)
        (self.participant_qos.wire_protocol()
         .builtin.metatrafficUnicastLocatorList.push_back(listening_locator))
        (self.participant_qos.wire_protocol()
         .builtin.discovery_config.m_DiscoveryServers.push_back(listening_locator))
        # Uncomment to filter other hosts
        (
            self.participant_qos.wire_protocol().builtin.discovery_config
        ).ignoreParticipantFlags = fastdds.FILTER_DIFFERENT_HOST

        factory.get_default_participant_qos(self.participant_qos)

        self.listener = ServerListener()

        self.participant = factory.create_participant(
            domain, self.participant_qos, self.listener
        )
        if not self.participant:
            raise Exception("No server participant created")

    def __del__(self):
        factory = fastdds.DomainParticipantFactory.get_instance()
        self.participant.delete_contained_entities()
        factory.delete_participant(self.participant)

    def run(self):
        try:
            input('Press any key to stop\n')
        except Exception as _:
            pass


fastdds_server = Server(0)
fastdds_reader = Reader(0)
fastdds_server.run()
exit(0)

KeaneWong avatar Feb 27 '25 18:02 KeaneWong