Merge lp:~aacid/qtmir/fixAuthorizeDeadlock into lp:qtmir

Proposed by Albert Astals Cid
Status: Merged
Approved by: Gerry Boland
Approved revision: 623
Merged at revision: 628
Proposed branch: lp:~aacid/qtmir/fixAuthorizeDeadlock
Merge into: lp:qtmir
Prerequisite: lp:~aacid/qtmir/remove_non_interface_things
Diff against target: 607 lines (+133/-75)
6 files modified
src/modules/Unity/Application/application.cpp (+0/-27)
src/modules/Unity/Application/application.h (+0/-4)
src/modules/Unity/Application/application_manager.cpp (+120/-36)
src/modules/Unity/Application/application_manager.h (+12/-0)
src/modules/Unity/Application/taskcontroller.cpp (+1/-6)
src/modules/Unity/Application/taskcontroller.h (+0/-2)
To merge this branch: bzr merge lp:~aacid/qtmir/fixAuthorizeDeadlock
Reviewer Review Type Date Requested Status
Gerry Boland (community) Approve
Unity8 CI Bot (community) continuous-integration Approve
Review via email: mp+320614@code.launchpad.net

Commit message

Run ApplicationManager::authorizeSession in the calling thread

Instead of using a BlockingQueuedConnection

This fixes the deadlock we have when we are creating a player that uses gstreamer and
gstreamer needs to update its registry (which blocks waiting for an external process to finish)
and that external process tries to connect to mir.

Adds a QMutex in ApplicationManager that protects the public functions and slots

The mutex needs to be recursive since for example beginInsertRows call in add() will call rowCount()

The setPid/addApp functions are still run through a queued signal so that the model/QObjects are still all handled in the main thread.

findApplicationMutexHeld is not really necessary since the mutex is recursive but thought it's better to leave the recursive use for when it's really needed

Description of the change

 * Are there any related MPs required for this MP to build/function as expected?
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.
Revision history for this message
Gerry Boland (gerboland) wrote :

There is one code path I'm concerned about here, which is when an app is launched by UAL directly (say via command line, or url dispatcher) - so AppMan::startApplication wasn't called.

Ubuntu-app-launch library causes the TaskController::processStarting signal to be emitted. I *think* this happens on an internal UAL thread. So the slot ApplicationManager::onProcessStarting will be called as a queued event.

Then before this MP, TaskController::authorizationRequestedForSession signal is fired from a Mir thread, causing the slot AppMan::authorizeSession to also be queued event.

