Merge lp:~albaguirre/mir/fix-1421255 into lp:mir
- fix-1421255
- Merge into development-branch
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 |
Related bugs: |
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_
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
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2327
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
ABORTED: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Francis Ginther (fginther) wrote : | # |
The mir-mediumtests
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2327
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
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.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2328
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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.
Alberto Aguirre (albaguirre) wrote : | # |
> 211 + [this] { guard_lock =
> std::move(
> 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_
> 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_
> create_
> 119 + virtual std::unique_
> 120 + std::function<
> 121 + std::function<
> 122 + std::function<
>
> 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?
Alan Griffiths (alan-griffiths) wrote : | # |
Ran out of time trying to understand. Will look again tomorrow if it doesn't land meanwhile.
Alan Griffiths (alan-griffiths) wrote : | # |
This seems elaborate and easy to make mistakes using but I don't have a better suggestion (yet).
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2331
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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).
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2332
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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.
Preview Diff
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 | +} |
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<FrameDroppi ngPolicy> create_ policy( std::function< void()> const& drop_frame) const = 0; ptr<FrameDroppi ngPolicy> create_policy( void()> const& drop_frame, void()> const& lock, void()> const& unlock) const = 0;
119 + virtual std::unique_
120 + std::function<
121 + std::function<
122 + std::function<
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.