Merge lp:~phablet-team/media-hub/fix-1542947 into lp:media-hub

Proposed by Jim Hodapp
Status: Merged
Approved by: Alfonso Sanchez-Beato
Approved revision: 180
Merged at revision: 176
Proposed branch: lp:~phablet-team/media-hub/fix-1542947
Merge into: lp:media-hub
Diff against target: 284 lines (+131/-14)
6 files modified
src/core/media/hashed_keyed_player_store.cpp (+11/-5)
src/core/media/hashed_keyed_player_store.h (+4/-1)
src/core/media/service_implementation.cpp (+5/-2)
src/core/media/service_skeleton.cpp (+8/-6)
tests/unit-tests/CMakeLists.txt (+36/-0)
tests/unit-tests/test-player-store.cpp (+67/-0)
To merge this branch: bzr merge lp:~phablet-team/media-hub/fix-1542947
Reviewer Review Type Date Requested Status
Alfonso Sanchez-Beato Approve
PS Jenkins bot continuous-integration Needs Fixing
Review via email: mp+287943@code.launchpad.net

Commit message

Fix deadlock issue when adding/removing Player instances from the HashedKeyedPlayerStore

Description of the change

Fix deadlock issue when adding/removing Player instances from the HashedKeyedPlayerStore

To post a comment you must log in.
Revision history for this message
Alfonso Sanchez-Beato (alfonsosanchezbeato) wrote :

Some comments below.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
178. By Jim Hodapp

Remove debugging lines

179. By Jim Hodapp

Address review comments

180. By Jim Hodapp

Make sure it compiles

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Alfonso Sanchez-Beato (alfonsosanchezbeato) wrote :

LGTM

review: Approve
181. By Jim Hodapp

Make the multithreaded unit test more reliable.

182. By Jim Hodapp