This ensures the order of execution in AppMan is always first onProcessStarting, second authorizeSession. If the order is wrong, authoriseSession will reject the app (because UAL hadn't told it to expect it yet).

My worry is if UAL launched a super fast app, it would be possible for this sequence of events to occur:
- TaskController::processStarting signal emitted
- TaskController::authorizationRequestedForSession signal emitted
- authorizeSession (called Direct)
- onProcessStarting (called Queued)
causing the app to be rejected.

Unfortunately I can't point you to an easy way to reproduce this. I can try constructing a test to prove/disprove my worry here, if you'd like.

review: Needs Fixing
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
lp:~aacid/qtmir/fixAuthorizeDeadlock updated
621. By Albert Astals Cid

Move pid storing to ApplicationManager

Instead of application since the only thing that needs it is the ApplicationManager
this way instead of queuing its setting we can just set it directly

Fixes crash we had on a test

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

PASSED: Continuous integration, rev:621
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-ci/608/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4610
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4638
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4465
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4465/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4465
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4465/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4465
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4465/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4465
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4465/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4465
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4465/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4465
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4465/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-ci/608/rebuild

review: Approve (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

> Ubuntu-app-launch library causes the TaskController::processStarting signal to
> be emitted. I *think* this happens on an internal UAL thread. So the slot
> ApplicationManager::onProcessStarting will be called as a queued event.

Can not reproduce that, added QThread::currentThread logging to the preStartCallback lambda and to ApplicationManager::onProcessStarting and they are both run on the same thread when doing
   ubuntu-app-launch webbrowser-app
from a terminal.

lp:~aacid/qtmir/fixAuthorizeDeadlock updated
622. By Albert Astals Cid

spacing

Revision history for this message
Albert Astals Cid (aacid) wrote :

> > Ubuntu-app-launch library causes the TaskController::processStarting signal
> to
> > be emitted. I *think* this happens on an internal UAL thread. So the slot
> > ApplicationManager::onProcessStarting will be called as a queued event.
>
> Can not reproduce that, added QThread::currentThread logging to the
> preStartCallback lambda and to ApplicationManager::onProcessStarting and they
> are both run on the same thread when doing
> ubuntu-app-launch webbrowser-app
> from a terminal.

Ted confirmed: "The C API will always callback on the thread of the caller if it has a GMainContext set. Otherwise the default thread context."

So i guess we're fine?

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

PASSED: Continuous integration, rev:622
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-ci/610/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4613
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4641
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4467
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4467/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4467
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4467/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4467
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4467/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4467
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4467/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4467
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4467/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4467
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4467/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-ci/610/rebuild

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

> Ted confirmed: "The C API will always callback on the thread of the caller if
> it has a GMainContext set. Otherwise the default thread context."
>
> So i guess we're fine?

Ok, that's news to me. Then yes, I don't see any big problem with this. Will test & review tomorrow.

lp:~aacid/qtmir/fixAuthorizeDeadlock updated
623. By Albert Astals Cid

Merge

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

PASSED: Continuous integration, rev:623
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-ci/622/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4656
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4684
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4507
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4507/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4507
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4507/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4507
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4507/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4507
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4507/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4507
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4507/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4507
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4507/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-ci/622/rebuild

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

Yep, tested and the order of events is as you say. Works reliably, thank you

review: Approve

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 2017-02-07 20:59:34 +0000
3+++ src/modules/Unity/Application/application.cpp 2017-03-24 09:01:47 +0000
4@@ -28,7 +28,6 @@
5
6 // QPA mirserver
7 #include "logging.h"
8-#include "initialsurfacesizes.h"
9
10 // Unity API
11 #include <unity/shell/application/MirSurfaceInterface.h>
12@@ -48,7 +47,6 @@
13 : ApplicationInfoInterface(appInfo->appId(), parent)
14 , m_sharedWakelock(sharedWakelock)
15 , m_appInfo(appInfo)
16- , m_pid(0)
17 , m_supportedStages(Application::MainStage|Application::SideStage)
18 , m_state(InternalState::Starting)
19 , m_arguments(arguments)
20@@ -107,10 +105,6 @@
21 delete m_session;
22 }
23
24- if (m_pid != 0) {
25- InitialSurfaceSizes::remove(m_pid);
26- }
27-
28 delete m_stopTimer;
29 }
30
31@@ -424,11 +418,6 @@
32 return m_session ? m_session->fullscreen() : false;
33 }
34
35-pid_t Application::pid() const
36-{
37- return m_pid;
38-}
39-
40 void Application::close()
41 {
42 INFO_MSG << "()";
43@@ -459,19 +448,6 @@
44 }
45 }
46
47-void Application::setPid(pid_t pid)
48-{
49- if (m_pid != 0) {
50- InitialSurfaceSizes::remove(m_pid);
51- }
52-
53- m_pid = pid;
54-
55- if (m_initialSurfaceSize.isValid() && m_pid != 0) {
56- InitialSurfaceSizes::set(m_pid, m_initialSurfaceSize);
57- }
58-}
59-
60 void Application::setArguments(const QStringList &arguments)
61 {
62 m_arguments = arguments;
63@@ -849,9 +825,6 @@
64
65 if (size != m_initialSurfaceSize) {
66 m_initialSurfaceSize = size;
67- if (m_pid != 0 && m_initialSurfaceSize.isValid()) {
68- InitialSurfaceSizes::set(m_pid, size);
69- }
70 Q_EMIT initialSurfaceSizeChanged(m_initialSurfaceSize);
71 }
72 }
73
74=== modified file 'src/modules/Unity/Application/application.h'
75--- src/modules/Unity/Application/application.h 2016-11-15 17:54:03 +0000
76+++ src/modules/Unity/Application/application.h 2017-03-24 09:01:47 +0000
77@@ -118,8 +118,6 @@
78
79 Stages supportedStages() const;
80
81- pid_t pid() const;
82-
83 // internal as in "not exposed in unity-api", so qtmir-internal.
84 InternalState internalState() const { return m_state; }
85
86@@ -149,7 +147,6 @@
87
88 void acquireWakelock() const;
89 void releaseWakelock() const;
90- void setPid(pid_t pid);
91 void setArguments(const QStringList &arguments);
92 void setInternalState(InternalState state);
93 void wipeQMLCache();
94@@ -166,7 +163,6 @@
95
96 QSharedPointer<SharedWakelock> m_sharedWakelock;
97 QSharedPointer<ApplicationInfo> m_appInfo;
98- pid_t m_pid;
99 Stages m_supportedStages;
100 InternalState m_state;
101 QStringList m_arguments;
102
103=== modified file 'src/modules/Unity/Application/application_manager.cpp'
104--- src/modules/Unity/Application/application_manager.cpp 2017-03-24 09:01:47 +0000
105+++ src/modules/Unity/Application/application_manager.cpp 2017-03-24 09:01:47 +0000
106@@ -28,6 +28,7 @@
107 #include "settings.h"
108
109 // mirserver
110+#include "initialsurfacesizes.h"
111 #include "nativeinterface.h"
112 #include "logging.h"
113
114@@ -84,6 +85,9 @@
115 return nullptr;
116 }
117
118+ qRegisterMetaType<QSharedPointer<qtmir::ApplicationInfo>>("QSharedPointer<qtmir::ApplicationInfo>");
119+ qRegisterMetaType<pid_t>("pid_t");
120+
121 QSharedPointer<TaskController> taskController(new upstart::TaskController());
122 QSharedPointer<ProcInfo> procInfo(new ProcInfo());
123 QSharedPointer<SharedWakelock> sharedWakelock(new SharedWakelock);
124@@ -135,6 +139,7 @@
125 , m_procInfo(procInfo)
126 , m_sharedWakelock(sharedWakelock)
127 , m_settings(settings)
128+ , m_mutex(QMutex::Recursive) // Needs to be recursive since e.g. beginInsertRows will call rowCount
129 {
130 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::ApplicationManager (this=%p)" << this;
131 setObjectName(QStringLiteral("qtmir::ApplicationManager"));
132@@ -152,9 +157,11 @@
133 QObject::connect(m_taskController.data(), &TaskController::resumeRequested,
134 this, &ApplicationManager::onResumeRequested);
135 QObject::connect(m_taskController.data(), &TaskController::authorizationRequestedForSession,
136- this, &ApplicationManager::authorizeSession);
137+ this, &ApplicationManager::authorizeSession, Qt::DirectConnection);
138 QObject::connect(m_taskController.data(), &TaskController::sessionStarting,
139 this, &ApplicationManager::onSessionStarting);
140+
141+ connect(this, &ApplicationManager::queuedAddApp, this, &ApplicationManager::addApp);
142 }
143
144 ApplicationManager::~ApplicationManager()
145@@ -165,11 +172,13 @@
146
147 int ApplicationManager::rowCount(const QModelIndex &parent) const
148 {
149+ QMutexLocker locker(&m_mutex);
150 return !parent.isValid() ? m_applications.size() : 0;
151 }
152
153 QVariant ApplicationManager::data(const QModelIndex &index, int role) const
154 {
155+ QMutexLocker locker(&m_mutex);
156 if (index.row() >= 0 && index.row() < m_applications.size()) {
157 Application *application = m_applications.at(index.row());
158 switch (role) {
159@@ -201,6 +210,7 @@
160
161 Application* ApplicationManager::get(int index) const
162 {
163+ QMutexLocker locker(&m_mutex);
164 if (index < 0 || index >= m_applications.count()) {
165 return nullptr;
166 }
167@@ -209,6 +219,14 @@
168
169 Application* ApplicationManager::findApplication(const QString &inputAppId) const
170 {
171+ QMutexLocker locker(&m_mutex);
172+ return findApplicationMutexHeld(inputAppId);
173+}
174+
175+Application* ApplicationManager::findApplicationMutexHeld(const QString &inputAppId) const
176+{
177+ // We don't really need this function since the mutex is recursive but is a bit
178+ // better if we just lock the mutex recursively when really need it
179 const QString appId = toShortAppIdIfPossible(inputAppId);
180
181 for (Application *app : m_applications) {
182@@ -221,10 +239,12 @@
183
184 bool ApplicationManager::requestFocusApplication(const QString &inputAppId)
185 {
186+ QMutexLocker locker(&m_mutex);
187+
188 const QString appId = toShortAppIdIfPossible(inputAppId);
189
190 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::requestFocusApplication - appId=" << appId;
191- Application *application = findApplication(appId);
192+ Application *application = findApplicationMutexHeld(appId);
193
194 if (!application) {
195 qDebug() << "No such running application with appId=" << appId;
196@@ -238,6 +258,7 @@
197
198 QString ApplicationManager::focusedApplicationId() const
199 {
200+ QMutexLocker locker(&m_mutex);
201 for (const auto application : m_applications) {
202 if (application->focused()) {
203 return application->appId();
204@@ -264,11 +285,13 @@
205 Application* ApplicationManager::startApplication(const QString &inputAppId,
206 const QStringList &arguments)
207 {
208+ QMutexLocker locker(&m_mutex);
209+
210 tracepoint(qtmir, startApplication);
211 QString appId = toShortAppIdIfPossible(inputAppId);
212 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::startApplication - this=" << this << "appId" << qPrintable(appId);
213
214- Application *application = findApplication(appId);
215+ Application *application = findApplicationMutexHeld(appId);
216 if (application) {
217 qWarning() << "ApplicationManager::startApplication - application appId=" << appId << " already exists";
218 return nullptr;
219@@ -297,7 +320,7 @@
220 }
221
222 // The TaskController may synchroneously callback onProcessStarting, so check if application already added
223- application = findApplication(appId);
224+ application = findApplicationMutexHeld(appId);
225 if (application) {
226 application->setArguments(arguments);
227 } else {
228@@ -320,10 +343,12 @@
229
230 void ApplicationManager::onProcessStarting(const QString &appId)
231 {
232+ QMutexLocker locker(&m_mutex);
233+
234 tracepoint(qtmir, onProcessStarting);
235 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessStarting - appId=" << appId;
236
237- Application *application = findApplication(appId);
238+ Application *application = findApplicationMutexHeld(appId);
239 if (!application) { // then shell did not start this application, so ubuntu-app-launch must have - add to list
240 auto appInfo = m_taskController->getInfoForApp(appId);
241 if (!appInfo) {
242@@ -360,10 +385,12 @@
243 */
244 bool ApplicationManager::stopApplication(const QString &inputAppId)
245 {
246+ QMutexLocker locker(&m_mutex);
247+
248 const QString appId = toShortAppIdIfPossible(inputAppId);
249 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::stopApplication - appId=" << appId;
250
251- Application *application = findApplication(appId);
252+ Application *application = findApplicationMutexHeld(appId);
253 if (!application) {
254 qCritical() << "No such running application with appId" << appId;
255 return false;
256@@ -376,21 +403,20 @@
257
258 void ApplicationManager::onApplicationClosing(Application *application)
259 {
260+ QMutexLocker locker(&m_mutex);
261 remove(application);
262
263- connect(application, &QObject::destroyed, this, [this, application](QObject*) {
264- m_closingApplications.removeAll(application);
265- });
266 m_closingApplications.append(application);
267 }
268
269 void ApplicationManager::onProcessFailed(const QString &appId, TaskController::Error error)
270 {
271+ QMutexLocker locker(&m_mutex);
272 // Applications fail if they fail to launch, crash or are killed.
273
274 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessFailed - appId=" << appId;
275
276- Application *application = findApplication(appId);
277+ Application *application = findApplicationMutexHeld(appId);
278 if (!application) {
279 qWarning() << "ApplicationManager::onProcessFailed - upstart reports failure of application" << appId
280 << "that AppManager is not managing";
281@@ -399,15 +425,17 @@
282
283 Q_UNUSED(error); // FIXME(greyback) upstart reports app that fully started up & crashes as failing during startup??
284 application->setProcessState(Application::ProcessFailed);
285- application->setPid(0);
286+ setApplicationPid(application, 0);
287 }
288
289 void ApplicationManager::onProcessStopped(const QString &appId)
290 {
291+ QMutexLocker locker(&m_mutex);
292+
293 tracepoint(qtmir, onProcessStopped);
294 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessStopped - appId=" << appId;
295
296- Application *application = findApplication(appId);
297+ Application *application = findApplicationMutexHeld(appId);
298 if (!application) {
299 application = findClosingApplication(appId);
300 }
301@@ -422,15 +450,17 @@
302 // we don't want to override what onProcessFailed already set.
303 if (application->processState() != Application::ProcessFailed) {
304 application->setProcessState(Application::ProcessStopped);
305- application->setPid(0);
306+ setApplicationPid(application, 0);
307 }
308 }
309
310 void ApplicationManager::onProcessSuspended(const QString &appId)
311 {
312+ QMutexLocker locker(&m_mutex);
313+
314 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessSuspended - appId=" << appId;
315
316- Application *application = findApplication(appId);
317+ Application *application = findApplicationMutexHeld(appId);
318
319 if (!application) {
320 qDebug() << "ApplicationManager::onProcessSuspended reports stop of appId=" << appId
321@@ -443,9 +473,10 @@
322
323 void ApplicationManager::onFocusRequested(const QString& appId)
324 {
325+ QMutexLocker locker(&m_mutex);
326 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onFocusRequested - appId=" << appId;
327
328- Application *application = findApplication(appId);
329+ Application *application = findApplicationMutexHeld(appId);
330 if (application) {
331 application->requestFocus();
332 }
333@@ -453,9 +484,10 @@
334
335 void ApplicationManager::onResumeRequested(const QString& appId)
336 {
337+ QMutexLocker locker(&m_mutex);
338 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onResumeRequested - appId=" << appId;
339
340- Application *application = findApplication(appId);
341+ Application *application = findApplicationMutexHeld(appId);
342
343 if (!application) {
344 qCritical() << "ApplicationManager::onResumeRequested: No such running application" << appId;
345@@ -471,6 +503,7 @@
346
347 void ApplicationManager::onAppDataChanged(const int role)
348 {
349+ QMutexLocker locker(&m_mutex);
350 if (sender()) {
351 Application *application = static_cast<Application*>(sender());
352 QModelIndex appIndex = findIndex(application);
353@@ -480,6 +513,11 @@
354
355 void ApplicationManager::authorizeSession(const pid_t pid, bool &authorized)
356 {
357+ // This is the only function that is called from a different thread than the one
358+ // in which the object lives, that's why we use queuedAddApp
359+
360+ QMutexLocker locker(&m_mutex);
361+
362 tracepoint(qtmir, authorizeSession);
363 authorized = false; //to be proven wrong
364
365@@ -489,7 +527,7 @@
366 if (app->state() == Application::Starting) {
367 tracepoint(qtmir, appIdHasProcessId_start);
368 if (m_taskController->appIdHasProcessId(app->appId(), pid)) {
369- app->setPid(pid);
370+ setApplicationPid(app, pid);
371 authorized = true;
372 tracepoint(qtmir, appIdHasProcessId_end, 1); //found
373 return;
374@@ -538,26 +576,17 @@
375
376 // some naughty applications use a script to launch the actual application. Check for the
377 // case where shell actually launched the script.
378- Application *application = findApplication(appInfo->appId());
379+ Application *application = findApplicationMutexHeld(appInfo->appId());
380 if (application && application->state() == Application::Starting) {
381 qCDebug(QTMIR_APPLICATIONS) << "Process with pid" << pid << "appeared, attaching to existing entry"
382 << "in application list with appId:" << application->appId();
383- application->setPid(pid);
384+ setApplicationPid(application, pid);
385 authorized = true;
386 return;
387 }
388
389- qCDebug(QTMIR_APPLICATIONS) << "New process with pid" << pid << "appeared, adding new application to the"
390- << "application list with appId:" << appInfo->appId();
391-
392- QStringList arguments(info->asStringList());
393- application = new Application(
394- m_sharedWakelock,
395- appInfo,
396- arguments,
397- this);
398- application->setPid(pid);
399- add(application);
400+ const QStringList arguments(info->asStringList());
401+ queuedAddApp(appInfo, arguments, pid);
402 authorized = true;
403 }
404
405@@ -569,6 +598,7 @@
406
407 auto qtmirSurface = static_cast<qtmir::MirSurfaceInterface*>(surface);
408
409+ QMutexLocker locker(&m_mutex);
410 return findApplicationWithPid(miral::pid_of(qtmirSurface->session()->session()));
411 }
412
413@@ -585,13 +615,29 @@
414 return nullptr;
415
416 for (Application *app : m_applications) {
417- if (app->pid() == pid) {
418+ if (m_applicationsPid.value(app) == pid) {
419 return app;
420 }
421 }
422 return nullptr;
423 }
424
425+void ApplicationManager::addApp(const QSharedPointer<qtmir::ApplicationInfo> &appInfo, const QStringList &arguments, const pid_t pid)
426+{
427+ QMutexLocker locker(&m_mutex);
428+
429+ qCDebug(QTMIR_APPLICATIONS) << "New process with pid" << pid << "appeared, adding new application to the"
430+ << "application list with appId:" << appInfo->appId();
431+
432+ Application *application = new Application(
433+ m_sharedWakelock,
434+ appInfo,
435+ arguments,
436+ this);
437+ setApplicationPid(application, pid);
438+ add(application);
439+}
440+
441 void ApplicationManager::add(Application* application)
442 {
443 Q_ASSERT(application != nullptr);
444@@ -602,6 +648,24 @@
445 }
446 DEBUG_MSG << "(appId=" << application->appId() << ")";
447
448+ connect(application, &QObject::destroyed, this, [this, application] {
449+ const pid_t pid = m_applicationsPid.value(application);
450+ if (pid != 0) {
451+ InitialSurfaceSizes::remove(pid);
452+ m_applicationsPid.remove(application);
453+ }
454+ m_closingApplications.removeAll(application);
455+ });
456+ connect(application, &Application::initialSurfaceSizeChanged, this, [this, application] {
457+ const pid_t pid = m_applicationsPid.value(application);
458+ if (pid != 0) {
459+ const QSize size = application->initialSurfaceSize();
460+ if (size.isValid()) {
461+ InitialSurfaceSizes::set(pid, size);
462+ }
463+ }
464+ });
465+
466 Q_ASSERT(!m_modelUnderChange);
467 m_modelUnderChange = true;
468
469@@ -638,11 +702,14 @@
470 Qt::QueuedConnection);
471
472 connect(application, &Application::stopProcessRequested, this, [=]() {
473- if (!m_taskController->stop(appId) && application->pid() > 0) {
474- qWarning() << "FAILED to ask Upstart to stop application with appId" << appId
475- << "Sending SIGTERM to process:" << appId;
476- kill(application->pid(), SIGTERM);
477- application->setProcessState(Application::ProcessStopped);
478+ if (!m_taskController->stop(appId)) {
479+ const pid_t pid = m_applicationsPid.value(application);
480+ if (pid > 0) {
481+ qWarning() << "FAILED to ask Upstart to stop application with appId" << appId
482+ << "Sending SIGTERM to process:" << appId;
483+ kill(pid, SIGTERM);
484+ application->setProcessState(Application::ProcessStopped);
485+ }
486 }
487 });
488
489@@ -734,8 +801,24 @@
490 return nullptr;
491 }
492
493+void ApplicationManager::setApplicationPid(Application *app, pid_t pid)
494+{
495+ const pid_t oldPid = m_applicationsPid.value(app);
496+ if (oldPid != 0) {
497+ InitialSurfaceSizes::remove(oldPid);
498+ }
499+
500+ m_applicationsPid.insert(app, pid);
501+
502+ if (app->initialSurfaceSize().isValid() && pid != 0) {
503+ InitialSurfaceSizes::set(pid, app->initialSurfaceSize());
504+ }
505+}
506+
507 void ApplicationManager::onSessionStarting(SessionInterface *qmlSession)
508 {
509+ QMutexLocker locker(&m_mutex);
510+
511 Application* application = findApplicationWithSession(qmlSession->session());
512 if (application && application->state() != Application::Running) {
513 application->setSession(qmlSession);
514@@ -744,6 +827,7 @@
515
516 SessionInterface *ApplicationManager::findSession(const mir::scene::Session* session) const
517 {
518+ QMutexLocker locker(&m_mutex);
519 return m_taskController->findSession(session);
520 }
521
522
523=== modified file 'src/modules/Unity/Application/application_manager.h'
524--- src/modules/Unity/Application/application_manager.h 2017-03-24 09:01:47 +0000
525+++ src/modules/Unity/Application/application_manager.h 2017-03-24 09:01:47 +0000
526@@ -65,6 +65,7 @@
527 static ApplicationManager* create();
528 static ApplicationManager* singleton();
529
530+ // Noone else will use the objects passed in this contructor
531 explicit ApplicationManager(
532 const QSharedPointer<TaskController> &taskController,
533 const QSharedPointer<SharedWakelock> &sharedWakelock,
534@@ -102,9 +103,16 @@
535 private Q_SLOTS:
536 void onAppDataChanged(const int role);
537 void onApplicationClosing(Application *application);
538+ void addApp(const QSharedPointer<qtmir::ApplicationInfo> &appInfo, const QStringList &arguments, const pid_t pid);
539+
540+Q_SIGNALS:
541+ void queuedAddApp(const QSharedPointer<qtmir::ApplicationInfo> &appInfo, const QStringList &arguments, const pid_t pid);
542
543 private:
544+ // All calls to private functions happen with the mutex held
545 qtmir::Application* findApplicationWithPid(const pid_t pid) const;
546+ Application* findApplicationMutexHeld(const QString &inputAppId) const;
547+
548 Application* findApplicationWithSession(const std::shared_ptr<mir::scene::Session> &session);
549 void setFocused(Application *application);
550 void add(Application *application);
551@@ -117,7 +125,10 @@
552 Application* findApplicationWithPromptSession(const mir::scene::PromptSession* promptSession);
553 Application *findClosingApplication(const QString &inputAppId) const;
554
555+ void setApplicationPid(Application *application, pid_t pid);
556+
557 QList<Application*> m_applications;
558+ QHash<Application*, pid_t> m_applicationsPid;
559 DBusFocusInfo *m_dbusFocusInfo;
560 QSharedPointer<TaskController> m_taskController;
561 QSharedPointer<ProcInfo> m_procInfo;
562@@ -130,6 +141,7 @@
563
564 friend class Application;
565 friend class DBusWindowStack;
566+ mutable QMutex m_mutex;
567 };
568
569 } // namespace qtmir
570
571=== modified file 'src/modules/Unity/Application/taskcontroller.cpp'
572--- src/modules/Unity/Application/taskcontroller.cpp 2017-02-21 18:46:30 +0000
573+++ src/modules/Unity/Application/taskcontroller.cpp 2017-03-24 09:01:47 +0000
574@@ -53,7 +53,7 @@
575
576 auto sessionAuthorizer = static_cast<SessionAuthorizer*>(nativeInterface->nativeResourceForIntegration("SessionAuthorizer"));
577 QObject::connect(sessionAuthorizer, &SessionAuthorizer::requestAuthorizationForSession,
578- this, &TaskController::onAuthorizationForSessionRequested, Qt::BlockingQueuedConnection);
579+ this, &TaskController::authorizationRequestedForSession, Qt::DirectConnection);
580 }
581
582 TaskController::TaskController(std::shared_ptr<PromptSessionManager> &promptSessionManager, QObject *parent)
583@@ -159,11 +159,6 @@
584 return nullptr;
585 }
586
587-void TaskController::onAuthorizationForSessionRequested(const pid_t &pid, bool &authorized)
588-{
589- Q_EMIT authorizationRequestedForSession(pid, authorized);
590-}
591-
592 void TaskController::connectToAppNotifier(AppNotifier *appNotifier)
593 {
594 QObject::connect(appNotifier, &AppNotifier::appAdded,
595
596=== modified file 'src/modules/Unity/Application/taskcontroller.h'
597--- src/modules/Unity/Application/taskcontroller.h 2017-02-21 18:46:30 +0000
598+++ src/modules/Unity/Application/taskcontroller.h 2017-03-24 09:01:47 +0000
599@@ -104,8 +104,6 @@
600 void onPromptProviderAdded(const qtmir::PromptSession &promptSession, const miral::Application &);
601 void onPromptProviderRemoved(const qtmir::PromptSession &promptSession, const miral::Application &);
602
603- void onAuthorizationForSessionRequested(const pid_t &pid, bool &authorized);
604-
605 protected:
606 TaskController(QObject *parent = nullptr);
607 TaskController(std::shared_ptr<PromptSessionManager>&, QObject *parent = nullptr);

Subscribers

People subscribed via source and target branches