Merge lp:~aacid/qtmir/fixAuthorizeDeadlock into lp:qtmir
- fixAuthorizeDeadlock
- Merge into trunk
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 |
Related bugs: |
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 ApplicationMana
Instead of using a BlockingQueuedC
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.
findApplication
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
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:620
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 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 directlyFixes crash we had on a test
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:621
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Albert Astals Cid (aacid) wrote : | # |
> Ubuntu-app-launch library causes the TaskController:
> be emitted. I *think* this happens on an internal UAL thread. So the slot
> ApplicationMana
Can not reproduce that, added QThread:
ubuntu-
from a terminal.
- 622. By Albert Astals Cid
-
spacing
Albert Astals Cid (aacid) wrote : | # |
> > Ubuntu-app-launch library causes the TaskController:
> to
> > be emitted. I *think* this happens on an internal UAL thread. So the slot
> > ApplicationMana
>
> Can not reproduce that, added QThread:
> preStartCallback lambda and to ApplicationMana
> 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?
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:622
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
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.
- 623. By Albert Astals Cid
-
Merge
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:623
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Gerry Boland (gerboland) wrote : | # |
Yep, tested and the order of events is as you say. Works reliably, thank you
Preview Diff
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); |
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: :startApplicati on wasn't called.
Ubuntu-app-launch library causes the TaskController: :processStartin g signal to be emitted. I *think* this happens on an internal UAL thread. So the slot ApplicationMana ger::onProcessS tarting will be called as a queued event.
Then before this MP, TaskController: :authorizationR equestedForSess ion signal is fired from a Mir thread, causing the slot AppMan: :authorizeSessi on 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: :processStartin g signal emitted :authorizationR equestedForSess ion signal emitted
- TaskController:
- TaskController:
- 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.