Merge lp:unity-system-compositor/0.1 into lp:unity-system-compositor/ubuntu

Proposed by Alexandros Frantzis
Status: Merged
Approved by: Alexandros Frantzis
Approved revision: 256
Merged at revision: 219
Proposed branch: lp:unity-system-compositor/0.1
Merge into: lp:unity-system-compositor/ubuntu
Diff against target: 527 lines (+382/-18) (has conflicts)
6 files modified
debian/changelog (+16/-3)
src/mir_screen.cpp (+47/-4)
src/mir_screen.h (+9/-2)
tests/integration-tests/CMakeLists.txt (+1/-0)
tests/integration-tests/test_deadlock_lp1491566.cpp (+283/-0)
tests/unit-tests/advanceable_timer.cpp (+26/-9)
Text conflict in debian/changelog
To merge this branch: bzr merge lp:unity-system-compositor/0.1
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Unity System Compositor Development Team Pending
Review via email: mp+272394@code.launchpad.net

Commit message

Release 0.1.3

Description of the change

Release 0.1.3

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/changelog'
2--- debian/changelog 2015-09-22 16:44:54 +0000
3+++ debian/changelog 2015-09-25 14:03:36 +0000
4@@ -1,6 +1,19 @@
5-unity-system-compositor (0.1.2+15.10.20150922-0ubuntu1) wily; urgency=medium
6-
7- [ Cemil Azizoglu ]
8+<<<<<<< TREE
9+unity-system-compositor (0.1.2+15.10.20150922-0ubuntu1) wily; urgency=medium
10+
11+ [ Cemil Azizoglu ]
12+=======
13+unity-system-compositor (0.1.3+15.10.20150925-0ubuntu1) UNRELEASED; urgency=medium
14+
15+ * Fix "unresponsive shell including power button doesn't blank/unblank
16+ display" (LP: #1491566)
17+
18+ -- Alexandros Frantzis <alexandros.frantzis@canonical.com> Fri, 25 Sep 2015 16:53:27 +0300
19+
20+unity-system-compositor (0.1.2+15.10.20150922-0ubuntu1) wily; urgency=medium
21+
22+ [ Cemil Azizoglu ]
23+>>>>>>> MERGE-SOURCE
24 * Rebuild for Mir 0.16
25
26 [ CI Train Bot ]
27
28=== modified file 'src/mir_screen.cpp'
29--- src/mir_screen.cpp 2015-08-24 18:14:36 +0000
30+++ src/mir_screen.cpp 2015-09-25 14:03:36 +0000
31@@ -18,6 +18,7 @@
32 #include "clock.h"
33
34 #include <mir/main_loop.h>
35+#include <mir/lockable_callback.h>
36 #include <mir/time/alarm_factory.h>
37 #include <mir/compositor/compositor.h>
38 #include <mir/graphics/display.h>
39@@ -32,6 +33,50 @@
40 namespace mi = mir::input;
41 namespace mg = mir::graphics;
42
43+class usc::MirScreen::LockableCallback : public mir::LockableCallback
44+{
45+public:
46+ LockableCallback(MirScreen* mir_screen)
47+ : mir_screen{mir_screen}
48+ {
49+ }
50+
51+ void lock() override
52+ {
53+ guard_lock = std::unique_lock<std::mutex>{mir_screen->guard};
54+ }
55+
56+ void unlock() override
57+ {
58+ if (guard_lock.owns_lock())
59+ guard_lock.unlock();
60+ }
61+
62+protected:
63+ MirScreen* const mir_screen;
64+ std::unique_lock<std::mutex> guard_lock;
65+};
66+
67+class usc::MirScreen::PowerOffLockableCallback : public usc::MirScreen::LockableCallback
68+{
69+ using usc::MirScreen::LockableCallback::LockableCallback;
70+
71+ void operator()() override
72+ {
73+ mir_screen->power_off_alarm_notification();
74+ }
75+};
76+
77+class usc::MirScreen::DimmerLockableCallback : public usc::MirScreen::LockableCallback
78+{
79+ using usc::MirScreen::LockableCallback::LockableCallback;
80+
81+ void operator()() override
82+ {
83+ mir_screen->dimmer_alarm_notification();
84+ }
85+};
86+
87 usc::MirScreen::MirScreen(
88 std::shared_ptr<usc::ScreenHardware> const& screen_hardware,
89 std::shared_ptr<mir::compositor::Compositor> const& compositor,
90@@ -48,9 +93,9 @@
91 alarm_factory{alarm_factory},
92 clock{clock},
93 power_off_alarm{alarm_factory->create_alarm(
94- std::bind(&usc::MirScreen::power_off_alarm_notification, this))},
95+ std::make_shared<PowerOffLockableCallback>(this))},
96 dimmer_alarm{alarm_factory->create_alarm(
97- std::bind(&usc::MirScreen::dimmer_alarm_notification, this))},
98+ std::make_shared<DimmerLockableCallback>(this))},
99 inactivity_timeouts(inactivity_timeouts),
100 notification_timeouts(notification_timeouts),
101 current_power_mode{MirPowerMode::mir_power_mode_on},
102@@ -313,14 +358,12 @@
103
104 void usc::MirScreen::power_off_alarm_notification()
105 {
106- std::lock_guard<std::mutex> lock{guard};
107 configure_display_l(MirPowerMode::mir_power_mode_off, PowerStateChangeReason::inactivity);
108 next_power_off = {};
109 }
110
111 void usc::MirScreen::dimmer_alarm_notification()
112 {
113- std::lock_guard<std::mutex> lock{guard};
114 screen_hardware->set_dim_backlight();
115 next_dimming = {};
116 }
117
118=== modified file 'src/mir_screen.h'
119--- src/mir_screen.h 2015-07-17 12:43:46 +0000
120+++ src/mir_screen.h 2015-09-25 14:03:36 +0000
121@@ -73,7 +73,16 @@
122 void register_power_state_change_handler(
123 PowerStateChangeHandler const& power_state_change_handler) override;
124
125+protected:
126+ // These are protected virtual because we need to override them in tests
127+ virtual void power_off_alarm_notification();
128+ virtual void dimmer_alarm_notification();
129+
130 private:
131+ class LockableCallback;
132+ class PowerOffLockableCallback;
133+ class DimmerLockableCallback;
134+
135 void set_screen_power_mode_l(MirPowerMode mode, PowerStateChangeReason reason);
136 void configure_display_l(MirPowerMode mode, PowerStateChangeReason reason);
137
138@@ -84,8 +93,6 @@
139 Timeouts timeouts_for(PowerStateChangeReason reason);
140 bool is_screen_change_allowed(MirPowerMode mode, PowerStateChangeReason reason);
141
142- void power_off_alarm_notification();
143- void dimmer_alarm_notification();
144 void long_press_alarm_notification();
145
146 std::shared_ptr<usc::ScreenHardware> const screen_hardware;
147
148=== modified file 'tests/integration-tests/CMakeLists.txt'
149--- tests/integration-tests/CMakeLists.txt 2015-08-26 08:00:27 +0000
150+++ tests/integration-tests/CMakeLists.txt 2015-09-25 14:03:36 +0000
151@@ -38,6 +38,7 @@
152 test_unity_input_service.cpp
153 test_external_spinner.cpp
154 test_powerd_mediator.cpp
155+ test_deadlock_lp1491566.cpp
156 )
157
158 target_link_libraries(
159
160=== added file 'tests/integration-tests/test_deadlock_lp1491566.cpp'
161--- tests/integration-tests/test_deadlock_lp1491566.cpp 1970-01-01 00:00:00 +0000
162+++ tests/integration-tests/test_deadlock_lp1491566.cpp 2015-09-25 14:03:36 +0000
163@@ -0,0 +1,283 @@
164+/*
165+ * Copyright © 2015 Canonical Ltd.
166+ *
167+ * This program is free software: you can redistribute it and/or modify
168+ * it under the terms of the GNU General Public License version 3 as
169+ * published by the Free Software Foundation.
170+ *
171+ * This program is distributed in the hope that it will be useful,
172+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
173+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
174+ * GNU General Public License for more details.
175+ *
176+ * You should have received a copy of the GNU General Public License
177+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
178+ *
179+ * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
180+ */
181+
182+#include "src/server.h"
183+#include "src/mir_screen.h"
184+#include "src/screen_hardware.h"
185+#include "src/power_state_change_reason.h"
186+#include "spin_wait.h"
187+
188+#include <mir/compositor/compositor.h>
189+#include <mir/main_loop.h>
190+#include <mir/graphics/display.h>
191+#include <mir/graphics/display_configuration.h>
192+#include <mir/graphics/gl_context.h>
193+#include <mir/input/touch_visualizer.h>
194+
195+#include <gtest/gtest.h>
196+#include <gmock/gmock.h>
197+
198+#include <thread>
199+#include <future>
200+
201+namespace mg = mir::graphics;
202+
203+namespace
204+{
205+
206+struct NullCompositor : mir::compositor::Compositor
207+{
208+ void start() override {}
209+ void stop() override {}
210+};
211+
212+struct StubDisplayConfiguration : mg::DisplayConfiguration
213+{
214+ StubDisplayConfiguration()
215+ {
216+ conf_output.power_mode = MirPowerMode::mir_power_mode_on;
217+ }
218+
219+ void for_each_card(std::function<void(mg::DisplayConfigurationCard const&)> f) const override
220+ {
221+ }
222+
223+ void for_each_output(std::function<void(mg::DisplayConfigurationOutput const&)> f) const override
224+ {
225+ f(conf_output);
226+ }
227+
228+ void for_each_output(std::function<void(mg::UserDisplayConfigurationOutput&)> f)
229+ {
230+ mg::UserDisplayConfigurationOutput user{conf_output};
231+ f(user);
232+ }
233+
234+ mg::DisplayConfigurationOutput conf_output;
235+};
236+
237+struct StubDisplay : mg::Display
238+{
239+ void for_each_display_sync_group(std::function<void(mg::DisplaySyncGroup&)> const& f) override
240+ {
241+ }
242+
243+ std::unique_ptr<mg::DisplayConfiguration> configuration() const override
244+ {
245+ return std::unique_ptr<mg::DisplayConfiguration>{new StubDisplayConfiguration{}};
246+ }
247+
248+ void configure(mg::DisplayConfiguration const&) override {}
249+
250+ void register_configuration_change_handler(
251+ mg::EventHandlerRegister& ,
252+ mg::DisplayConfigurationChangeHandler const& ) override
253+ {
254+ }
255+
256+ void register_pause_resume_handlers(
257+ mg::EventHandlerRegister&,
258+ mg::DisplayPauseHandler const&,
259+ mg::DisplayResumeHandler const&) override
260+ {
261+ }
262+
263+ void pause() override {}
264+ void resume() override {}
265+
266+ std::shared_ptr<mg::Cursor> create_hardware_cursor(
267+ std::shared_ptr<mg::CursorImage> const&) override
268+ {
269+ return{};
270+ }
271+
272+ std::unique_ptr<mg::GLContext> create_gl_context() override
273+ {
274+ return std::unique_ptr<mg::GLContext>{};
275+ }
276+};
277+
278+struct StubScreenHardware : usc::ScreenHardware
279+{
280+ void set_dim_backlight() override {}
281+ void set_normal_backlight() override {}
282+ void turn_off_backlight() override {}
283+
284+ void change_backlight_values(int dim_brightness, int normal_brightness) override {}
285+
286+ void allow_suspend() override {}
287+ void disable_suspend() override {}
288+
289+ void enable_auto_brightness(bool) override {}
290+ bool auto_brightness_supported() override { return true; }
291+ void set_brightness(int) override {}
292+ int min_brightness() override { return 100; }
293+ int max_brightness() override { return 0; }
294+
295+ void enable_proximity(bool) override { }
296+};
297+
298+struct NullTouchVisualizer : mir::input::TouchVisualizer
299+{
300+ void enable() override {}
301+ void disable() override {}
302+ void visualize_touches(std::vector<Spot> const& touches) override {}
303+};
304+
305+class TestMirScreen : public usc::MirScreen
306+{
307+public:
308+ using usc::MirScreen::MirScreen;
309+
310+ void dimmer_alarm_notification() override
311+ {
312+ before_dimmer_alarm_func();
313+ usc::MirScreen::dimmer_alarm_notification();
314+ }
315+
316+ void power_off_alarm_notification() override
317+ {
318+ before_power_off_alarm_func();
319+ usc::MirScreen::power_off_alarm_notification();
320+ }
321+
322+ std::function<void()> before_dimmer_alarm_func = []{};
323+ std::function<void()> before_power_off_alarm_func = []{};
324+};
325+
326+struct DeadlockLP1491566 : public testing::Test
327+{
328+ DeadlockLP1491566()
329+ {
330+ main_loop_thread = std::thread{[this] { main_loop->run(); }};
331+ }
332+
333+ ~DeadlockLP1491566()
334+ {
335+ main_loop->stop();
336+ main_loop_thread.join();
337+ }
338+
339+ std::future<void> async_set_screen_power_mode()
340+ {
341+ return std::async(
342+ std::launch::async,
343+ [this]
344+ {
345+ mir_screen.set_screen_power_mode(
346+ MirPowerMode::mir_power_mode_on,
347+ PowerStateChangeReason::power_key);
348+ });
349+ }
350+
351+ void schedule_inactivity_handlers()
352+ {
353+ // We set the screen power mode to on, which will
354+ // schedule inactivity handlers
355+ mir_screen.set_screen_power_mode(
356+ MirPowerMode::mir_power_mode_on,
357+ PowerStateChangeReason::power_key);
358+ }
359+
360+ void wait_for_async_operation(std::future<void>& future)
361+ {
362+ if (!usc::test::spin_wait_for_condition_or_timeout(
363+ [&future] { return future.valid(); },
364+ std::chrono::seconds{3}))
365+ {
366+ throw std::runtime_error{"Future is not valid!"};
367+ }
368+
369+ if (future.wait_for(std::chrono::seconds{3}) != std::future_status::ready)
370+ {
371+ std::cerr << "Deadlock detected. Aborting." << std::endl;
372+ abort();
373+ }
374+ }
375+
376+ char *argv[1] = {nullptr};
377+ usc::Server server{0, argv};
378+ std::shared_ptr<mir::MainLoop> const main_loop{server.the_main_loop()};
379+ std::chrono::milliseconds const power_off_timeout{200};
380+ std::chrono::milliseconds const dimmer_timeout{100};
381+
382+ TestMirScreen mir_screen{
383+ std::make_shared<StubScreenHardware>(),
384+ std::make_shared<NullCompositor>(),
385+ std::make_shared<StubDisplay>(),
386+ std::make_shared<NullTouchVisualizer>(),
387+ main_loop,
388+ server.the_clock(),
389+ {power_off_timeout, dimmer_timeout},
390+ {power_off_timeout, dimmer_timeout}};
391+
392+ std::thread main_loop_thread;
393+};
394+
395+}
396+
397+// The following tests reproduce the situation we are seeing in bug 1491566,
398+// where a set_screen_power_mode request tries to run concurrently with either
399+// the dimmer or power-off timeout handlers. The deadlock is caused by two
400+// threads trying to acquire the internal alarm lock and the MirScreen lock in
401+// opposite orders.
402+//
403+// To reproduce the race in these tests, we need to run the
404+// set_screen_power_mode request just before the timeout handlers are invoked,
405+// while the thread invoking the timeout handlers holds the internal alarm
406+// lock, but before it has acquired the MirScreen lock. The thread calling
407+// set_screen_power_mode will then first acquire the MirScreen lock and then
408+// try to acquire the internal alarm clock and block. The timeout handler
409+// thread will eventually resume and try to get the MirScreen lock, leading to
410+// a deadlock.
411+
412+TEST_F(DeadlockLP1491566, between_dimmer_handler_and_screen_power_mode_request_is_averted)
413+{
414+ std::future<void> future;
415+
416+ mir_screen.before_dimmer_alarm_func =
417+ [this, &future]
418+ {
419+ future = async_set_screen_power_mode();
420+ // Wait a bit for async operation to get processed
421+ std::this_thread::sleep_for(
422+ std::chrono::milliseconds{500});
423+ };
424+
425+ schedule_inactivity_handlers();
426+
427+ wait_for_async_operation(future);
428+}
429+
430+TEST_F(DeadlockLP1491566, between_power_off_handler_and_screen_power_mode_request_is_averted)
431+{
432+ std::future<void> future;
433+
434+ mir_screen.before_power_off_alarm_func =
435+ [this, &future]
436+ {
437+ future = async_set_screen_power_mode();
438+ // Wait a bit for async operation to get processed
439+ std::this_thread::sleep_for(
440+ std::chrono::milliseconds{500});
441+ };
442+
443+ schedule_inactivity_handlers();
444+
445+ wait_for_async_operation(future);
446+}
447
448=== modified file 'tests/unit-tests/advanceable_timer.cpp'
449--- tests/unit-tests/advanceable_timer.cpp 2015-07-15 13:48:30 +0000
450+++ tests/unit-tests/advanceable_timer.cpp 2015-09-25 14:03:36 +0000
451@@ -17,6 +17,7 @@
452 */
453
454 #include "advanceable_timer.h"
455+#include <mir/lockable_callback.h>
456 #include <algorithm>
457
458 namespace
459@@ -59,7 +60,7 @@
460 {
461 AdvanceableAlarm(
462 mir::time::Timestamp now,
463- std::function<void(void)> const& callback)
464+ std::shared_ptr<mir::LockableCallback> const& callback)
465 : now{now}, callback{callback}
466 {
467 }
468@@ -101,7 +102,9 @@
469 {
470 state_ = mir::time::Alarm::triggered;
471 lock.unlock();
472- callback();
473+ callback->lock();
474+ (*callback)();
475+ callback->unlock();
476 }
477 }
478
479@@ -113,7 +116,7 @@
480
481 private:
482 mir::time::Timestamp now;
483- std::function<void()> const callback;
484+ std::shared_ptr<mir::LockableCallback> const callback;
485 mir::time::Timestamp next_trigger_;
486 mutable std::mutex mutex;
487 mir::time::Alarm::State state_ = mir::time::Alarm::triggered;
488@@ -123,6 +126,26 @@
489 std::unique_ptr<mir::time::Alarm> AdvanceableTimer::create_alarm(
490 std::function<void()> const& callback)
491 {
492+ struct SimpleLockableCallback : public mir::LockableCallback
493+ {
494+ SimpleLockableCallback(std::function<void()> const& callback)
495+ : callback{callback}
496+ {
497+ }
498+
499+ void operator()() override { callback(); }
500+ void lock() override {}
501+ void unlock() override {}
502+
503+ std::function<void()> const callback;
504+ };
505+
506+ return create_alarm(std::make_shared<SimpleLockableCallback>(callback));
507+}
508+
509+std::unique_ptr<mir::time::Alarm> AdvanceableTimer::create_alarm(
510+ std::shared_ptr<mir::LockableCallback> const& callback)
511+{
512 auto const adv_alarm =
513 std::make_shared<detail::AdvanceableAlarm>(now(), callback);
514
515@@ -131,12 +154,6 @@
516 return std::unique_ptr<AlarmWrapper>(new AlarmWrapper{adv_alarm});
517 }
518
519-std::unique_ptr<mir::time::Alarm> AdvanceableTimer::create_alarm(
520- std::shared_ptr<mir::LockableCallback> const&)
521-{
522- throw std::runtime_error("Not implemented");
523-}
524-
525 void AdvanceableTimer::advance_by(std::chrono::milliseconds advance)
526 {
527 {

Subscribers

People subscribed via source and target branches