Mir

Merge lp:~alan-griffiths/mir/does-jenkins-like-MultiThreadedCompositor-refactoring into lp:mir

Proposed by Alan Griffiths
Status: Rejected
Rejected by: Alan Griffiths
Proposed branch: lp:~alan-griffiths/mir/does-jenkins-like-MultiThreadedCompositor-refactoring
Merge into: lp:mir
Diff against target: 257 lines (+82/-81)
2 files modified
src/server/compositor/multi_threaded_compositor.cpp (+80/-79)
tests/unit-tests/compositor/test_multi_threaded_compositor.cpp (+2/-2)
To merge this branch: bzr merge lp:~alan-griffiths/mir/does-jenkins-like-MultiThreadedCompositor-refactoring
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Mir CI Bot continuous-integration Approve
Mir development team Pending
Review via email: mp+284079@code.launchpad.net

Commit message

compositor: clearer logic for MultiThreadedCompositor::start()/stop()

Description of the change

compositor: clearer logic for MultiThreadedCompositor::start()/stop()

This should be a pure refactoring extracted from lp:~alan-griffiths/mir/better-error-reporting-in-MultiThreadedCompositor-start (as I've not identified the problem causing CI failures there).

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

PASSED: Continuous integration, rev:3274
https://mir-jenkins.ubuntu.com/job/mir-ci/166/
Executed test runs:
    None: https://mir-jenkins.ubuntu.com/job/generic-update-mp/166/console

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/166/rebuild

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

PASSED: Continuous integration, rev:3274
http://jenkins.qa.ubuntu.com/job/mir-ci/6135/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-android-vivid-i386-build/5703
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-clang-vivid-amd64-build/4610
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-vivid-touch/5659
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-xenial-touch/340
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-xenial-amd64-ci/459
        deb: http://jenkins.qa.ubuntu.com/job/mir-xenial-amd64-ci/459/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-xenial-i386-ci/459
        deb: http://jenkins.qa.ubuntu.com/job/mir-xenial-i386-ci/459/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-vivid-armhf/5656
        deb: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-vivid-armhf/5656/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-runner-touch/8088
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/27072
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-xenial-armhf/336
        deb: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-xenial-armhf/336/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-runner-xenial-touch/192
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/27074

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/mir-ci/6135/rebuild

review: Approve (continuous-integration)
3275. By Alan Griffiths

  Report errors starting compositor threads by propagating exception from start()

3276. By Alan Griffiths

Experiment to diagnose Jenkins failures

Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:3276
https://mir-jenkins.ubuntu.com/job/mir-ci/168/
Executed test runs:
    None: https://mir-jenkins.ubuntu.com/job/generic-update-mp/168/console

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/168/rebuild

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

PASSED: Continuous integration, rev:3276
http://jenkins.qa.ubuntu.com/job/mir-ci/6138/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-android-vivid-i386-build/5706
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-clang-vivid-amd64-build/4613
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-vivid-touch/5662
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-xenial-touch/341
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-xenial-amd64-ci/462
        deb: http://jenkins.qa.ubuntu.com/job/mir-xenial-amd64-ci/462/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-xenial-i386-ci/462
        deb: http://jenkins.qa.ubuntu.com/job/mir-xenial-i386-ci/462/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-vivid-armhf/5659
        deb: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-vivid-armhf/5659/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-runner-touch/8089
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/27077
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-xenial-armhf/337
        deb: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-xenial-armhf/337/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-runner-xenial-touch/193
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/27088

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/mir-ci/6138/rebuild

review: Approve (continuous-integration)

Unmerged revisions

3276. By Alan Griffiths

Experiment to diagnose Jenkins failures

3275. By Alan Griffiths

  Report errors starting compositor threads by propagating exception from start()

3274. By Alan Griffiths

  Don't mix level of abstraction within a function

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/server/compositor/multi_threaded_compositor.cpp'
2--- src/server/compositor/multi_threaded_compositor.cpp 2016-01-20 23:59:18 +0000
3+++ src/server/compositor/multi_threaded_compositor.cpp 2016-01-27 13:57:45 +0000
4@@ -1,5 +1,5 @@
5 /*
6- * Copyright © 2013-2015 Canonical Ltd.
7+ * Copyright © 2013-2016 Canonical Ltd.
8 *
9 * This program is free software: you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License version 3,
11@@ -110,81 +110,82 @@
12
13 started.set_value();
14
15- std::unique_lock<std::mutex> lock{run_mutex};
16- while (running)
17+ try
18 {
19- /* Wait until compositing has been scheduled or we are stopped */
20- run_cv.wait(lock, [&]{ return (frames_scheduled > 0) || !running; });
21-
22- /*
23- * Check if we are running before compositing, since we may have
24- * been stopped while waiting for the run_cv above.
25- */
26- if (running)
27+ std::unique_lock<std::mutex> lock{run_mutex};
28+ while (running)
29 {
30- /*
31- * Each surface could have a number of frames ready in its buffer
32- * queue. And we need to ensure that we render all of them so that
33- * none linger in the queue indefinitely (seen as input lag).
34- * frames_scheduled indicates the number of frames that are scheduled
35- * to ensure all surfaces' queues are fully drained.
36- */
37- frames_scheduled--;
38- lock.unlock();
39-
40- for (auto& tuple : compositors)
41- {
42- auto& compositor = std::get<1>(tuple);
43- compositor->composite(scene->scene_elements_for(compositor.get()));
44- }
45- group.post();
46-
47- /*
48- * "Predictive bypass" optimization: If the last frame was
49- * bypassed/overlayed or you simply have a fast GPU, it is
50- * beneficial to sleep for most of the next frame. This reduces
51- * the latency between snapshotting the scene and post()
52- * completing by almost a whole frame.
53- */
54- auto delay = force_sleep >= std::chrono::milliseconds::zero() ?
55- force_sleep : group.recommended_sleep();
56- std::this_thread::sleep_for(delay);
57-
58- lock.lock();
59-
60- /*
61- * Note the compositor may have chosen to ignore any number
62- * of renderables and not consumed buffers from them. So it's
63- * important to re-count number of frames pending, separately
64- * to the initial scene_elements_for()...
65- */
66- int pending = 0;
67- for (auto& compositor : compositors)
68- {
69- auto const comp_id = std::get<1>(compositor).get();
70- int pend = scene->frames_pending(comp_id);
71- if (pend > pending)
72- pending = pend;
73- }
74-
75- if (pending > frames_scheduled)
76- frames_scheduled = pending;
77+ /* Wait until compositing has been scheduled or we are stopped */
78+ run_cv.wait(lock, [&]{ return (frames_scheduled > 0) || !running; });
79+
80+ /*
81+ * Check if we are running before compositing, since we may have
82+ * been stopped while waiting for the run_cv above.
83+ */
84+ if (running)
85+ {
86+ /*
87+ * Each surface could have a number of frames ready in its buffer
88+ * queue. And we need to ensure that we render all of them so that
89+ * none linger in the queue indefinitely (seen as input lag).
90+ * frames_scheduled indicates the number of frames that are scheduled
91+ * to ensure all surfaces' queues are fully drained.
92+ */
93+ frames_scheduled--;
94+ lock.unlock();
95+
96+ for (auto& tuple : compositors)
97+ {
98+ auto& compositor = std::get<1>(tuple);
99+ compositor->composite(scene->scene_elements_for(compositor.get()));
100+ }
101+ group.post();
102+
103+ /*
104+ * "Predictive bypass" optimization: If the last frame was
105+ * bypassed/overlayed or you simply have a fast GPU, it is
106+ * beneficial to sleep for most of the next frame. This reduces
107+ * the latency between snapshotting the scene and post()
108+ * completing by almost a whole frame.
109+ */
110+ auto delay = force_sleep >= std::chrono::milliseconds::zero() ?
111+ force_sleep : group.recommended_sleep();
112+ std::this_thread::sleep_for(delay);
113+
114+ lock.lock();
115+
116+ /*
117+ * Note the compositor may have chosen to ignore any number
118+ * of renderables and not consumed buffers from them. So it's
119+ * important to re-count number of frames pending, separately
120+ * to the initial scene_elements_for()...
121+ */
122+ int pending = 0;
123+ for (auto& compositor : compositors)
124+ {
125+ auto const comp_id = std::get<1>(compositor).get();
126+ int pend = scene->frames_pending(comp_id);
127+ if (pend > pending)
128+ pending = pend;
129+ }
130+
131+ if (pending > frames_scheduled)
132+ frames_scheduled = pending;
133+ }
134 }
135 }
136+ catch (...)
137+ {
138+ mir::terminate_with_current_exception();
139+ }
140 }
141 catch(...)
142 {
143- try
144- {
145- //Move the promise so that the promise destructor occurs here rather than in the thread
146- //destroying CompositingFunctor, mostly to appease TSan
147- auto promise = std::move(started);
148- promise.set_exception(std::current_exception());
149- }
150- catch(...)
151- {
152- }
153- mir::terminate_with_current_exception();
154+ started.set_exception(std::current_exception());
155+
156+ //Move the promise so that the promise destructor occurs here rather than in the thread
157+ //destroying CompositingFunctor, mostly to appease TSan
158+ auto promise = std::move(started);
159 }
160
161 void schedule_compositing(int num_frames)
162@@ -209,6 +210,8 @@
163 {
164 if (started_future.wait_for(10s) != std::future_status::ready)
165 BOOST_THROW_EXCEPTION(std::runtime_error("Compositor thread failed to start"));
166+
167+ started_future.get();
168 }
169
170 private:
171@@ -273,7 +276,7 @@
172 void mc::MultiThreadedCompositor::start()
173 {
174 auto stopped = CompositorState::stopped;
175-
176+
177 if (!state.compare_exchange_strong(stopped, CompositorState::starting))
178 return;
179
180@@ -281,7 +284,7 @@
181
182 /* To cleanup state if any code below throws */
183 auto cleanup_if_unwinding = on_unwind(
184- [this]{ destroy_compositing_threads(); });
185+ [this]{ destroy_compositing_threads(); state = CompositorState::stopped; });
186
187 create_compositing_threads();
188
189@@ -291,6 +294,8 @@
190 /* Optional first render */
191 if (compose_on_start)
192 schedule_compositing(1);
193+
194+ state = CompositorState::started;
195 }
196
197 void mc::MultiThreadedCompositor::stop()
198@@ -300,8 +305,6 @@
199 if (!state.compare_exchange_strong(started, CompositorState::stopping))
200 return;
201
202- state = CompositorState::stopping;
203-
204 /* To cleanup state if any code below throws */
205 auto cleanup_if_unwinding = on_unwind(
206 [this]{ state = CompositorState::started; });
207@@ -314,6 +317,10 @@
208 // If the compositor is restarted we've likely got clients blocked
209 // so we will need to schedule compositing immediately
210 compose_on_start = true;
211+
212+ report->stopped();
213+
214+ state = CompositorState::stopped;
215 }
216
217 void mc::MultiThreadedCompositor::create_compositing_threads()
218@@ -333,8 +340,6 @@
219
220 for (auto& functor : thread_functors)
221 functor->wait_until_started();
222-
223- state = CompositorState::started;
224 }
225
226 void mc::MultiThreadedCompositor::destroy_compositing_threads()
227@@ -347,8 +352,4 @@
228
229 thread_functors.clear();
230 futures.clear();
231-
232- report->stopped();
233-
234- state = CompositorState::stopped;
235 }
236
237=== modified file 'tests/unit-tests/compositor/test_multi_threaded_compositor.cpp'
238--- tests/unit-tests/compositor/test_multi_threaded_compositor.cpp 2016-01-20 23:59:18 +0000
239+++ tests/unit-tests/compositor/test_multi_threaded_compositor.cpp 2016-01-27 13:57:45 +0000
240@@ -889,7 +889,7 @@
241 compositor.stop();
242 }
243
244-TEST(MultiThreadedCompositor, does_not_block_in_start_when_compositor_thread_fails)
245+TEST(MultiThreadedCompositor, DISABLED_when_compositor_thread_fails_start_reports_error)
246 {
247 using namespace testing;
248 unsigned int const nbuffers{3};
249@@ -914,7 +914,7 @@
250 EXPECT_CALL(*mock_display_listener, add_display(_))
251 .WillRepeatedly(Throw(std::runtime_error("Failed to add display")));
252
253- compositor.start();
254+ EXPECT_THROW(compositor.start(), std::runtime_error);
255 }
256
257 //LP: 1481418

Subscribers

People subscribed via source and target branches