@@ -859,6 +859,67 @@ TYPED_TEST(TestExecutors, stressAddRemoveNode)
859859 spinner_thread.join ();
860860}
861861
862+ // Check that executors are correctly notified while they are spinning
863+ // we notify twice to ensure that the notify waitable is still working
864+ // after the first notification
865+ TYPED_TEST (TestExecutors, notifyTwiceWhileSpinning)
866+ {
867+ using ExecutorType = TypeParam;
868+
869+ // Create executor, add the node and start spinning
870+ ExecutorType executor;
871+ executor.add_node (this ->node );
872+ std::thread spinner ([&]() {executor.spin ();});
873+
874+ // Wait for executor to be spinning
875+ while (!executor.is_spinning ()) {
876+ std::this_thread::sleep_for (std::chrono::milliseconds (1 ));
877+ }
878+
879+ // Create the first subscription while the executor is already spinning
880+ std::atomic<size_t > sub1_msg_count {0 };
881+ auto sub1 = this ->node ->template create_subscription <test_msgs::msg::Empty>(
882+ this ->publisher ->get_topic_name (),
883+ rclcpp::QoS (10 ),
884+ [&sub1_msg_count](test_msgs::msg::Empty::ConstSharedPtr) {
885+ sub1_msg_count++;
886+ });
887+
888+ // Publish a message and verify it's received
889+ this ->publisher ->publish (test_msgs::msg::Empty ());
890+ auto start = std::chrono::steady_clock::now ();
891+ while (sub1_msg_count == 0 && (std::chrono::steady_clock::now () - start) < 10s) {
892+ std::this_thread::sleep_for (1ms);
893+ }
894+ EXPECT_EQ (sub1_msg_count, 1u );
895+
896+ // Create a second subscription while the executor is already spinning
897+ std::atomic<size_t > sub2_msg_count {0 };
898+ auto sub2 = this ->node ->template create_subscription <test_msgs::msg::Empty>(
899+ this ->publisher ->get_topic_name (),
900+ rclcpp::QoS (10 ),
901+ [&sub2_msg_count](test_msgs::msg::Empty::ConstSharedPtr) {
902+ sub2_msg_count++;
903+ });
904+
905+ // Publish a message and verify it's received by both subscriptions
906+ this ->publisher ->publish (test_msgs::msg::Empty ());
907+ start = std::chrono::steady_clock::now ();
908+ while (
909+ sub1_msg_count == 1 &&
910+ sub2_msg_count == 0 &&
911+ (std::chrono::steady_clock::now () - start) < 10s)
912+ {
913+ std::this_thread::sleep_for (1ms);
914+ }
915+ EXPECT_EQ (sub1_msg_count, 2u );
916+ EXPECT_EQ (sub2_msg_count, 1u );
917+
918+ // Cancel needs to be called before join, so that executor.spin() returns.
919+ executor.cancel ();
920+ spinner.join ();
921+ }
922+
862923// Check spin_until_future_complete with node base pointer (instantiates its own executor)
863924TEST (TestExecutors, testSpinUntilFutureCompleteNodeBasePtr)
864925{
0 commit comments