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
=== modified file 'src/core/media/hashed_keyed_player_store.cpp'
--- src/core/media/hashed_keyed_player_store.cpp 2014-12-01 13:11:16 +0000
+++ src/core/media/hashed_keyed_player_store.cpp 2016-03-08 17:05:32 +0000
@@ -31,13 +31,13 @@
3131
32bool media::HashedKeyedPlayerStore::has_player_for_key(const media::Player::PlayerKey& key) const32bool media::HashedKeyedPlayerStore::has_player_for_key(const media::Player::PlayerKey& key) const
33{33{
34 std::lock_guard<std::mutex> lg{guard};34 std::lock_guard<std::recursive_mutex> lg{guard};
35 return map.count(key) > 0;35 return map.count(key) > 0;
36}36}
3737
38std::shared_ptr<media::Player> media::HashedKeyedPlayerStore::player_for_key(const media::Player::PlayerKey& key) const38std::shared_ptr<media::Player> media::HashedKeyedPlayerStore::player_for_key(const media::Player::PlayerKey& key) const
39{39{
40 std::lock_guard<std::mutex> lg{guard};40 std::lock_guard<std::recursive_mutex> lg{guard};
41 auto it = map.find(key);41 auto it = map.find(key);
4242
43 if (it == map.end()) throw std::out_of_range43 if (it == map.end()) throw std::out_of_range
@@ -50,20 +50,20 @@
5050
51void media::HashedKeyedPlayerStore::enumerate_players(const media::KeyedPlayerStore::PlayerEnumerator& enumerator) const51void media::HashedKeyedPlayerStore::enumerate_players(const media::KeyedPlayerStore::PlayerEnumerator& enumerator) const
52{52{
53 std::lock_guard<std::mutex> lg{guard};53 std::lock_guard<std::recursive_mutex> lg{guard};
54 for (const auto& pair : map)54 for (const auto& pair : map)
55 enumerator(pair.first, pair.second);55 enumerator(pair.first, pair.second);
56}56}
5757
58void media::HashedKeyedPlayerStore::add_player_for_key(const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player)58void media::HashedKeyedPlayerStore::add_player_for_key(const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player)
59{59{
60 std::lock_guard<std::mutex> lg{guard};60 std::lock_guard<std::recursive_mutex> lg{guard};
61 map[key] = player;61 map[key] = player;
62}62}
6363
64void media::HashedKeyedPlayerStore::remove_player_for_key(const media::Player::PlayerKey& key)64void media::HashedKeyedPlayerStore::remove_player_for_key(const media::Player::PlayerKey& key)
65{65{
66 std::lock_guard<std::mutex> lg{guard};66 std::lock_guard<std::recursive_mutex> lg{guard};
67 auto it = map.find(key);67 auto it = map.find(key);
68 if (it != map.end())68 if (it != map.end())
69 {69 {
@@ -74,6 +74,12 @@
74 }74 }
75}75}
7676
77size_t media::HashedKeyedPlayerStore::number_of_players() const
78{
79 std::lock_guard<std::recursive_mutex> lg{guard};
80 return map.size();
81}
82
77void media::HashedKeyedPlayerStore::set_current_player_for_key(const media::Player::PlayerKey& key)83void media::HashedKeyedPlayerStore::set_current_player_for_key(const media::Player::PlayerKey& key)
78{84{
79 prop_current_player = player_for_key(key);85 prop_current_player = player_for_key(key);
8086
=== modified file 'src/core/media/hashed_keyed_player_store.h'
--- src/core/media/hashed_keyed_player_store.h 2014-12-15 14:43:44 +0000
+++ src/core/media/hashed_keyed_player_store.h 2016-03-08 17:05:32 +0000
@@ -60,13 +60,16 @@
60 // Removes the player for the given key, and unsets it if it is the current one.60 // Removes the player for the given key, and unsets it if it is the current one.
61 void remove_player_for_key(const Player::PlayerKey& key) override;61 void remove_player_for_key(const Player::PlayerKey& key) override;
6262
63 // Returns the number of players in the store.
64 size_t number_of_players() const;
65
63 // Makes the player known under the given key current.66 // Makes the player known under the given key current.
64 // Throws std::out_of_range if no player is known for the key.67 // Throws std::out_of_range if no player is known for the key.
65 void set_current_player_for_key(const Player::PlayerKey& key) override;68 void set_current_player_for_key(const Player::PlayerKey& key) override;
6669
67private:70private:
68 core::Property<std::shared_ptr<Player>> prop_current_player;71 core::Property<std::shared_ptr<Player>> prop_current_player;
69 mutable std::mutex guard;72 mutable std::recursive_mutex guard;
70 std::unordered_map<Player::PlayerKey, std::shared_ptr<Player>> map;73 std::unordered_map<Player::PlayerKey, std::shared_ptr<Player>> map;
71};74};
72}75}
7376
=== modified file 'src/core/media/service_implementation.cpp'
--- src/core/media/service_implementation.cpp 2016-01-14 19:47:40 +0000
+++ src/core/media/service_implementation.cpp 2016-03-08 17:05:32 +0000
@@ -182,7 +182,8 @@
182std::shared_ptr<media::Player> media::ServiceImplementation::create_session(182std::shared_ptr<media::Player> media::ServiceImplementation::create_session(
183 const media::Player::Configuration& conf)183 const media::Player::Configuration& conf)
184{184{
185 auto player = std::make_shared<media::PlayerImplementation<media::PlayerSkeleton>>(media::PlayerImplementation<media::PlayerSkeleton>::Configuration185 auto player = std::make_shared<media::PlayerImplementation<media::PlayerSkeleton>>
186 (media::PlayerImplementation<media::PlayerSkeleton>::Configuration
186 {187 {
187 media::PlayerSkeleton::Configuration188 media::PlayerSkeleton::Configuration
188 {189 {
@@ -198,6 +199,7 @@
198 });199 });
199200
200 auto key = conf.key;201 auto key = conf.key;
202 // *Note: on_client_disconnected() is called from a Binder thread context
201 player->on_client_disconnected().connect([this, key]()203 player->on_client_disconnected().connect([this, key]()
202 {204 {
203 // Call remove_player_for_key asynchronously otherwise deadlock can occur205 // Call remove_player_for_key asynchronously otherwise deadlock can occur
@@ -270,7 +272,8 @@
270 return;272 return;
271 }273 }
272274
273 const std::shared_ptr<media::Player> current_player = d->configuration.player_store->player_for_key(key);275 const std::shared_ptr<media::Player> current_player =
276 d->configuration.player_store->player_for_key(key);
274277
275 // We immediately make the player known as new current player.278 // We immediately make the player known as new current player.
276 if (current_player->audio_stream_role() == media::Player::multimedia)279 if (current_player->audio_stream_role() == media::Player::multimedia)
277280
=== modified file 'src/core/media/service_skeleton.cpp'
--- src/core/media/service_skeleton.cpp 2016-01-04 08:30:35 +0000
+++ src/core/media/service_skeleton.cpp 2016-03-08 17:05:32 +0000
@@ -140,14 +140,15 @@
140140
141 try141 try
142 {142 {
143 configuration.player_store->add_player_for_key(key, impl->create_session(config));143 const std::shared_ptr<media::Player> player {impl->create_session(config)};
144 uuid_player_map.insert(std::make_pair(uuid, key));144 configuration.player_store->add_player_for_key(key, player);
145 uuid_player_map.emplace(std::make_pair(uuid, key));
145146
146 request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(),147 request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(),
147 [this, key, msg](const media::apparmor::ubuntu::Context& context)148 [this, key, msg](const media::apparmor::ubuntu::Context& context)
148 {149 {
149 fprintf(stderr, "%s():%d -- app_name='%s', attached\n", __func__, __LINE__, context.str().c_str());150 fprintf(stderr, "%s():%d -- app_name='%s', attached\n", __func__, __LINE__, context.str().c_str());
150 player_owner_map.insert(std::make_pair(key, std::make_tuple(context.str(), true, msg->sender())));151 player_owner_map.emplace(std::make_pair(key, std::make_tuple(context.str(), true, msg->sender())));
151 });152 });
152153
153 auto reply = dbus::Message::make_method_return(msg);154 auto reply = dbus::Message::make_method_return(msg);
@@ -183,6 +184,7 @@
183 if (std::get<1>(info) && (std::get<2>(info) == msg->sender())) { // Player is attached184 if (std::get<1>(info) && (std::get<2>(info) == msg->sender())) { // Player is attached
184 std::get<1>(info) = false; // Detached185 std::get<1>(info) = false; // Detached
185 std::get<2>(info).clear(); // Clear registered sender/peer186 std::get<2>(info).clear(); // Clear registered sender/peer
187
186 auto player = configuration.player_store->player_for_key(key);188 auto player = configuration.player_store->player_for_key(key);
187 player->lifetime().set(media::Player::Lifetime::resumable);189 player->lifetime().set(media::Player::Lifetime::resumable);
188 }190 }
@@ -211,7 +213,7 @@
211213
212 if (uuid_player_map.count(uuid) != 0)214 if (uuid_player_map.count(uuid) != 0)
213 {215 {
214 auto key = uuid_player_map.at(uuid);216 const auto key = uuid_player_map.at(uuid);
215 if (not configuration.player_store->has_player_for_key(key))217 if (not configuration.player_store->has_player_for_key(key))
216 {218 {
217 auto reply = dbus::Message::make_error(219 auto reply = dbus::Message::make_error(
@@ -285,7 +287,7 @@
285 msg->reader() >> uuid;287 msg->reader() >> uuid;
286288
287 if (uuid_player_map.count(uuid) != 0) {289 if (uuid_player_map.count(uuid) != 0) {
288 auto key = uuid_player_map.at(uuid);290 const auto key = uuid_player_map.at(uuid);
289 if (not configuration.player_store->has_player_for_key(key)) {291 if (not configuration.player_store->has_player_for_key(key)) {
290 auto reply = dbus::Message::make_error(292 auto reply = dbus::Message::make_error(
291 msg,293 msg,
@@ -381,7 +383,7 @@
381 }383 }
382 else {384 else {
383 // Resume previous session385 // Resume previous session
384 auto key = named_player_map.at(name);386 const auto key = named_player_map.at(name);
385 if (not configuration.player_store->has_player_for_key(key)) {387 if (not configuration.player_store->has_player_for_key(key)) {
386 auto reply = dbus::Message::make_error(388 auto reply = dbus::Message::make_error(
387 msg,389 msg,
388390
=== modified file 'tests/unit-tests/CMakeLists.txt'
--- tests/unit-tests/CMakeLists.txt 2014-12-09 16:00:00 +0000
+++ tests/unit-tests/CMakeLists.txt 2016-03-08 17:05:32 +0000
@@ -44,3 +44,39 @@
44)44)
4545
46add_test(test-gstreamer-engine ${CMAKE_CURRENT_BINARY_DIR}/test-gstreamer-engine)46add_test(test-gstreamer-engine ${CMAKE_CURRENT_BINARY_DIR}/test-gstreamer-engine)
47
48#-----------------------------------------
49
50add_executable(
51 test-player-store
52
53 libmedia-mock.cpp
54 test-player-store.cpp
55)
56
57target_link_libraries(
58 test-player-store
59
60 media-hub-common
61 media-hub-client
62 media-hub-service
63 call-monitor
64 media-hub-test-framework
65
66 ${CMAKE_THREAD_LIBS_INIT}
67 ${Boost_LIBRARIES}
68 ${DBUS_LIBRARIES}
69 ${DBUS_CPP_LDFLAGS}
70 ${GLog_LIBRARY}
71 ${PC_GSTREAMER_1_0_LIBRARIES}
72 ${PROCESS_CPP_LDFLAGS}
73 ${GIO_LIBRARIES}
74 ${PROCESS_CPP_LIBRARIES}
75 ${PC_PULSE_AUDIO_LIBRARIES}
76
77 gmock
78 gmock_main
79 gtest
80)
81
82add_test(test-player-store ${CMAKE_CURRENT_BINARY_DIR}/test-player-store)
4783
=== added file 'tests/unit-tests/test-player-store.cpp'
--- tests/unit-tests/test-player-store.cpp 1970-01-01 00:00:00 +0000
+++ tests/unit-tests/test-player-store.cpp 2016-03-08 17:05:32 +0000
@@ -0,0 +1,67 @@
1/*
2 * Copyright © 2016 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Jim Hodapp <jim.hodapp@canonical.com>
17 */
18
19#include "core/media/client_death_observer.h"
20#include "core/media/hashed_keyed_player_store.h"
21#include "core/media/player_implementation.h"
22#include "core/media/player_skeleton.h"
23#include "core/media/power/state_controller.h"
24#include "core/media/apparmor/ubuntu.h"
25
26#include <core/media/service.h>
27#include <core/media/player.h>
28
29#include <gtest/gtest.h>
30
31#include <condition_variable>
32#include <functional>
33#include <iostream>
34#include <thread>
35
36namespace media = core::ubuntu::media;
37
38// FIXME: I can't get this test to reliably pass every time. What happens
39// is number of Player instances actually added to the HashedKeyedPlayerStore
40// sometimes is less than the actual number of Players added.
41TEST(PlayerStore, DISABLED_adding_players_from_multiple_threads_works)
42{
43 media::HashedKeyedPlayerStore store;
44 media::Player::PlayerKey key = 0;
45
46 static constexpr uint16_t num_workers = 3;
47 size_t i;
48 std::vector<std::thread> workers;
49 for (i=0; i<num_workers; i++)
50 {
51 workers.push_back(std::thread([&store, i, &key]()
52 {
53 const std::shared_ptr<media::Player> player;
54 const size_t num_players_before_add = store.number_of_players();
55 while (store.number_of_players() == num_players_before_add)
56 store.add_player_for_key(key, player);
57
58 std::cout << "Added Player instance with key: " << key << std::endl;
59 ++key;
60 }));
61 }
62
63 for (i=0; i<workers.size(); i++)
64 workers[i].join();
65
66 ASSERT_EQ(store.number_of_players(), workers.size());
67}

Subscribers

People subscribed via source and target branches

to all changes: