Mir

Merge lp:~vanvugt/mir/fix-1194384 into lp:~mir-team/mir/trunk

Proposed by Daniel van Vugt
Status: Rejected
Rejected by: Daniel van Vugt
Proposed branch: lp:~vanvugt/mir/fix-1194384
Merge into: lp:~mir-team/mir/trunk
Diff against target: 346 lines (+182/-18)
6 files modified
examples/fingerpaint.c (+4/-2)
include/client/mir_toolkit/mir_client_library.h (+22/-0)
src/client/mir_client_library.cpp (+10/-0)
src/client/mir_surface.cpp (+33/-12)
src/client/mir_surface.h (+8/-4)
tests/acceptance-tests/test_client_library.cpp (+105/-0)
To merge this branch: bzr merge lp:~vanvugt/mir/fix-1194384
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Mir development team Pending
Review via email: mp+171520@code.launchpad.net

Commit message

Extend the client API to support thread-safe data sharing between event
callbacks and the rest of the client. Also document the risks. (LP: #1194384)

No existing behaviour changes. Thread safety is optional, but recommended. :)

To post a comment you must log in.
lp:~vanvugt/mir/fix-1194384 updated
781. By Daniel van Vugt

Fix a possible race in:
TEST_F(DefaultDisplayServerTestFixture, client_event_handler_is_thread_safe)

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

One can argue that this whole proposal is unnecessary, except for the docs improvements. Because client authors can use their own locking instead.

The rationale for the proposal then is: Without this, it is impossible to write a reliable, predictable client that handles events without also forcing the client to depend on some third-party locking primitives (pthread, C++11). That's really the issue. With all the features you may choose to include or exclude from an API, determinism is something you need to include.

Also, assuming that all toolkits are thread-aware is a bad assumption. I'm sure there are plenty of toolkits out there that don't have, or care about, threads. And Mir should support them too.

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

One can also argue that a more reusable alternative would be to create a mir_mutex API for clients. That would work too...

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

Hmm, I forgot about non-event callbacks. They probably deserve consideration for a generic approach.

Work in progress again.

Unmerged revisions

781. By Daniel van Vugt

Fix a possible race in:
TEST_F(DefaultDisplayServerTestFixture, client_event_handler_is_thread_safe)

780. By Daniel van Vugt

Add acceptance test for {lock,unlock}_event_handler

779. By Daniel van Vugt

Clarify that set_event_handler is a synchronization point too.

778. By Daniel van Vugt

Prototype event handler locking

777. By Daniel van Vugt

Fix build failure due to differing exception specifiers. noexcept destructors
should be enforced in derived classes if the base uses them. (LP: #1194703). Fixes: https://bugs.launchpad.net/bugs/1194703.

Approved by Alexandros Frantzis, Thomas Voß, PS Jenkins bot.

776. By kevin gunn

this is to change the doc's fpr preinstalled binaries to reference using the system-compositor-testing ppa.

Approved by Robert Ancell, Kevin DuBois, PS Jenkins bot, Chris Halse Rogers.

775. By Robert Ancell

Update debian/copyright for 3rd_party files. Fixes: https://bugs.launchpad.net/bugs/1194073.

Approved by PS Jenkins bot.

774. By Alexandros Frantzis

graphics: Implement GLBufferPixels to extract buffer pixels using GL.

Approved by PS Jenkins bot.

773. By Daniel van Vugt

Don't pass a clang-only option to gcc. It will not understand and cause
build failure (LP: #1194385). Fixes: https://bugs.launchpad.net/bugs/1194385.

Approved by Alexandros Frantzis, PS Jenkins bot.

772. By Didier Roche-Tolomelli

Various packaging fixes:
- cleanup the package for daily release standard.
- multiarch the packaging.
- reorganize the packages, having demos and examples not shipped in usr/bin
  but rather in libexec.
- fix some dependency ordering.
- clean some olds transitions, lintian warnings, and cruft files.
- run tests on all archs and remove deprecated warnings.

Approved by PS Jenkins bot, Chris Halse Rogers, Kevin DuBois.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'examples/fingerpaint.c'
--- examples/fingerpaint.c 2013-06-13 12:12:58 +0000
+++ examples/fingerpaint.c 2013-06-26 11:58:26 +0000
@@ -253,7 +253,6 @@
253 MirDisplayInfo dinfo;253 MirDisplayInfo dinfo;
254 MirSurface *surf;254 MirSurface *surf;
255 MirGraphicsRegion canvas;255 MirGraphicsRegion canvas;
256 MirEventDelegate delegate = {&on_event, &canvas};
257 int f;256 int f;
258257
259 (void)argc;258 (void)argc;
@@ -290,7 +289,6 @@
290 surf = mir_connection_create_surface_sync(conn, &parm);289 surf = mir_connection_create_surface_sync(conn, &parm);
291 if (surf != NULL)290 if (surf != NULL)
292 {291 {
293 mir_surface_set_event_handler(surf, &delegate);
294 292
295 canvas.width = parm.width;293 canvas.width = parm.width;
296 canvas.height = parm.height;294 canvas.height = parm.height;
@@ -300,12 +298,16 @@
300298
301 if (canvas.vaddr != NULL)299 if (canvas.vaddr != NULL)
302 {300 {
301 MirEventDelegate delegate = {&on_event, &canvas};
302
303 signal(SIGINT, shutdown);303 signal(SIGINT, shutdown);
304 signal(SIGTERM, shutdown);304 signal(SIGTERM, shutdown);
305 305
306 clear_region(&canvas, &background);306 clear_region(&canvas, &background);
307 redraw(surf, &canvas);307 redraw(surf, &canvas);
308 308
309 mir_surface_set_event_handler(surf, &delegate);
310
309 while (running)311 while (running)
310 {312 {
311 sleep(1); /* Is there a better way yet? */313 sleep(1); /* Is there a better way yet? */
312314
=== modified file 'include/client/mir_toolkit/mir_client_library.h'
--- include/client/mir_toolkit/mir_client_library.h 2013-06-20 09:43:30 +0000
+++ include/client/mir_toolkit/mir_client_library.h 2013-06-26 11:58:26 +0000
@@ -136,12 +136,34 @@
136136
137/**137/**
138 * Set the event handler to be called when events arrive for a surface.138 * Set the event handler to be called when events arrive for a surface.
139 * \warning Event callbacks are called from a <b>different thread</b>, so if
140 * you don't already have your own locking, you will need to use
141 * mir_surface_lock_event_handler to protect any data shared between
142 * event_handler and your other code.
139 * \param [in] surface The surface143 * \param [in] surface The surface
140 * \param [in] event_handler The event handler to call144 * \param [in] event_handler The event handler to call
145 * \post The old event handler (if any) has returned
141 */146 */
142void mir_surface_set_event_handler(MirSurface *surface,147void mir_surface_set_event_handler(MirSurface *surface,
143 MirEventDelegate const *event_handler);148 MirEventDelegate const *event_handler);
144149
150/**
151 * Acquire the mutex that surrounds event callbacks for a given surface. Use
152 * this to guarantee safe access to shared data you use inside the event
153 * handler.
154 * \param [in] surface The surface whose event handler to lock
155 * \post The event handler is not executing and will not be
156 * entered until you mir_surface_unlock_event_handler
157 */
158void mir_surface_lock_event_handler(MirSurface *surface);
159
160/**
161 * Release the mutex that surrounds event callbacks for a given surface.
162 * \pre You have called mir_surface_lock_event_handler
163 * \param [in] surface The surface whose event handler to unlock
164 */
165void mir_surface_unlock_event_handler(MirSurface *surface);
166
145/**167/**
146 * Get a window type that can be used for OpenGL ES 2.0 acceleration.168 * Get a window type that can be used for OpenGL ES 2.0 acceleration.
147 * \param [in] surface The surface169 * \param [in] surface The surface
148170
=== modified file 'src/client/mir_client_library.cpp'
--- src/client/mir_client_library.cpp 2013-06-20 09:43:30 +0000
+++ src/client/mir_client_library.cpp 2013-06-26 11:58:26 +0000
@@ -178,6 +178,16 @@
178 surface->set_event_handler(event_handler);178 surface->set_event_handler(event_handler);
179}179}
180180
181void mir_surface_lock_event_handler(MirSurface *surface)
182{
183 surface->lock_event_handler();
184}
185
186void mir_surface_unlock_event_handler(MirSurface *surface)
187{
188 surface->unlock_event_handler();
189}
190
181MirWaitHandle* mir_surface_release(191MirWaitHandle* mir_surface_release(
182 MirSurface * surface,192 MirSurface * surface,
183 mir_surface_callback callback, void * context)193 mir_surface_callback callback, void * context)
184194
=== modified file 'src/client/mir_surface.cpp'
--- src/client/mir_surface.cpp 2013-06-20 09:43:30 +0000
+++ src/client/mir_surface.cpp 2013-06-26 11:58:26 +0000
@@ -43,7 +43,8 @@
43 : server(server),43 : server(server),
44 connection(allocating_connection),44 connection(allocating_connection),
45 buffer_depository(std::make_shared<mcl::ClientBufferDepository>(factory, mir::frontend::client_buffer_cache_size)),45 buffer_depository(std::make_shared<mcl::ClientBufferDepository>(factory, mir::frontend::client_buffer_cache_size)),
46 input_platform(input_platform)46 input_platform(input_platform),
47 event_delegate(nullptr)
47{48{
48 mir::protobuf::SurfaceParameters message;49 mir::protobuf::SurfaceParameters message;
49 message.set_surface_name(params.name ? params.name : std::string());50 message.set_surface_name(params.name ? params.name : std::string());
@@ -288,6 +289,14 @@
288 return attrib_cache[at];289 return attrib_cache[at];
289}290}
290291
292void MirSurface::event_callback(MirEvent const* event)
293{
294 lock_event_handler();
295 if (event_delegate)
296 event_delegate->callback(this, event, event_delegate->context);
297 unlock_event_handler();
298}
299
291void MirSurface::set_event_handler(MirEventDelegate const* delegate)300void MirSurface::set_event_handler(MirEventDelegate const* delegate)
292{301{
293 if (input_thread)302 if (input_thread)
@@ -297,19 +306,32 @@
297 input_thread = nullptr;306 input_thread = nullptr;
298 }307 }
299308
300 if (delegate)309 lock_event_handler(); // can only block if the user still owns it
310
311 event_delegate = delegate; // can be NULL, to remove the handler
312
313 if (delegate && surface.fd_size() > 0)
301 {314 {
302 handle_event_callback = std::bind(delegate->callback, this,315 auto handle_event_callback = std::bind(&MirSurface::event_callback,
303 std::placeholders::_1,316 this,
304 delegate->context);317 std::placeholders::_1);
305318
306 if (surface.fd_size() > 0 && handle_event_callback)319 input_thread = input_platform->create_input_thread(surface.fd(0),
307 {
308 input_thread = input_platform->create_input_thread(surface.fd(0),
309 handle_event_callback);320 handle_event_callback);
310 input_thread->start();321 input_thread->start();
311 }
312 }322 }
323
324 unlock_event_handler();
325}
326
327void MirSurface::lock_event_handler()
328{
329 event_mutex.lock();
330}
331
332void MirSurface::unlock_event_handler()
333{
334 event_mutex.unlock();
313}335}
314336
315void MirSurface::handle_event(MirEvent const& e)337void MirSurface::handle_event(MirEvent const& e)
@@ -321,8 +343,7 @@
321 attrib_cache[a] = e.surface.value;343 attrib_cache[a] = e.surface.value;
322 }344 }
323345
324 if (handle_event_callback)346 event_callback(&e);
325 handle_event_callback(&e);
326}347}
327348
328MirPlatformType MirSurface::platform_type()349MirPlatformType MirSurface::platform_type()
329350
=== modified file 'src/client/mir_surface.h'
--- src/client/mir_surface.h 2013-06-20 09:43:30 +0000
+++ src/client/mir_surface.h 2013-06-26 11:58:26 +0000
@@ -31,7 +31,7 @@
31#include "client_platform.h"31#include "client_platform.h"
3232
33#include <memory>33#include <memory>
34#include <functional>34#include <mutex>
3535
36namespace mir36namespace mir
37{37{
@@ -88,7 +88,10 @@
88 MirWaitHandle* configure(MirSurfaceAttrib a, int value);88 MirWaitHandle* configure(MirSurfaceAttrib a, int value);
89 int attrib(MirSurfaceAttrib a) const;89 int attrib(MirSurfaceAttrib a) const;
9090
91 void event_callback(MirEvent const* event);
91 void set_event_handler(MirEventDelegate const* delegate);92 void set_event_handler(MirEventDelegate const* delegate);
93 void lock_event_handler();
94 void unlock_event_handler();
92 void handle_event(MirEvent const& e);95 void handle_event(MirEvent const& e);
9396
94private:97private:
@@ -99,8 +102,6 @@
99 void new_buffer(mir_surface_callback callback, void * context);102 void new_buffer(mir_surface_callback callback, void * context);
100 mir::geometry::PixelFormat convert_ipc_pf_to_geometry(google::protobuf::int32 pf);103 mir::geometry::PixelFormat convert_ipc_pf_to_geometry(google::protobuf::int32 pf);
101104
102 /* todo: race condition. protobuf does not guarantee that callbacks will be synchronized. potential
103 race in surface, last_buffer_id */
104 mir::protobuf::DisplayServer::Stub & server;105 mir::protobuf::DisplayServer::Stub & server;
105 mir::protobuf::Surface surface;106 mir::protobuf::Surface surface;
106 std::string error_message;107 std::string error_message;
@@ -121,8 +122,11 @@
121 // Cache of latest SurfaceSettings returned from the server122 // Cache of latest SurfaceSettings returned from the server
122 int attrib_cache[mir_surface_attrib_arraysize_];123 int attrib_cache[mir_surface_attrib_arraysize_];
123124
124 std::function<void(MirEvent const*)> handle_event_callback;
125 std::shared_ptr<mir::input::receiver::InputReceiverThread> input_thread;125 std::shared_ptr<mir::input::receiver::InputReceiverThread> input_thread;
126
127 // event_mutex protects the execution of event_delgate->callback only.
128 std::mutex event_mutex;
129 const MirEventDelegate *event_delegate;
126};130};
127131
128#endif /* MIR_CLIENT_PRIVATE_MIR_WAIT_HANDLE_H_ */132#endif /* MIR_CLIENT_PRIVATE_MIR_WAIT_HANDLE_H_ */
129133
=== modified file 'tests/acceptance-tests/test_client_library.cpp'
--- tests/acceptance-tests/test_client_library.cpp 2013-06-06 16:44:49 +0000
+++ tests/acceptance-tests/test_client_library.cpp 2013-06-26 11:58:26 +0000
@@ -27,6 +27,7 @@
2727
28#include <gtest/gtest.h>28#include <gtest/gtest.h>
29#include <chrono>29#include <chrono>
30#include <thread>
30#include <cstring>31#include <cstring>
3132
32namespace mf = mir::frontend;33namespace mf = mir::frontend;
@@ -345,6 +346,110 @@
345 launch_client_process(client_config);346 launch_client_process(client_config);
346}347}
347348
349TEST_F(DefaultDisplayServerTestFixture, client_event_handler_is_thread_safe)
350{
351 struct ClientConfig : ClientConfigCommon
352 {
353 static void event_callback(MirSurface* surface, MirEvent const* event,
354 void* ctx)
355 {
356 std::this_thread::sleep_for(std::chrono::seconds(1));
357 ClientConfig* self = static_cast<ClientConfig*>(ctx);
358 self->last_event = *event;
359 self->last_event_surface = surface;
360 self->prev_owner = self->owner;
361 self->owner = "event_callback";
362 }
363
364 void exec()
365 {
366 connection = mir_connect_sync(mir_test_socket, __PRETTY_FUNCTION__);
367 ASSERT_TRUE(connection != NULL);
368 ASSERT_TRUE(mir_connection_is_valid(connection));
369
370 MirSurfaceParameters const request_params =
371 {
372 __PRETTY_FUNCTION__,
373 640, 480,
374 mir_pixel_format_abgr_8888,
375 mir_buffer_usage_hardware
376 };
377
378 memset(&last_event, 0, sizeof last_event);
379 last_event_surface = nullptr;
380 owner = "no one";
381 prev_owner = "nobody";
382
383 MirEventDelegate delegate{&event_callback, this};
384
385 surface =
386 mir_connection_create_surface_sync(connection, &request_params);
387 ASSERT_TRUE(surface != NULL);
388 ASSERT_TRUE(mir_surface_is_valid(surface));
389
390 mir_surface_set_event_handler(surface, &delegate);
391
392 int surface_id = mir_surface_get_id(surface);
393
394 mir_surface_lock_event_handler(surface);
395 MirWaitHandle *w = mir_surface_set_state(surface,
396 mir_surface_state_fullscreen);
397 std::this_thread::sleep_for(std::chrono::seconds(3));
398 // by now, event_callback is trying to start, but is blocked.
399 EXPECT_STREQ("no one", owner);
400 EXPECT_STREQ("nobody", prev_owner);
401 mir_surface_unlock_event_handler(surface);
402
403 mir_wait_for(w);
404 mir_surface_lock_event_handler(surface);
405 EXPECT_STREQ("event_callback", owner);
406 EXPECT_STREQ("no one", prev_owner);
407 prev_owner = owner;
408 owner = "exec";
409 mir_surface_unlock_event_handler(surface);
410
411 EXPECT_EQ(surface, last_event_surface);
412 EXPECT_EQ(mir_event_type_surface, last_event.type);
413 EXPECT_EQ(surface_id, last_event.surface.id);
414 EXPECT_EQ(mir_surface_attrib_state, last_event.surface.attrib);
415 EXPECT_EQ(mir_surface_state_fullscreen, last_event.surface.value);
416
417 memset(&last_event, 0, sizeof last_event);
418 last_event_surface = nullptr;
419
420 mir_wait_for(mir_surface_set_state(surface,
421 static_cast<MirSurfaceState>(777)));
422 EXPECT_EQ(0, last_event_surface);
423 EXPECT_EQ(0, last_event.type);
424 EXPECT_EQ(0, last_event.surface.id);
425 EXPECT_EQ(0, last_event.surface.attrib);
426 EXPECT_EQ(0, last_event.surface.value);
427
428 mir_surface_lock_event_handler(surface);
429 EXPECT_STREQ("exec", owner);
430 EXPECT_STREQ("event_callback", prev_owner);
431 mir_surface_unlock_event_handler(surface);
432
433 mir_wait_for(mir_surface_set_state(surface,
434 mir_surface_state_minimized));
435 mir_surface_set_event_handler(surface, nullptr);
436 // Event handler is dead. Locking no longer required.
437 EXPECT_STREQ("event_callback", owner);
438 EXPECT_STREQ("exec", prev_owner);
439
440 mir_surface_release_sync(surface);
441 mir_connection_release(connection);
442 }
443
444 MirEvent last_event;
445 MirSurface* last_event_surface;
446 const char* prev_owner;
447 const char* owner;
448 } client_config;
449
450 launch_client_process(client_config);
451}
452
348TEST_F(DefaultDisplayServerTestFixture, client_receives_surface_state_events)453TEST_F(DefaultDisplayServerTestFixture, client_receives_surface_state_events)
349{454{
350 struct ClientConfig : ClientConfigCommon455 struct ClientConfig : ClientConfigCommon

Subscribers

People subscribed via source and target branches