Mir

Merge lp:~afrantzis/mir/fix-1477467-callback-before-wait-handle into lp:mir

Proposed by Alexandros Frantzis
Status: Merged
Approved by: Alan Griffiths
Approved revision: no longer in the source branch.
Merged at revision: 2776
Proposed branch: lp:~afrantzis/mir/fix-1477467-callback-before-wait-handle
Merge into: lp:mir
Diff against target: 309 lines (+242/-3)
6 files modified
src/client/buffer_stream.cpp (+3/-3)
src/client/mir_wait_handle.cpp (+6/-0)
src/client/mir_wait_handle.h (+2/-0)
tests/acceptance-tests/CMakeLists.txt (+1/-0)
tests/acceptance-tests/test_client_library_callbacks.cpp (+180/-0)
tests/unit-tests/client/test_client_buffer_stream.cpp (+50/-0)
To merge this branch: bzr merge lp:~afrantzis/mir/fix-1477467-callback-before-wait-handle
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Alan Griffiths Approve
Kevin DuBois (community) Approve
Review via email: mp+265636@code.launchpad.net

Commit message

client: Ensure the callback for mir_buffer_stream_swap_buffers is called before the corresponding MirWaitHandle is signaled

Description of the change

client: Ensure the callback for mir_buffer_stream_swap_buffers is called before the corresponding MirWaitHandle is signaled

This MP also adds acceptance tests to check this behavior in a few other callbacks too. Since the acceptance tests don't have access to mirclient internal code, they depend on a small artificial delay to trigger the desired effect/race. Although not ideal, this is good enough in practice.

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

lgtm

review: Approve
Revision history for this message
Kevin DuBois (kdub) wrote :

eh, still +1, but the bug seems to be covered well enough from the acceptance test, and the unit tests and the addition to WaitHandle seem less valuable

Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

> eh, still +1, but the bug seems to be covered well enough from the acceptance test,
> and the unit tests and the addition to WaitHandle seem less valuable

I added the unit tests because it was not easy to test the fix for the buffer_unavailable() path in the acceptance tests. Since I added the buffer_unavailable unit test I also added the buffer_available() test for completeness.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

