Merge lp:~dandrader/qtmir/bogusRespawn-lp1575577 into lp:qtmir

Proposed by Daniel d'Andrada
Status: Merged
Approved by: Gerry Boland
Approved revision: 484
Merged at revision: 491
Proposed branch: lp:~dandrader/qtmir/bogusRespawn-lp1575577
Merge into: lp:qtmir
Diff against target: 203 lines (+87/-30)
4 files modified
src/modules/Unity/Application/application.cpp (+54/-23)
src/modules/Unity/Application/application.h (+2/-3)
tests/modules/Application/application_test.cpp (+27/-0)
tests/modules/ApplicationManager/application_manager_test.cpp (+4/-4)
To merge this branch: bzr merge lp:~dandrader/qtmir/bogusRespawn-lp1575577
Reviewer Review Type Date Requested Status
Gerry Boland (community) Approve
Unity8 CI Bot (community) continuous-integration Approve
Michael Terry (community) testing Approve
Review via email: mp+293526@code.launchpad.net

Commit message

Application: Don't respawn if closed while still starting up

+ refactoring of Application::onSessionStateChanged to explicitly cover every single internal state on session stopping.

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, although I couldn't reproduce the bug ever. But wrote a regression test that covers it well though.

* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
Not applicable.

To post a comment you must log in.
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

FAILED: Continuous integration, rev:483
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-ci/195/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/1480
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/1445
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/1445
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/1445
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/1445/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/1445
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/1445/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/1445
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/1445/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/1445
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/1445/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/1445
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/1445/artifact/output/*zip*/output.zip
    FAILURE: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/1445/console

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

review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Terry (mterry) wrote :

I am not qualified to review the refactor itself, but I tested the debs and I can't reproduce anymore (and it was very easy for me to reproduce before, so I believe it's fixed).

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

+ switch(m_state) {
Missing space between "switch" and the bracket.

+ /* killed by upstart while in background. Keep it in the window list
upstart doesn't kill apps in background, the kernel OOM killer does.

+ /* we're not able to respawn this application */
I think it's a good idea to explain some reasons why, e.g. "not managed by upstart (launched via cmd line by user)"

+void Application::onSessionStopped()
name sounds like a slot. Not a complaint, just initial thought on reading the code.

Nothing (except existing tests) is using the canBeResumed() method now. Please either use it in this code, or remove it completely. I would lean toward keeping it, as it interprets the meaning of "processState == ProcessUnknown" slightly more clearly in the code. But I'll not argue with your decision.

I'm going to run some UITK AP tests, since they're great at exercising AppMan code.

review: Needs Fixing
484. By Daniel d'Andrada

Address review comments

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

On 03/05/2016 11:44, Gerry Boland wrote:
> Review: Needs Fixing
>
> + switch(m_state) {
> Missing space between "switch" and the bracket.

Fixed.

>
> + /* killed by upstart while in background. Keep it in the window list
> upstart doesn't kill apps in background, the kernel OOM killer does.

Fixed.

> + /* we're not able to respawn this application */
> I think it's a good idea to explain some reasons why, e.g. "not managed by upstart (launched via cmd line by user)"

Done.

> +void Application::onSessionStopped()
> name sounds like a slot. Not a complaint, just initial thought on reading the code.

Suggestions?

> Nothing (except existing tests) is using the canBeResumed() method now. Please either use it in this code, or remove it completely. I would lean toward keeping it, as it interprets the meaning of "processState == ProcessUnknown" slightly more clearly in the code. But I'll not argue with your decision.

Removed canBeResumed() and updated tests.

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

PASSED: Continuous integration, rev:484
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-ci/198/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/1496
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/1460
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/1460
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/1460
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/1460/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/1460
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/1460/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/1460
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/1460/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/1460
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/1460/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/1460
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/1460/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/1460
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/1460/artifact/output/*zip*/output.zip

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

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

I tested with ubuntu-ui-toolkit AP tests - always a good tester - and any issues that appeared with due to other reasons. This looks good

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 2016-04-26 08:56:36 +0000
3+++ src/modules/Unity/Application/application.cpp 2016-05-03 15:28:09 +0000
4@@ -427,11 +427,6 @@
5 return m_session ? m_session->fullscreen() : false;
6 }
7
8-bool Application::canBeResumed() const
9-{
10- return m_processState != ProcessUnknown;
11-}
12-
13 pid_t Application::pid() const
14 {
15 return m_pid;
16@@ -774,28 +769,64 @@
17 Q_EMIT suspendProcessRequested();
18 break;
19 case Session::Stopped:
20- if ((m_state == InternalState::SuspendingWaitSession || m_state == InternalState::SuspendingWaitProcess)
21- && m_processState != Application::ProcessFailed) {
22- // Session stopped normally while we're waiting for suspension
23+ onSessionStopped();
24+ }
25+}
26+
27+void Application::onSessionStopped()
28+{
29+ switch (m_state) {
30+ case InternalState::Starting:
31+ /* application has stopped before it managed to create a surface, we can
32+ assume it crashed on startup, and thus cannot be resumed */
33+ setInternalState(InternalState::Stopped);
34+ break;
35+ case InternalState::Running:
36+ /* application is on foreground, if Mir reports the application disconnects,
37+ it either crashed or stopped itself. Either way, it must go away. */
38+ setInternalState(InternalState::Stopped);
39+ break;
40+ case InternalState::RunningInBackground:
41+ if (m_processState == Application::ProcessFailed) {
42+ /* killed by the Out-Of-Memory killer while in background. Keep it in the window list
43+ as the user didn't want it to go away */
44+ setInternalState(InternalState::StoppedResumable);
45+ } else {
46+ /* the application closed itself while in the background. Let it go away */
47+ setInternalState(InternalState::Stopped);
48+ }
49+ break;
50+ case InternalState::SuspendingWaitSession:
51+ case InternalState::SuspendingWaitProcess:
52+ if (m_processState == Application::ProcessFailed) {
53+ /* killed by the Out-Of-Memory killer while suspended (or getting there), keep it around as the user
54+ doesn't expect it to disappear */
55+ setInternalState(InternalState::StoppedResumable);
56+ } else {
57+ /* Session stopped normally while we're waiting for suspension */
58 stop();
59 setInternalState(InternalState::Stopped);
60- } else if (!canBeResumed()
61- || m_state == InternalState::Starting
62- || m_state == InternalState::Running
63- || m_state == InternalState::Closing) {
64- /* 1. application is not managed by upstart
65- * 2. application is managed by upstart, but has stopped before it managed
66- * to create a surface, we can assume it crashed on startup, and thus
67- * cannot be resumed
68- * 3. application is managed by upstart and is in foreground (i.e. has
69- * Running state), if Mir reports the application disconnects, it
70- * either crashed or stopped itself.
71- * 4. We're expecting the application to stop after a close request
72- */
73- setInternalState(InternalState::Stopped);
74+ }
75+ break;
76+ case InternalState::Suspended:
77+ if (m_processState != ProcessUnknown) {
78+ /* If the user explicly closed this application, we would have it resumed before doing so.
79+ Since this is not the case, keep it around. */
80+ setInternalState(InternalState::StoppedResumable);
81 } else {
82- setInternalState(InternalState::StoppedResumable);
83+ /* We're not able to respawn this application because it's not managed by upstart
84+ (probably was launched via cmd line by user) */
85+ setInternalState(InternalState::Stopped);
86 }
87+ break;
88+ case InternalState::Closing:
89+ /* We're expecting the application to stop after a close request */
90+ setInternalState(InternalState::Stopped);
91+ break;
92+ case InternalState::StoppedResumable:
93+ case InternalState::Stopped:
94+ /* NOOP */
95+ break;
96 }
97 }
98
99
100=== modified file 'src/modules/Unity/Application/application.h'
101--- src/modules/Unity/Application/application.h 2016-04-26 08:56:36 +0000
102+++ src/modules/Unity/Application/application.h 2016-05-03 15:28:09 +0000
103@@ -56,7 +56,7 @@
104 Q_DECLARE_FLAGS(Stages, Stage)
105
106 enum ProcessState {
107- ProcessUnknown,
108+ ProcessUnknown, // not managed by upstart, so we can't respawn that application
109 ProcessRunning,
110 ProcessSuspended,
111 ProcessFailed, // it stopped, but because it was killed or because it crashed
112@@ -118,8 +118,6 @@
113 SessionInterface* session() const;
114 void setSession(SessionInterface *session);
115
116- bool canBeResumed() const;
117-
118 bool isValid() const;
119 bool fullscreen() const;
120
121@@ -172,6 +170,7 @@
122 void applyRequestedRunning();
123 void applyRequestedSuspended();
124 void applyClosing();
125+ void onSessionStopped();
126
127 QSharedPointer<SharedWakelock> m_sharedWakelock;
128 QSharedPointer<ApplicationInfo> m_appInfo;
129
130=== modified file 'tests/modules/Application/application_test.cpp'
131--- tests/modules/Application/application_test.cpp 2016-04-26 08:56:36 +0000
132+++ tests/modules/Application/application_test.cpp 2016-05-03 15:28:09 +0000
133@@ -589,3 +589,30 @@
134
135 EXPECT_EQ(Application::InternalState::StoppedResumable, application->internalState());
136 }
137+
138+/*
139+ Regression test for bug "App respawns if manually closed while it's launching"
140+ https://bugs.launchpad.net/ubuntu/+source/qtmir/+bug/1575577
141+ */
142+TEST_F(ApplicationTests, dontRespawnIfClosedWhileStillStartingUp)
143+{
144+ using namespace ::testing;
145+
146+ QScopedPointer<Application> application(createApplicationWithFakes());
147+
148+ application->setProcessState(Application::ProcessRunning);
149+
150+ FakeSession *session = new FakeSession;
151+
152+ application->setSession(session);
153+
154+ QSignalSpy spyStartProcess(application.data(), SIGNAL(startProcessRequested()));
155+
156+ // Close the application before it even gets a surface (it's still in "starting" state)
157+ application->close();
158+
159+ session->setState(SessionInterface::Stopped);
160+
161+ EXPECT_EQ(Application::InternalState::Stopped, application->internalState());
162+ EXPECT_EQ(0, spyStartProcess.count());
163+}
164
165=== modified file 'tests/modules/ApplicationManager/application_manager_test.cpp'
166--- tests/modules/ApplicationManager/application_manager_test.cpp 2016-04-26 08:56:36 +0000
167+++ tests/modules/ApplicationManager/application_manager_test.cpp 2016-05-03 15:28:09 +0000
168@@ -437,7 +437,7 @@
169 EXPECT_EQ(Application::Starting, theApp->state());
170 EXPECT_EQ(appId, theApp->appId());
171 EXPECT_EQ(name, theApp->name());
172- EXPECT_FALSE(theApp->canBeResumed());
173+ EXPECT_EQ(Application::ProcessUnknown, theApp->processState());
174
175 // check signals were emitted
176 EXPECT_EQ(2, countSpy.count()); //FIXME(greyback)
177@@ -478,7 +478,7 @@
178 EXPECT_EQ(Application::Starting, theApp->state());
179 EXPECT_EQ(appId, theApp->appId());
180 EXPECT_EQ(name, theApp->name());
181- EXPECT_EQ(true, theApp->canBeResumed());
182+ EXPECT_NE(Application::ProcessUnknown, theApp->processState());
183
184 // check signals were emitted
185 EXPECT_EQ(2, countSpy.count()); //FIXME(greyback)
186@@ -525,7 +525,7 @@
187 EXPECT_EQ(theApp->state(), Application::Starting);
188 EXPECT_EQ(theApp->appId(), appId);
189 EXPECT_EQ(theApp->name(), name);
190- EXPECT_EQ(theApp->canBeResumed(), false);
191+ EXPECT_EQ(theApp->processState(), Application::ProcessUnknown);
192
193 // check signals were emitted
194 EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback)
195@@ -597,7 +597,7 @@
196 EXPECT_EQ(theApp->state(), Application::Starting);
197 EXPECT_EQ(theApp->appId(), appId);
198 EXPECT_EQ(theApp->name(), name);
199- EXPECT_EQ(theApp->canBeResumed(), true);
200+ EXPECT_NE(Application::ProcessUnknown, theApp->processState());
201
202 // check only once instance in the model
203 EXPECT_EQ(applicationManager.count(), 1);

Subscribers

People subscribed via source and target branches