Mir

Merge lp:~albaguirre/mir/fix-1421255 into lp:mir

Proposed by Alberto Aguirre
Status: Merged
Approved by: Alan Griffiths
Approved revision: no longer in the source branch.
Merged at revision: 2335
Proposed branch: lp:~albaguirre/mir/fix-1421255
Merge into: lp:mir
Diff against target: 1283 lines (+368/-132)
23 files modified
include/server/mir/time/timer.h (+28/-4)
src/include/server/mir/compositor/frame_dropping_policy_factory.h (+22/-5)
src/include/server/mir/glib_main_loop.h (+11/-6)
src/include/server/mir/glib_main_loop_sources.h (+3/-1)
src/server/compositor/buffer_queue.cpp (+28/-13)
src/server/compositor/buffer_queue.h (+12/-4)
src/server/compositor/timeout_frame_dropping_policy_factory.cpp (+17/-9)
src/server/compositor/timeout_frame_dropping_policy_factory.h (+6/-2)
src/server/glib_main_loop.cpp (+23/-6)
src/server/glib_main_loop_sources.cpp (+15/-3)
tests/include/mir_test_doubles/mock_frame_dropping_policy_factory.h (+23/-4)
tests/include/mir_test_doubles/mock_main_loop.h (+14/-6)
tests/include/mir_test_doubles/mock_timer.h (+7/-5)
tests/include/mir_test_doubles/stub_frame_dropping_policy_factory.h (+7/-3)
tests/include/mir_test_doubles/stub_timer.h (+3/-3)
tests/include/mir_test_doubles/triggered_main_loop.h (+1/-1)
tests/mir_test_doubles/mock_frame_dropping_policy_factory.cpp (+12/-4)
tests/mir_test_doubles/mock_timer.cpp (+38/-15)
tests/mir_test_doubles/triggered_main_loop.cpp (+3/-2)
tests/unit-tests/compositor/test_buffer_queue.cpp (+10/-1)
tests/unit-tests/compositor/test_timeout_frame_dropping_policy.cpp (+36/-9)
tests/unit-tests/test_glib_main_loop.cpp (+29/-26)
tests/unit-tests/test_raii.cpp (+20/-0)
To merge this branch: bzr merge lp:~albaguirre/mir/fix-1421255
Reviewer Review Type Date Requested Status
Kevin DuBois (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Alan Griffiths Approve
Review via email: mp+250182@code.launchpad.net

Commit message

Fix deadlock caused by lock ordering issues.

The framedropping policy acquires an internal lock while invoking the user callback. BufferQueue calls framedrop policy methods while holding its own lock; the callback implementation given to the policy also acquires the same lock which results in a classic lock ordering deadlock issue.

To avoid such lock order issues, the framedrop policy and alarm factory interfaces expose an additional API to allow users to pass in lock/unlock functions, which can then be used by the callback dispatching context to preserve lock ordering.

Description of the change

Fix deadlock caused by lock ordering issues.

The framedropping policy acquires an internal lock while invoking the user callback. BufferQueue calls framedrop policy methods while holding its own lock; the callback implementation given to the policy also acquires the same lock which results in a classic lock ordering deadlock issue.

To avoid such lock order issues, the framedrop policy and alarm factory interfaces expose an additional API to allow users to pass in lock/unlock functions, which can then be used by the callback dispatching context to preserve lock ordering.

The alternatives include:
- Avoid holding a lock while dispatching callback
--- Lifetime of objects gets tricky when trying to destroy the underlying TimerGSource and releated resources.
- Make async versions of the current alarm APIs (reschedule APIs have an implicit cancel operation)
--- Needs some sort of garbage collector of GSourceHandles (to keep existing implementation)
--- or rework of TimerGSource and md::add_timer_gsource to be able to handle an async cancel call with all the object lifetime issues it opens up.

This approach was chosen because:
- Solves the actual root cause - lock ordering
- It's a bit easier to reason about than the alternatives; Object lifetimes concerns are already addressed by existing code

To post a comment you must log in.
Revision history for this message
Kevin DuBois (kdub) wrote :

211 + [this] { guard_lock = std::move(std::unique_lock<decltype(guard)>{guard}); },
212 + []{});
It seems strange that if I were to call:
lock();
unlock();
lock()
The second lock call would block, because we haven't called the callback, which is what will end up releasing the lock.

594 + CallerAutoLock caller_lock{ctx.lock, ctx.unlock};
Could mir/raii.h help reduce diff? (and don't need CallerAutoLock)

99 + virtual std::unique_ptr<FrameDroppingPolicy> create_policy(std::function<void()> const& drop_frame) const = 0;
119 + virtual std::unique_ptr<FrameDroppingPolicy> create_policy(
120 + std::function<void()> const& drop_frame,
121 + std::function<void()> const& lock,
122 + std::function<void()> const& unlock) const = 0;
I guess I'd rather just keep one function (the latter), and 3 parameters in create_policy seem like they're becoming an interface that meets the requirements of BasicLockable.

review: Needs Information
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Francis Ginther (fginther) wrote :

The mir-mediumtests-runner-mako job failed due to a failed device. I've restarted the mir-ci job.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

A simpler and guaranteed more reliable solution would be to move away from a pure callback approach (which we use in many places)...

Instead, if we replaced such logic:
   call callback(args)
with:
   signal event(args)
then there is zero risk of deadlocks.

Receivers could then wait for an event, multiple events, or even join a thread pool that turns events into callbacks if they want to work that way. But the important point is even in the callback case then, the thread making the callback has no risk of causing deadlocks. Because it's dedicated just to making unlocked callbacks and nothing else. But of course, it's more efficient to not need additional threads too.

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

> A simpler and guaranteed more reliable solution would be to move away from a
> pure callback approach (which we use in many places)...
>
> Instead, if we replaced such logic:
> call callback(args)
> with:
> signal event(args)
> then there is zero risk of deadlocks.
>

How would that be simpler? I guess simpler in which respect? Development, maintenance, usage?

A high level event-hub concept cannot guarantee zero deadlocks however, that's an implementation detail.

Maybe such an event hub could just post and dispatch events through the main loop to save a thread.

In any case, that's probably a much bigger re-factoring than what is proposed here.

Revision history for this message
Alberto Aguirre (albaguirre) wrote :

> 211 + [this] { guard_lock =
> std::move(std::unique_lock<decltype(guard)>{guard}); },
> 212 + []{});
> It seems strange that if I were to call:
> lock();
> unlock();
> lock()
> The second lock call would block, because we haven't called the callback,
> which is what will end up releasing the lock.

OK, I suppose I can change BufferQueue private methods to take a unique_lock reference instead
of value, so I don't have to move the instance and consequently I can use it in the "unlock" lambda.

> 594 + CallerAutoLock caller_lock{ctx.lock, ctx.unlock};
> Could mir/raii.h help reduce diff? (and don't need CallerAutoLock)

I tried, but couldn't figure out why paired_call did not like std::function. I'll try harder I suppose :)

> 99 + virtual std::unique_ptr<FrameDroppingPolicy>
> create_policy(std::function<void()> const& drop_frame) const = 0;
> 119 + virtual std::unique_ptr<FrameDroppingPolicy> create_policy(
> 120 + std::function<void()> const& drop_frame,
> 121 + std::function<void()> const& lock,
> 122 + std::function<void()> const& unlock) const = 0;
>
> I guess I'd rather just keep one function (the latter), and 3 parameters in
> create_policy seem like they're becoming an interface that meets the
> requirements of BasicLockable.

OK. Maybe a LockableCallable?

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

Ran out of time trying to understand. Will look again tomorrow if it doesn't land meanwhile.

review: Abstain
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

This seems elaborate and easy to make mistakes using but I don't have a better suggestion (yet).

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

> This seems elaborate and easy to make mistakes using but I don't have a better
> suggestion (yet).

Yeah but no different that what we currently have (i.e. easy to make mistakes too).

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

