@@ -483,6 +483,165 @@ TEST(DDSDiscovery, UpdateMatchedStatus)
483483 datawriter_2.destroy ();
484484}
485485
486+ /*
487+ * This is a regression test for redmine issue 21355.
488+ *
489+ * In order to check that the on_publication_matched() and on_subscription_matched() callbacks are always called with
490+ * either +1 or -1 as the current_count_change, this test creates and destroys multiple DataReaders and DataWriters in
491+ * different threads, with a listener that counts the number of times on_publication_matched() and
492+ * on_subscription_matched() are called with current_count_change equal to 1, -1 or other value.
493+ *
494+ * The test fails if any of the calls to on_publication_matched() or on_subscription_matched() is made with a
495+ * current_count_change different than 1 or -1, or if the total number of calls with current_count_change equal to 1
496+ * is different than the total number of calls with current_count_change equal to -1.
497+ */
498+ TEST (DDSDiscovery, MatchedCallbackListenerMultithread)
499+ {
500+ constexpr size_t NUM_THREADS = 25 ;
501+
502+ struct TestListener
503+ : public eprosima::fastdds::dds::DataReaderListener
504+ , public eprosima::fastdds::dds::DataWriterListener
505+ {
506+ std::atomic<uint32_t > reader_plus_one_calls{ 0 };
507+ std::atomic<uint32_t > reader_minus_one_calls{ 0 };
508+ std::atomic<uint32_t > reader_other_calls{ 0 };
509+ std::atomic<uint32_t > reader_total_calls{ 0 };
510+
511+ std::atomic<uint32_t > writer_plus_one_calls{ 0 };
512+ std::atomic<uint32_t > writer_minus_one_calls{ 0 };
513+ std::atomic<uint32_t > writer_other_calls{ 0 };
514+ std::atomic<uint32_t > writer_total_calls{ 0 };
515+
516+ void on_publication_matched (
517+ eprosima::fastdds::dds::DataWriter*,
518+ const eprosima::fastdds::dds::PublicationMatchedStatus& info) override
519+ {
520+ if (info.current_count_change == 1 )
521+ {
522+ ++writer_plus_one_calls;
523+ }
524+ else if (info.current_count_change == -1 )
525+ {
526+ ++writer_minus_one_calls;
527+ }
528+ else
529+ {
530+ ++writer_other_calls;
531+ }
532+
533+ ++writer_total_calls;
534+ }
535+
536+ void on_subscription_matched (
537+ eprosima::fastdds::dds::DataReader*,
538+ const eprosima::fastdds::dds::SubscriptionMatchedStatus& info) override
539+ {
540+ if (info.current_count_change == 1 )
541+ {
542+ ++reader_plus_one_calls;
543+ }
544+ else if (info.current_count_change == -1 )
545+ {
546+ ++reader_minus_one_calls;
547+ }
548+ else
549+ {
550+ ++reader_other_calls;
551+ }
552+
553+ ++reader_total_calls;
554+ }
555+
556+ };
557+
558+ std::ostringstream t;
559+ t << TEST_TOPIC_NAME << " _" << asio::ip::host_name () << " _" << GET_PID ();
560+ std::string topic_name = t.str ();
561+ TestListener listener;
562+ eprosima::fastdds::dds::TypeSupport type (new HelloWorldPubSubType ());
563+
564+ auto create_entities = [&listener, &topic_name, &type]()
565+ {
566+ using namespace eprosima ::fastdds::dds;
567+
568+ uint32_t domain_id = static_cast <uint32_t >(GET_PID ()) % 100 ;
569+ auto factory = DomainParticipantFactory::get_shared_instance ();
570+ DomainParticipantQos participant_qos;
571+ factory->get_default_participant_qos (participant_qos);
572+ participant_qos.setup_transports (eprosima::fastdds::rtps::BuiltinTransports::UDPv4);
573+ participant_qos.wire_protocol ().builtin .discovery_config .leaseDuration .seconds = 0 ;
574+ participant_qos.wire_protocol ().builtin .discovery_config .leaseDuration .nanosec = 100000000 ;
575+ participant_qos.wire_protocol ().builtin .discovery_config .leaseDuration_announcementperiod .seconds = 0 ;
576+ participant_qos.wire_protocol ().builtin .discovery_config .leaseDuration_announcementperiod .nanosec =
577+ 50000000 ;
578+
579+ auto participant = factory->create_participant (domain_id, participant_qos);
580+ ASSERT_NE (participant, nullptr );
581+
582+ type.register_type (participant);
583+ auto topic = participant->create_topic (topic_name, type.get_type_name (), TOPIC_QOS_DEFAULT );
584+ ASSERT_NE (topic, nullptr );
585+
586+ auto publisher = participant->create_publisher (PUBLISHER_QOS_DEFAULT );
587+ ASSERT_NE (publisher, nullptr );
588+
589+ DataWriterQos datawriter_qos;
590+ publisher->get_default_datawriter_qos (datawriter_qos);
591+ datawriter_qos.data_sharing ().off ();
592+ auto datawriter = publisher->create_datawriter (topic, datawriter_qos, &listener);
593+ ASSERT_NE (datawriter, nullptr );
594+
595+ auto subscriber = participant->create_subscriber (SUBSCRIBER_QOS_DEFAULT );
596+ ASSERT_NE (subscriber, nullptr );
597+
598+ DataReaderQos datareader_qos;
599+ subscriber->get_default_datareader_qos (datareader_qos);
600+ datareader_qos.data_sharing ().off ();
601+ auto datareader = subscriber->create_datareader (topic, datareader_qos, &listener);
602+ ASSERT_NE (datareader, nullptr );
603+
604+ HelloWorld msg;
605+ msg.index (1 );
606+ datawriter->write (&msg);
607+
608+ SampleInfo info;
609+ datareader->take_next_sample (&msg, &info);
610+
611+ participant->delete_contained_entities ();
612+ factory->delete_participant (participant);
613+
614+ std::this_thread::sleep_for (std::chrono::milliseconds (200 ));
615+ };
616+
617+ std::vector<std::thread> threads;
618+ for (int i = 0 ; i < NUM_THREADS ; ++i)
619+ {
620+ threads.emplace_back (create_entities);
621+ }
622+
623+ for (auto & thread : threads)
624+ {
625+ thread.join ();
626+ }
627+
628+ std::cout << " Reader +1 calls: " << listener.reader_plus_one_calls .load () << std::endl;
629+ std::cout << " Reader -1 calls: " << listener.reader_minus_one_calls .load () << std::endl;
630+ std::cout << " Reader other calls: " << listener.reader_other_calls .load () << std::endl;
631+ std::cout << " Reader total calls: " << listener.reader_total_calls .load () << std::endl;
632+
633+ std::cout << " Writer +1 calls: " << listener.writer_plus_one_calls .load () << std::endl;
634+ std::cout << " Writer -1 calls: " << listener.writer_minus_one_calls .load () << std::endl;
635+ std::cout << " Writer other calls: " << listener.writer_other_calls .load () << std::endl;
636+ std::cout << " Writer total calls: " << listener.writer_total_calls .load () << std::endl;
637+
638+
639+ EXPECT_EQ (listener.reader_other_calls .load (), 0u );
640+ EXPECT_EQ (listener.writer_other_calls .load (), 0u );
641+ EXPECT_GE (listener.reader_plus_one_calls .load (), listener.reader_minus_one_calls .load ());
642+ EXPECT_GE (listener.writer_plus_one_calls .load (), listener.writer_minus_one_calls .load ());
643+ }
644+
486645TEST (DDSDiscovery, EndpointMatchingCallbackAlwaysTrue)
487646{
488647 using namespace std ::chrono_literals;
0 commit comments