Mir

Merge lp:~mir-team/mir/fix-1339700-take2-in-0.4 into lp:mir/0.4

Proposed by Alberto Aguirre
Status: Rejected
Rejected by: Daniel van Vugt
Proposed branch: lp:~mir-team/mir/fix-1339700-take2-in-0.4
Merge into: lp:mir/0.4
Diff against target: 191 lines (+89/-7)
5 files modified
debian/changelog (+10/-0)
src/server/asio_main_loop.cpp (+26/-3)
src/server/compositor/buffer_queue.h (+3/-1)
src/server/compositor/timeout_frame_dropping_policy_factory.cpp (+4/-3)
tests/unit-tests/test_asio_main_loop.cpp (+46/-0)
To merge this branch: bzr merge lp:~mir-team/mir/fix-1339700-take2-in-0.4
Reviewer Review Type Date Requested Status
Daniel van Vugt Disapprove
Review via email: mp+226537@code.launchpad.net

Commit message

Ensure AlarmImpl is not locked during callbacks to arbitrary user code so
that it can't form a deadlock with the caller's own mutexes. (LP: #1339700).

Description of the change

Ensure AlarmImpl is not locked during callbacks to arbitrary user code so
that it can't form a deadlock with the caller's own mutexes. (LP: #1339700)

This is a combination of devel r1766 and https://code.launchpad.net/~mir-team/mir/fix-1339700-take2/+merge/226534

To post a comment you must log in.
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Resubmit.

This proposal merges a couple of different branches, one of which is not approved and needs fixing upstream still:
https://code.launchpad.net/~mir-team/mir/fix-1339700-take2/+merge/226534

review: Needs Resubmitting
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Also, the beginnings of the semantic changes to Alarm should never appear in a stable maintenance branch.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :
review: Disapprove

Unmerged revisions

1742. By Alberto Aguirre

Update changelog

1741. By Alberto Aguirre

Only ~AlarmImpl should wait for callback completion (LP: #1339700)

Keep the semantics of AlarmImpl::cancel similar to boost::::deadline_timer. However,
make the destructor of ~AlarmImpl wait for current executing callbacks to avoid lifetime
issues. In the future, mir::time::Alarm should expose the stop method.

1740. By Alberto Aguirre

Ensure AlarmImpl is not locked during callbacks to arbitrary user code so
that it can't form a deadlock with the caller's own mutexes.

1739. By Daniel van Vugt

Correct the changelog text for 0.4.0+14.10.20140701.1-0ubuntu1.

It mistakenly listed enhancements under ABI/API changes and didn't mention
most of the bugs that got fixed.

1738. By Daniel van Vugt

Merge released changelog from lp:mir ...
mir (0.4.0+14.10.20140701.1-0ubuntu1) utopic; urgency=medium

1737. By Daniel van Vugt

Revert my corrections from r1736. Annoyingly Tarmac lost it and landed r1735
to lp:mir instead :(

Also moved the v0.4.0 tag to accurately reflect which revision got released.

1736. By Daniel van Vugt

Corrections to the new debian/changelog entry for 0.4.0

1735. By Cemil Azizoglu

Merge r1737 and r1738 from devel.

1734. By Cemil Azizoglu

Integrate r1734 from devel.

1733. By Cemil Azizoglu

Update changelog.

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 2014-07-04 07:37:55 +0000
3+++ debian/changelog 2014-07-11 21:08:53 +0000
4@@ -1,3 +1,13 @@
5+mir (0.4.1) UNRELEASED; urgency=medium
6+
7+ * New upstream release 0.4.1 (https://launchpad.net/mir/+milestone/0.4.1)
8+ - mirclient ABI unchanged, still at 8. Clients do not need rebuilding.
9+ - mirserver ABI unchanged, still at 22. Shells do not need rebuilding.
10+ - Bugs fixed:
11+ . fix for deadlock when invoking alarm handlers (LP: #1339700)
12+
13+ -- Alberto Aguirre <alberto.aguirre@canonical.com> Fri, 11 Jul 2014 15:38:20 -0500
14+
15 mir (0.4.0+14.10.20140701.1-0ubuntu1) utopic; urgency=medium
16
17 * New upstream release 0.4.0 (https://launchpad.net/mir/+milestone/0.4.0)
18
19=== modified file 'src/server/asio_main_loop.cpp'
20--- src/server/asio_main_loop.cpp 2014-06-27 07:07:35 +0000
21+++ src/server/asio_main_loop.cpp 2014-07-11 21:08:53 +0000
22@@ -344,15 +344,19 @@
23 bool reschedule_in(std::chrono::milliseconds delay) override;
24 bool reschedule_for(mir::time::Timestamp time_point) override;
25 private:
26+ void stop();
27+ bool cancel_l();
28 void update_timer();
29 struct InternalState
30 {
31 explicit InternalState(std::function<void(void)> callback)
32- : callback{callback}
33+ : callback{callback}, state{pending}
34 {
35 }
36
37- std::recursive_mutex m;
38+ std::mutex m;
39+ int callbacks_running = 0;
40+ std::condition_variable callback_done;
41 std::function<void(void)> const callback;
42 State state;
43 };
44@@ -381,7 +385,12 @@
45 if (data->state == mir::time::Alarm::pending)
46 {
47 data->state = mir::time::Alarm::triggered;
48+ ++data->callbacks_running;
49+ lock.unlock();
50 data->callback();
51+ lock.lock();
52+ --data->callbacks_running;
53+ data->callback_done.notify_all();
54 }
55 }
56 }
57@@ -415,12 +424,26 @@
58
59 AlarmImpl::~AlarmImpl() noexcept
60 {
61- AlarmImpl::cancel();
62+ AlarmImpl::stop();
63+}
64+
65+void AlarmImpl::stop()
66+{
67+ std::unique_lock<decltype(data->m)> lock(data->m);
68+
69+ data->callback_done.wait(lock,[this]{return data->callbacks_running == 0;});
70+
71+ cancel_l();
72 }
73
74 bool AlarmImpl::cancel()
75 {
76 std::lock_guard<decltype(data->m)> lock(data->m);
77+ return cancel_l();
78+}
79+
80+bool AlarmImpl::cancel_l()
81+{
82 if (data->state == triggered)
83 return false;
84
85
86=== modified file 'src/server/compositor/buffer_queue.h'
87--- src/server/compositor/buffer_queue.h 2014-06-12 15:35:08 +0000
88+++ src/server/compositor/buffer_queue.h 2014-07-11 21:08:53 +0000
89@@ -86,11 +86,13 @@
90
91 int nbuffers;
92 bool frame_dropping_enabled;
93- std::unique_ptr<FrameDroppingPolicy> framedrop_policy;
94 graphics::BufferProperties the_properties;
95
96 std::condition_variable snapshot_released;
97 std::shared_ptr<graphics::GraphicBufferAllocator> gralloc;
98+ //Ensure it gets destroyed first so the callback installed
99+ //does not access dead objects
100+ std::unique_ptr<FrameDroppingPolicy> framedrop_policy;
101 };
102
103 }
104
105=== modified file 'src/server/compositor/timeout_frame_dropping_policy_factory.cpp'
106--- src/server/compositor/timeout_frame_dropping_policy_factory.cpp 2014-06-25 16:30:46 +0000
107+++ src/server/compositor/timeout_frame_dropping_policy_factory.cpp 2014-07-11 21:08:53 +0000
108@@ -41,22 +41,23 @@
109
110 private:
111 std::chrono::milliseconds const timeout;
112+ std::atomic<unsigned int> pending_swaps;
113+ //Ensure alarm gets destroyed first so its handler does not access dead objects.
114 std::unique_ptr<mir::time::Alarm> const alarm;
115- std::atomic<unsigned int> pending_swaps;
116 };
117
118 TimeoutFrameDroppingPolicy::TimeoutFrameDroppingPolicy(std::shared_ptr<mir::time::Timer> const& timer,
119 std::chrono::milliseconds timeout,
120 std::function<void(void)> drop_frame)
121 : timeout{timeout},
122+ pending_swaps{0},
123 alarm{timer->create_alarm([this, drop_frame]
124 {
125 assert(pending_swaps.load() > 0);
126 drop_frame();
127 if (--pending_swaps > 0)
128 alarm->reschedule_in(this->timeout);
129- })},
130- pending_swaps{0}
131+ })}
132 {
133 }
134
135
136=== modified file 'tests/unit-tests/test_asio_main_loop.cpp'
137--- tests/unit-tests/test_asio_main_loop.cpp 2014-06-27 09:07:33 +0000
138+++ tests/unit-tests/test_asio_main_loop.cpp 2014-07-11 21:08:53 +0000
139@@ -631,6 +631,52 @@
140 EXPECT_EQ(1, call_count);
141 }
142
143+TEST_F(AsioMainLoopAlarmTest, alarm_callback_cannot_deadlock)
144+{ // Regression test for deadlock bug LP: #1339700
145+ std::mutex m;
146+ std::atomic_bool failed(false);
147+ int i = 0;
148+ int const loops = 5;
149+
150+ auto alarm = ml.notify_in(std::chrono::milliseconds{0}, [&]()
151+ {
152+ // From this angle, ensure we can lock m (alarm should be unlocked)
153+ int tries = 0;
154+ while (!m.try_lock() && tries < 100) // 100 x 100 = try for 10 seconds
155+ {
156+ ++tries;
157+ std::this_thread::sleep_for(std::chrono::milliseconds{100});
158+ }
159+ failed = (tries >= 100);
160+ ASSERT_FALSE(failed);
161+ ++i;
162+ m.unlock();
163+ });
164+
165+ std::thread t([&]()
166+ {
167+ m.lock();
168+ while (i < loops && !failed)
169+ {
170+ // From this angle, ensure we can lock alarm while holding m
171+ (void)alarm->state();
172+ m.unlock();
173+ std::this_thread::yield();
174+ m.lock();
175+ }
176+ m.unlock();
177+ });
178+
179+ UnblockMainLoop unblocker(ml);
180+ for (int j = 0; j < loops; ++j)
181+ {
182+ clock->advance_by(std::chrono::milliseconds{101}, ml);
183+ alarm->reschedule_in(std::chrono::milliseconds{100});
184+ }
185+
186+ t.join();
187+}
188+
189 TEST_F(AsioMainLoopAlarmTest, alarm_fires_at_correct_time_point)
190 {
191 mir::time::Timestamp real_soon = clock->sample() + std::chrono::milliseconds{120};

Subscribers

People subscribed via source and target branches

to all changes: