Skip to content
This repository was archived by the owner on Feb 3, 2025. It is now read-only.

Commit 23cdb5d

Browse files
committed
Fix performance metrics deadlock (#2917)
* Add test for performance metrics deadlock Load the pr2 world with --lockstep and then subscribe to /gazebo/performance_metrics to reproduce the deadlock. * Fix deadlock when publishing performance metrics Currently the PublishPerformanceMetrics function is called within SensorManager.cc by each SensorContainer with protection by the mutex of each SensorContainer. This leads to a deadlock when a world has sensors in more than one container (i.e. more than one category of sensors) as described in #2902. Since the PublishPerformanceMetrics function is not a member function of SensorContainer and uses only global variables, the function can be called by the SensorManager thread instead of by each SensorContainer thread. This change will avoid the deadlock. Signed-off-by: Steve Peters <scpeters@openrobotics.org>
1 parent fc2f8e2 commit 23cdb5d

File tree

3 files changed

+96
-2
lines changed

3 files changed

+96
-2
lines changed

gazebo/sensors/SensorManager.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,8 @@ void SensorManager::Update(bool _force)
368368
// Only update if there are sensors
369369
if (this->sensorContainers[sensors::IMAGE]->sensors.size() > 0)
370370
this->sensorContainers[sensors::IMAGE]->Update(_force);
371+
372+
PublishPerformanceMetrics();
371373
}
372374

373375
//////////////////////////////////////////////////
@@ -850,8 +852,6 @@ void SensorManager::SensorContainer::Update(bool _force)
850852
{
851853
boost::recursive_mutex::scoped_lock lock(this->mutex);
852854

853-
PublishPerformanceMetrics();
854-
855855
if (this->sensors.empty())
856856
gzlog << "Updating a sensor container without any sensors.\n";
857857

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright (C) 2021 Open Source Robotics Foundation
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
#include "gazebo/test/ServerFixture.hh"
19+
20+
using namespace gazebo;
21+
22+
class Issue2902Test : public ServerFixture
23+
{
24+
/// \brief Test with PR2 world run with --lockstep.
25+
public: void PR2Test();
26+
27+
/// \brief Callback for performance_metrics subscriber.
28+
/// \param[in] _msg Performance Metrics message.
29+
public: void Callback(const ConstPerformanceMetricsPtr &_msg);
30+
};
31+
32+
unsigned int g_messageCount = 0;
33+
34+
////////////////////////////////////////////////////////////////////////
35+
void Issue2902Test::Callback(const ConstPerformanceMetricsPtr &/*_msg*/)
36+
{
37+
g_messageCount++;
38+
}
39+
40+
/////////////////////////////////////////////////
41+
TEST_F(Issue2902Test, PR2)
42+
{
43+
PR2Test();
44+
}
45+
46+
////////////////////////////////////////////////////////////////////////
47+
void Issue2902Test::PR2Test()
48+
{
49+
this->LoadArgs(" --lockstep -u worlds/pr2.world");
50+
physics::WorldPtr world = physics::get_world();
51+
ASSERT_TRUE(world != NULL);
52+
53+
// Check that transport is running and there are advertised topics
54+
EXPECT_FALSE(transport::is_stopped());
55+
EXPECT_TRUE(transport::ConnectionManager::Instance()->IsRunning());
56+
EXPECT_FALSE(transport::getAdvertisedTopics().empty());
57+
58+
// Check that image, laser scan and contacts topics are advertised
59+
auto topics = transport::getAdvertisedTopics("gazebo.msgs.Contacts");
60+
EXPECT_EQ(13u, topics.size());
61+
62+
topics = transport::getAdvertisedTopics("gazebo.msgs.ImageStamped");
63+
EXPECT_EQ(10u, topics.size());
64+
65+
topics = transport::getAdvertisedTopics("gazebo.msgs.LaserScanStamped");
66+
EXPECT_EQ(2u, topics.size());
67+
68+
gzdbg << "Step simulation before subscribing" << std::endl;
69+
world->Step(1000);
70+
gzdbg << " -- Completed simulation steps" << std::endl;
71+
72+
// Initialize transport node
73+
transport::NodePtr node = transport::NodePtr(new transport::Node());
74+
node->Init();
75+
ASSERT_TRUE(node != NULL);
76+
77+
// Subscribe to performance metrics
78+
std::string topicName = "/gazebo/performance_metrics";
79+
auto subscriber = node->Subscribe(topicName, &Issue2902Test::Callback, this);
80+
81+
gzdbg << "Step simulation after subscribing (#2902 deadlock here)"
82+
<< std::endl;
83+
world->Step(1000);
84+
gzdbg << " -- Completed simulation steps" << std::endl;
85+
}
86+
87+
/////////////////////////////////////////////////
88+
// Main
89+
int main(int argc, char **argv)
90+
{
91+
::testing::InitGoogleTest(&argc, argv);
92+
return RUN_ALL_TESTS();
93+
}

test/regression/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ set(tests
4545
2505_revolute_joint_SetAxis.cc
4646
2728_nested_urdf.cc
4747
2896_gazebo_subnamespace.cc
48+
2902_performance_metrics_deadlock.cc
4849
)
4950
gz_build_tests(${tests} EXTRA_LIBS gazebo_test_fixture)
5051

0 commit comments

Comments
 (0)