Merge lp:~albaguirre/media-hub/fix-1373722 into lp:media-hub

Proposed by Alberto Aguirre
Status: Merged
Approved by: Jim Hodapp
Approved revision: 70
Merged at revision: 68
Proposed branch: lp:~albaguirre/media-hub/fix-1373722
Merge into: lp:media-hub
Diff against target: 164 lines (+43/-22)
2 files modified
src/core/media/player_implementation.cpp (+42/-21)
src/core/media/player_implementation.h (+1/-1)
To merge this branch: bzr merge lp:~albaguirre/media-hub/fix-1373722
Reviewer Review Type Date Requested Status
Jim Hodapp (community) Approve
PS Jenkins bot continuous-integration Approve
Review via email: mp+235900@code.launchpad.net

Commit message

Prevent dead object access by asynchronous calls to clear_wakelock in media::PlayerImplementation::Private

On an player engine state change - detached threads are launched to clear system/display wakelocks in the future.
Before those threads get a chance to execute, media::PlayerImplementation can be destroyed. By holding a weak_ptr to
 media::PlayerImplementation::Private in the launched threads, access to dead Private objects is prevented.

Description of the change

Prevent dead object access by asynchronous calls to clear_wakelock in media::PlayerImplementation::Private

On an player engine state change - detached threads are launched to clear system/display wakelocks in the future.
Before those threads get a chance to execute, media::PlayerImplementation can be destroyed. By holding a weak_ptr to
 media::PlayerImplementation::Private in the launched threads, access to dead Private objects is prevented.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:~albaguirre/media-hub/fix-1373722 updated
69. By Alberto Aguirre

Ensure state change handler is not called during destruction.

Revision history for this message
Jim Hodapp (jhodapp) wrote :

Looks great, one suggestion inline.

review: Needs Fixing (code)
lp:~albaguirre/media-hub/fix-1373722 updated
70. By Alberto Aguirre

document issue in code

Revision history for this message
Jim Hodapp (jhodapp) wrote :

Looks fantastic, thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/core/media/player_implementation.cpp'
2--- src/core/media/player_implementation.cpp 2014-09-17 01:23:39 +0000
3+++ src/core/media/player_implementation.cpp 2014-09-25 16:27:57 +0000
4@@ -28,6 +28,7 @@
5 #include "unity_screen_service.h"
6 #include "gstreamer/engine.h"
7
8+#include <memory>
9 #include <exception>
10 #include <iostream>
11 #include <mutex>
12@@ -39,7 +40,8 @@
13
14 using namespace std;
15
16-struct media::PlayerImplementation::Private
17+struct media::PlayerImplementation::Private :
18+ public std::enable_shared_from_this<Private>
19 {
20 enum class wakelock_clear_t
21 {
22@@ -66,7 +68,8 @@
23 system_wakelock_count(0),
24 display_wakelock_count(0),
25 previous_state(Engine::State::stopped),
26- key(key)
27+ key(key),
28+ engine_state_change_connection(engine->state().changed().connect(make_state_change_handler()))
29 {
30 auto bus = std::shared_ptr<dbus::Bus>(new dbus::Bus(core::dbus::WellKnownBus::system));
31 bus->install_executor(dbus::asio::make_executor(bus));
32@@ -76,14 +79,28 @@
33
34 auto uscreen_stub_service = dbus::Service::use_service(bus, dbus::traits::Service<core::UScreen>::interface_name());
35 uscreen_session = uscreen_stub_service->object_for_path(dbus::types::ObjectPath("/com/canonical/Unity/Screen"));
36-
37+ }
38+
39+ ~Private()
40+ {
41+ // Make sure that we don't hold on to the wakelocks if media-hub-server
42+ // ever gets restarted manually or automatically
43+ clear_wakelocks();
44+
45+ // The engine destructor can lead to a stop change state which will
46+ // trigger the state change handler. Ensure the handler is not called
47+ // by disconnecting the state change signal
48+ engine_state_change_connection.disconnect();
49+ }
50+
51+ std::function<void(const Engine::State& state)> make_state_change_handler()
52+ {
53 /*
54 * Wakelock state logic:
55 * PLAYING->READY or PLAYING->PAUSED or PLAYING->STOPPED: delay 4 seconds and try to clear current wakelock type
56 * ANY STATE->PLAYING: request a new wakelock (system or display)
57 */
58- engine->state().changed().connect(
59- [parent, this](const Engine::State& state)
60+ return [this](const Engine::State& state)
61 {
62 switch(state)
63 {
64@@ -92,8 +109,7 @@
65 parent->playback_status().set(media::Player::ready);
66 if (previous_state == Engine::State::playing)
67 {
68- wakelock_timeout.reset(new timeout(4000, true, std::bind(&Private::clear_wakelock,
69- this, std::placeholders::_1), current_wakelock_type()));
70+ timeout(4000, true, make_clear_wakelock_functor());
71 }
72 break;
73 }
74@@ -112,8 +128,7 @@
75 parent->playback_status().set(media::Player::stopped);
76 if (previous_state == Engine::State::playing)
77 {
78- wakelock_timeout.reset(new timeout(4000, true, std::bind(&Private::clear_wakelock,
79- this, std::placeholders::_1), current_wakelock_type()));
80+ timeout(4000, true, make_clear_wakelock_functor());
81 }
82 break;
83 }
84@@ -122,8 +137,7 @@
85 parent->playback_status().set(media::Player::paused);
86 if (previous_state == Engine::State::playing)
87 {
88- wakelock_timeout.reset(new timeout(4000, true, std::bind(&Private::clear_wakelock,
89- this, std::placeholders::_1), current_wakelock_type()));
90+ timeout(4000, true, make_clear_wakelock_functor());
91 }
92 break;
93 }
94@@ -133,14 +147,7 @@
95
96 // Keep track of the previous Engine playback state:
97 previous_state = state;
98- });
99- }
100-
101- ~Private()
102- {
103- // Make sure that we don't hold on to the wakelocks if media-hub-server
104- // ever gets restarted manually or automatically
105- clear_wakelocks();
106+ };
107 }
108
109 void request_power_state()
110@@ -237,6 +244,20 @@
111 }
112 }
113
114+ std::function<void()> make_clear_wakelock_functor()
115+ {
116+ // Since this functor will be executed on a separate detached thread
117+ // the execution of the functor may surpass the lifetime of this Private
118+ // object instance. By keeping a weak_ptr to the private object instance
119+ // we can check if the object is dead before calling methods on it
120+ std::weak_ptr<Private> weak_self{shared_from_this()};
121+ auto wakelock_type = current_wakelock_type();
122+ return [weak_self, wakelock_type] {
123+ if (auto self = weak_self.lock())
124+ self->clear_wakelock(wakelock_type);
125+ };
126+ }
127+
128 PlayerImplementation* parent;
129 std::shared_ptr<Service> service;
130 std::shared_ptr<Engine> engine;
131@@ -249,10 +270,10 @@
132 std::string sys_cookie;
133 std::atomic<int> system_wakelock_count;
134 std::atomic<int> display_wakelock_count;
135- std::unique_ptr<timeout> wakelock_timeout;
136 Engine::State previous_state;
137 PlayerImplementation::PlayerKey key;
138 core::Signal<> on_client_disconnected;
139+ core::Connection engine_state_change_connection;
140 };
141
142 media::PlayerImplementation::PlayerImplementation(
143@@ -270,7 +291,7 @@
144 identity
145 }
146 },
147- d(new Private(
148+ d(make_shared<Private>(
149 this,
150 session->path(),
151 service,
152
153=== modified file 'src/core/media/player_implementation.h'
154--- src/core/media/player_implementation.h 2014-09-09 21:27:29 +0000
155+++ src/core/media/player_implementation.h 2014-09-25 16:27:57 +0000
156@@ -61,7 +61,7 @@
157 const core::Signal<>& on_client_disconnected() const;
158 private:
159 struct Private;
160- std::unique_ptr<Private> d;
161+ std::shared_ptr<Private> d;
162 };
163 }
164 }

Subscribers

People subscribed via source and target branches