I'm not convinced by the acceptance tests, but the unit tests seem adequate.

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/client/buffer_stream.cpp'
2--- src/client/buffer_stream.cpp 2015-07-16 07:03:19 +0000
3+++ src/client/buffer_stream.cpp 2015-07-23 11:00:27 +0000
4@@ -424,9 +424,9 @@
5 if (on_incoming_buffer)
6 {
7 process_buffer(buffer, lock);
8+ on_incoming_buffer();
9+ on_incoming_buffer = std::function<void()>{};
10 next_buffer_wait_handle.result_received();
11- on_incoming_buffer();
12- on_incoming_buffer = std::function<void()>{};
13 }
14 else
15 {
16@@ -438,10 +438,10 @@
17 {
18 std::unique_lock<decltype(mutex)> lock(mutex);
19 server_connection_lost = true;
20- next_buffer_wait_handle.result_received();
21 if (on_incoming_buffer)
22 {
23 on_incoming_buffer();
24 on_incoming_buffer = std::function<void()>{};
25 }
26+ next_buffer_wait_handle.result_received();
27 }
28
29=== modified file 'src/client/mir_wait_handle.cpp'
30--- src/client/mir_wait_handle.cpp 2015-02-22 07:46:25 +0000
31+++ src/client/mir_wait_handle.cpp 2015-07-23 11:00:27 +0000
32@@ -74,3 +74,9 @@
33 --expecting;
34 }
35
36+bool MirWaitHandle::has_result()
37+{
38+ std::lock_guard<std::mutex> lock(guard);
39+
40+ return received > 0;
41+}
42
43=== modified file 'src/client/mir_wait_handle.h'
44--- src/client/mir_wait_handle.h 2013-10-03 03:44:08 +0000
45+++ src/client/mir_wait_handle.h 2015-07-23 11:00:27 +0000
46@@ -40,6 +40,8 @@
47 void wait_for_one();
48 void wait_for_pending(std::chrono::milliseconds limit);
49
50+ bool has_result();
51+
52 private:
53 std::mutex guard;
54 std::condition_variable wait_condition;
55
56=== modified file 'tests/acceptance-tests/CMakeLists.txt'
57--- tests/acceptance-tests/CMakeLists.txt 2015-07-23 02:39:20 +0000
58+++ tests/acceptance-tests/CMakeLists.txt 2015-07-23 11:00:27 +0000
59@@ -13,6 +13,7 @@
60 test_client_header_version.cpp
61 test_client_input.cpp
62 test_client_library.cpp
63+ test_client_library_callbacks.cpp
64 test_client_library_errors.cpp
65 test_client_library_old.cpp
66 test_client_surface_events.cpp
67
68=== added file 'tests/acceptance-tests/test_client_library_callbacks.cpp'
69--- tests/acceptance-tests/test_client_library_callbacks.cpp 1970-01-01 00:00:00 +0000
70+++ tests/acceptance-tests/test_client_library_callbacks.cpp 2015-07-23 11:00:27 +0000
71@@ -0,0 +1,180 @@
72+/*
73+ * Copyright © 2015 Canonical Ltd.
74+ *
75+ * This program is free software: you can redistribute it and/or modify
76+ * it under the terms of the GNU General Public License version 3 as
77+ * published by the Free Software Foundation.
78+ *
79+ * This program is distributed in the hope that it will be useful,
80+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
81+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
82+ * GNU General Public License for more details.
83+ *
84+ * You should have received a copy of the GNU General Public License
85+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
86+ *
87+ * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
88+ */
89+
90+#include "mir_toolkit/mir_client_library.h"
91+
92+#include "mir_test_framework/headless_in_process_server.h"
93+#include "mir_test_framework/using_stub_client_platform.h"
94+#include "mir_test_framework/any_surface.h"
95+#include "mir/test/spin_wait.h"
96+
97+#include <gtest/gtest.h>
98+#include <gmock/gmock.h>
99+
100+#include <atomic>
101+#include <chrono>
102+#include <thread>
103+
104+namespace mt = mir::test;
105+namespace mtf = mir_test_framework;
106+
107+using namespace std::chrono_literals;
108+
109+namespace
110+{
111+
112+struct ClientLibraryCallbacks : mtf::HeadlessInProcessServer
113+{
114+ std::atomic<MirConnection*> connection{nullptr};
115+ std::atomic<MirSurface*> surface{nullptr};
116+ std::atomic<int> buffers{0};
117+
118+ void TearDown() override
119+ {
120+ if (surface)
121+ mir_surface_release_sync(surface);
122+ if (connection)
123+ mir_connection_release(connection);
124+
125+ mtf::HeadlessInProcessServer::TearDown();
126+ }
127+
128+ static void connection_callback(MirConnection* connection, void* context)
129+ {
130+ auto const config = reinterpret_cast<ClientLibraryCallbacks*>(context);
131+ config->connected(connection);
132+ }
133+
134+ static void create_surface_callback(MirSurface* surface, void* context)
135+ {
136+ auto const config = reinterpret_cast<ClientLibraryCallbacks*>(context);
137+ config->surface_created(surface);
138+ }
139+
140+ static void next_buffer_callback(MirBufferStream* bs, void* context)
141+ {
142+ auto const config = reinterpret_cast<ClientLibraryCallbacks*>(context);
143+ config->next_buffer(bs);
144+ }
145+
146+ static void release_surface_callback(MirSurface* surface, void* context)
147+ {
148+ auto const config = reinterpret_cast<ClientLibraryCallbacks*>(context);
149+ config->surface_released(surface);
150+ }
151+
152+ virtual void connected(MirConnection* conn)
153+ {
154+ std::this_thread::sleep_for(10ms);
155+ connection = conn;
156+ }
157+
158+ virtual void surface_created(MirSurface* new_surface)
159+ {
160+ std::this_thread::sleep_for(10ms);
161+ surface = new_surface;
162+ }
163+
164+ virtual void next_buffer(MirBufferStream*)
165+ {
166+ std::this_thread::sleep_for(10ms);
167+ ++buffers;
168+ }
169+
170+ void surface_released(MirSurface*)
171+ {
172+ std::this_thread::sleep_for(10ms);
173+ surface = nullptr;
174+ }
175+
176+ mtf::UsingStubClientPlatform using_stub_client_platform;
177+};
178+
179+}
180+
181+using namespace testing;
182+
183+TEST_F(ClientLibraryCallbacks, connect_callback_is_called_before_wait_handler_has_result)
184+{
185+ auto const wh = mir_connect(
186+ new_connection().c_str(), __PRETTY_FUNCTION__,
187+ connection_callback, this);
188+ mir_wait_for(wh);
189+
190+ EXPECT_THAT(connection.load(), NotNull());
191+
192+ // Even if the test fails, wait for object to become ready so we can
193+ // tear down properly
194+ mt::spin_wait_for_condition_or_timeout(
195+ [this] { return connection != nullptr; },
196+ 3s);
197+}
198+
199+TEST_F(ClientLibraryCallbacks, create_surface_callback_is_called_before_wait_handler_has_result)
200+{
201+ connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
202+
203+ auto const spec = mir_connection_create_spec_for_normal_surface(
204+ connection, 100, 100, mir_pixel_format_argb_8888);
205+ auto const wh = mir_surface_create(spec, create_surface_callback, this);
206+ mir_surface_spec_release(spec);
207+ mir_wait_for(wh);
208+
209+ EXPECT_THAT(surface.load(), NotNull());
210+
211+ // Even if the test fails, wait for object to become ready so we can
212+ // tear down properly
213+ mt::spin_wait_for_condition_or_timeout(
214+ [this] { return surface != nullptr; },
215+ 3s);
216+}
217+
218+TEST_F(ClientLibraryCallbacks, swap_buffers_callback_is_called_before_wait_handler_has_result)
219+{
220+ connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
221+ surface = mtf::make_any_surface(connection);
222+
223+ auto const wh = mir_buffer_stream_swap_buffers(
224+ mir_surface_get_buffer_stream(surface), next_buffer_callback, this);
225+ mir_wait_for(wh);
226+
227+ EXPECT_THAT(buffers, Eq(1));
228+
229+ // Even if the test fails, wait for object to become ready so we can
230+ // tear down properly
231+ mt::spin_wait_for_condition_or_timeout(
232+ [this] { return buffers == 1; },
233+ 3s);
234+}
235+
236+TEST_F(ClientLibraryCallbacks, release_surface_callback_is_called_before_wait_handler_has_result)
237+{
238+ connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
239+ surface = mtf::make_any_surface(connection);
240+
241+ auto const wh = mir_surface_release(surface, release_surface_callback, this);
242+ mir_wait_for(wh);
243+
244+ EXPECT_THAT(surface.load(), IsNull());
245+
246+ // Even if the test fails, wait for object to become ready so we can
247+ // tear down properly
248+ mt::spin_wait_for_condition_or_timeout(
249+ [this] { return surface == nullptr; },
250+ 3s);
251+}
252
253=== modified file 'tests/unit-tests/client/test_client_buffer_stream.cpp'
254--- tests/unit-tests/client/test_client_buffer_stream.cpp 2015-07-16 08:13:28 +0000
255+++ tests/unit-tests/client/test_client_buffer_stream.cpp 2015-07-23 11:00:27 +0000
256@@ -530,3 +530,53 @@
257 bs->request_and_wait_for_next_buffer();
258 }, std::runtime_error);
259 }
260+
261+TEST_F(ClientBufferStreamTest, invokes_callback_on_buffer_available_before_wait_handle_has_result)
262+{
263+ using namespace ::testing;
264+
265+ MirWaitHandle* wh{nullptr};
266+ bool wait_handle_has_result_in_callback = false;
267+
268+ EXPECT_CALL(mock_protobuf_server, submit_buffer(_,_,_,_))
269+ .WillOnce(RunProtobufClosure());
270+
271+ auto const protobuf_bs = a_protobuf_buffer_stream(
272+ default_pixel_format, default_buffer_usage, a_buffer_package());
273+ auto const bs = make_buffer_stream(protobuf_bs);
274+
275+ wh = bs->next_buffer(
276+ [&]
277+ {
278+ wait_handle_has_result_in_callback = wh->has_result();
279+ });
280+
281+ bs->buffer_available(mp::Buffer{});
282+
283+ EXPECT_FALSE(wait_handle_has_result_in_callback);
284+}
285+
286+TEST_F(ClientBufferStreamTest, invokes_callback_on_buffer_unavailable_before_wait_handle_has_result)
287+{
288+ using namespace ::testing;
289+
290+ MirWaitHandle* wh{nullptr};
291+ bool wait_handle_has_result_in_callback = false;
292+
293+ EXPECT_CALL(mock_protobuf_server, submit_buffer(_,_,_,_))
294+ .WillOnce(RunProtobufClosure());
295+
296+ auto const protobuf_bs = a_protobuf_buffer_stream(
297+ default_pixel_format, default_buffer_usage, a_buffer_package());
298+ auto const bs = make_buffer_stream(protobuf_bs);
299+
300+ wh = bs->next_buffer(
301+ [&]
302+ {
303+ wait_handle_has_result_in_callback = wh->has_result();
304+ });
305+
306+ bs->buffer_unavailable();
307+
308+ EXPECT_FALSE(wait_handle_has_result_in_callback);
309+}

Subscribers

People subscribed via source and target branches