Disable multithread unit test for now since it's not reliable enough to build with Jenkins.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/core/media/hashed_keyed_player_store.cpp'
2--- src/core/media/hashed_keyed_player_store.cpp 2014-12-01 13:11:16 +0000
3+++ src/core/media/hashed_keyed_player_store.cpp 2016-03-08 17:05:32 +0000
4@@ -31,13 +31,13 @@
5
6 bool media::HashedKeyedPlayerStore::has_player_for_key(const media::Player::PlayerKey& key) const
7 {
8- std::lock_guard<std::mutex> lg{guard};
9+ std::lock_guard<std::recursive_mutex> lg{guard};
10 return map.count(key) > 0;
11 }
12
13 std::shared_ptr<media::Player> media::HashedKeyedPlayerStore::player_for_key(const media::Player::PlayerKey& key) const
14 {
15- std::lock_guard<std::mutex> lg{guard};
16+ std::lock_guard<std::recursive_mutex> lg{guard};
17 auto it = map.find(key);
18
19 if (it == map.end()) throw std::out_of_range
20@@ -50,20 +50,20 @@
21
22 void media::HashedKeyedPlayerStore::enumerate_players(const media::KeyedPlayerStore::PlayerEnumerator& enumerator) const
23 {
24- std::lock_guard<std::mutex> lg{guard};
25+ std::lock_guard<std::recursive_mutex> lg{guard};
26 for (const auto& pair : map)
27 enumerator(pair.first, pair.second);
28 }
29
30 void media::HashedKeyedPlayerStore::add_player_for_key(const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player)
31 {
32- std::lock_guard<std::mutex> lg{guard};
33+ std::lock_guard<std::recursive_mutex> lg{guard};
34 map[key] = player;
35 }
36
37 void media::HashedKeyedPlayerStore::remove_player_for_key(const media::Player::PlayerKey& key)
38 {
39- std::lock_guard<std::mutex> lg{guard};
40+ std::lock_guard<std::recursive_mutex> lg{guard};
41 auto it = map.find(key);
42 if (it != map.end())
43 {
44@@ -74,6 +74,12 @@
45 }
46 }
47
48+size_t media::HashedKeyedPlayerStore::number_of_players() const
49+{
50+ std::lock_guard<std::recursive_mutex> lg{guard};
51+ return map.size();
52+}
53+
54 void media::HashedKeyedPlayerStore::set_current_player_for_key(const media::Player::PlayerKey& key)
55 {
56 prop_current_player = player_for_key(key);
57
58=== modified file 'src/core/media/hashed_keyed_player_store.h'
59--- src/core/media/hashed_keyed_player_store.h 2014-12-15 14:43:44 +0000
60+++ src/core/media/hashed_keyed_player_store.h 2016-03-08 17:05:32 +0000
61@@ -60,13 +60,16 @@
62 // Removes the player for the given key, and unsets it if it is the current one.
63 void remove_player_for_key(const Player::PlayerKey& key) override;
64
65+ // Returns the number of players in the store.
66+ size_t number_of_players() const;
67+
68 // Makes the player known under the given key current.
69 // Throws std::out_of_range if no player is known for the key.
70 void set_current_player_for_key(const Player::PlayerKey& key) override;
71
72 private:
73 core::Property<std::shared_ptr<Player>> prop_current_player;
74- mutable std::mutex guard;
75+ mutable std::recursive_mutex guard;
76 std::unordered_map<Player::PlayerKey, std::shared_ptr<Player>> map;
77 };
78 }
79
80=== modified file 'src/core/media/service_implementation.cpp'
81--- src/core/media/service_implementation.cpp 2016-01-14 19:47:40 +0000
82+++ src/core/media/service_implementation.cpp 2016-03-08 17:05:32 +0000
83@@ -182,7 +182,8 @@
84 std::shared_ptr<media::Player> media::ServiceImplementation::create_session(
85 const media::Player::Configuration& conf)
86 {
87- auto player = std::make_shared<media::PlayerImplementation<media::PlayerSkeleton>>(media::PlayerImplementation<media::PlayerSkeleton>::Configuration
88+ auto player = std::make_shared<media::PlayerImplementation<media::PlayerSkeleton>>
89+ (media::PlayerImplementation<media::PlayerSkeleton>::Configuration
90 {
91 media::PlayerSkeleton::Configuration
92 {
93@@ -198,6 +199,7 @@
94 });
95
96 auto key = conf.key;
97+ // *Note: on_client_disconnected() is called from a Binder thread context
98 player->on_client_disconnected().connect([this, key]()
99 {
100 // Call remove_player_for_key asynchronously otherwise deadlock can occur
101@@ -270,7 +272,8 @@
102 return;
103 }
104
105- const std::shared_ptr<media::Player> current_player = d->configuration.player_store->player_for_key(key);
106+ const std::shared_ptr<media::Player> current_player =
107+ d->configuration.player_store->player_for_key(key);
108
109 // We immediately make the player known as new current player.
110 if (current_player->audio_stream_role() == media::Player::multimedia)
111
112=== modified file 'src/core/media/service_skeleton.cpp'
113--- src/core/media/service_skeleton.cpp 2016-01-04 08:30:35 +0000
114+++ src/core/media/service_skeleton.cpp 2016-03-08 17:05:32 +0000
115@@ -140,14 +140,15 @@
116
117 try
118 {
119- configuration.player_store->add_player_for_key(key, impl->create_session(config));
120- uuid_player_map.insert(std::make_pair(uuid, key));
121+ const std::shared_ptr<media::Player> player {impl->create_session(config)};
122+ configuration.player_store->add_player_for_key(key, player);
123+ uuid_player_map.emplace(std::make_pair(uuid, key));
124
125 request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(),
126 [this, key, msg](const media::apparmor::ubuntu::Context& context)
127 {
128 fprintf(stderr, "%s():%d -- app_name='%s', attached\n", __func__, __LINE__, context.str().c_str());
129- player_owner_map.insert(std::make_pair(key, std::make_tuple(context.str(), true, msg->sender())));
130+ player_owner_map.emplace(std::make_pair(key, std::make_tuple(context.str(), true, msg->sender())));
131 });
132
133 auto reply = dbus::Message::make_method_return(msg);
134@@ -183,6 +184,7 @@
135 if (std::get<1>(info) && (std::get<2>(info) == msg->sender())) { // Player is attached
136 std::get<1>(info) = false; // Detached
137 std::get<2>(info).clear(); // Clear registered sender/peer
138+
139 auto player = configuration.player_store->player_for_key(key);
140 player->lifetime().set(media::Player::Lifetime::resumable);
141 }
142@@ -211,7 +213,7 @@
143
144 if (uuid_player_map.count(uuid) != 0)
145 {
146- auto key = uuid_player_map.at(uuid);
147+ const auto key = uuid_player_map.at(uuid);
148 if (not configuration.player_store->has_player_for_key(key))
149 {
150 auto reply = dbus::Message::make_error(
151@@ -285,7 +287,7 @@
152 msg->reader() >> uuid;
153
154 if (uuid_player_map.count(uuid) != 0) {
155- auto key = uuid_player_map.at(uuid);
156+ const auto key = uuid_player_map.at(uuid);
157 if (not configuration.player_store->has_player_for_key(key)) {
158 auto reply = dbus::Message::make_error(
159 msg,
160@@ -381,7 +383,7 @@
161 }
162 else {
163 // Resume previous session
164- auto key = named_player_map.at(name);
165+ const auto key = named_player_map.at(name);
166 if (not configuration.player_store->has_player_for_key(key)) {
167 auto reply = dbus::Message::make_error(
168 msg,
169
170=== modified file 'tests/unit-tests/CMakeLists.txt'
171--- tests/unit-tests/CMakeLists.txt 2014-12-09 16:00:00 +0000
172+++ tests/unit-tests/CMakeLists.txt 2016-03-08 17:05:32 +0000
173@@ -44,3 +44,39 @@
174 )
175
176 add_test(test-gstreamer-engine ${CMAKE_CURRENT_BINARY_DIR}/test-gstreamer-engine)
177+
178+#-----------------------------------------
179+
180+add_executable(
181+ test-player-store
182+
183+ libmedia-mock.cpp
184+ test-player-store.cpp
185+)
186+
187+target_link_libraries(
188+ test-player-store
189+
190+ media-hub-common
191+ media-hub-client
192+ media-hub-service
193+ call-monitor
194+ media-hub-test-framework
195+
196+ ${CMAKE_THREAD_LIBS_INIT}
197+ ${Boost_LIBRARIES}
198+ ${DBUS_LIBRARIES}
199+ ${DBUS_CPP_LDFLAGS}
200+ ${GLog_LIBRARY}
201+ ${PC_GSTREAMER_1_0_LIBRARIES}
202+ ${PROCESS_CPP_LDFLAGS}
203+ ${GIO_LIBRARIES}
204+ ${PROCESS_CPP_LIBRARIES}
205+ ${PC_PULSE_AUDIO_LIBRARIES}
206+
207+ gmock
208+ gmock_main
209+ gtest
210+)
211+
212+add_test(test-player-store ${CMAKE_CURRENT_BINARY_DIR}/test-player-store)
213
214=== added file 'tests/unit-tests/test-player-store.cpp'
215--- tests/unit-tests/test-player-store.cpp 1970-01-01 00:00:00 +0000
216+++ tests/unit-tests/test-player-store.cpp 2016-03-08 17:05:32 +0000
217@@ -0,0 +1,67 @@
218+/*
219+ * Copyright © 2016 Canonical Ltd.
220+ *
221+ * This program is free software: you can redistribute it and/or modify it
222+ * under the terms of the GNU Lesser General Public License version 3,
223+ * as published by the Free Software Foundation.
224+ *
225+ * This program is distributed in the hope that it will be useful,
226+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
227+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
228+ * GNU Lesser General Public License for more details.
229+ *
230+ * You should have received a copy of the GNU Lesser General Public License
231+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
232+ *
233+ * Authored by: Jim Hodapp <jim.hodapp@canonical.com>
234+ */
235+
236+#include "core/media/client_death_observer.h"
237+#include "core/media/hashed_keyed_player_store.h"
238+#include "core/media/player_implementation.h"
239+#include "core/media/player_skeleton.h"
240+#include "core/media/power/state_controller.h"
241+#include "core/media/apparmor/ubuntu.h"
242+
243+#include <core/media/service.h>
244+#include <core/media/player.h>
245+
246+#include <gtest/gtest.h>
247+
248+#include <condition_variable>
249+#include <functional>
250+#include <iostream>
251+#include <thread>
252+
253+namespace media = core::ubuntu::media;
254+
255+// FIXME: I can't get this test to reliably pass every time. What happens
256+// is number of Player instances actually added to the HashedKeyedPlayerStore
257+// sometimes is less than the actual number of Players added.
258+TEST(PlayerStore, DISABLED_adding_players_from_multiple_threads_works)
259+{
260+ media::HashedKeyedPlayerStore store;
261+ media::Player::PlayerKey key = 0;
262+
263+ static constexpr uint16_t num_workers = 3;
264+ size_t i;
265+ std::vector<std::thread> workers;
266+ for (i=0; i<num_workers; i++)
267+ {
268+ workers.push_back(std::thread([&store, i, &key]()
269+ {
270+ const std::shared_ptr<media::Player> player;
271+ const size_t num_players_before_add = store.number_of_players();
272+ while (store.number_of_players() == num_players_before_add)
273+ store.add_player_for_key(key, player);
274+
275+ std::cout << "Added Player instance with key: " << key << std::endl;
276+ ++key;
277+ }));
278+ }
279+
280+ for (i=0; i<workers.size(); i++)
281+ workers[i].join();
282+
283+ ASSERT_EQ(store.number_of_players(), workers.size());
284+}

Subscribers

People subscribed via source and target branches

to all changes: