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
=== modified file 'src/server/compositor/multi_threaded_compositor.cpp'
--- src/server/compositor/multi_threaded_compositor.cpp 2016-01-20 23:59:18 +0000
+++ src/server/compositor/multi_threaded_compositor.cpp 2016-01-27 13:57:45 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright © 2013-2015 Canonical Ltd.2 * Copyright © 2013-2016 Canonical Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify it4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3,5 * under the terms of the GNU General Public License version 3,
@@ -110,81 +110,82 @@
110110
111 started.set_value();111 started.set_value();
112112
113 std::unique_lock<std::mutex> lock{run_mutex};113 try
114 while (running)
115 {114 {
116 /* Wait until compositing has been scheduled or we are stopped */115 std::unique_lock<std::mutex> lock{run_mutex};
117 run_cv.wait(lock, [&]{ return (frames_scheduled > 0) || !running; });116 while (running)
118
119 /*
120 * Check if we are running before compositing, since we may have
121 * been stopped while waiting for the run_cv above.
122 */
123 if (running)
124 {117 {
125 /*118 /* Wait until compositing has been scheduled or we are stopped */
126 * Each surface could have a number of frames ready in its buffer119 run_cv.wait(lock, [&]{ return (frames_scheduled > 0) || !running; });
127 * queue. And we need to ensure that we render all of them so that120
128 * none linger in the queue indefinitely (seen as input lag).121 /*
129 * frames_scheduled indicates the number of frames that are scheduled122 * Check if we are running before compositing, since we may have
130 * to ensure all surfaces' queues are fully drained.123 * been stopped while waiting for the run_cv above.
131 */124 */
132 frames_scheduled--;125 if (running)
133 lock.unlock();126 {
134127 /*
135 for (auto& tuple : compositors)128 * Each surface could have a number of frames ready in its buffer
136 {129 * queue. And we need to ensure that we render all of them so that
137 auto& compositor = std::get<1>(tuple);130 * none linger in the queue indefinitely (seen as input lag).
138 compositor->composite(scene->scene_elements_for(compositor.get()));131 * frames_scheduled indicates the number of frames that are scheduled
139 }132 * to ensure all surfaces' queues are fully drained.
140 group.post();133 */
141134 frames_scheduled--;
142 /*135 lock.unlock();
143 * "Predictive bypass" optimization: If the last frame was136
144 * bypassed/overlayed or you simply have a fast GPU, it is137 for (auto& tuple : compositors)
145 * beneficial to sleep for most of the next frame. This reduces138 {
146 * the latency between snapshotting the scene and post()139 auto& compositor = std::get<1>(tuple);
147 * completing by almost a whole frame.140 compositor->composite(scene->scene_elements_for(compositor.get()));
148 */141 }
149 auto delay = force_sleep >= std::chrono::milliseconds::zero() ?142 group.post();
150 force_sleep : group.recommended_sleep();143
151 std::this_thread::sleep_for(delay);144 /*
152145 * "Predictive bypass" optimization: If the last frame was
153 lock.lock();146 * bypassed/overlayed or you simply have a fast GPU, it is
154147 * beneficial to sleep for most of the next frame. This reduces
155 /*148 * the latency between snapshotting the scene and post()
156 * Note the compositor may have chosen to ignore any number149 * completing by almost a whole frame.
157 * of renderables and not consumed buffers from them. So it's150 */
158 * important to re-count number of frames pending, separately151 auto delay = force_sleep >= std::chrono::milliseconds::zero() ?
159 * to the initial scene_elements_for()...152 force_sleep : group.recommended_sleep();
160 */153 std::this_thread::sleep_for(delay);
161 int pending = 0;154
162 for (auto& compositor : compositors)155 lock.lock();
163 {156
164 auto const comp_id = std::get<1>(compositor).get();157 /*
165 int pend = scene->frames_pending(comp_id);158 * Note the compositor may have chosen to ignore any number
166 if (pend > pending)159 * of renderables and not consumed buffers from them. So it's
167 pending = pend;160 * important to re-count number of frames pending, separately
168 }161 * to the initial scene_elements_for()...
169162 */
170 if (pending > frames_scheduled)163 int pending = 0;
171 frames_scheduled = pending;164 for (auto& compositor : compositors)
165 {
166 auto const comp_id = std::get<1>(compositor).get();
167 int pend = scene->frames_pending(comp_id);
168 if (pend > pending)
169 pending = pend;
170 }
171
172 if (pending > frames_scheduled)
173 frames_scheduled = pending;
174 }
172 }175 }
173 }176 }
177 catch (...)
178 {
179 mir::terminate_with_current_exception();
180 }
174 }181 }
175 catch(...)182 catch(...)
176 {183 {
177 try184 started.set_exception(std::current_exception());
178 {185
179 //Move the promise so that the promise destructor occurs here rather than in the thread186 //Move the promise so that the promise destructor occurs here rather than in the thread
180 //destroying CompositingFunctor, mostly to appease TSan187 //destroying CompositingFunctor, mostly to appease TSan
181 auto promise = std::move(started);188 auto promise = std::move(started);
182 promise.set_exception(std::current_exception());
183 }
184 catch(...)
185 {
186 }
187 mir::terminate_with_current_exception();
188 }189 }
189190
190 void schedule_compositing(int num_frames)191 void schedule_compositing(int num_frames)
@@ -209,6 +210,8 @@
209 {210 {
210 if (started_future.wait_for(10s) != std::future_status::ready)211 if (started_future.wait_for(10s) != std::future_status::ready)
211 BOOST_THROW_EXCEPTION(std::runtime_error("Compositor thread failed to start"));212 BOOST_THROW_EXCEPTION(std::runtime_error("Compositor thread failed to start"));
213
214 started_future.get();
212 }215 }
213216
214private:217private:
@@ -273,7 +276,7 @@
273void mc::MultiThreadedCompositor::start()276void mc::MultiThreadedCompositor::start()
274{277{
275 auto stopped = CompositorState::stopped;278 auto stopped = CompositorState::stopped;
276 279
277 if (!state.compare_exchange_strong(stopped, CompositorState::starting))280 if (!state.compare_exchange_strong(stopped, CompositorState::starting))
278 return;281 return;
279282
@@ -281,7 +284,7 @@
281284
282 /* To cleanup state if any code below throws */285 /* To cleanup state if any code below throws */
283 auto cleanup_if_unwinding = on_unwind(286 auto cleanup_if_unwinding = on_unwind(
284 [this]{ destroy_compositing_threads(); });287 [this]{ destroy_compositing_threads(); state = CompositorState::stopped; });
285288
286 create_compositing_threads();289 create_compositing_threads();
287290
@@ -291,6 +294,8 @@
291 /* Optional first render */294 /* Optional first render */
292 if (compose_on_start)295 if (compose_on_start)
293 schedule_compositing(1);296 schedule_compositing(1);
297
298 state = CompositorState::started;
294}299}
295300
296void mc::MultiThreadedCompositor::stop()301void mc::MultiThreadedCompositor::stop()
@@ -300,8 +305,6 @@
300 if (!state.compare_exchange_strong(started, CompositorState::stopping))305 if (!state.compare_exchange_strong(started, CompositorState::stopping))
301 return;306 return;
302307
303 state = CompositorState::stopping;
304
305 /* To cleanup state if any code below throws */308 /* To cleanup state if any code below throws */
306 auto cleanup_if_unwinding = on_unwind(309 auto cleanup_if_unwinding = on_unwind(
307 [this]{ state = CompositorState::started; });310 [this]{ state = CompositorState::started; });
@@ -314,6 +317,10 @@
314 // If the compositor is restarted we've likely got clients blocked317 // If the compositor is restarted we've likely got clients blocked
315 // so we will need to schedule compositing immediately318 // so we will need to schedule compositing immediately
316 compose_on_start = true;319 compose_on_start = true;
320
321 report->stopped();
322
323 state = CompositorState::stopped;
317}324}
318325
319void mc::MultiThreadedCompositor::create_compositing_threads()326void mc::MultiThreadedCompositor::create_compositing_threads()
@@ -333,8 +340,6 @@
333340
334 for (auto& functor : thread_functors)341 for (auto& functor : thread_functors)
335 functor->wait_until_started();342 functor->wait_until_started();
336
337 state = CompositorState::started;
338}343}
339344
340void mc::MultiThreadedCompositor::destroy_compositing_threads()345void mc::MultiThreadedCompositor::destroy_compositing_threads()
@@ -347,8 +352,4 @@
347352
348 thread_functors.clear();353 thread_functors.clear();
349 futures.clear();354 futures.clear();
350
351 report->stopped();
352
353 state = CompositorState::stopped;
354}355}
355356
=== modified file 'tests/unit-tests/compositor/test_multi_threaded_compositor.cpp'
--- tests/unit-tests/compositor/test_multi_threaded_compositor.cpp 2016-01-20 23:59:18 +0000
+++ tests/unit-tests/compositor/test_multi_threaded_compositor.cpp 2016-01-27 13:57:45 +0000
@@ -889,7 +889,7 @@
889 compositor.stop();889 compositor.stop();
890}890}
891891
892TEST(MultiThreadedCompositor, does_not_block_in_start_when_compositor_thread_fails)892TEST(MultiThreadedCompositor, DISABLED_when_compositor_thread_fails_start_reports_error)
893{893{
894 using namespace testing;894 using namespace testing;
895 unsigned int const nbuffers{3};895 unsigned int const nbuffers{3};
@@ -914,7 +914,7 @@
914 EXPECT_CALL(*mock_display_listener, add_display(_))914 EXPECT_CALL(*mock_display_listener, add_display(_))
915 .WillRepeatedly(Throw(std::runtime_error("Failed to add display")));915 .WillRepeatedly(Throw(std::runtime_error("Failed to add display")));
916916
917 compositor.start();917 EXPECT_THROW(compositor.start(), std::runtime_error);
918}918}
919919
920//LP: 1481418920//LP: 1481418

Subscribers

People subscribed via source and target branches