seems the issues were addressed, so okay by me. This buffer queue code has gotten hard to understand again, but thats a pre-existing issue.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/server/mir/time/timer.h'
2--- include/server/mir/time/timer.h 2015-01-21 07:34:50 +0000
3+++ include/server/mir/time/timer.h 2015-02-20 17:57:50 +0000
4@@ -1,5 +1,5 @@
5 /*
6- * Copyright © 2014 Canonical Ltd.
7+ * Copyright © 2014-2015 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@@ -14,6 +14,7 @@
12 * along with this program. If not, see <http://www.gnu.org/licenses/>.
13 *
14 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
15+ * Alberto Aguirre <alberto.aguirre@canonical.com>
16 */
17
18
19@@ -47,7 +48,7 @@
20 * \return A handle to an Alarm that will fire after delay ms.
21 */
22 virtual std::unique_ptr<Alarm> notify_in(std::chrono::milliseconds delay,
23- std::function<void()> callback) = 0;
24+ std::function<void()> const& callback) = 0;
25 /**
26 * \brief Create an Alarm that calls the callback at the specified time
27 *
28@@ -57,7 +58,7 @@
29 * \return A handle to an Alarm that will fire after delay ms.
30 */
31 virtual std::unique_ptr<Alarm> notify_at(Timestamp time_point,
32- std::function<void()> callback) = 0;
33+ std::function<void()> const& callback) = 0;
34 /**
35 * \brief Create an Alarm that will not fire until scheduled
36 *
37@@ -65,7 +66,30 @@
38 *
39 * \return A handle to an Alarm that can later be scheduled
40 */
41- virtual std::unique_ptr<Alarm> create_alarm(std::function<void()> callback) = 0;
42+ virtual std::unique_ptr<Alarm> create_alarm(std::function<void()> const& callback) = 0;
43+
44+ /**
45+ * \brief Create an Alarm that will not fire until scheduled
46+ *
47+ * The lock/unlock functions allow the user to preserve lock ordering
48+ * in situations where Alarm methods need to be called under external lock
49+ * and the callback implementation needs to run code protected by the same
50+ * lock. An alarm implementation may have internal locks of its own, which
51+ * maybe acquired during callback dispatching; to preserve lock ordering
52+ * the given lock function will be invoked during callback dispatch before
53+ * any internal locks are acquired.
54+ *
55+ * \param callback Function to call when the Alarm signals
56+ * \param lock Function called within callback dispatching context
57+ * before the alarm implementation acquires any internal
58+ * lock
59+ * \param unlock Function called within callback dispatching context after
60+ * the alarm implementation releases any internal lock
61+ *
62+ * \return A handle to an Alarm that can later be scheduled
63+ */
64+ virtual std::unique_ptr<Alarm> create_alarm(std::function<void()> const& callback,
65+ std::function<void()> const& lock, std::function<void()> const& unlock) = 0;
66
67 Timer(Timer const&) = delete;
68 Timer& operator=(Timer const&) = delete;
69
70=== modified file 'src/include/server/mir/compositor/frame_dropping_policy_factory.h'
71--- src/include/server/mir/compositor/frame_dropping_policy_factory.h 2015-01-21 07:34:50 +0000
72+++ src/include/server/mir/compositor/frame_dropping_policy_factory.h 2015-02-20 17:57:50 +0000
73@@ -1,5 +1,5 @@
74 /*
75- * Copyright © 2014 Canonical Ltd.
76+ * Copyright © 2014-2015 Canonical Ltd.
77 *
78 * This program is free software: you can redistribute it and/or modify it
79 * under the terms of the GNU General Public License version 3,
80@@ -14,6 +14,7 @@
81 * along with this program. If not, see <http://www.gnu.org/licenses/>.
82 *
83 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
84+ * Alberto Aguirre <alberto.aguirre@canonical.com>
85 */
86
87 #ifndef MIR_COMPOSITOR_FRAME_DROPPING_POLICY_FACTORY_H_
88@@ -44,11 +45,27 @@
89 FrameDroppingPolicyFactory& operator=(FrameDroppingPolicyFactory const&) = delete;
90
91 /**
92- * \brief Create a FrameDroppingPolicy that will call \a drop_frame when it decides to drop a frame
93- * \param drop_frame Function to call when a frame needs to be dropped
94- * \return The policy object.
95+ * \brief Create a FrameDroppingPolicy that will call \a drop_frame when it
96+ * decides to drop a frame
97+ *
98+ * The lock/unlock functions allow the user to preserve lock ordering
99+ * in situations where FrameDroppingPolicy methods need to be called under
100+ * external lock and the callback implementation needs to run code protected
101+ * by the same lock. A FrameDroppingPolicy implementation may have internal
102+ * locks of its own, which maybe acquired during callback dispatching;
103+ * to preserve lock ordering the given lock function will be invoked during
104+ * callback dispatch before any internal locks are acquired.
105+ *
106+ * \param drop_frame Function to call when a frame needs to be dropped
107+ * \param lock Function called within the callback dispatcher context
108+ * before any internal locks are acquired.
109+ * \param unlock Function called within the callback dispatcher context
110+ * after any internal locks are released.
111 */
112- virtual std::unique_ptr<FrameDroppingPolicy> create_policy(std::function<void(void)> drop_frame) const = 0;
113+ virtual std::unique_ptr<FrameDroppingPolicy> create_policy(
114+ std::function<void()> const& drop_frame,
115+ std::function<void()> const& lock,
116+ std::function<void()> const& unlock) const = 0;
117 };
118
119 }
120
121=== modified file 'src/include/server/mir/glib_main_loop.h'
122--- src/include/server/mir/glib_main_loop.h 2015-01-21 07:34:50 +0000
123+++ src/include/server/mir/glib_main_loop.h 2015-02-20 17:57:50 +0000
124@@ -1,5 +1,5 @@
125 /*
126- * Copyright © 2014 Canonical Ltd.
127+ * Copyright © 2014-2015 Canonical Ltd.
128 *
129 * This program is free software: you can redistribute it and/or modify it
130 * under the terms of the GNU General Public License version 3,
131@@ -72,14 +72,19 @@
132
133 std::unique_ptr<mir::time::Alarm> notify_in(
134 std::chrono::milliseconds delay,
135- std::function<void()> callback) override;
136+ std::function<void()> const& callback) override;
137
138 std::unique_ptr<mir::time::Alarm> notify_at(
139 mir::time::Timestamp t,
140- std::function<void()> callback) override;
141-
142- std::unique_ptr<mir::time::Alarm> create_alarm(
143- std::function<void()> callback) override;
144+ std::function<void()> const& callback) override;
145+
146+ std::unique_ptr<mir::time::Alarm> create_alarm(
147+ std::function<void()> const& callback) override;
148+
149+ std::unique_ptr<mir::time::Alarm> create_alarm(
150+ std::function<void()> const& callback,
151+ std::function<void()> const& lock,
152+ std::function<void()> const& unlock) override;
153
154 void reprocess_all_sources();
155
156
157=== modified file 'src/include/server/mir/glib_main_loop_sources.h'
158--- src/include/server/mir/glib_main_loop_sources.h 2015-02-13 06:12:34 +0000
159+++ src/include/server/mir/glib_main_loop_sources.h 2015-02-20 17:57:50 +0000
160@@ -1,5 +1,5 @@
161 /*
162- * Copyright © 2014 Canonical Ltd.
163+ * Copyright © 2014-2015 Canonical Ltd.
164 *
165 * This program is free software: you can redistribute it and/or modify it
166 * under the terms of the GNU General Public License version 3,
167@@ -74,6 +74,8 @@
168 GMainContext* main_context,
169 std::shared_ptr<time::Clock> const& clock,
170 std::function<void()> const& handler,
171+ std::function<void()> const& lock,
172+ std::function<void()> const& unlock,
173 time::Timestamp target_time);
174
175 class FdSources
176
177=== modified file 'src/server/compositor/buffer_queue.cpp'
178--- src/server/compositor/buffer_queue.cpp 2015-02-17 06:38:35 +0000
179+++ src/server/compositor/buffer_queue.cpp 2015-02-20 17:57:50 +0000
180@@ -1,5 +1,5 @@
181 /*
182- * Copyright © 2014 Canonical Ltd.
183+ * Copyright © 2014-2015 Canonical Ltd.
184 *
185 * This program is free software: you can redistribute it and/or modify it
186 * under the terms of the GNU General Public License version 3,
187@@ -132,11 +132,18 @@
188 if (nbuffers == 1)
189 free_buffers.push_back(current_compositor_buffer);
190
191+ // A lock_guard will be created by the policy dispatcher invoking the given "lock" lambda
192+ // before it acquires any internal locks of its own.
193 framedrop_policy = policy_provider.create_policy([this]
194 {
195- std::unique_lock<decltype(guard)> lock{guard};
196- drop_frame(std::move(lock));
197- });
198+ // We ignore any ongoing snapshotting as it could lead to deadlock.
199+ // In order to wait the guard_lock needs to be released; a BufferQueue::release
200+ // call can sneak in at that time from a different thread which
201+ // can invoke framedrop_policy methods
202+ drop_frame(guard_lock, ignore_snapshot);
203+ },
204+ [this] { guard_lock = std::move(std::unique_lock<decltype(guard)>{guard}); },
205+ [this] { if (guard_lock.owns_lock()) guard_lock.unlock(); });
206 }
207
208 void mc::BufferQueue::client_acquire(mc::BufferQueue::Callback complete)
209@@ -149,7 +156,7 @@
210 {
211 auto const buffer = free_buffers.back();
212 free_buffers.pop_back();
213- give_buffer_to_client(buffer, std::move(lock));
214+ give_buffer_to_client(buffer, lock);
215 return;
216 }
217
218@@ -162,14 +169,14 @@
219 {
220 auto const& buffer = gralloc->alloc_buffer(the_properties);
221 buffers.push_back(buffer);
222- give_buffer_to_client(buffer.get(), std::move(lock));
223+ give_buffer_to_client(buffer.get(), lock);
224 return;
225 }
226
227 /* Last resort, drop oldest buffer from the ready queue */
228 if (frame_dropping_enabled)
229 {
230- drop_frame(std::move(lock));
231+ drop_frame(lock, wait_for_snapshot);
232 return;
233 }
234
235@@ -323,7 +330,7 @@
236 {
237 free_buffers.push_back(pop(ready_to_composite_queue));
238 }
239- give_buffer_to_client(buffer, std::move(lock));
240+ give_buffer_to_client(buffer, lock, ignore_snapshot);
241 }
242 }
243
244@@ -363,7 +370,15 @@
245
246 void mc::BufferQueue::give_buffer_to_client(
247 mg::Buffer* buffer,
248- std::unique_lock<std::mutex> lock)
249+ std::unique_lock<std::mutex>& lock)
250+{
251+ give_buffer_to_client(buffer, lock, wait_for_snapshot);
252+}
253+
254+void mc::BufferQueue::give_buffer_to_client(
255+ mg::Buffer* buffer,
256+ std::unique_lock<std::mutex>& lock,
257+ SnapshotWait wait_type)
258 {
259 /* Clears callback */
260 auto give_to_client_cb = std::move(pending_client_notifications.front());
261@@ -383,7 +398,7 @@
262 }
263
264 /* Don't give to the client just yet if there's a pending snapshot */
265- if (!resize_buffer && contains(buffer, pending_snapshots))
266+ if (wait_type == wait_for_snapshot && !resize_buffer && contains(buffer, pending_snapshots))
267 {
268 snapshot_released.wait(lock,
269 [&]{ return !contains(buffer, pending_snapshots); });
270@@ -420,7 +435,7 @@
271 if (!pending_client_notifications.empty())
272 {
273 framedrop_policy->swap_unblocked();
274- give_buffer_to_client(buffer, std::move(lock));
275+ give_buffer_to_client(buffer, lock);
276 }
277 else if (!frame_dropping_enabled && buffers.size() > size_t(nbuffers))
278 {
279@@ -446,7 +461,7 @@
280 free_buffers.push_back(buffer);
281 }
282
283-void mc::BufferQueue::drop_frame(std::unique_lock<std::mutex> lock)
284+void mc::BufferQueue::drop_frame(std::unique_lock<std::mutex>& lock, SnapshotWait wait_type)
285 {
286 // Make sure there is a client waiting for the frame before we drop it.
287 // If not, then there's nothing to do.
288@@ -498,7 +513,7 @@
289 buffer_to_give = buffer.get();
290 }
291
292- give_buffer_to_client(buffer_to_give, std::move(lock));
293+ give_buffer_to_client(buffer_to_give, lock, wait_type);
294 }
295
296 void mc::BufferQueue::drop_old_buffers()
297
298=== modified file 'src/server/compositor/buffer_queue.h'
299--- src/server/compositor/buffer_queue.h 2015-01-21 08:53:28 +0000
300+++ src/server/compositor/buffer_queue.h 2015-02-20 17:57:50 +0000
301@@ -1,5 +1,5 @@
302 /*
303- * Copyright © 2014 Canonical Ltd.
304+ * Copyright © 2014-2015 Canonical Ltd.
305 *
306 * This program is free software: you can redistribute it and/or modify it
307 * under the terms of the GNU General Public License version 3,
308@@ -66,13 +66,21 @@
309 void drop_client_requests() override;
310
311 private:
312- void give_buffer_to_client(graphics::Buffer* buffer,
313- std::unique_lock<std::mutex> lock);
314+ enum SnapshotWait
315+ {
316+ wait_for_snapshot,
317+ ignore_snapshot
318+ };
319+ void give_buffer_to_client(graphics::Buffer* buffer,
320+ std::unique_lock<std::mutex>& lock);
321+ void give_buffer_to_client(graphics::Buffer* buffer,
322+ std::unique_lock<std::mutex>& lock, SnapshotWait wait_type);
323 void release(graphics::Buffer* buffer,
324 std::unique_lock<std::mutex> lock);
325- void drop_frame(std::unique_lock<std::mutex> lock);
326+ void drop_frame(std::unique_lock<std::mutex>& lock, SnapshotWait wait_type);
327
328 mutable std::mutex guard;
329+ std::unique_lock<decltype(guard)> guard_lock;
330
331 std::vector<std::shared_ptr<graphics::Buffer>> buffers;
332 std::deque<graphics::Buffer*> ready_to_composite_queue;
333
334=== modified file 'src/server/compositor/timeout_frame_dropping_policy_factory.cpp'
335--- src/server/compositor/timeout_frame_dropping_policy_factory.cpp 2015-02-17 11:58:56 +0000
336+++ src/server/compositor/timeout_frame_dropping_policy_factory.cpp 2015-02-20 17:57:50 +0000
337@@ -1,5 +1,5 @@
338 /*
339- * Copyright © 2014 Canonical Ltd.
340+ * Copyright © 2014-2015 Canonical Ltd.
341 *
342 * This program is free software: you can redistribute it and/or modify it
343 * under the terms of the GNU General Public License version 3,
344@@ -14,6 +14,7 @@
345 * along with this program. If not, see <http://www.gnu.org/licenses/>.
346 *
347 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
348+ * Alberto Aguirre <alberto.aguirre@canonical.com>
349 */
350
351 #include "mir/compositor/frame_dropping_policy.h"
352@@ -34,7 +35,9 @@
353 public:
354 TimeoutFrameDroppingPolicy(std::shared_ptr<mir::time::Timer> const& timer,
355 std::chrono::milliseconds timeout,
356- std::function<void(void)> drop_frame);
357+ std::function<void()> const& drop_frame,
358+ std::function<void()> const& lock,
359+ std::function<void()> const& unlock);
360
361 void swap_now_blocking() override;
362 void swap_unblocked() override;
363@@ -50,7 +53,9 @@
364
365 TimeoutFrameDroppingPolicy::TimeoutFrameDroppingPolicy(std::shared_ptr<mir::time::Timer> const& timer,
366 std::chrono::milliseconds timeout,
367- std::function<void(void)> drop_frame)
368+ std::function<void()> const& drop_frame,
369+ std::function<void()> const& lock,
370+ std::function<void()> const& unlock)
371 : timeout{timeout},
372 pending_swaps{0},
373 alarm{timer->create_alarm([this, drop_frame]
374@@ -59,7 +64,7 @@
375 drop_frame();
376 if (--pending_swaps > 0)
377 alarm->reschedule_in(this->timeout);
378- })}
379+ }, lock, unlock)}
380 {
381 }
382
383@@ -81,15 +86,18 @@
384 }
385 }
386
387-mc::TimeoutFrameDroppingPolicyFactory::TimeoutFrameDroppingPolicyFactory(std::shared_ptr<mir::time::Timer> const& timer,
388- std::chrono::milliseconds timeout)
389+mc::TimeoutFrameDroppingPolicyFactory::TimeoutFrameDroppingPolicyFactory(
390+ std::shared_ptr<mir::time::Timer> const& timer,
391+ std::chrono::milliseconds timeout)
392 : timer{timer},
393 timeout{timeout}
394 {
395 }
396
397-
398-std::unique_ptr<mc::FrameDroppingPolicy> mc::TimeoutFrameDroppingPolicyFactory::create_policy(std::function<void ()> drop_frame) const
399+std::unique_ptr<mc::FrameDroppingPolicy>
400+mc::TimeoutFrameDroppingPolicyFactory::create_policy(std::function<void()> const& drop_frame,
401+ std::function<void()> const& lock,
402+ std::function<void()> const& unlock) const
403 {
404- return std::make_unique<TimeoutFrameDroppingPolicy>(timer, timeout, drop_frame);
405+ return std::make_unique<TimeoutFrameDroppingPolicy>(timer, timeout, drop_frame, lock, unlock);
406 }
407
408=== modified file 'src/server/compositor/timeout_frame_dropping_policy_factory.h'
409--- src/server/compositor/timeout_frame_dropping_policy_factory.h 2014-05-22 20:48:20 +0000
410+++ src/server/compositor/timeout_frame_dropping_policy_factory.h 2015-02-20 17:57:50 +0000
411@@ -1,5 +1,5 @@
412 /*
413- * Copyright © 2014 Canonical Ltd.
414+ * Copyright © 2014-2015 Canonical Ltd.
415 *
416 * This program is free software: you can redistribute it and/or modify it
417 * under the terms of the GNU General Public License version 3,
418@@ -14,6 +14,7 @@
419 * along with this program. If not, see <http://www.gnu.org/licenses/>.
420 *
421 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
422+ * Alberto Aguirre <alberto.aguirre@canonical.com>
423 */
424
425
426@@ -44,7 +45,10 @@
427 TimeoutFrameDroppingPolicyFactory(std::shared_ptr<mir::time::Timer> const& timer,
428 std::chrono::milliseconds timeout);
429
430- std::unique_ptr<FrameDroppingPolicy> create_policy(std::function<void(void)> drop_frame) const override;
431+ std::unique_ptr<FrameDroppingPolicy> create_policy(
432+ std::function<void()> const& drop_frame,
433+ std::function<void()> const& lock,
434+ std::function<void()> const& unlock) const override;
435 private:
436 std::shared_ptr<mir::time::Timer> const timer;
437 std::chrono::milliseconds timeout;
438
439=== modified file 'src/server/glib_main_loop.cpp'
440--- src/server/glib_main_loop.cpp 2015-02-17 11:58:56 +0000
441+++ src/server/glib_main_loop.cpp 2015-02-20 17:57:50 +0000
442@@ -1,5 +1,5 @@
443 /*
444- * Copyright © 2014 Canonical Ltd.
445+ * Copyright © 2014-2015 Canonical Ltd.
446 *
447 * This program is free software: you can redistribute it and/or modify it
448 * under the terms of the GNU General Public License version 3,
449@@ -14,6 +14,7 @@
450 * along with this program. If not, see <http://www.gnu.org/licenses/>.
451 *
452 * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
453+ * Alberto Aguirre <alberto.aguirre@canonical.com>
454 */
455
456 #include "mir/glib_main_loop.h"
457@@ -33,10 +34,14 @@
458 AlarmImpl(
459 GMainContext* main_context,
460 std::shared_ptr<mir::time::Clock> const& clock,
461- std::function<void()> const& callback)
462+ std::function<void()> const& callback,
463+ std::function<void()> const& lock,
464+ std::function<void()> const& unlock)
465 : main_context{main_context},
466 clock{clock},
467 callback{callback},
468+ ext_lock{lock},
469+ ext_unlock{unlock},
470 state_{State::cancelled}
471 {
472 }
473@@ -70,6 +75,8 @@
474 main_context,
475 clock,
476 [&] { state_ = State::triggered; callback(); },
477+ ext_lock,
478+ ext_unlock,
479 time_point);
480
481 return true;
482@@ -80,6 +87,8 @@
483 GMainContext* main_context;
484 std::shared_ptr<mir::time::Clock> const clock;
485 std::function<void()> const callback;
486+ std::function<void()> const ext_lock;
487+ std::function<void()> const ext_unlock;
488 State state_;
489 mir::detail::GSourceHandle gsource;
490 };
491@@ -223,7 +232,7 @@
492
493 std::unique_ptr<mir::time::Alarm> mir::GLibMainLoop::notify_in(
494 std::chrono::milliseconds delay,
495- std::function<void()> callback)
496+ std::function<void()> const& callback)
497 {
498 auto alarm = create_alarm(callback);
499
500@@ -234,7 +243,7 @@
501
502 std::unique_ptr<mir::time::Alarm> mir::GLibMainLoop::notify_at(
503 mir::time::Timestamp t,
504- std::function<void()> callback)
505+ std::function<void()> const& callback)
506 {
507 auto alarm = create_alarm(callback);
508
509@@ -244,7 +253,15 @@
510 }
511
512 std::unique_ptr<mir::time::Alarm> mir::GLibMainLoop::create_alarm(
513- std::function<void()> callback)
514+ std::function<void()> const& callback)
515+{
516+ return create_alarm(callback, []{}, []{});
517+}
518+
519+std::unique_ptr<mir::time::Alarm> mir::GLibMainLoop::create_alarm(
520+ std::function<void()> const& callback,
521+ std::function<void()> const& lock,
522+ std::function<void()> const& unlock)
523 {
524 auto const callback_with_exception_handling =
525 [this, callback]
526@@ -254,7 +271,7 @@
527 };
528
529 return std::make_unique<AlarmImpl>(
530- main_context, clock, callback_with_exception_handling);
531+ main_context, clock, callback_with_exception_handling, lock, unlock);
532 }
533
534 void mir::GLibMainLoop::reprocess_all_sources()
535
536=== modified file 'src/server/glib_main_loop_sources.cpp'
537--- src/server/glib_main_loop_sources.cpp 2015-02-13 06:12:34 +0000
538+++ src/server/glib_main_loop_sources.cpp 2015-02-20 17:57:50 +0000
539@@ -1,5 +1,5 @@
540 /*
541- * Copyright © 2014 Canonical Ltd.
542+ * Copyright © 2014-2015 Canonical Ltd.
543 *
544 * This program is free software: you can redistribute it and/or modify it
545 * under the terms of the GNU General Public License version 3,
546@@ -14,11 +14,13 @@
547 * along with this program. If not, see <http://www.gnu.org/licenses/>.
548 *
549 * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
550+ * Alberto Aguirre <alberto.aguirre@canonical.com>
551 */
552
553 #include "mir/glib_main_loop_sources.h"
554 #include "mir/recursive_read_write_mutex.h"
555 #include "mir/thread_safe_list.h"
556+#include "mir/raii.h"
557
558 #include <algorithm>
559 #include <atomic>
560@@ -233,18 +235,25 @@
561 GMainContext* main_context,
562 std::shared_ptr<time::Clock> const& clock,
563 std::function<void()> const& handler,
564+ std::function<void()> const& lock,
565+ std::function<void()> const& unlock,
566 time::Timestamp target_time)
567 {
568 struct TimerContext
569 {
570 TimerContext(std::shared_ptr<time::Clock> const& clock,
571 std::function<void()> const& handler,
572+ std::function<void()> const& lock,
573+ std::function<void()> const& unlock,
574 time::Timestamp target_time)
575- : clock{clock}, handler{handler}, target_time{target_time}, enabled{true}
576+ : clock{clock}, handler{handler}, lock{lock}, unlock{unlock},
577+ target_time{target_time}, enabled{true}
578 {
579 }
580 std::shared_ptr<time::Clock> clock;
581 std::function<void()> handler;
582+ std::function<void()> lock;
583+ std::function<void()> unlock;
584 time::Timestamp target_time;
585 bool enabled;
586 mir::RecursiveReadWriteMutex mutex;
587@@ -284,6 +293,9 @@
588 {
589 auto& ctx = reinterpret_cast<TimerGSource*>(source)->ctx;
590
591+ // Caller may pass std::function objects to preserve locking
592+ // order during callback dispatching, so aquire them first.
593+ auto caller_lock = mir::raii::paired_calls(std::ref(ctx.lock), std::ref(ctx.unlock));
594 RecursiveReadLock lock{ctx.mutex};
595 if (ctx.enabled)
596 ctx.handler();
597@@ -321,7 +333,7 @@
598 auto const timer_gsource = reinterpret_cast<TimerGSource*>(static_cast<GSource*>(gsource));
599
600 timer_gsource->ctx_constructed = false;
601- new (&timer_gsource->ctx) TimerContext{clock, handler, target_time};
602+ new (&timer_gsource->ctx) TimerContext{clock, handler, lock, unlock, target_time};
603 timer_gsource->ctx_constructed = true;
604
605 g_source_attach(gsource, main_context);
606
607=== modified file 'tests/include/mir_test_doubles/mock_frame_dropping_policy_factory.h'
608--- tests/include/mir_test_doubles/mock_frame_dropping_policy_factory.h 2014-05-22 20:48:20 +0000
609+++ tests/include/mir_test_doubles/mock_frame_dropping_policy_factory.h 2015-02-20 17:57:50 +0000
610@@ -1,5 +1,5 @@
611 /*
612- * Copyright © 2014 Canonical Ltd.
613+ * Copyright © 2014-2015 Canonical Ltd.
614 *
615 * This program is free software: you can redistribute it and/or modify it
616 * under the terms of the GNU General Public License version 3,
617@@ -14,6 +14,7 @@
618 * along with this program. If not, see <http://www.gnu.org/licenses/>.
619 *
620 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
621+ * Alberto Aguirre <alberto.aguirre@canonical.com>
622 */
623
624
625@@ -23,6 +24,8 @@
626 #include "mir/compositor/frame_dropping_policy_factory.h"
627 #include "mir/compositor/frame_dropping_policy.h"
628
629+#include "mir_test/gmock_fixes.h"
630+
631 #include <unordered_set>
632 #include <functional>
633
634@@ -42,7 +45,9 @@
635 class MockFrameDroppingPolicy : public mc::FrameDroppingPolicy
636 {
637 public:
638- MockFrameDroppingPolicy(std::function<void(void)> callback,
639+ MockFrameDroppingPolicy(std::function<void()> const& callback,
640+ std::function<void()> const& lock,
641+ std::function<void()> const& unlock,
642 MockFrameDroppingPolicyFactory const* parent);
643 ~MockFrameDroppingPolicy();
644
645@@ -55,14 +60,19 @@
646 friend class MockFrameDroppingPolicyFactory;
647 void parent_destroyed();
648
649- std::function<void(void)> callback;
650+ std::function<void()> callback;
651+ std::function<void()> lock;
652+ std::function<void()> unlock;
653 MockFrameDroppingPolicyFactory const* parent;
654 };
655
656 class MockFrameDroppingPolicyFactory : public mc::FrameDroppingPolicyFactory
657 {
658 public:
659- std::unique_ptr<mc::FrameDroppingPolicy> create_policy(std::function<void(void)> drop_frame) const override;
660+ std::unique_ptr<mc::FrameDroppingPolicy> create_policy(
661+ std::function<void()> const& drop_frame,
662+ std::function<void()> const& lock,
663+ std::function<void()> const& unlock) const override;
664
665 ~MockFrameDroppingPolicyFactory();
666
667@@ -75,6 +85,15 @@
668 mutable std::unordered_set<MockFrameDroppingPolicy*> policies;
669 };
670
671+class FrameDroppingPolicyFactoryMock : public mc::FrameDroppingPolicyFactory
672+{
673+public:
674+ MOCK_CONST_METHOD3(create_policy, std::unique_ptr<mc::FrameDroppingPolicy>(
675+ std::function<void()> const&,
676+ std::function<void()> const&,
677+ std::function<void()> const&));
678+};
679+
680 }
681 }
682 }
683
684=== modified file 'tests/include/mir_test_doubles/mock_main_loop.h'
685--- tests/include/mir_test_doubles/mock_main_loop.h 2014-06-24 14:12:25 +0000
686+++ tests/include/mir_test_doubles/mock_main_loop.h 2015-02-20 17:57:50 +0000
687@@ -1,5 +1,5 @@
688 /*
689- * Copyright © 2014 Canonical Ltd.
690+ * Copyright © 2014-2015 Canonical Ltd.
691 *
692 * This program is free software: you can redistribute it and/or modify
693 * it under the terms of the GNU General Public License version 3 as
694@@ -52,11 +52,19 @@
695 MOCK_METHOD1(pause_processing_for,void (void const*));
696 MOCK_METHOD1(resume_processing_for,void (void const*));
697
698- MOCK_METHOD2(notify_in, std::unique_ptr<time::Alarm>(std::chrono::milliseconds delay,
699- std::function<void(void)> callback));
700- MOCK_METHOD2(notify_at, std::unique_ptr<time::Alarm>(time::Timestamp time_point,
701- std::function<void(void)> callback));
702- MOCK_METHOD1(create_alarm, std::unique_ptr<time::Alarm>(std::function<void ()> callback));
703+ MOCK_METHOD2(notify_in,
704+ std::unique_ptr<time::Alarm>(std::chrono::milliseconds delay,
705+ std::function<void()> const& callback));
706+ MOCK_METHOD2(notify_at,
707+ std::unique_ptr<time::Alarm>(time::Timestamp time_point,
708+ std::function<void()> const& callback));
709+ MOCK_METHOD1(create_alarm, std::unique_ptr<time::Alarm>(std::function<void()> const& callback));
710+ MOCK_METHOD3(create_alarm,
711+ std::unique_ptr<time::Alarm>(
712+ std::function<void()> const& callback,
713+ std::function<void()> const& lock,
714+ std::function<void()> const& unlock));
715+
716 };
717
718 }
719
720=== modified file 'tests/include/mir_test_doubles/mock_timer.h'
721--- tests/include/mir_test_doubles/mock_timer.h 2014-05-22 20:48:20 +0000
722+++ tests/include/mir_test_doubles/mock_timer.h 2015-02-20 17:57:50 +0000
723@@ -1,5 +1,5 @@
724 /*
725- * Copyright © 2014 Canonical Ltd.
726+ * Copyright © 2014-2015 Canonical Ltd.
727 *
728 * This program is free software: you can redistribute it and/or modify it
729 * under the terms of the GNU General Public License version 3,
730@@ -14,6 +14,7 @@
731 * along with this program. If not, see <http://www.gnu.org/licenses/>.
732 *
733 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
734+ * Alberto Aguirre <alberto.aguirre@canonical.com>
735 */
736
737 #ifndef MIR_TEST_DOUBLES_MOCK_TIMER_H_
738@@ -36,11 +37,12 @@
739 FakeTimer(std::shared_ptr<FakeClock> const& clock);
740
741 std::unique_ptr<time::Alarm> notify_in(std::chrono::milliseconds delay,
742- std::function<void(void)> callback) override;
743+ std::function<void()> const& callback) override;
744 std::unique_ptr<time::Alarm> notify_at(time::Timestamp time_point,
745- std::function<void(void)> callback) override;
746- std::unique_ptr<time::Alarm> create_alarm(std::function<void ()> callback) override;
747-
748+ std::function<void()> const& callback) override;
749+ std::unique_ptr<time::Alarm> create_alarm(std::function<void()> const& callback) override;
750+ std::unique_ptr<time::Alarm> create_alarm(std::function<void()> const& callback,
751+ std::function<void()> const& lock, std::function<void()> const& unlock) override;
752 private:
753 std::shared_ptr<FakeClock> const clock;
754 };
755
756=== modified file 'tests/include/mir_test_doubles/stub_frame_dropping_policy_factory.h'
757--- tests/include/mir_test_doubles/stub_frame_dropping_policy_factory.h 2014-05-22 20:48:20 +0000
758+++ tests/include/mir_test_doubles/stub_frame_dropping_policy_factory.h 2015-02-20 17:57:50 +0000
759@@ -1,5 +1,5 @@
760 /*
761- * Copyright © 2014 Canonical Ltd.
762+ * Copyright © 2014-2015 Canonical Ltd.
763 *
764 * This program is free software: you can redistribute it and/or modify it
765 * under the terms of the GNU General Public License version 3,
766@@ -14,6 +14,7 @@
767 * along with this program. If not, see <http://www.gnu.org/licenses/>.
768 *
769 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
770+ * Alberto Aguirre <alberto.aguirre@canonical.com>
771 */
772
773
774@@ -46,9 +47,12 @@
775 class StubFrameDroppingPolicyFactory : public mc::FrameDroppingPolicyFactory
776 {
777 public:
778- std::unique_ptr<mc::FrameDroppingPolicy> create_policy(std::function<void(void)>) const override
779+ std::unique_ptr<mc::FrameDroppingPolicy> create_policy(
780+ std::function<void()> const&,
781+ std::function<void()> const&,
782+ std::function<void()> const& ) const override
783 {
784- return std::unique_ptr<mc::FrameDroppingPolicy>{new StubFrameDroppingPolicy};
785+ return std::unique_ptr<mc::FrameDroppingPolicy>{new StubFrameDroppingPolicy};
786 }
787 };
788
789
790=== modified file 'tests/include/mir_test_doubles/stub_timer.h'
791--- tests/include/mir_test_doubles/stub_timer.h 2015-01-21 07:34:50 +0000
792+++ tests/include/mir_test_doubles/stub_timer.h 2015-02-20 17:57:50 +0000
793@@ -1,5 +1,5 @@
794 /*
795- * Copyright © 2014 Canonical Ltd.
796+ * Copyright © 2014-2015 Canonical Ltd.
797 *
798 * This program is free software: you can redistribute it and/or modify it
799 * under the terms of the GNU General Public License version 3,
800@@ -51,12 +51,12 @@
801
802 class StubTimer : public mir::time::Timer
803 {
804- std::unique_ptr<mir::time::Alarm> notify_in(std::chrono::milliseconds, std::function<void(void)>) override
805+ std::unique_ptr<mir::time::Alarm> notify_in(std::chrono::milliseconds, std::function<void()> const&) override
806 {
807 return std::unique_ptr<mir::time::Alarm>{new StubAlarm};
808 }
809
810- std::unique_ptr<mir::time::Alarm> notify_at(mir::time::Timestamp, std::function<void(void)>) override
811+ std::unique_ptr<mir::time::Alarm> notify_at(mir::time::Timestamp, std::function<void()> const&) override
812 {
813 return std::unique_ptr<mir::time::Alarm>{new StubAlarm};
814 }
815
816=== modified file 'tests/include/mir_test_doubles/triggered_main_loop.h'
817--- tests/include/mir_test_doubles/triggered_main_loop.h 2015-01-27 11:47:52 +0000
818+++ tests/include/mir_test_doubles/triggered_main_loop.h 2015-02-20 17:57:50 +0000
819@@ -39,7 +39,7 @@
820
821 void register_fd_handler(std::initializer_list<int> fds, void const* owner, fd_callback const& handler) override;
822 void unregister_fd_handler(void const* owner) override;
823- std::unique_ptr<mir::time::Alarm> notify_in(std::chrono::milliseconds delay, callback call) override;
824+ std::unique_ptr<mir::time::Alarm> notify_in(std::chrono::milliseconds delay, callback const& call) override;
825 void enqueue(void const* owner, ServerAction const& action) override;
826
827 void trigger_pending_fds();
828
829=== modified file 'tests/mir_test_doubles/mock_frame_dropping_policy_factory.cpp'
830--- tests/mir_test_doubles/mock_frame_dropping_policy_factory.cpp 2014-05-22 20:48:20 +0000
831+++ tests/mir_test_doubles/mock_frame_dropping_policy_factory.cpp 2015-02-20 17:57:50 +0000
832@@ -1,5 +1,5 @@
833 /*
834- * Copyright © 2014 Canonical Ltd.
835+ * Copyright © 2014-2015 Canonical Ltd.
836 *
837 * This program is free software: you can redistribute it and/or modify it
838 * under the terms of the GNU General Public License version 3,
839@@ -14,6 +14,7 @@
840 * along with this program. If not, see <http://www.gnu.org/licenses/>.
841 *
842 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
843+ * Alberto Aguirre <alberto.aguirre@canonical.com>
844 */
845
846 #include "mir_test_doubles/mock_frame_dropping_policy_factory.h"
847@@ -21,9 +22,13 @@
848 namespace mc = mir::compositor;
849 namespace mtd = mir::test::doubles;
850
851-mtd::MockFrameDroppingPolicy::MockFrameDroppingPolicy(std::function<void(void)> callback,
852+mtd::MockFrameDroppingPolicy::MockFrameDroppingPolicy(std::function<void()> const& callback,
853+ std::function<void()> const& lock,
854+ std::function<void()> const& unlock,
855 MockFrameDroppingPolicyFactory const* parent)
856 : callback{callback},
857+ lock{lock},
858+ unlock{unlock},
859 parent{parent}
860 {
861 }
862@@ -36,7 +41,9 @@
863
864 void mtd::MockFrameDroppingPolicy::trigger()
865 {
866+ lock();
867 callback();
868+ unlock();
869 }
870
871 void mtd::MockFrameDroppingPolicy::parent_destroyed()
872@@ -45,9 +52,10 @@
873 }
874
875 std::unique_ptr<mc::FrameDroppingPolicy>
876-mtd::MockFrameDroppingPolicyFactory::create_policy(std::function<void(void)> drop_frame) const
877+mtd::MockFrameDroppingPolicyFactory::create_policy(std::function<void()> const& drop_frame,
878+ std::function<void()> const& lock, std::function<void()> const& unlock) const
879 {
880- auto policy = new ::testing::NiceMock<MockFrameDroppingPolicy>{drop_frame, this};
881+ auto policy = new ::testing::NiceMock<MockFrameDroppingPolicy>{drop_frame, lock, unlock, this};
882 policies.insert(policy);
883 return std::unique_ptr<mc::FrameDroppingPolicy>{policy};
884 }
885
886=== modified file 'tests/mir_test_doubles/mock_timer.cpp'
887--- tests/mir_test_doubles/mock_timer.cpp 2015-01-21 07:34:50 +0000
888+++ tests/mir_test_doubles/mock_timer.cpp 2015-02-20 17:57:50 +0000
889@@ -1,5 +1,5 @@
890 /*
891- * Copyright © 2014 Canonical Ltd.
892+ * Copyright © 2014-2015 Canonical Ltd.
893 *
894 * This program is free software: you can redistribute it and/or modify it
895 * under the terms of the GNU General Public License version 3,
896@@ -14,6 +14,7 @@
897 * along with this program. If not, see <http://www.gnu.org/licenses/>.
898 *
899 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
900+ * Alberto Aguirre <alberto.aguirre@canonical.com>
901 */
902
903 #include "mir_test_doubles/mock_timer.h"
904@@ -27,7 +28,10 @@
905 class FakeAlarm : public mir::time::Alarm
906 {
907 public:
908- FakeAlarm(std::function<void(void)> callback, std::shared_ptr<mt::FakeClock> const& clock);
909+ FakeAlarm(std::function<void()> const& callback,
910+ std::function<void()> const& lock,
911+ std::function<void()> const& unlock,
912+ std::shared_ptr<mt::FakeClock> const& clock);
913 ~FakeAlarm() override;
914
915
916@@ -39,9 +43,13 @@
917 private:
918 struct InternalState
919 {
920- explicit InternalState(std::function<void(void)> callback);
921+ explicit InternalState(std::function<void()> const& callback,
922+ std::function<void()> const& lock,
923+ std::function<void()> const& unlock);
924 State state;
925- std::function<void(void)> const callback;
926+ std::function<void()> const callback;
927+ std::function<void()> const lock;
928+ std::function<void()> const unlock;
929 mt::FakeClock::time_point threshold;
930 };
931
932@@ -51,13 +59,20 @@
933 std::shared_ptr<mt::FakeClock> const clock;
934 };
935
936-FakeAlarm::InternalState::InternalState(std::function<void(void)> callback)
937- : state{mir::time::Alarm::pending}, callback{callback}
938+FakeAlarm::InternalState::InternalState(
939+ std::function<void()> const& callback,
940+ std::function<void()> const& lock,
941+ std::function<void()> const& unlock)
942+ : state{mir::time::Alarm::pending}, callback{callback}, lock{lock}, unlock{unlock}
943 {
944 }
945
946-FakeAlarm::FakeAlarm(std::function<void(void)> callback, std::shared_ptr<mt::FakeClock> const& clock)
947- : internal_state{std::make_shared<InternalState>(callback)}, clock{clock}
948+FakeAlarm::FakeAlarm(
949+ std::function<void()> const& callback,
950+ std::function<void()> const& lock,
951+ std::function<void()> const& unlock,
952+ std::shared_ptr<mt::FakeClock> const& clock)
953+ : internal_state{std::make_shared<InternalState>(callback, lock, unlock)}, clock{clock}
954 {
955 }
956
957@@ -88,7 +103,9 @@
958 if (now > state.threshold)
959 {
960 state.state = triggered;
961+ state.lock();
962 state.callback();
963+ state.unlock();
964 return false;
965 }
966 return true;
967@@ -128,22 +145,28 @@
968 }
969
970 std::unique_ptr<mir::time::Alarm> mtd::FakeTimer::notify_in(std::chrono::milliseconds delay,
971- std::function<void(void)> callback)
972+ std::function<void()> const& callback)
973 {
974- auto alarm = std::unique_ptr<mir::time::Alarm>{new FakeAlarm{callback, clock}};
975+ auto alarm = std::unique_ptr<mir::time::Alarm>{new FakeAlarm{callback, []{}, []{}, clock}};
976 alarm->reschedule_in(delay);
977 return std::move(alarm);
978 }
979
980 std::unique_ptr<mir::time::Alarm> mtd::FakeTimer::notify_at(time::Timestamp time_point,
981- std::function<void(void)> callback)
982+ std::function<void()> const& callback)
983 {
984- auto alarm = std::unique_ptr<mir::time::Alarm>{new FakeAlarm{callback, clock}};
985+ auto alarm = std::unique_ptr<mir::time::Alarm>{new FakeAlarm{callback, []{}, []{}, clock}};
986 alarm->reschedule_for(time_point);
987 return std::move(alarm);
988 }
989
990-std::unique_ptr<mir::time::Alarm> mir::test::doubles::FakeTimer::create_alarm(std::function<void(void)> callback)
991-{
992- return std::unique_ptr<mir::time::Alarm>{new FakeAlarm{callback, clock}};
993+std::unique_ptr<mir::time::Alarm> mir::test::doubles::FakeTimer::create_alarm(std::function<void()> const& callback)
994+{
995+ return std::unique_ptr<mir::time::Alarm>{new FakeAlarm{callback, []{}, []{}, clock}};
996+}
997+
998+std::unique_ptr<mir::time::Alarm> mir::test::doubles::FakeTimer::create_alarm(std::function<void()> const& callback,
999+ std::function<void()> const& lock, std::function<void()> const& unlock)
1000+{
1001+ return std::unique_ptr<mir::time::Alarm>{new FakeAlarm{callback, lock, unlock, clock}};
1002 }
1003
1004=== modified file 'tests/mir_test_doubles/triggered_main_loop.cpp'
1005--- tests/mir_test_doubles/triggered_main_loop.cpp 2015-01-27 11:47:52 +0000
1006+++ tests/mir_test_doubles/triggered_main_loop.cpp 2015-02-20 17:57:50 +0000
1007@@ -85,8 +85,9 @@
1008 action();
1009 }
1010
1011-std::unique_ptr<mir::time::Alarm> mtd::TriggeredMainLoop::notify_in(std::chrono::milliseconds delay,
1012- callback call)
1013+std::unique_ptr<mir::time::Alarm>
1014+mtd::TriggeredMainLoop::notify_in(std::chrono::milliseconds delay,
1015+ callback const& call)
1016 {
1017 base::notify_in(delay, call);
1018 timeout_callbacks.push_back(call);
1019
1020=== modified file 'tests/unit-tests/compositor/test_buffer_queue.cpp'
1021--- tests/unit-tests/compositor/test_buffer_queue.cpp 2015-02-17 06:38:35 +0000
1022+++ tests/unit-tests/compositor/test_buffer_queue.cpp 2015-02-20 17:57:50 +0000
1023@@ -1,5 +1,5 @@
1024 /*
1025- * Copyright © 2013-2014 Canonical Ltd.
1026+ * Copyright © 2013-2015 Canonical Ltd.
1027 *
1028 * This program is free software: you can redistribute it and/or modify
1029 * it under the terms of the GNU General Public License version 3 as
1030@@ -1740,3 +1740,12 @@
1031 auto comp2 = q.compositor_acquire(new_compositor_id);
1032 ASSERT_THAT(comp2->id(), Eq(handle2->id()));
1033 }
1034+
1035+TEST_F(BufferQueueTest, creates_policy_with_lock_unlock_functions)
1036+{
1037+ int const nbuffers = 3;
1038+
1039+ mtd::FrameDroppingPolicyFactoryMock mock_policy_factory;
1040+ EXPECT_CALL(mock_policy_factory, create_policy(_, _, _));
1041+ mc::BufferQueue q(nbuffers, allocator, basic_properties, mock_policy_factory);
1042+}
1043
1044=== modified file 'tests/unit-tests/compositor/test_timeout_frame_dropping_policy.cpp'
1045--- tests/unit-tests/compositor/test_timeout_frame_dropping_policy.cpp 2014-05-22 20:48:20 +0000
1046+++ tests/unit-tests/compositor/test_timeout_frame_dropping_policy.cpp 2015-02-20 17:57:50 +0000
1047@@ -1,5 +1,5 @@
1048 /*
1049- * Copyright © 2014 Canonical Ltd.
1050+ * Copyright © 2014-2015 Canonical Ltd.
1051 *
1052 * This program is free software: you can redistribute it and/or modify it
1053 * under the terms of the GNU General Public License version 3,
1054@@ -14,16 +14,18 @@
1055 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1056 *
1057 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
1058+ * Alberto Aguirre <alberto.aguirre@canonical.com>
1059 */
1060
1061 #include "src/server/compositor/timeout_frame_dropping_policy_factory.h"
1062 #include "mir/compositor/frame_dropping_policy.h"
1063
1064 #include "mir_test_doubles/mock_timer.h"
1065-
1066+#include "mir_test/auto_unblock_thread.h"
1067 #include "mir_test/gmock_fixes.h"
1068
1069 #include <stdexcept>
1070+#include <mutex>
1071
1072 #include <gtest/gtest.h>
1073 #include <gmock/gmock.h>
1074@@ -40,7 +42,7 @@
1075 bool frame_dropped{false};
1076
1077 mc::TimeoutFrameDroppingPolicyFactory factory{timer, timeout};
1078- auto policy = factory.create_policy([&frame_dropped]{ frame_dropped = true; });
1079+ auto policy = factory.create_policy([&frame_dropped]{ frame_dropped = true; }, []{}, []{});
1080
1081 clock->advance_time(timeout + std::chrono::milliseconds{1});
1082 EXPECT_FALSE(frame_dropped);
1083@@ -54,7 +56,7 @@
1084 bool frame_dropped{false};
1085
1086 mc::TimeoutFrameDroppingPolicyFactory factory{timer, timeout};
1087- auto policy = factory.create_policy([&frame_dropped]{ frame_dropped = true; });
1088+ auto policy = factory.create_policy([&frame_dropped]{ frame_dropped = true; }, []{}, []{});
1089 policy->swap_now_blocking();
1090
1091 clock->advance_time(timeout - std::chrono::milliseconds{1});
1092@@ -71,7 +73,7 @@
1093
1094 bool frame_dropped{false};
1095 mc::TimeoutFrameDroppingPolicyFactory factory{timer, timeout};
1096- auto policy = factory.create_policy([&frame_dropped]{ frame_dropped = true; });
1097+ auto policy = factory.create_policy([&frame_dropped]{ frame_dropped = true; }, []{}, []{});
1098
1099 policy->swap_now_blocking();
1100 policy->swap_unblocked();
1101@@ -89,7 +91,7 @@
1102
1103 bool frame_dropped{false};
1104 mc::TimeoutFrameDroppingPolicyFactory factory{timer, timeout};
1105- auto policy = factory.create_policy([&frame_dropped]{ frame_dropped = true; });
1106+ auto policy = factory.create_policy([&frame_dropped]{ frame_dropped = true; }, []{}, []{});
1107
1108 policy->swap_now_blocking();
1109 policy->swap_now_blocking();
1110@@ -119,7 +121,7 @@
1111
1112 bool frame_dropped{false};
1113 mc::TimeoutFrameDroppingPolicyFactory factory{timer, timeout};
1114- auto policy = factory.create_policy([&frame_dropped]{ frame_dropped = true; });
1115+ auto policy = factory.create_policy([&frame_dropped]{ frame_dropped = true; }, []{}, []{});
1116
1117 policy->swap_now_blocking();
1118 policy->swap_now_blocking();
1119@@ -142,7 +144,7 @@
1120
1121 bool frame_dropped{false};
1122 mc::TimeoutFrameDroppingPolicyFactory factory{timer, timeout};
1123- auto policy = factory.create_policy([&frame_dropped]{ frame_dropped = true; });
1124+ auto policy = factory.create_policy([&frame_dropped]{ frame_dropped = true; }, []{}, []{});
1125
1126 policy->swap_now_blocking();
1127 clock->advance_time(timeout - std::chrono::milliseconds{1});
1128@@ -160,7 +162,7 @@
1129
1130 bool frame_dropped{false};
1131 mc::TimeoutFrameDroppingPolicyFactory factory{timer, timeout};
1132- auto policy = factory.create_policy([&frame_dropped]{ frame_dropped = true; });
1133+ auto policy = factory.create_policy([&frame_dropped]{ frame_dropped = true; }, []{}, []{});
1134
1135 policy->swap_now_blocking();
1136 policy->swap_now_blocking();
1137@@ -184,3 +186,28 @@
1138 clock->advance_time(timeout + std::chrono::milliseconds{1});
1139 EXPECT_FALSE(frame_dropped);
1140 }
1141+
1142+TEST(TimeoutFrameDroppingPolicy, policy_calls_lock_unlock_functions)
1143+{
1144+ using namespace testing;
1145+
1146+ auto clock = std::make_shared<mt::FakeClock>();
1147+ auto timer = std::make_shared<mtd::FakeTimer>(clock);
1148+ std::chrono::milliseconds const timeout{1000};
1149+
1150+ mc::TimeoutFrameDroppingPolicyFactory factory{timer, timeout};
1151+
1152+ std::mutex m;
1153+ std::unique_lock<std::mutex> handler_guard{m, std::defer_lock};
1154+
1155+ auto policy = factory.create_policy([&]
1156+ {
1157+ EXPECT_THAT(handler_guard.owns_lock(), Eq(true));
1158+ },
1159+ [&] { handler_guard.lock(); },
1160+ [&] { handler_guard.unlock(); });
1161+
1162+
1163+ policy->swap_now_blocking();
1164+ clock->advance_time(timeout + std::chrono::milliseconds{1});
1165+}
1166
1167=== modified file 'tests/unit-tests/test_glib_main_loop.cpp'
1168--- tests/unit-tests/test_glib_main_loop.cpp 2015-02-03 15:07:44 +0000
1169+++ tests/unit-tests/test_glib_main_loop.cpp 2015-02-20 17:57:50 +0000
1170@@ -1,5 +1,5 @@
1171 /*
1172- * Copyright © 2014 Canonical Ltd.
1173+ * Copyright © 2014-2015 Canonical Ltd.
1174 *
1175 * This program is free software: you can redistribute it and/or modify
1176 * it under the terms of the GNU General Public License version 3 as
1177@@ -14,6 +14,7 @@
1178 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1179 *
1180 * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
1181+ * Alberto Aguirre <alberto.aguirre@canonical.com>
1182 */
1183
1184 #include "mir/glib_main_loop.h"
1185@@ -960,43 +961,45 @@
1186 }
1187
1188 TEST_F(GLibMainLoopAlarmTest, alarm_callback_cannot_deadlock)
1189-{ // Regression test for deadlock bug LP: #1339700
1190- std::timed_mutex m;
1191- std::atomic_bool failed(false);
1192+{
1193+ using namespace testing;
1194+
1195+ std::mutex m;
1196+ std::unique_lock<std::mutex> handler_guard{m, std::defer_lock};
1197+ std::atomic_bool exit{false};
1198 int i = 0;
1199 int const loops = 5;
1200
1201- auto alarm = ml.notify_in(std::chrono::milliseconds{0}, [&]()
1202+ auto alarm = ml.create_alarm([&]()
1203 {
1204- // From this angle, ensure we can lock m (alarm should be unlocked)
1205- failed = !m.try_lock_for(std::chrono::seconds{5});
1206- ASSERT_FALSE(failed);
1207+ //In this handler context, the alarm dispatcher should have
1208+ //acquired the handler guard
1209+ EXPECT_THAT(handler_guard.owns_lock(), Eq(true));
1210 ++i;
1211- m.unlock();
1212+ },
1213+ [&] { handler_guard.lock(); },
1214+ [&] { handler_guard.unlock(); });
1215+
1216+ mt::AutoUnblockThread t([&]{ exit = true; }, [&]()
1217+ {
1218+ std::unique_lock<std::mutex> guard{m};
1219+ while (i < loops && !exit)
1220+ {
1221+ // It's safe to call cancel now because lock ordering is enforced
1222+ // by passing the lock/unlock lambdas to alarm above
1223+ alarm->cancel();
1224+ guard.unlock();
1225+ std::this_thread::yield();
1226+ guard.lock();
1227+ }
1228 });
1229
1230- std::thread t([&]()
1231- {
1232- m.lock();
1233- while (i < loops && !failed)
1234- {
1235- // From this angle, ensure we can lock alarm while holding m
1236- (void)alarm->state();
1237- m.unlock();
1238- std::this_thread::yield();
1239- m.lock();
1240- }
1241- m.unlock();
1242- });
1243-
1244 UnblockMainLoop unblocker(ml);
1245 for (int j = 0; j < loops; ++j)
1246 {
1247+ alarm->reschedule_in(std::chrono::milliseconds{10});
1248 clock->advance_by(std::chrono::milliseconds{11}, ml);
1249- alarm->reschedule_in(std::chrono::milliseconds{10});
1250 }
1251-
1252- t.join();
1253 }
1254
1255 TEST_F(GLibMainLoopAlarmTest, alarm_fires_at_correct_time_point)
1256
1257=== modified file 'tests/unit-tests/test_raii.cpp'
1258--- tests/unit-tests/test_raii.cpp 2013-10-21 14:21:00 +0000
1259+++ tests/unit-tests/test_raii.cpp 2015-02-20 17:57:50 +0000
1260@@ -179,3 +179,23 @@
1261
1262 EXPECT_EQ(this, raii.get());
1263 }
1264+
1265+TEST_F(RaiiTest, paired_call_takes_std_function_refs)
1266+{
1267+ int creator_call_count = 0;
1268+ int deleter_call_count = 0;
1269+ int const expected_calls = 2;
1270+
1271+ std::function<void()> creator = [&creator_call_count] { creator_call_count++; };
1272+ std::function<void()> deleter = [&deleter_call_count] { deleter_call_count++; };
1273+
1274+ for (int i = 0; i < expected_calls; i++)
1275+ {
1276+ auto const raii = mir::raii::paired_calls(
1277+ std::ref(creator),
1278+ std::ref(deleter));
1279+ }
1280+
1281+ EXPECT_THAT(creator_call_count, Eq(expected_calls));
1282+ EXPECT_THAT(deleter_call_count, Eq(expected_calls));
1283+}

Subscribers

People subscribed via source and target branches