Merge lp:~nick-dedekind/unity-mir/thready-screenshotting into lp:unity-mir

Proposed by Nick Dedekind
Status: Merged
Approved by: Gerry Boland
Approved revision: 246
Merged at revision: 241
Proposed branch: lp:~nick-dedekind/unity-mir/thready-screenshotting
Merge into: lp:unity-mir
Diff against target: 212 lines (+145/-1)
3 files modified
src/modules/Unity/Application/application.cpp (+17/-1)
src/modules/Unity/Application/application.h (+3/-0)
tests/application_manager_test.cpp (+125/-0)
To merge this branch: bzr merge lp:~nick-dedekind/unity-mir/thready-screenshotting
Reviewer Review Type Date Requested Status
Gerry Boland (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+226282@code.launchpad.net

Commit message

Fixed threading issue when screen-shotting application about to be stopped.

Description of the change

 * Are there any related MPs required for this MP to build/function as expected? Please list.
No

 * Did you perform an exploratory manual test run of your code change and any related functionality?
Yes

 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A

To post a comment you must log in.
243. By Nick Dedekind

use QMutex

244. By Nick Dedekind

use qt shared/weak ptr

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

+QMutex screenshotMutex;
does this need to live outside the namespace?

+ QMutexLocker lk(&screenshotMutex);
a quick 1 line comment explaining why we're doing this would help the casual reader see why we block in a deconstructor

Code looks good otherwise, am getting ready to test

review: Needs Fixing
245. By Nick Dedekind

added comment

Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

> +QMutex screenshotMutex;
> does this need to live outside the namespace?

Yes, so it can be used in the callback when the application is deleted (member would be deleted).

>
> + QMutexLocker lk(&screenshotMutex);
> a quick 1 line comment explaining why we're doing this would help the casual
> reader see why we block in a deconstructor

Done.

>
> Code looks good otherwise, am getting ready to test

246. By Nick Dedekind

moved mutex into namespace

Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

> > +QMutex screenshotMutex;
> > does this need to live outside the namespace?
>
> Yes, so it can be used in the callback when the application is deleted (member
> would be deleted).

Sorry, misunderstood. no, moved it inside.

>
> >
> > + QMutexLocker lk(&screenshotMutex);
> > a quick 1 line comment explaining why we're doing this would help the casual
> > reader see why we block in a deconstructor
>
> Done.
>
> >
> > Code looks good otherwise, am getting ready to test

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

Testing this on a device, for each screenshot take, the UI freezes for 30+ seconds.

review: Needs Fixing
Revision history for this message
Gerry Boland (gerboland) wrote :

Tested just now on N4, worked fine. The UI freeze was a different issue

review: Approve
Revision history for this message
Gerry Boland (gerboland) wrote :

 * Did you perform an exploratory manual test run of the code change and any related functionality?
Y
 * Did CI run pass? If not, please explain why.
Y

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/modules/Unity/Application/application.cpp'
2--- src/modules/Unity/Application/application.cpp 2014-05-26 15:54:44 +0000
3+++ src/modules/Unity/Application/application.cpp 2014-07-10 13:29:20 +0000
4@@ -30,6 +30,8 @@
5 namespace unitymir
6 {
7
8+QMutex screenshotMutex;
9+
10 Application::Application(const QSharedPointer<TaskController>& taskController,
11 DesktopFileReader *desktopFileReader,
12 State state,
13@@ -47,6 +49,7 @@
14 , m_visible(false)
15 , m_arguments(arguments)
16 , m_suspendTimer(new QTimer(this))
17+ , m_screenShotGuard(new Guard)
18 {
19 DLOG("Application::Application (this=%p, appId=%s, state=%d", this, qPrintable(desktopFileReader->appId()),
20 static_cast<int>(state));
21@@ -60,6 +63,12 @@
22
23 Application::~Application()
24 {
25+ {
26+ // In case we get a threaded screenshot callback once the application is deleted.
27+ QMutexLocker lk(&screenshotMutex);
28+ m_screenShotGuard.clear();
29+ }
30+
31 DLOG("Application::~Application (this=%p)", this);
32 delete m_desktopData;
33 }
34@@ -198,9 +207,16 @@
35
36 void Application::updateScreenshot()
37 {
38+ QWeakPointer<Guard> wk(m_screenShotGuard.toWeakRef());
39+
40 session()->take_snapshot(
41- [&](mir::scene::Snapshot const& snapshot)
42+ [&, wk](mir::scene::Snapshot const& snapshot)
43 {
44+ // In case we get a threaded screenshot callback once the application is deleted.
45+ QMutexLocker lk(&screenshotMutex);
46+ if (wk.isNull())
47+ return;
48+
49 DLOG("ApplicationScreenshotProvider - Mir snapshot ready with size %d x %d",
50 snapshot.size.height.as_int(), snapshot.size.width.as_int());
51
52
53=== modified file 'src/modules/Unity/Application/application.h'
54--- src/modules/Unity/Application/application.h 2014-05-16 15:40:00 +0000
55+++ src/modules/Unity/Application/application.h 2014-07-10 13:29:20 +0000
56@@ -117,6 +117,9 @@
57 QStringList m_arguments;
58 QTimer* m_suspendTimer;
59
60+ class Guard {};
61+ QSharedPointer<Guard> m_screenShotGuard;
62+
63 friend class ApplicationManager;
64 friend class MirSurfaceManager;
65 };
66
67=== modified file 'tests/application_manager_test.cpp'
68--- tests/application_manager_test.cpp 2014-06-25 15:30:50 +0000
69+++ tests/application_manager_test.cpp 2014-07-10 13:29:20 +0000
70@@ -25,6 +25,8 @@
71
72 #include <gmock/gmock.h>
73 #include <gtest/gtest.h>
74+#include <thread>
75+#include <condition_variable>
76 #include <QSignalSpy>
77
78 #include "mock_application_controller.h"
79@@ -70,6 +72,37 @@
80 }
81 {
82 }
83+
84+ Application* startApplication(quint64 procId, QString const& appId)
85+ {
86+ using namespace testing;
87+
88+ ON_CALL(appController,appIdHasProcessId(procId, appId)).WillByDefault(Return(true));
89+
90+ // Set up Mocks & signal watcher
91+ auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
92+ ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
93+ ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
94+
95+ ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
96+
97+ EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
98+ .Times(1)
99+ .WillOnce(Return(true));
100+
101+ auto application = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
102+ applicationManager.onProcessStarting(appId);
103+
104+ bool authed = false;
105+ applicationManager.authorizeSession(procId, authed);
106+ EXPECT_EQ(authed, true);
107+
108+ auto appSession = std::make_shared<MockSession>(appId.toStdString(), procId);
109+
110+ applicationManager.onSessionStarting(appSession);
111+ return application;
112+ }
113+
114 testing::NiceMock<testing::MockOomController> oomController;
115 testing::NiceMock<testing::MockProcessController> processController;
116 testing::NiceMock<testing::MockApplicationController> appController;
117@@ -1879,3 +1912,95 @@
118 EXPECT_EQ(countSpy.count(), 0);
119 EXPECT_EQ(removedSpy.count(), 0);
120 }
121+
122+/*
123+ * Test that screenshotting callback works cross thread.
124+ */
125+TEST_F(ApplicationManagerTests, threadedScreenshot)
126+{
127+ using namespace testing;
128+ quint64 procId1 = 5551;
129+
130+ std::mutex mutex;
131+ std::condition_variable cv;
132+ bool done = false;
133+
134+ auto application = startApplication(procId1, "webapp");
135+ auto session = std::dynamic_pointer_cast<MockSession>(application->session());
136+ ON_CALL(*session, take_snapshot(_)).WillByDefault(Invoke(
137+ [&](mir::scene::SnapshotCallback const& callback)
138+ {
139+ std::thread ([&, callback]() {
140+ std::unique_lock<std::mutex> lk(mutex);
141+
142+ mir::scene::Snapshot snapshot{mir::geometry::Size{0,0},
143+ mir::geometry::Stride{0},
144+ NULL};
145+
146+ callback(snapshot);
147+
148+ done = true;
149+ lk.unlock();
150+ cv.notify_one();
151+ }).detach();
152+ }));
153+
154+ application->updateScreenshot();
155+
156+ {
157+ std::unique_lock<decltype(mutex)> lk(mutex);
158+ cv.wait(lk, [&] { return done; } );
159+ EXPECT_TRUE(done);
160+ }
161+
162+ applicationManager.stopApplication(application->appId());
163+}
164+
165+/*
166+ * Test that screenshotting callback works when application has been deleted
167+ */
168+TEST_F(ApplicationManagerTests, threadedScreenshotAfterAppDelete)
169+{
170+ using namespace testing;
171+ quint64 procId1 = 5551;
172+
173+ std::mutex mutex;
174+ std::condition_variable cv;
175+ bool ready = false;
176+ bool done = false;
177+
178+ auto application = startApplication(procId1, "webapp");
179+ auto session = std::dynamic_pointer_cast<MockSession>(application->session());
180+ ON_CALL(*session, take_snapshot(_)).WillByDefault(Invoke(
181+ [&](mir::scene::SnapshotCallback const& callback)
182+ {
183+ std::thread ([&, callback]() {
184+
185+ std::unique_lock<std::mutex> lk(mutex);
186+ cv.wait(lk, [&]{ return ready; } );
187+
188+ mir::scene::Snapshot snapshot{mir::geometry::Size{0,0},
189+ mir::geometry::Stride{0},
190+ NULL};
191+
192+ callback(snapshot);
193+
194+ done = true;
195+ lk.unlock();
196+ cv.notify_one();
197+ }).detach();
198+ }));
199+ application->updateScreenshot();
200+
201+ {
202+ std::lock_guard<decltype(mutex)> lk(mutex);
203+ applicationManager.stopApplication(application->appId());
204+ ready = true;
205+ }
206+ cv.notify_one();
207+
208+ {
209+ std::unique_lock<decltype(mutex)> lk(mutex);
210+ cv.wait(lk, [&] { return done; } );
211+ }
212+}

Subscribers

People subscribed via source and target branches