Mir

Merge lp:~mir-team/mir/eventloop-integration into lp:mir

Proposed by Chris Halse Rogers
Status: Work in progress
Proposed branch: lp:~mir-team/mir/eventloop-integration
Merge into: lp:mir
Diff against target: 5401 lines (+2778/-885)
68 files modified
benchmarks/benchmark_multiplexing_dispatchable.cpp (+1/-1)
debian/control (+1/-0)
debian/mir-test-tools.install (+1/-0)
include/client/mir_toolkit/mir_connection.h (+51/-0)
include/client/mir_toolkit/mir_wait.h (+1/-0)
include/common/mir/dispatch/simple_dispatch_thread.h (+0/-50)
include/common/mir/dispatch/threaded_dispatcher.h (+75/-0)
src/client/CMakeLists.txt (+1/-0)
src/client/buffer_stream.cpp (+25/-6)
src/client/buffer_stream.h (+10/-2)
src/client/client_buffer_stream.h (+2/-0)
src/client/client_buffer_stream_factory.h (+15/-5)
src/client/connection_configuration.h (+6/-1)
src/client/default_client_buffer_stream_factory.cpp (+27/-17)
src/client/default_client_buffer_stream_factory.h (+16/-6)
src/client/default_connection_configuration.cpp (+1/-1)
src/client/default_connection_configuration.h (+2/-2)
src/client/mir_buffer_stream_api.cpp (+23/-16)
src/client/mir_connection.cpp (+52/-4)
src/client/mir_connection.h (+27/-9)
src/client/mir_connection_api.cpp (+60/-1)
src/client/mir_prompt_session_api.cpp (+10/-3)
src/client/mir_screencast.cpp (+15/-4)
src/client/mir_screencast.h (+3/-0)
src/client/mir_screencast_api.cpp (+20/-2)
src/client/mir_surface.cpp (+20/-8)
src/client/mir_surface.h (+4/-2)
src/client/mir_surface_api.cpp (+12/-18)
src/client/mir_wait_api.cpp (+9/-0)
src/client/mir_wait_handle.cpp (+6/-0)
src/client/mir_wait_handle.h (+1/-0)
src/client/rpc/make_rpc_channel.h (+2/-3)
src/client/rpc/make_socket_rpc_channel.cpp (+1/-1)
src/client/rpc/mir_basic_rpc_channel.cpp (+7/-2)
src/client/rpc/mir_basic_rpc_channel.h (+12/-5)
src/client/rpc/mir_protobuf_rpc_channel.cpp (+52/-18)
src/client/rpc/mir_protobuf_rpc_channel.h (+33/-14)
src/client/symbols.map (+4/-0)
src/client/synchronous_helper.cpp (+33/-0)
src/client/synchronous_helper.h (+160/-0)
src/common/dispatch/CMakeLists.txt (+1/-2)
src/common/dispatch/simple_dispatch_thread.cpp (+0/-163)
src/common/dispatch/threaded_dispatcher.cpp (+294/-0)
src/common/symbols.map (+6/-4)
src/server/input/default_input_manager.cpp (+3/-3)
src/server/input/default_input_manager.h (+2/-2)
tests/acceptance-tests/CMakeLists.txt (+2/-1)
tests/acceptance-tests/test_client_library.cpp (+331/-105)
tests/include/mir_test/test_protobuf_client.h (+2/-2)
tests/include/mir_test_doubles/mock_client_buffer_stream.h (+1/-0)
tests/include/mir_test_doubles/mock_client_buffer_stream_factory.h (+16/-6)
tests/include/mir_test_doubles/stub_client_buffer_stream_factory.h (+5/-1)
tests/include/mir_test_framework/udev_environment.h (+26/-0)
tests/integration-tests/client/test_screencast.cpp (+5/-5)
tests/mir_test_doubles/test_protobuf_client.cpp (+2/-2)
tests/mir_test_framework/CMakeLists.txt (+3/-0)
tests/mir_test_framework/udev_environment.cpp (+66/-1)
tests/mir_test_framework/udev_recordings/laptop-keyboard-hello.evemu (+272/-0)
tests/unit-tests/client/test_client_buffer_stream.cpp (+2/-2)
tests/unit-tests/client/test_client_mir_surface.cpp (+1/-1)
tests/unit-tests/client/test_mir_connection.cpp (+162/-11)
tests/unit-tests/client/test_mir_screencast.cpp (+10/-1)
tests/unit-tests/client/test_protobuf_rpc_channel.cpp (+295/-62)
tests/unit-tests/dispatch/CMakeLists.txt (+1/-1)
tests/unit-tests/dispatch/test_multiplexing_dispatchable.cpp (+21/-18)
tests/unit-tests/dispatch/test_simple_dispatch_thread.cpp (+0/-291)
tests/unit-tests/dispatch/test_threaded_dispatcher.cpp (+403/-0)
tools/valgrind_suppressions_generic (+45/-0)
To merge this branch: bzr merge lp:~mir-team/mir/eventloop-integration
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Mir development team Pending
Review via email: mp+245926@code.launchpad.net

Commit message

Add a client-driven MirConnection dispatch option.

Lots of clients aren't appreciative of our devil-may-care approach to calling their code on threads of our choosing. For them, we offer mir_connection_dispatch().

This will simplify XMir, make a Mir backend for Plymouth feasible, and greatly please Ryan.

Fixes: https://bugs.launchpad.net/mir/+bug/1397375

Description of the change

Add a client-driven MirConnection dispatch option.

Lots of clients aren't appreciative of our devil-may-care approach to calling their code on threads of our choosing. For them, we offer mir_connection_dispatch().

This will simplify XMir, make a Mir backend for Plymouth feasible, and greatly please Ryan.

Fixes: https://bugs.launchpad.net/mir/+bug/1397375

To post a comment you must log in.
Revision history for this message
Chris Halse Rogers (raof) wrote :

Marked as WIP; I'll break this up to make the review easier.

Revision history for this message
Chris Halse Rogers (raof) wrote :

Looks like I'll be breaking this up a bit more, but now actually works!

Bounce off CI to fully test that it fully really fully does fully work.

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

Unmerged revisions

2387. By Chris Halse Rogers

Add valgrind suppressions for GDBus bollocks.

2386. By Chris Halse Rogers

Free the surface_spec in ClientLibrary test.

Now doesn't leak!

2385. By Chris Halse Rogers

Don't call a synchronous method from a manually-dispatched async callback.

This fails for... some reason. Don't know at the moment.

2384. By Chris Halse Rogers

Initialise ProtobufRpcChannel::prioritise_next_request.

Thanks, valgrind.

2383. By Chris Halse Rogers

Minor improvements to synchronous helper

2382. By Chris Halse Rogers

Use make_synchronous_call helper for all synchronous RPC calls

2381. By Chris Halse Rogers

Add helper infrastructure for making synchronous RPC calls

2380. By Chris Halse Rogers

Enable getting a MirConnection out of a MirScreencast

2379. By Chris Halse Rogers

We treat MirBufferStream as a ClientBufferStream internally - make sure the pointers we return *are* ClientBufferStreams

2378. By Chris Halse Rogers

Add a way to get the MirConnection associated with a MirSurface

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'benchmarks/benchmark_multiplexing_dispatchable.cpp'
2--- benchmarks/benchmark_multiplexing_dispatchable.cpp 2015-04-24 15:36:12 +0000
3+++ benchmarks/benchmark_multiplexing_dispatchable.cpp 2015-05-14 07:50:26 +0000
4@@ -17,9 +17,9 @@
5 */
6
7 #include "mir/dispatch/multiplexing_dispatchable.h"
8-#include "mir/dispatch/simple_dispatch_thread.h"
9
10 #include <iostream>
11+#include <thread>
12 #include <vector>
13 #include <memory>
14 #include <chrono>
15
16=== modified file 'debian/control'
17--- debian/control 2015-05-08 15:40:20 +0000
18+++ debian/control 2015-05-14 07:50:26 +0000
19@@ -245,6 +245,7 @@
20 Pre-Depends: ${misc:Pre-Depends}
21 Depends: ${misc:Depends},
22 ${shlibs:Depends},
23+ umockdev,
24 Recommends: mir-demos,
25 Description: Display Server for Ubuntu - stress tests and other test tools
26 Mir is a display server running on linux systems, with a focus on efficiency,
27
28=== modified file 'debian/mir-test-tools.install'
29--- debian/mir-test-tools.install 2015-04-09 06:20:31 +0000
30+++ debian/mir-test-tools.install 2015-05-14 07:50:26 +0000
31@@ -8,3 +8,4 @@
32 usr/lib/*/mir/server-platform/graphics-dummy.so
33 usr/lib/*/mir/server-platform/input-stub.so
34 usr/lib/*/mir/client-platform/dummy.so
35+usr/share/mir/udev_recordings
36
37=== modified file 'include/client/mir_toolkit/mir_connection.h'
38--- include/client/mir_toolkit/mir_connection.h 2015-03-31 02:35:42 +0000
39+++ include/client/mir_toolkit/mir_connection.h 2015-05-14 07:50:26 +0000
40@@ -63,6 +63,35 @@
41 MirConnection *mir_connect_sync(char const *server, char const *app_name);
42
43 /**
44+ * Request a connection to the Mir server.
45+ *
46+ * The client is responsible for handling event consumption via
47+ * mir_connection_dispatch(). Events will be available when the fd returned
48+ * by mir_connection_get_event_fd() becomes readable.
49+ *
50+ * All callbacks for this MirConnection and any objects created on it will be
51+ * called from the thread calling mir_connection_dispatch().
52+ *
53+ * \param [in] server A string specifying the server to connect to.
54+ * Connection strings can either be path to the socket file on
55+ * the filesystem, or an open file descriptor fd://
56+ * \param [in] app_name A name referring to the application
57+ * \param [in] callback Callback function to be invoked when request
58+ * completes. mir_connection_is_valid() will return false
59+ * until this callback has completed.
60+ * \param [in,out] context User data passed to the callback function
61+ * \returns The resulting MirConnection
62+ * \todo Currently manual dispatch interacts awkwardly with *_sync() calls;
63+ * Synchronous calls do *not* automatically dispatch, so unless client
64+ * code has a separate thread running dispatch, any *_sync() call will
65+ * deadlock. This restriction will be fixed in a later release.
66+ */
67+MirConnection* mir_connect_with_manual_dispatch(char const* server,
68+ char const* app_name,
69+ mir_connected_callback callback,
70+ void* context);
71+
72+/**
73 * Test for a valid connection
74 * \param [in] connection The connection
75 * \return True if the supplied connection is valid, or
76@@ -87,6 +116,28 @@
77 void mir_connection_release(MirConnection *connection);
78
79 /**
80+ * \brief Get the notification fd for this connection
81+ * \param [in] connection The connection. This connection must have been created by
82+ * mir_connect_with_manual_dispatch().
83+ * \return A file descriptor that supports select/poll/epoll and similar
84+ * APIs that becomes readable when there are events to dispatch,
85+ * or -1 if \ref connection was not created by
86+ * mir_connect_with_manual_dispatch();
87+ * \note This fd is owned by the MirConnection. Do not close the fd yourself.
88+ * The fd remains valid until mir_connection_release() returns.
89+ */
90+int mir_connection_get_event_fd(MirConnection* connection);
91+
92+/**
93+ * \brief Dispatch a single pending event on the connection
94+ * \param [in] connection The connection. This connection must have been created by
95+ * mir_connect_with_manual_dispatch().
96+ * \note It is an error to call this on a \ref connection not created by
97+ * mir_connect_with_manual_dispatch().
98+ */
99+void mir_connection_dispatch(MirConnection* connection);
100+
101+/**
102 * Query platform-specific data and/or file descriptors that are required to
103 * initialize GL/EGL features.
104 * \param [in] connection The connection
105
106=== modified file 'include/client/mir_toolkit/mir_wait.h'
107--- include/client/mir_toolkit/mir_wait.h 2014-03-31 14:36:08 +0000
108+++ include/client/mir_toolkit/mir_wait.h 2015-05-14 07:50:26 +0000
109@@ -46,6 +46,7 @@
110 */
111 void mir_wait_for_one(MirWaitHandle *wait_handle);
112
113+bool mir_wait_handle_ready(MirWaitHandle* wait_handle);
114
115 #ifdef __cplusplus
116 }
117
118=== removed file 'include/common/mir/dispatch/simple_dispatch_thread.h'
119--- include/common/mir/dispatch/simple_dispatch_thread.h 2015-04-23 18:29:35 +0000
120+++ include/common/mir/dispatch/simple_dispatch_thread.h 1970-01-01 00:00:00 +0000
121@@ -1,50 +0,0 @@
122-/*
123- * Copyright © 2015 Canonical Ltd.
124- *
125- * This program is free software: you can redistribute it and/or modify it
126- * under the terms of the GNU Lesser General Public License version 3,
127- * as published by the Free Software Foundation.
128- *
129- * This program is distributed in the hope that it will be useful,
130- * but WITHOUT ANY WARRANTY; without even the implied warranty of
131- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
132- * GNU Lesser General Public License for more details.
133- *
134- * You should have received a copy of the GNU Lesser General Public License
135- * along with this program. If not, see <http://www.gnu.org/licenses/>.
136- *
137- * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
138- */
139-
140-#ifndef MIR_DISPATCH_SIMPLE_DISPATCH_THREAD_H_
141-#define MIR_DISPATCH_SIMPLE_DISPATCH_THREAD_H_
142-
143-#include <memory>
144-#include <thread>
145-#include "mir/fd.h"
146-
147-namespace mir
148-{
149-namespace dispatch
150-{
151-class Dispatchable;
152-
153-
154-class SimpleDispatchThread
155-{
156-public:
157- SimpleDispatchThread(std::shared_ptr<Dispatchable> const& dispatchee);
158- SimpleDispatchThread(std::shared_ptr<Dispatchable> const& dispatchee,
159- std::function<void()> const& exception_handler);
160- ~SimpleDispatchThread() noexcept;
161-
162-private:
163- Fd shutdown_fd;
164- std::thread eventloop;
165-};
166-
167-}
168-}
169-
170-
171-#endif // MIR_DISPATCH_SIMPLE_DISPATCH_THREAD_H_
172
173=== added file 'include/common/mir/dispatch/threaded_dispatcher.h'
174--- include/common/mir/dispatch/threaded_dispatcher.h 1970-01-01 00:00:00 +0000
175+++ include/common/mir/dispatch/threaded_dispatcher.h 2015-05-14 07:50:26 +0000
176@@ -0,0 +1,75 @@
177+/*
178+ * Copyright © 2015 Canonical Ltd.
179+ *
180+ * This program is free software: you can redistribute it and/or modify it
181+ * under the terms of the GNU Lesser General Public License version 3,
182+ * as published by the Free Software Foundation.
183+ *
184+ * This program is distributed in the hope that it will be useful,
185+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
186+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
187+ * GNU Lesser General Public License for more details.
188+ *
189+ * You should have received a copy of the GNU Lesser General Public License
190+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
191+ *
192+ * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
193+ */
194+
195+#ifndef MIR_DISPATCH_SIMPLE_DISPATCH_THREAD_H_
196+#define MIR_DISPATCH_SIMPLE_DISPATCH_THREAD_H_
197+
198+#include <string>
199+#include <memory>
200+#include <thread>
201+#include <vector>
202+#include <mutex>
203+#include <condition_variable>
204+
205+#include "mir/dispatch/multiplexing_dispatchable.h"
206+#include "mir/fd.h"
207+
208+namespace mir
209+{
210+namespace dispatch
211+{
212+class Dispatchable;
213+
214+class ThreadedDispatcher
215+{
216+public:
217+ ThreadedDispatcher(std::string const& name, std::shared_ptr<Dispatchable> const& dispatchee);
218+ ThreadedDispatcher(std::string const& name,
219+ std::shared_ptr<Dispatchable> const& dispatchee,
220+ std::function<void()> const& exception_handler);
221+ ~ThreadedDispatcher() noexcept;
222+
223+ void add_thread();
224+ void remove_thread();
225+
226+private:
227+ class ThreadShutdownRequestHandler;
228+ friend class ThreadShutdownRequestHandler;
229+
230+ std::string const name_base;
231+
232+ std::shared_ptr<ThreadShutdownRequestHandler> thread_exiter;
233+ std::shared_ptr<MultiplexingDispatchable> dispatcher;
234+
235+ std::mutex thread_pool_mutex;
236+ std::vector<std::thread> threadpool;
237+
238+ std::function<void()> const exception_handler;
239+
240+ static void dispatch_loop(std::string const& name,
241+ std::shared_ptr<ThreadShutdownRequestHandler> thread_register,
242+ std::shared_ptr<Dispatchable> dispatcher,
243+ std::function<void()> const& exception_handler);
244+
245+};
246+
247+}
248+}
249+
250+
251+#endif // MIR_DISPATCH_SIMPLE_DISPATCH_THREAD_H_
252
253=== modified file 'src/client/CMakeLists.txt'
254--- src/client/CMakeLists.txt 2015-05-08 09:01:55 +0000
255+++ src/client/CMakeLists.txt 2015-05-14 07:50:26 +0000
256@@ -70,6 +70,7 @@
257 buffer_stream.cpp
258 mir_buffer_stream_api.cpp
259 default_client_buffer_stream_factory.cpp
260+ synchronous_helper.cpp
261 ${MIR_CLIENT_SOURCES}
262 )
263
264
265=== modified file 'src/client/buffer_stream.cpp'
266--- src/client/buffer_stream.cpp 2015-04-23 14:09:57 +0000
267+++ src/client/buffer_stream.cpp 2015-05-14 07:50:26 +0000
268@@ -87,13 +87,16 @@
269 // TODO: It seems like a bit of a wart that we have to pass the Logger specifically here...perhaps
270 // due to the lack of an easily mockable client configuration interface (passing around
271 // connection can complicate unit tests ala MirSurface and test_client_mir_surface.cpp)
272-mcl::BufferStream::BufferStream(mp::DisplayServer& server,
273+mcl::BufferStream::BufferStream(
274+ MirConnection* allocating_connection,
275+ mp::DisplayServer& server,
276 mcl::BufferStreamMode mode,
277 std::shared_ptr<mcl::ClientPlatform> const& client_platform,
278 mp::BufferStream const& protobuf_bs,
279 std::shared_ptr<mcl::PerfReport> const& perf_report,
280 std::string const& surface_name)
281- : display_server(server),
282+ : connection{allocating_connection},
283+ display_server(server),
284 mode(mode),
285 client_platform(client_platform),
286 protobuf_bs(protobuf_bs),
287@@ -106,13 +109,16 @@
288 perf_report->name_surface(surface_name.c_str());
289 }
290
291-mcl::BufferStream::BufferStream(mp::DisplayServer& server,
292+mcl::BufferStream::BufferStream(
293+ MirConnection* allocating_connection,
294+ mp::DisplayServer& server,
295 std::shared_ptr<mcl::ClientPlatform> const& client_platform,
296 mp::BufferStreamParameters const& parameters,
297 std::shared_ptr<mcl::PerfReport> const& perf_report,
298 mir_buffer_stream_callback callback,
299 void *context)
300- : display_server(server),
301+ : connection{allocating_connection},
302+ display_server(server),
303 mode(BufferStreamMode::Producer),
304 client_platform(client_platform),
305 buffer_depository{client_platform->create_buffer_factory(), mir::frontend::client_buffer_cache_size},
306@@ -146,7 +152,11 @@
307 egl_native_window_ = client_platform->create_egl_native_window(this);
308
309 if (callback)
310- callback(reinterpret_cast<MirBufferStream*>(this), context);
311+ {
312+ // Fun fact! The offset of mcl::ClientBufferStream's vtable in *this is *not* 0!
313+ auto cbs_object = static_cast<mcl::ClientBufferStream*>(this);
314+ callback(reinterpret_cast<MirBufferStream*>(cbs_object), context);
315+ }
316 create_wait_handle.result_received();
317 }
318
319@@ -382,7 +392,11 @@
320 mir_buffer_stream_callback callback, void* context)
321 {
322 if (callback)
323- callback(reinterpret_cast<MirBufferStream*>(this), context);
324+ {
325+ // Fun fact! The offset of mcl::ClientBufferStream's vtable in *this is *not* 0!
326+ auto cbs_object = static_cast<mcl::ClientBufferStream*>(this);
327+ callback(reinterpret_cast<MirBufferStream*>(cbs_object), context);
328+ }
329 release_wait_handle.result_received();
330 }
331
332@@ -403,3 +417,8 @@
333 {
334 buffer_depository.set_max_buffers(cache_size);
335 }
336+
337+MirConnection* mcl::BufferStream::get_connection() const
338+{
339+ return connection;
340+}
341
342=== modified file 'src/client/buffer_stream.h'
343--- src/client/buffer_stream.h 2015-04-23 14:09:57 +0000
344+++ src/client/buffer_stream.h 2015-05-14 07:50:26 +0000
345@@ -57,14 +57,18 @@
346 class BufferStream : public EGLNativeSurface, public ClientBufferStream
347 {
348 public:
349- BufferStream(mir::protobuf::DisplayServer& server,
350+ BufferStream(
351+ MirConnection* allocating_connection,
352+ mir::protobuf::DisplayServer& server,
353 BufferStreamMode mode,
354 std::shared_ptr<ClientPlatform> const& native_window_factory,
355 protobuf::BufferStream const& protobuf_bs,
356 std::shared_ptr<PerfReport> const& perf_report,
357 std::string const& surface_name);
358 // For surfaceless buffer streams
359- BufferStream(mir::protobuf::DisplayServer& server,
360+ BufferStream(
361+ MirConnection* allocating_connection,
362+ mir::protobuf::DisplayServer& server,
363 std::shared_ptr<ClientPlatform> const& native_window_factory,
364 mir::protobuf::BufferStreamParameters const& parameters,
365 std::shared_ptr<PerfReport> const& perf_report,
366@@ -100,6 +104,8 @@
367
368 frontend::BufferStreamId rpc_id() const override;
369 bool valid() const override;
370+
371+ MirConnection* get_connection() const override;
372
373 protected:
374 BufferStream(BufferStream const&) = delete;
375@@ -116,6 +122,8 @@
376
377 mutable std::mutex mutex; // Protects all members of *this
378
379+ MirConnection* const connection;
380+
381 mir::protobuf::DisplayServer& display_server;
382
383 BufferStreamMode const mode;
384
385=== modified file 'src/client/client_buffer_stream.h'
386--- src/client/client_buffer_stream.h 2015-04-01 20:00:19 +0000
387+++ src/client/client_buffer_stream.h 2015-05-14 07:50:26 +0000
388@@ -63,6 +63,8 @@
389 virtual MirWaitHandle* release(mir_buffer_stream_callback callback, void* context) = 0;
390
391 virtual bool valid() const = 0;
392+
393+ virtual MirConnection* get_connection() const = 0;
394
395 protected:
396 ClientBufferStream() = default;
397
398=== modified file 'src/client/client_buffer_stream_factory.h'
399--- src/client/client_buffer_stream_factory.h 2015-04-01 20:00:19 +0000
400+++ src/client/client_buffer_stream_factory.h 2015-05-14 07:50:26 +0000
401@@ -25,6 +25,8 @@
402
403 #include <memory>
404
405+class MirConnection;
406+
407 namespace mir
408 {
409 namespace client
410@@ -34,13 +36,21 @@
411 class ClientBufferStreamFactory
412 {
413 public:
414- virtual std::shared_ptr<ClientBufferStream> make_consumer_stream(protobuf::DisplayServer& server,
415- protobuf::BufferStream const& protobuf_bs, std::string const& surface_name) = 0;
416- virtual std::shared_ptr<ClientBufferStream> make_producer_stream(protobuf::DisplayServer& server,
417- protobuf::BufferStream const& protobuf_bs, std::string const& surface_name) = 0;
418+ virtual std::shared_ptr<ClientBufferStream> make_consumer_stream(
419+ MirConnection* allocating_connection,
420+ protobuf::DisplayServer& server,
421+ protobuf::BufferStream const& protobuf_bs,
422+ std::string const& surface_name) = 0;
423+ virtual std::shared_ptr<ClientBufferStream> make_producer_stream(
424+ MirConnection* allocating_connection,
425+ protobuf::DisplayServer& server,
426+ protobuf::BufferStream const& protobuf_bs,
427+ std::string const& surface_name) = 0;
428
429 // For creating buffer stream owned by client.
430- virtual ClientBufferStream* make_producer_stream(protobuf::DisplayServer& server,
431+ virtual ClientBufferStream* make_producer_stream(
432+ MirConnection* allocating_connection,
433+ protobuf::DisplayServer& server,
434 protobuf::BufferStreamParameters const& params,
435 mir_buffer_stream_callback callback, void* context) = 0;
436
437
438=== modified file 'src/client/connection_configuration.h'
439--- src/client/connection_configuration.h 2015-01-21 07:34:50 +0000
440+++ src/client/connection_configuration.h 2015-05-14 07:50:26 +0000
441@@ -49,13 +49,18 @@
442 class EventSink;
443 class EventHandlerRegister;
444
445+namespace rpc
446+{
447+class MirBasicRpcChannel;
448+}
449+
450 class ConnectionConfiguration
451 {
452 public:
453 virtual ~ConnectionConfiguration() = default;
454
455 virtual std::shared_ptr<ConnectionSurfaceMap> the_surface_map() = 0;
456- virtual std::shared_ptr<google::protobuf::RpcChannel> the_rpc_channel() = 0;
457+ virtual std::shared_ptr<rpc::MirBasicRpcChannel> the_rpc_channel() = 0;
458 virtual std::shared_ptr<mir::logging::Logger> the_logger() = 0;
459 virtual std::shared_ptr<ClientPlatformFactory> the_client_platform_factory() = 0;
460 virtual std::shared_ptr<input::receiver::InputPlatform> the_input_platform() = 0;
461
462=== modified file 'src/client/default_client_buffer_stream_factory.cpp'
463--- src/client/default_client_buffer_stream_factory.cpp 2015-04-01 20:00:19 +0000
464+++ src/client/default_client_buffer_stream_factory.cpp 2015-05-14 07:50:26 +0000
465@@ -57,21 +57,31 @@
466 {
467 }
468
469-std::shared_ptr<mcl::ClientBufferStream> mcl::DefaultClientBufferStreamFactory::make_consumer_stream(mp::DisplayServer& server,
470- mp::BufferStream const& protobuf_bs, std::string const& surface_name)
471-{
472- return std::make_shared<mcl::BufferStream>(server, mcl::BufferStreamMode::Consumer, client_platform, protobuf_bs, make_perf_report(logger), surface_name);
473-}
474-
475-std::shared_ptr<mcl::ClientBufferStream> mcl::DefaultClientBufferStreamFactory::make_producer_stream(mp::DisplayServer& server,
476- mp::BufferStream const& protobuf_bs, std::string const& surface_name)
477-{
478- return std::make_shared<mcl::BufferStream>(server, mcl::BufferStreamMode::Producer, client_platform, protobuf_bs, make_perf_report(logger), surface_name);
479-}
480-
481-
482-mcl::ClientBufferStream* mcl::DefaultClientBufferStreamFactory::make_producer_stream(mp::DisplayServer& server,
483- mp::BufferStreamParameters const& params, mir_buffer_stream_callback callback, void* context)
484-{
485- return new mcl::BufferStream(server, client_platform, params, make_perf_report(logger), callback, context);
486+std::shared_ptr<mcl::ClientBufferStream> mcl::DefaultClientBufferStreamFactory::make_consumer_stream(
487+ MirConnection* allocating_connection,
488+ mp::DisplayServer& server,
489+ mp::BufferStream const& protobuf_bs,
490+ std::string const& surface_name)
491+{
492+ return std::make_shared<mcl::BufferStream>(allocating_connection, server, mcl::BufferStreamMode::Consumer, client_platform, protobuf_bs, make_perf_report(logger), surface_name);
493+}
494+
495+std::shared_ptr<mcl::ClientBufferStream> mcl::DefaultClientBufferStreamFactory::make_producer_stream(
496+ MirConnection* allocating_connection,
497+ mp::DisplayServer& server,
498+ mp::BufferStream const& protobuf_bs,
499+ std::string const& surface_name)
500+{
501+ return std::make_shared<mcl::BufferStream>(allocating_connection, server, mcl::BufferStreamMode::Producer, client_platform, protobuf_bs, make_perf_report(logger), surface_name);
502+}
503+
504+
505+mcl::ClientBufferStream* mcl::DefaultClientBufferStreamFactory::make_producer_stream(
506+ MirConnection* allocating_connection,
507+ mp::DisplayServer& server,
508+ mp::BufferStreamParameters const& params,
509+ mir_buffer_stream_callback callback,
510+ void* context)
511+{
512+ return new mcl::BufferStream(allocating_connection, server, client_platform, params, make_perf_report(logger), callback, context);
513 }
514
515=== modified file 'src/client/default_client_buffer_stream_factory.h'
516--- src/client/default_client_buffer_stream_factory.h 2015-04-01 20:00:19 +0000
517+++ src/client/default_client_buffer_stream_factory.h 2015-05-14 07:50:26 +0000
518@@ -21,6 +21,8 @@
519
520 #include "client_buffer_stream_factory.h"
521
522+class MirConnection;
523+
524 namespace mir
525 {
526 namespace logging
527@@ -40,14 +42,22 @@
528 std::shared_ptr<logging::Logger> const& logger);
529 virtual ~DefaultClientBufferStreamFactory() = default;
530
531- std::shared_ptr<ClientBufferStream> make_consumer_stream(protobuf::DisplayServer& server,
532- protobuf::BufferStream const& protobuf_bs, std::string const& surface_name);
533- std::shared_ptr<ClientBufferStream> make_producer_stream(protobuf::DisplayServer& server,
534- protobuf::BufferStream const& protobuf_bs, std::string const& surface_name);
535+ std::shared_ptr<ClientBufferStream> make_consumer_stream(
536+ MirConnection* allocating_connection,
537+ protobuf::DisplayServer& server,
538+ protobuf::BufferStream const& protobuf_bs,
539+ std::string const& surface_name) override;
540+ std::shared_ptr<ClientBufferStream> make_producer_stream(
541+ MirConnection* allocating_connection,
542+ protobuf::DisplayServer& server,
543+ protobuf::BufferStream const& protobuf_bs,
544+ std::string const& surface_name) override;
545
546- ClientBufferStream* make_producer_stream(protobuf::DisplayServer& server,
547+ ClientBufferStream* make_producer_stream(
548+ MirConnection* allocating_connection,
549+ protobuf::DisplayServer& server,
550 protobuf::BufferStreamParameters const& params,
551- mir_buffer_stream_callback callback, void* context);
552+ mir_buffer_stream_callback callback, void* context) override;
553
554 private:
555 std::shared_ptr<ClientPlatform> const client_platform;
556
557=== modified file 'src/client/default_connection_configuration.cpp'
558--- src/client/default_connection_configuration.cpp 2015-03-31 02:35:42 +0000
559+++ src/client/default_connection_configuration.cpp 2015-05-14 07:50:26 +0000
560@@ -85,7 +85,7 @@
561 });
562 }
563
564-std::shared_ptr<google::protobuf::RpcChannel>
565+std::shared_ptr<mcl::rpc::MirBasicRpcChannel>
566 mcl::DefaultConnectionConfiguration::the_rpc_channel()
567 {
568 return rpc_channel(
569
570=== modified file 'src/client/default_connection_configuration.h'
571--- src/client/default_connection_configuration.h 2015-01-21 07:34:50 +0000
572+++ src/client/default_connection_configuration.h 2015-05-14 07:50:26 +0000
573@@ -51,7 +51,7 @@
574 DefaultConnectionConfiguration(std::string const& socket_file);
575
576 std::shared_ptr<ConnectionSurfaceMap> the_surface_map() override;
577- std::shared_ptr<google::protobuf::RpcChannel> the_rpc_channel() override;
578+ std::shared_ptr<rpc::MirBasicRpcChannel> the_rpc_channel() override;
579 std::shared_ptr<mir::logging::Logger> the_logger() override;
580 std::shared_ptr<ClientPlatformFactory> the_client_platform_factory() override;
581 std::shared_ptr<input::receiver::InputPlatform> the_input_platform() override;
582@@ -66,7 +66,7 @@
583 virtual std::shared_ptr<input::receiver::InputReceiverReport> the_input_receiver_report();
584
585 protected:
586- CachedPtr<google::protobuf::RpcChannel> rpc_channel;
587+ CachedPtr<rpc::MirBasicRpcChannel> rpc_channel;
588 CachedPtr<mir::logging::Logger> logger;
589 CachedPtr<ClientPlatformFactory> client_platform_factory;
590 CachedPtr<input::receiver::InputPlatform> input_platform;
591
592=== modified file 'src/client/mir_buffer_stream_api.cpp'
593--- src/client/mir_buffer_stream_api.cpp 2015-03-31 02:35:42 +0000
594+++ src/client/mir_buffer_stream_api.cpp 2015-05-14 07:50:26 +0000
595@@ -28,6 +28,8 @@
596
597 #include "mir/uncaught.h"
598
599+#include "synchronous_helper.h"
600+
601 #include <stdexcept>
602 #include <boost/throw_exception.hpp>
603
604@@ -49,13 +51,6 @@
605 mcl::ClientBufferStream *bs = reinterpret_cast<mcl::ClientBufferStream*>(stream);
606 delete bs;
607 }
608-// assign_result is compatible with all 2-parameter callbacks
609-void assign_result(void* result, void** context)
610-{
611- if (context)
612- *context = result;
613-}
614-
615 }
616
617
618@@ -74,7 +69,7 @@
619 params.set_pixel_format(format);
620 params.set_buffer_usage(buffer_usage);
621
622- return connection->get_client_buffer_stream_factory()->make_producer_stream(connection->display_server(), params, callback, context)
623+ return connection->get_client_buffer_stream_factory()->make_producer_stream(connection, connection->display_server(), params, callback, context)
624 ->get_create_wait_handle();
625 }
626 catch (std::exception const& ex)
627@@ -89,10 +84,14 @@
628 MirBufferUsage buffer_usage)
629 try
630 {
631- mcl::BufferStream *stream = nullptr;
632- mir_connection_create_buffer_stream(connection, width, height, format, buffer_usage,
633- reinterpret_cast<mir_buffer_stream_callback>(assign_result), &stream)->wait_for_all();
634- return reinterpret_cast<MirBufferStream*>(dynamic_cast<mcl::ClientBufferStream*>(stream));
635+ MirBufferStream* stream = nullptr;
636+ make_synchronous_call(connection,
637+ mir_connection_create_buffer_stream,
638+ connection,
639+ width, height, format, buffer_usage,
640+ &assign_result<MirBufferStream>,
641+ &stream);
642+ return stream;
643 }
644 catch (std::exception const& ex)
645 {
646@@ -113,7 +112,11 @@
647 void mir_buffer_stream_release_sync(MirBufferStream *buffer_stream)
648 {
649 mcl::ClientBufferStream *bs = reinterpret_cast<mcl::ClientBufferStream*>(buffer_stream);
650- bs->release(nullptr, nullptr)->wait_for_all();
651+ make_synchronous_call(bs->get_connection(),
652+ std::mem_fn(&mcl::ClientBufferStream::release),
653+ bs,
654+ &assign_result<MirBufferStream>,
655+ static_cast<void*>(nullptr));
656 delete bs;
657 }
658
659@@ -148,9 +151,13 @@
660
661 void mir_buffer_stream_swap_buffers_sync(MirBufferStream* buffer_stream)
662 {
663- mir_wait_for(mir_buffer_stream_swap_buffers(buffer_stream,
664- reinterpret_cast<mir_buffer_stream_callback>(assign_result),
665- nullptr));
666+ mcl::ClientBufferStream *bs = reinterpret_cast<mcl::ClientBufferStream*>(buffer_stream);
667+ make_synchronous_call(
668+ bs->get_connection(),
669+ &mir_buffer_stream_swap_buffers,
670+ buffer_stream,
671+ &assign_result<MirBufferStream>,
672+ static_cast<void*>(nullptr));
673 }
674
675 void mir_buffer_stream_get_graphics_region(
676
677=== modified file 'src/client/mir_connection.cpp'
678--- src/client/mir_connection.cpp 2015-04-01 19:39:19 +0000
679+++ src/client/mir_connection.cpp 2015-05-14 07:50:26 +0000
680@@ -24,8 +24,10 @@
681 #include "mir/client_platform.h"
682 #include "mir/client_platform_factory.h"
683 #include "rpc/mir_basic_rpc_channel.h"
684+#include "rpc/mir_protobuf_rpc_channel.h"
685 #include "mir/dispatch/dispatchable.h"
686-#include "mir/dispatch/simple_dispatch_thread.h"
687+#include "mir/dispatch/multiplexing_dispatchable.h"
688+#include "mir/dispatch/threaded_dispatcher.h"
689 #include "connection_configuration.h"
690 #include "display_configuration.h"
691 #include "connection_surface_map.h"
692@@ -33,6 +35,7 @@
693
694 #include "mir/events/event_builders.h"
695 #include "mir/logging/logger.h"
696+#include "mir/require.h"
697
698 #include <algorithm>
699 #include <cstddef>
700@@ -42,6 +45,7 @@
701 #include <boost/exception/diagnostic_information.hpp>
702
703 namespace mcl = mir::client;
704+namespace mclr = mir::client::rpc;
705 namespace md = mir::dispatch;
706 namespace mircv = mir::input::receiver;
707 namespace mev = mir::events;
708@@ -99,10 +103,15 @@
709 {
710 }
711
712+MirConnection::MirConnection(mir::client::ConnectionConfiguration& conf) :
713+ MirConnection(conf, DispatchType::automatic)
714+{
715+}
716+
717 MirConnection::MirConnection(
718- mir::client::ConnectionConfiguration& conf) :
719+ mir::client::ConnectionConfiguration& conf, DispatchType dispatch) :
720 deregisterer{this},
721- channel(conf.the_rpc_channel()),
722+ channel{conf.the_rpc_channel()},
723 server(channel.get(), ::google::protobuf::Service::STUB_DOESNT_OWN_CHANNEL),
724 debug(channel.get(), ::google::protobuf::Service::STUB_DOESNT_OWN_CHANNEL),
725 logger(conf.the_logger()),
726@@ -113,7 +122,11 @@
727 lifecycle_control(conf.the_lifecycle_control()),
728 surface_map(conf.the_surface_map()),
729 event_handler_register(conf.the_event_handler_register()),
730- eventloop{new md::SimpleDispatchThread{std::dynamic_pointer_cast<md::Dispatchable>(channel)}}
731+ dispatcher{std::shared_ptr<md::MultiplexingDispatchable>(new md::MultiplexingDispatchable{channel})},
732+ eventloop{dispatch == DispatchType::automatic ?
733+ new md::ThreadedDispatcher{"I/O loop", dispatcher} :
734+ nullptr
735+ }
736 {
737 connect_result.set_error("connect not called");
738 {
739@@ -325,6 +338,36 @@
740 return &disconnect_wait_handle;
741 }
742
743+mir::Fd MirConnection::watch_fd() const
744+{
745+ return eventloop ? mir::Fd{} : dispatcher->watch_fd();
746+}
747+
748+void MirConnection::dispatch()
749+{
750+ mir::require(!eventloop);
751+
752+ dispatcher->dispatch(md::FdEvent::readable);
753+}
754+
755+void MirConnection::add_dispatchee(std::shared_ptr<mir::dispatch::Dispatchable> const& dispatchee)
756+{
757+ dispatcher->add_watch(dispatchee);
758+ if (eventloop)
759+ {
760+ eventloop->add_thread();
761+ }
762+}
763+
764+void MirConnection::remove_dispatchee(std::shared_ptr<mir::dispatch::Dispatchable> const& dispatchee)
765+{
766+ dispatcher->remove_watch(dispatchee);
767+ if (eventloop)
768+ {
769+ eventloop->remove_thread();
770+ }
771+}
772+
773 void MirConnection::done_platform_operation(
774 mir_platform_operation_callback callback, void* context)
775 {
776@@ -568,3 +611,8 @@
777 {
778 return logger;
779 }
780+
781+void MirConnection::process_next_request_first()
782+{
783+ channel->process_next_request_first();
784+}
785
786=== modified file 'src/client/mir_connection.h'
787--- src/client/mir_connection.h 2015-03-31 02:35:42 +0000
788+++ src/client/mir_connection.h 2015-05-14 07:50:26 +0000
789@@ -26,6 +26,8 @@
790
791 #include <mutex>
792
793+#include "rpc/mir_basic_rpc_channel.h"
794+
795 #include "mir_protobuf.pb.h"
796
797 #include "mir_toolkit/mir_client_library.h"
798@@ -36,6 +38,8 @@
799
800 #include "mir_wait_handle.h"
801
802+#include "mir/fd.h"
803+
804 namespace mir
805 {
806 class SharedLibrary;
807@@ -51,10 +55,6 @@
808 class LifecycleControl;
809 class EventHandlerRegister;
810
811-namespace rpc
812-{
813-class MirBasicRpcChannel;
814-}
815 }
816
817 namespace input
818@@ -72,9 +72,17 @@
819
820 namespace dispatch
821 {
822-class SimpleDispatchThread;
823-}
824-}
825+class ThreadedDispatcher;
826+class Dispatchable;
827+class MultiplexingDispatchable;
828+}
829+}
830+
831+enum class DispatchType
832+{
833+ automatic,
834+ manual
835+};
836
837 struct MirConnection : mir::client::ClientContext
838 {
839@@ -82,6 +90,7 @@
840 MirConnection(std::string const& error_message);
841
842 MirConnection(mir::client::ConnectionConfiguration& conf);
843+ MirConnection(mir::client::ConnectionConfiguration &conf, DispatchType dispatch);
844 ~MirConnection() noexcept;
845
846 MirConnection(MirConnection const &) = delete;
847@@ -107,6 +116,12 @@
848
849 MirWaitHandle* disconnect();
850
851+ mir::Fd watch_fd() const;
852+ void dispatch();
853+
854+ void add_dispatchee(std::shared_ptr<mir::dispatch::Dispatchable> const& dispatchee);
855+ void remove_dispatchee(std::shared_ptr<mir::dispatch::Dispatchable> const& dispatchee);
856+
857 MirWaitHandle* platform_operation(
858 MirPlatformMessage const* request,
859 mir_platform_operation_callback callback, void* context);
860@@ -140,6 +155,7 @@
861 mir::protobuf::DisplayServer& display_server();
862 std::shared_ptr<mir::logging::Logger> const& the_logger() const;
863
864+ void process_next_request_first();
865 private:
866 void populate_server_package(MirPlatformPackage& platform_package) override;
867 // MUST be first data member so it is destroyed last.
868@@ -152,7 +168,7 @@
869
870 std::mutex mutex; // Protects all members of *this (except release_wait_handles)
871
872- std::shared_ptr<google::protobuf::RpcChannel> const channel;
873+ std::shared_ptr<mir::client::rpc::MirBasicRpcChannel> const channel;
874 mir::protobuf::DisplayServer::Stub server;
875 mir::protobuf::Debug::Stub debug;
876 std::shared_ptr<mir::logging::Logger> const logger;
877@@ -189,7 +205,9 @@
878
879 std::shared_ptr<mir::client::EventHandlerRegister> const event_handler_register;
880
881- std::unique_ptr<mir::dispatch::SimpleDispatchThread> const eventloop;
882+ std::shared_ptr<mir::dispatch::MultiplexingDispatchable> const dispatcher;
883+ std::unique_ptr<mir::dispatch::ThreadedDispatcher> const eventloop;
884+
885
886 std::shared_ptr<mir::client::ClientBufferStreamFactory> buffer_stream_factory;
887
888
889=== modified file 'src/client/mir_connection_api.cpp'
890--- src/client/mir_connection_api.cpp 2015-03-31 02:35:42 +0000
891+++ src/client/mir_connection_api.cpp 2015-05-14 07:50:26 +0000
892@@ -39,6 +39,7 @@
893 #include <unordered_set>
894 #include <cstddef>
895 #include <cstring>
896+#include <poll.h>
897
898 namespace mcl = mir::client;
899
900@@ -98,7 +99,20 @@
901 try
902 {
903 auto wait_handle = connection->disconnect();
904- wait_handle->wait_for_all();
905+ if (connection->watch_fd() != mir::Fd::invalid)
906+ {
907+ pollfd fd;
908+ fd.fd = connection->watch_fd();
909+ fd.events = POLLIN;
910+ while(!wait_handle->ready() && (poll(&fd, 1, -1) > 0))
911+ {
912+ connection->dispatch();
913+ }
914+ }
915+ else
916+ {
917+ wait_handle->wait_for_all();
918+ }
919 }
920 catch (std::exception const& ex)
921 {
922@@ -165,6 +179,41 @@
923 return conn;
924 }
925
926+MirConnection* mir_connect_with_manual_dispatch(
927+ char const* server,
928+ char const* app_name,
929+ mir_connected_callback callback,
930+ void* context)
931+{
932+ try
933+ {
934+ std::string sock;
935+ if (server)
936+ sock = server;
937+ else
938+ {
939+ auto socket_env = getenv("MIR_SOCKET");
940+ if (socket_env)
941+ sock = socket_env;
942+ else
943+ sock = mir::default_server_socket;
944+ }
945+
946+ mcl::DefaultConnectionConfiguration conf{sock};
947+
948+ std::unique_ptr<MirConnection> connection{new MirConnection(conf, DispatchType::manual)};
949+ connection->connect(app_name, callback, context);
950+ return connection.release();
951+ }
952+ catch (std::exception const& x)
953+ {
954+ MirConnection* error_connection = new MirConnection(x.what());
955+ mcl::ErrorConnections::instance().insert(error_connection);
956+ callback(error_connection, context);
957+ return error_connection;
958+ }
959+}
960+
961 bool mir_connection_is_valid(MirConnection* connection)
962 {
963 return MirConnection::is_valid(connection);
964@@ -187,6 +236,16 @@
965 }
966 }
967
968+int mir_connection_get_event_fd(MirConnection* connection)
969+{
970+ return connection->watch_fd();
971+}
972+
973+void mir_connection_dispatch(MirConnection* connection)
974+{
975+ connection->dispatch();
976+}
977+
978 void mir_connection_get_platform(
979 MirConnection* connection,
980 MirPlatformPackage* platform_package)
981
982=== modified file 'src/client/mir_prompt_session_api.cpp'
983--- src/client/mir_prompt_session_api.cpp 2015-03-31 02:35:42 +0000
984+++ src/client/mir_prompt_session_api.cpp 2015-05-14 07:50:26 +0000
985@@ -24,6 +24,8 @@
986
987 #include "mir/uncaught.h"
988
989+#include "synchronous_helper.h"
990+
991 #include <stdexcept>
992 #include <boost/throw_exception.hpp>
993
994@@ -44,9 +46,14 @@
995 if (state_change_callback)
996 prompt_session->register_prompt_session_state_change_callback(state_change_callback, context);
997
998- mir_wait_for(prompt_session->start(application_pid,
999- null_callback,
1000- nullptr));
1001+ make_synchronous_call(
1002+ connection,
1003+ std::mem_fn(&MirPromptSession::start),
1004+ prompt_session,
1005+ application_pid,
1006+ &assign_result<MirPromptSession>,
1007+ static_cast<void*>(nullptr));
1008+
1009 return prompt_session;
1010 }
1011 catch (std::exception const& ex)
1012
1013=== modified file 'src/client/mir_screencast.cpp'
1014--- src/client/mir_screencast.cpp 2015-04-25 11:38:31 +0000
1015+++ src/client/mir_screencast.cpp 2015-05-14 07:50:26 +0000
1016@@ -30,13 +30,15 @@
1017 namespace geom = mir::geometry;
1018
1019 MirScreencast::MirScreencast(
1020+ MirConnection* allocating_connection,
1021 geom::Rectangle const& region,
1022 geom::Size const& size,
1023 MirPixelFormat pixel_format,
1024 mir::protobuf::DisplayServer& server,
1025 std::shared_ptr<mcl::ClientBufferStreamFactory> const& buffer_stream_factory,
1026 mir_screencast_callback callback, void* context)
1027- : server(server),
1028+ : allocating_connection{allocating_connection},
1029+ server(server),
1030 output_size{size},
1031 buffer_stream_factory{buffer_stream_factory}
1032 {
1033@@ -102,12 +104,16 @@
1034 }
1035
1036 void MirScreencast::screencast_created(
1037- mir_screencast_callback callback, void* context)
1038+ mir_screencast_callback callback,
1039+ void* context)
1040 {
1041 if (!protobuf_screencast.has_error())
1042 {
1043- buffer_stream = buffer_stream_factory->make_consumer_stream(server,
1044- protobuf_screencast.buffer_stream(), "MirScreencast");
1045+ buffer_stream = buffer_stream_factory->make_consumer_stream(
1046+ allocating_connection,
1047+ server,
1048+ protobuf_screencast.buffer_stream(),
1049+ "MirScreencast");
1050 }
1051
1052 callback(this, context);
1053@@ -125,3 +131,8 @@
1054 {
1055 return buffer_stream.get();
1056 }
1057+
1058+MirConnection* MirScreencast::get_connection()
1059+{
1060+ return allocating_connection;
1061+}
1062
1063=== modified file 'src/client/mir_screencast.h'
1064--- src/client/mir_screencast.h 2015-03-31 02:35:42 +0000
1065+++ src/client/mir_screencast.h 2015-05-14 07:50:26 +0000
1066@@ -41,6 +41,7 @@
1067 {
1068 public:
1069 MirScreencast(
1070+ MirConnection* allocating_connection,
1071 mir::geometry::Rectangle const& region,
1072 mir::geometry::Size const& size,
1073 MirPixelFormat pixel_format,
1074@@ -60,12 +61,14 @@
1075
1076 mir::client::ClientBufferStream* get_buffer_stream();
1077
1078+ MirConnection* get_connection();
1079 private:
1080 void screencast_created(
1081 mir_screencast_callback callback, void* context);
1082 void released(
1083 mir_screencast_callback callback, void* context);
1084
1085+ MirConnection* const allocating_connection;
1086 mir::protobuf::DisplayServer& server;
1087 mir::geometry::Size const output_size;
1088 std::shared_ptr<mir::client::ClientBufferStreamFactory> const buffer_stream_factory;
1089
1090=== modified file 'src/client/mir_screencast_api.cpp'
1091--- src/client/mir_screencast_api.cpp 2015-03-31 02:35:42 +0000
1092+++ src/client/mir_screencast_api.cpp 2015-05-14 07:50:26 +0000
1093@@ -25,6 +25,8 @@
1094
1095 #include "mir/uncaught.h"
1096
1097+#include "synchronous_helper.h"
1098+
1099 #include <stdexcept>
1100 #include <boost/throw_exception.hpp>
1101
1102@@ -56,6 +58,7 @@
1103
1104 std::unique_ptr<MirScreencast> screencast_uptr{
1105 new MirScreencast{
1106+ connection,
1107 region,
1108 size,
1109 parameters->pixel_format,
1110@@ -63,7 +66,18 @@
1111 connection->get_client_buffer_stream_factory(),
1112 null_callback, nullptr}};
1113
1114- screencast_uptr->creation_wait_handle()->wait_for_all();
1115+
1116+ if (connection->watch_fd() != mir::Fd::invalid)
1117+ {
1118+ dispatch_connection_until(connection, [&screencast_uptr]()
1119+ {
1120+ return screencast_uptr->creation_wait_handle()->ready();
1121+ });
1122+ }
1123+ else
1124+ {
1125+ screencast_uptr->creation_wait_handle()->wait_for_all();
1126+ }
1127
1128 if (screencast_uptr->valid())
1129 {
1130@@ -82,7 +96,11 @@
1131
1132 void mir_screencast_release_sync(MirScreencast* screencast)
1133 {
1134- screencast->release(null_callback, nullptr)->wait_for_all();
1135+ make_synchronous_call(screencast->get_connection(),
1136+ std::mem_fn(&MirScreencast::release),
1137+ screencast,
1138+ &assign_result<MirScreencast>,
1139+ static_cast<void*>(nullptr));
1140 delete screencast;
1141 }
1142
1143
1144=== modified file 'src/client/mir_surface.cpp'
1145--- src/client/mir_surface.cpp 2015-04-28 10:30:44 +0000
1146+++ src/client/mir_surface.cpp 2015-05-14 07:50:26 +0000
1147@@ -23,7 +23,7 @@
1148 #include "cursor_configuration.h"
1149 #include "client_buffer_stream_factory.h"
1150 #include "mir_connection.h"
1151-#include "mir/dispatch/simple_dispatch_thread.h"
1152+#include "mir/dispatch/threaded_dispatcher.h"
1153 #include "mir/input/input_platform.h"
1154 #include "mir/input/xkb_mapper.h"
1155
1156@@ -179,7 +179,10 @@
1157
1158 std::lock_guard<decltype(mutex)> lock(mutex);
1159
1160- input_thread.reset();
1161+ if (input_dispatcher)
1162+ {
1163+ connection->remove_dispatchee(input_dispatcher);
1164+ }
1165
1166 for (auto i = 0, end = surface.fd_size(); i != end; ++i)
1167 close(surface.fd(i));
1168@@ -220,6 +223,11 @@
1169 return false;
1170 }
1171
1172+MirConnection* MirSurface::get_connection() const
1173+{
1174+ return connection;
1175+}
1176+
1177 MirWaitHandle* MirSurface::get_create_wait_handle()
1178 {
1179 return &create_wait_handle;
1180@@ -252,7 +260,7 @@
1181 std::lock_guard<decltype(mutex)> lock(mutex);
1182
1183 buffer_stream = buffer_stream_factory->
1184- make_producer_stream(*server, surface.buffer_stream(), name);
1185+ make_producer_stream(connection, *server, surface.buffer_stream(), name);
1186
1187 for(int i = 0; i < surface.attributes_size(); i++)
1188 {
1189@@ -458,7 +466,11 @@
1190 {
1191 std::lock_guard<decltype(mutex)> lock(mutex);
1192
1193- input_thread.reset();
1194+ if (input_dispatcher)
1195+ {
1196+ connection->remove_dispatchee(input_dispatcher);
1197+ input_dispatcher.reset();
1198+ }
1199
1200 if (callback)
1201 {
1202@@ -468,10 +480,10 @@
1203
1204 if (surface.fd_size() > 0 && handle_event_callback)
1205 {
1206- auto input_dispatcher = input_platform->create_input_receiver(surface.fd(0),
1207- keymapper,
1208- handle_event_callback);
1209- input_thread = std::make_shared<md::SimpleDispatchThread>(input_dispatcher);
1210+ input_dispatcher = input_platform->create_input_receiver(surface.fd(0),
1211+ keymapper,
1212+ handle_event_callback);
1213+ connection->add_dispatchee(input_dispatcher);
1214 }
1215 }
1216 }
1217
1218=== modified file 'src/client/mir_surface.h'
1219--- src/client/mir_surface.h 2015-04-28 08:59:22 +0000
1220+++ src/client/mir_surface.h 2015-05-14 07:50:26 +0000
1221@@ -39,7 +39,7 @@
1222 {
1223 namespace dispatch
1224 {
1225-class SimpleDispatchThread;
1226+class Dispatchable;
1227 }
1228 namespace input
1229 {
1230@@ -153,6 +153,8 @@
1231 MirWaitHandle* modify(MirSurfaceSpec const& changes);
1232
1233 static bool is_valid(MirSurface* query);
1234+
1235+ MirConnection* get_connection() const;
1236 private:
1237 mutable std::mutex mutex; // Protects all members of *this
1238
1239@@ -191,7 +193,7 @@
1240 MirOrientation orientation = mir_orientation_normal;
1241
1242 std::function<void(MirEvent const*)> handle_event_callback;
1243- std::shared_ptr<mir::dispatch::SimpleDispatchThread> input_thread;
1244+ std::shared_ptr<mir::dispatch::Dispatchable> input_dispatcher;
1245 };
1246
1247 #endif /* MIR_CLIENT_PRIVATE_MIR_WAIT_HANDLE_H_ */
1248
1249=== modified file 'src/client/mir_surface_api.cpp'
1250--- src/client/mir_surface_api.cpp 2015-04-30 12:27:33 +0000
1251+++ src/client/mir_surface_api.cpp 2015-05-14 07:50:26 +0000
1252@@ -26,24 +26,13 @@
1253 #include "mir_surface.h"
1254 #include "error_connections.h"
1255 #include "mir/uncaught.h"
1256+#include "synchronous_helper.h"
1257
1258 #include <boost/exception/diagnostic_information.hpp>
1259 #include <functional>
1260
1261 namespace mcl = mir::client;
1262
1263-namespace
1264-{
1265-
1266-// assign_result is compatible with all 2-parameter callbacks
1267-void assign_result(void* result, void** context)
1268-{
1269- if (context)
1270- *context = result;
1271-}
1272-
1273-}
1274-
1275 MirSurfaceSpec* mir_connection_create_spec_for_normal_surface(MirConnection* connection,
1276 int width, int height,
1277 MirPixelFormat format)
1278@@ -128,9 +117,12 @@
1279 {
1280 MirSurface* surface = nullptr;
1281
1282- mir_wait_for(mir_surface_create(requested_specification,
1283- reinterpret_cast<mir_surface_callback>(assign_result),
1284- &surface));
1285+ make_synchronous_call(
1286+ requested_specification->connection,
1287+ &mir_surface_create,
1288+ requested_specification,
1289+ &assign_result<MirSurface>,
1290+ &surface);
1291
1292 return surface;
1293 }
1294@@ -364,9 +356,11 @@
1295
1296 void mir_surface_release_sync(MirSurface* surface)
1297 {
1298- mir_wait_for(mir_surface_release(surface,
1299- reinterpret_cast<mir_surface_callback>(assign_result),
1300- nullptr));
1301+ make_synchronous_call(surface->get_connection(),
1302+ mir_surface_release,
1303+ surface,
1304+ &assign_result<MirSurface>,
1305+ static_cast<void*>(nullptr));
1306 }
1307
1308 int mir_surface_get_id(MirSurface* /*surface*/)
1309
1310=== modified file 'src/client/mir_wait_api.cpp'
1311--- src/client/mir_wait_api.cpp 2014-03-31 14:36:08 +0000
1312+++ src/client/mir_wait_api.cpp 2015-05-14 07:50:26 +0000
1313@@ -30,3 +30,12 @@
1314 if (wait_handle)
1315 wait_handle->wait_for_one();
1316 }
1317+
1318+bool mir_wait_handle_ready(MirWaitHandle* wait_handle)
1319+{
1320+ if (wait_handle)
1321+ {
1322+ return wait_handle->ready();
1323+ }
1324+ return true;
1325+}
1326
1327=== modified file 'src/client/mir_wait_handle.cpp'
1328--- src/client/mir_wait_handle.cpp 2015-01-21 07:34:50 +0000
1329+++ src/client/mir_wait_handle.cpp 2015-05-14 07:50:26 +0000
1330@@ -74,3 +74,9 @@
1331 --expecting;
1332 }
1333
1334+bool MirWaitHandle::ready()
1335+{
1336+ std::lock_guard<std::mutex> lock{guard};
1337+
1338+ return received == expecting;
1339+}
1340
1341=== modified file 'src/client/mir_wait_handle.h'
1342--- src/client/mir_wait_handle.h 2013-10-03 03:44:08 +0000
1343+++ src/client/mir_wait_handle.h 2015-05-14 07:50:26 +0000
1344@@ -39,6 +39,7 @@
1345 void wait_for_all();
1346 void wait_for_one();
1347 void wait_for_pending(std::chrono::milliseconds limit);
1348+ bool ready();
1349
1350 private:
1351 std::mutex guard;
1352
1353=== modified file 'src/client/rpc/make_rpc_channel.h'
1354--- src/client/rpc/make_rpc_channel.h 2015-01-21 07:34:50 +0000
1355+++ src/client/rpc/make_rpc_channel.h 2015-05-14 07:50:26 +0000
1356@@ -20,8 +20,6 @@
1357
1358 #include <memory>
1359
1360-namespace google { namespace protobuf { class RpcChannel; } }
1361-
1362 namespace mir
1363 {
1364 namespace client
1365@@ -34,8 +32,9 @@
1366 namespace rpc
1367 {
1368 class RpcReport;
1369+class MirBasicRpcChannel;
1370
1371-std::shared_ptr<google::protobuf::RpcChannel>
1372+std::shared_ptr<MirBasicRpcChannel>
1373 make_rpc_channel(std::string const& name,
1374 std::shared_ptr<SurfaceMap> const& map,
1375 std::shared_ptr<DisplayConfiguration> const& disp_conf,
1376
1377=== modified file 'src/client/rpc/make_socket_rpc_channel.cpp'
1378--- src/client/rpc/make_socket_rpc_channel.cpp 2015-03-31 02:35:42 +0000
1379+++ src/client/rpc/make_socket_rpc_channel.cpp 2015-05-14 07:50:26 +0000
1380@@ -40,7 +40,7 @@
1381 } const fd_prefix("fd://");
1382 }
1383
1384-std::shared_ptr<google::protobuf::RpcChannel>
1385+std::shared_ptr<mclr::MirBasicRpcChannel>
1386 mclr::make_rpc_channel(std::string const& name,
1387 std::shared_ptr<mcl::SurfaceMap> const& map,
1388 std::shared_ptr<mcl::DisplayConfiguration> const& disp_conf,
1389
1390=== modified file 'src/client/rpc/mir_basic_rpc_channel.cpp'
1391--- src/client/rpc/mir_basic_rpc_channel.cpp 2015-01-21 07:34:50 +0000
1392+++ src/client/rpc/mir_basic_rpc_channel.cpp 2015-05-14 07:50:26 +0000
1393@@ -38,13 +38,19 @@
1394 void mclrd::PendingCallCache::save_completion_details(
1395 mir::protobuf::wire::Invocation const& invoke,
1396 google::protobuf::Message* response,
1397- std::shared_ptr<google::protobuf::Closure> const& complete)
1398+ google::protobuf::Closure* complete)
1399 {
1400 std::unique_lock<std::mutex> lock(mutex);
1401
1402 pending_calls[invoke.id()] = PendingCall(response, complete);
1403 }
1404
1405+google::protobuf::Message* mclrd::PendingCallCache::message_for_result(mir::protobuf::wire::Result& result)
1406+{
1407+ std::unique_lock<std::mutex> lock(mutex);
1408+ return pending_calls.at(result.id()).response;
1409+}
1410+
1411 void mclrd::PendingCallCache::complete_response(mir::protobuf::wire::Result& result)
1412 {
1413 PendingCall completion;
1414@@ -66,7 +72,6 @@
1415 else
1416 {
1417 rpc_report->complete_response(result);
1418- completion.response->ParseFromString(result.response());
1419 completion.complete->Run();
1420 }
1421 }
1422
1423=== modified file 'src/client/rpc/mir_basic_rpc_channel.h'
1424--- src/client/rpc/mir_basic_rpc_channel.h 2015-01-21 07:34:50 +0000
1425+++ src/client/rpc/mir_basic_rpc_channel.h 2015-05-14 07:50:26 +0000
1426@@ -19,6 +19,8 @@
1427 #ifndef MIR_CLIENT_RPC_MIR_BASIC_RPC_CHANNEL_H_
1428 #define MIR_CLIENT_RPC_MIR_BASIC_RPC_CHANNEL_H_
1429
1430+#include "mir/dispatch/dispatchable.h"
1431+
1432 #include <google/protobuf/service.h>
1433 #include <google/protobuf/descriptor.h>
1434
1435@@ -57,8 +59,10 @@
1436 void save_completion_details(
1437 mir::protobuf::wire::Invocation const& invoke,
1438 google::protobuf::Message* response,
1439- std::shared_ptr<google::protobuf::Closure> const& complete);
1440-
1441+ google::protobuf::Closure* complete);
1442+
1443+
1444+ google::protobuf::Message* message_for_result(mir::protobuf::wire::Result& result);
1445
1446 void complete_response(mir::protobuf::wire::Result& result);
1447
1448@@ -72,14 +76,14 @@
1449 {
1450 PendingCall(
1451 google::protobuf::Message* response,
1452- std::shared_ptr<google::protobuf::Closure> const& target)
1453+ google::protobuf::Closure* target)
1454 : response(response), complete(target) {}
1455
1456 PendingCall()
1457 : response(0), complete() {}
1458
1459 google::protobuf::Message* response;
1460- std::shared_ptr<google::protobuf::Closure> complete;
1461+ google::protobuf::Closure* complete;
1462 };
1463
1464 std::mutex mutable mutex;
1465@@ -88,12 +92,15 @@
1466 };
1467 }
1468
1469-class MirBasicRpcChannel : public google::protobuf::RpcChannel
1470+class MirBasicRpcChannel
1471+ : public google::protobuf::RpcChannel,
1472+ public mir::dispatch::Dispatchable
1473 {
1474 public:
1475 MirBasicRpcChannel();
1476 ~MirBasicRpcChannel();
1477
1478+ virtual void process_next_request_first() = 0;
1479 protected:
1480 mir::protobuf::wire::Invocation invocation_for(
1481 google::protobuf::MethodDescriptor const* method,
1482
1483=== modified file 'src/client/rpc/mir_protobuf_rpc_channel.cpp'
1484--- src/client/rpc/mir_protobuf_rpc_channel.cpp 2015-05-01 14:47:55 +0000
1485+++ src/client/rpc/mir_protobuf_rpc_channel.cpp 2015-05-14 07:50:26 +0000
1486@@ -60,7 +60,9 @@
1487 lifecycle_control(lifecycle_control),
1488 event_sink(event_sink),
1489 disconnected(false),
1490- transport{std::move(transport)}
1491+ transport{std::move(transport)},
1492+ delayed_processor{std::make_shared<md::ActionQueue>()},
1493+ multiplexer{this->transport, delayed_processor}
1494 {
1495 class NullDeleter
1496 {
1497@@ -105,8 +107,7 @@
1498 }
1499 }
1500
1501-void mclr::MirProtobufRpcChannel::receive_file_descriptors(google::protobuf::Message* response,
1502- google::protobuf::Closure* complete)
1503+void mclr::MirProtobufRpcChannel::receive_file_descriptors(google::protobuf::Message* response)
1504 {
1505 auto const message_type = response->GetTypeName();
1506
1507@@ -163,7 +164,6 @@
1508 receive_any_file_descriptors_for(platform);
1509 receive_any_file_descriptors_for(socket_fd);
1510 receive_any_file_descriptors_for(platform_operation_message);
1511- complete->Run();
1512 }
1513
1514 void mclr::MirProtobufRpcChannel::CallMethod(
1515@@ -193,10 +193,13 @@
1516
1517 rpc_report->invocation_requested(invocation);
1518
1519- std::shared_ptr<google::protobuf::Closure> callback(
1520- google::protobuf::NewPermanentCallback(this, &MirProtobufRpcChannel::receive_file_descriptors, response, complete));
1521+ pending_calls.save_completion_details(invocation, response, complete);
1522
1523- pending_calls.save_completion_details(invocation, response, callback);
1524+ if (prioritise_next_request)
1525+ {
1526+ id_to_wait_for = invocation.id();
1527+ prioritise_next_request = false;
1528+ }
1529
1530 send_message(invocation, invocation, fds);
1531 }
1532@@ -317,7 +320,7 @@
1533 */
1534 std::lock_guard<decltype(read_mutex)> lock(read_mutex);
1535
1536- mir::protobuf::wire::Result result;
1537+ auto result = std::make_unique<mir::protobuf::wire::Result>();
1538 try
1539 {
1540 uint16_t message_size;
1541@@ -327,9 +330,9 @@
1542 body_bytes.resize(message_size);
1543 transport->receive_data(body_bytes.data(), message_size);
1544
1545- result.ParseFromArray(body_bytes.data(), message_size);
1546+ result->ParseFromArray(body_bytes.data(), message_size);
1547
1548- rpc_report->result_receipt_succeeded(result);
1549+ rpc_report->result_receipt_succeeded(*result);
1550 }
1551 catch (std::exception const& x)
1552 {
1553@@ -339,14 +342,39 @@
1554
1555 try
1556 {
1557- for (int i = 0; i != result.events_size(); ++i)
1558+ for (int i = 0; i != result->events_size(); ++i)
1559 {
1560- process_event_sequence(result.events(i));
1561+ process_event_sequence(result->events(i));
1562 }
1563
1564- if (result.has_id())
1565+ if (result->has_id())
1566 {
1567- pending_calls.complete_response(result);
1568+ auto result_message = pending_calls.message_for_result(*result);
1569+ result_message->ParseFromString(result->response());
1570+ receive_file_descriptors(result_message);
1571+
1572+ if (id_to_wait_for)
1573+ {
1574+ if (result->id() == id_to_wait_for.value())
1575+ {
1576+ pending_calls.complete_response(*result);
1577+ multiplexer.add_watch(delayed_processor);
1578+ }
1579+ else
1580+ {
1581+ // It's too difficult to convince C++ to move this lambda everywhere, so
1582+ // just give up and let it pretend its a shared_ptr.
1583+ std::shared_ptr<mir::protobuf::wire::Result> appeaser{std::move(result)};
1584+ delayed_processor->enqueue([delayed_result = std::move(appeaser), this]() mutable
1585+ {
1586+ pending_calls.complete_response(*delayed_result);
1587+ });
1588+ }
1589+ }
1590+ else
1591+ {
1592+ pending_calls.complete_response(*result);
1593+ }
1594 }
1595 }
1596 catch (std::exception const& x)
1597@@ -354,7 +382,7 @@
1598 // TODO: This is dangerous as an error in result processing could cause a wait handle
1599 // to never fire. Could perhaps fix by catching and setting error on the response before invoking
1600 // callback ~racarr
1601- rpc_report->result_processing_failed(result, x);
1602+ rpc_report->result_processing_failed(*result, x);
1603 }
1604 }
1605
1606@@ -365,15 +393,21 @@
1607
1608 mir::Fd mir::client::rpc::MirProtobufRpcChannel::watch_fd() const
1609 {
1610- return transport->watch_fd();
1611+ return multiplexer.watch_fd();
1612 }
1613
1614 bool mir::client::rpc::MirProtobufRpcChannel::dispatch(md::FdEvents events)
1615 {
1616- return transport->dispatch(events);
1617+ return multiplexer.dispatch(events);
1618 }
1619
1620 md::FdEvents mclr::MirProtobufRpcChannel::relevant_events() const
1621 {
1622- return transport->relevant_events();
1623+ return multiplexer.relevant_events();
1624+}
1625+
1626+void mclr::MirProtobufRpcChannel::process_next_request_first()
1627+{
1628+ prioritise_next_request = true;
1629+ multiplexer.remove_watch(delayed_processor);
1630 }
1631
1632=== modified file 'src/client/rpc/mir_protobuf_rpc_channel.h'
1633--- src/client/rpc/mir_protobuf_rpc_channel.h 2015-03-31 02:35:42 +0000
1634+++ src/client/rpc/mir_protobuf_rpc_channel.h 2015-05-14 07:50:26 +0000
1635@@ -22,23 +22,21 @@
1636 #include "mir_basic_rpc_channel.h"
1637 #include "stream_transport.h"
1638 #include "mir/dispatch/dispatchable.h"
1639+#include "mir/dispatch/multiplexing_dispatchable.h"
1640+#include "mir/dispatch/action_queue.h"
1641+
1642+#include "mir_protobuf_wire.pb.h"
1643
1644 #include <google/protobuf/service.h>
1645 #include <google/protobuf/descriptor.h>
1646
1647 #include <thread>
1648 #include <atomic>
1649+#include <list>
1650+#include <experimental/optional>
1651
1652 namespace mir
1653 {
1654-namespace protobuf
1655-{
1656-namespace wire
1657-{
1658-class Invocation;
1659-class Result;
1660-}
1661-}
1662
1663 namespace client
1664 {
1665@@ -53,8 +51,7 @@
1666
1667 class MirProtobufRpcChannel :
1668 public MirBasicRpcChannel,
1669- public StreamTransport::Observer,
1670- public dispatch::Dispatchable
1671+ public StreamTransport::Observer
1672 {
1673 public:
1674 MirProtobufRpcChannel(std::unique_ptr<StreamTransport> transport,
1675@@ -74,6 +71,20 @@
1676 Fd watch_fd() const override;
1677 bool dispatch(mir::dispatch::FdEvents events) override;
1678 mir::dispatch::FdEvents relevant_events() const override;
1679+
1680+ /**
1681+ * \brief Switch the RpcChannel into out-of-order mode
1682+ *
1683+ * The first CallMethod after this method is called will be processed
1684+ * out of order - no server responses will be processed until the response
1685+ * for the next CallMethod is processed.
1686+ *
1687+ * After the response for the next CallMethod is processed, normal processing
1688+ * is resumed.
1689+ *
1690+ * No messages are discarded, only delayed.
1691+ */
1692+ void process_next_request_first() override;
1693 private:
1694 virtual void CallMethod(const google::protobuf::MethodDescriptor* method, google::protobuf::RpcController*,
1695 const google::protobuf::Message* parameters, google::protobuf::Message* response,
1696@@ -86,7 +97,7 @@
1697 detail::SendBuffer header_bytes;
1698 detail::SendBuffer body_bytes;
1699
1700- void receive_file_descriptors(google::protobuf::Message* response, google::protobuf::Closure* complete);
1701+ void receive_file_descriptors(google::protobuf::Message* response);
1702 template<class MessageType>
1703 void receive_any_file_descriptors_for(MessageType* response);
1704 void send_message(mir::protobuf::wire::Invocation const& body,
1705@@ -106,14 +117,22 @@
1706 std::mutex read_mutex;
1707 std::mutex write_mutex;
1708
1709+ bool prioritise_next_request{false};
1710+ std::experimental::optional<uint32_t> id_to_wait_for;
1711+
1712 /* We use the guarantee that the transport's destructor blocks until
1713 * pending processing has finished to ensure that on_data_available()
1714 * isn't called after the members it relies on are destroyed.
1715 *
1716- * This means the transport field must appear after any field used
1717- * by on_data_available. For simplicity, put it last.
1718+ * This means that anything that owns a reference to the transport
1719+ * needs to be after anything that can be accessed from on_data_available().
1720+ *
1721+ * For simplicity's sake keep all of the dispatch infrastructure at the
1722+ * end to guarantee this.
1723 */
1724- std::unique_ptr<StreamTransport> transport;
1725+ std::shared_ptr<StreamTransport> const transport;
1726+ std::shared_ptr<mir::dispatch::ActionQueue> const delayed_processor;
1727+ mir::dispatch::MultiplexingDispatchable multiplexer;
1728 };
1729
1730 }
1731
1732=== modified file 'src/client/symbols.map'
1733--- src/client/symbols.map 2015-05-07 19:41:08 +0000
1734+++ src/client/symbols.map 2015-05-14 07:50:26 +0000
1735@@ -196,6 +196,10 @@
1736 mir_buffer_stream_release;
1737 mir_buffer_stream_release_sync;
1738 mir_buffer_stream_is_valid;
1739+ mir_connect_with_manual_dispatch;
1740+ mir_connection_get_event_fd;
1741+ mir_connection_dispatch;
1742+ mir_wait_handle_ready;
1743 } MIR_CLIENT_8.3;
1744
1745 MIR_CLIENT_DETAIL_8 {
1746
1747=== added file 'src/client/synchronous_helper.cpp'
1748--- src/client/synchronous_helper.cpp 1970-01-01 00:00:00 +0000
1749+++ src/client/synchronous_helper.cpp 2015-05-14 07:50:26 +0000
1750@@ -0,0 +1,33 @@
1751+/*
1752+ * Copyright © 2015 Canonical Ltd.
1753+ *
1754+ * This program is free software: you can redistribute it and/or modify it
1755+ * under the terms of the GNU Lesser General Public License version 3,
1756+ * as published by the Free Software Foundation.
1757+ *
1758+ * This program is distributed in the hope that it will be useful,
1759+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1760+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1761+ * GNU Lesser General Public License for more details.
1762+ *
1763+ * You should have received a copy of the GNU Lesser General Public License
1764+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1765+ *
1766+ * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
1767+ */
1768+
1769+#include "synchronous_helper.h"
1770+
1771+#include <poll.h>
1772+
1773+void dispatch_connection_until(MirConnection* connection, std::function<bool()> predicate)
1774+{
1775+ pollfd fd;
1776+ fd.fd = connection->watch_fd();
1777+ fd.events = POLLIN;
1778+ while(!predicate() && (poll(&fd, 1, -1) > 0))
1779+ {
1780+ connection->dispatch();
1781+ }
1782+}
1783+
1784
1785=== added file 'src/client/synchronous_helper.h'
1786--- src/client/synchronous_helper.h 1970-01-01 00:00:00 +0000
1787+++ src/client/synchronous_helper.h 2015-05-14 07:50:26 +0000
1788@@ -0,0 +1,160 @@
1789+/*
1790+ * Copyright © 2015 Canonical Ltd.
1791+ *
1792+ * This program is free software: you can redistribute it and/or modify it
1793+ * under the terms of the GNU Lesser General Public License version 3,
1794+ * as published by the Free Software Foundation.
1795+ *
1796+ * This program is distributed in the hope that it will be useful,
1797+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1798+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1799+ * GNU Lesser General Public License for more details.
1800+ *
1801+ * You should have received a copy of the GNU Lesser General Public License
1802+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1803+ *
1804+ * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
1805+ */
1806+
1807+#ifndef MIR_CLIENT_SYNCHRONOUS_H_
1808+#define MIR_CLIENT_SYNCHRONOUS_H_
1809+
1810+#include "mir_connection.h"
1811+#include "mir_wait_handle.h"
1812+
1813+#include <functional>
1814+#include <type_traits>
1815+
1816+template<typename Result>
1817+void assign_result(Result* result, void* ctx)
1818+{
1819+ auto assignee = reinterpret_cast<Result**>(ctx);
1820+ if (assignee)
1821+ {
1822+ *assignee = result;
1823+ }
1824+}
1825+
1826+template<typename Callback>
1827+struct SynchronousContext
1828+{
1829+ Callback real_callback;
1830+ bool complete;
1831+ void* userdata;
1832+};
1833+
1834+template<typename Callable, class Tuple, std::size_t...I>
1835+void apply_substituting_last_arg(
1836+ Callable&& function,
1837+ Tuple&& args,
1838+ std::index_sequence<I...>,
1839+ void* context)
1840+{
1841+ return std::forward<Callable>(function)(
1842+ std::get<I>(std::forward<Tuple>(args))...,
1843+ context);
1844+}
1845+
1846+
1847+template<typename... Args>
1848+void synchronous_wrapper(Args... args)
1849+{
1850+ std::tuple<Args...> arguments{args...};
1851+ constexpr std::size_t arg_count = sizeof...(Args);
1852+
1853+ auto wrapped_context = reinterpret_cast<SynchronousContext<void(*)(Args...)>*>(std::get<arg_count - 1>(arguments));
1854+ if (wrapped_context->real_callback)
1855+ {
1856+ apply_substituting_last_arg(
1857+ wrapped_context->real_callback,
1858+ std::forward_as_tuple(args...),
1859+ std::make_index_sequence<arg_count - 1>(),
1860+ wrapped_context->userdata);
1861+ }
1862+ wrapped_context->complete = true;
1863+}
1864+
1865+
1866+template<typename Callable, typename Tuple, std::size_t...I>
1867+constexpr decltype(auto) call_impl(Callable&& function, Tuple&& args, std::index_sequence<I...>)
1868+{
1869+ return std::forward<Callable>(function)(std::get<I>(std::forward<Tuple>((args)))...);
1870+}
1871+
1872+template<typename Callable, typename Tuple>
1873+constexpr decltype(auto) apply(Callable&& function, Tuple&& args)
1874+{
1875+ return call_impl(
1876+ std::forward<Callable>(function),
1877+ std::forward<Tuple>(args),
1878+ std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>{}>{});
1879+}
1880+
1881+void dispatch_connection_until(MirConnection* connection, std::function<bool()> predicate);
1882+
1883+
1884+/**
1885+ * \brief Make a synchronous RPC call
1886+ *
1887+ * This wrapper takes care of manually dispatching the MirConnection if it is in
1888+ * manual dispatch mode, or waiting for the operation to complete if in automatic
1889+ * dispatch mode.
1890+ *
1891+ * Given a function mir_foo_do_thing(MirFoo* target, int arg, foo_callback callback, void* context)
1892+ * the correct way to call this function is
1893+ * make_synchronous_call(connection,
1894+ * &mir_foo_do_thing,
1895+ * target,
1896+ * arg,
1897+ * callback,
1898+ * static_cast<void*>(context));
1899+ *
1900+ * The last two parameters must be the callback function and the void* to pass to that callback.
1901+ * The last argument must have a pointer type; NULL or nullptr must be explicitly cast to
1902+ * void*.
1903+ */
1904+template<typename Callable, typename... Args>
1905+void make_synchronous_call(MirConnection* connection,
1906+ Callable&& function,
1907+ Args&&... args)
1908+{
1909+ static_assert(
1910+ std::is_same<typename std::result_of<Callable(Args...)>::type, MirWaitHandle*>::value,
1911+ "Second parameter must be a function that returns a MirWaitHandle*");
1912+
1913+ if (connection->watch_fd() != mir::Fd::invalid)
1914+ {
1915+ std::tuple<Args...> arguments{args...};
1916+ constexpr int arg_count = sizeof...(Args);
1917+ auto callback = std::get<arg_count - 2>(arguments);
1918+ auto context = std::get<arg_count - 1>(arguments);
1919+
1920+ static_assert(
1921+ std::is_pointer<typename std::tuple_element<arg_count - 1, std::tuple<Args...>>::type>::value,
1922+ "The final argument must be a pointer type");
1923+ static_assert(
1924+ std::is_pointer<typename std::tuple_element<arg_count - 2, std::tuple<Args...>>::type>::value,
1925+ "The second last argument must be a function pointer");
1926+
1927+ SynchronousContext<decltype(callback)> wrapper_context {
1928+ callback,
1929+ false,
1930+ context
1931+ };
1932+
1933+ std::get<arg_count - 2>(arguments) = &synchronous_wrapper;
1934+ std::get<arg_count - 1>(arguments) =
1935+ reinterpret_cast<typename std::tuple_element<arg_count - 1, std::tuple<Args...>>::type>(&wrapper_context);
1936+
1937+ connection->process_next_request_first();
1938+ apply(std::forward<Callable>(function), arguments);
1939+
1940+ dispatch_connection_until(connection, [&wrapper_context](){ return wrapper_context.complete; });
1941+ }
1942+ else
1943+ {
1944+ mir_wait_for(std::forward<Callable>(function)(std::forward<Args>(args)...));
1945+ }
1946+}
1947+
1948+#endif // MIR_CLIENT_SYNCHRONOUS_H_
1949
1950=== modified file 'src/common/dispatch/CMakeLists.txt'
1951--- src/common/dispatch/CMakeLists.txt 2015-04-09 06:20:31 +0000
1952+++ src/common/dispatch/CMakeLists.txt 2015-05-14 07:50:26 +0000
1953@@ -17,10 +17,9 @@
1954 list(
1955 APPEND MIR_COMMON_SOURCES
1956 ${CMAKE_CURRENT_SOURCE_DIR}/action_queue.cpp
1957- ${CMAKE_CURRENT_SOURCE_DIR}/simple_dispatch_thread.cpp
1958 ${CMAKE_CURRENT_SOURCE_DIR}/multiplexing_dispatchable.cpp
1959 ${CMAKE_CURRENT_SOURCE_DIR}/utils.cpp
1960- ${CMAKE_CURRENT_SOURCE_DIR}/action_queue.cpp
1961+ ${CMAKE_CURRENT_SOURCE_DIR}/threaded_dispatcher.cpp
1962 )
1963
1964 set(MIR_COMMON_SOURCES ${MIR_COMMON_SOURCES} PARENT_SCOPE)
1965
1966=== removed file 'src/common/dispatch/simple_dispatch_thread.cpp'
1967--- src/common/dispatch/simple_dispatch_thread.cpp 2015-04-30 17:41:03 +0000
1968+++ src/common/dispatch/simple_dispatch_thread.cpp 1970-01-01 00:00:00 +0000
1969@@ -1,163 +0,0 @@
1970-/*
1971- * Copyright © 2015 Canonical Ltd.
1972- *
1973- * This program is free software: you can redistribute it and/or modify it
1974- * under the terms of the GNU Lesser General Public License version 3,
1975- * as published by the Free Software Foundation.
1976- *
1977- * This program is distributed in the hope that it will be useful,
1978- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1979- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1980- * GNU Lesser General Public License for more details.
1981- *
1982- * You should have received a copy of the GNU Lesser General Public License
1983- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1984- *
1985- * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
1986- */
1987-
1988-#include "mir/dispatch/simple_dispatch_thread.h"
1989-#include "mir/dispatch/dispatchable.h"
1990-#include "mir/logging/logger.h"
1991-#include "utils.h"
1992-#include "mir/signal_blocker.h"
1993-
1994-#include <sys/epoll.h>
1995-#include <unistd.h>
1996-#include <system_error>
1997-#include <array>
1998-#include <signal.h>
1999-#include <boost/exception/all.hpp>
2000-
2001-namespace md = mir::dispatch;
2002-
2003-namespace
2004-{
2005-
2006-void wait_for_events_forever(std::shared_ptr<md::Dispatchable> const& dispatchee, mir::Fd shutdown_fd)
2007-{
2008- auto epoll_fd = mir::Fd{epoll_create1(0)};
2009- if (epoll_fd == mir::Fd::invalid)
2010- {
2011- BOOST_THROW_EXCEPTION((std::system_error{errno,
2012- std::system_category(),
2013- "Failed to create epoll IO monitor"}));
2014- }
2015- epoll_event event;
2016- memset(&event, 0, sizeof(event));
2017-
2018- enum fd_names : uint32_t {
2019- shutdown,
2020- dispatchee_fd
2021- };
2022-
2023- // We only care when the shutdown pipe has been closed
2024- event.data.u32 = fd_names::shutdown;
2025- event.events = EPOLLRDHUP;
2026- epoll_ctl(epoll_fd, EPOLL_CTL_ADD, shutdown_fd, &event);
2027-
2028- // Ask the dispatchee what it events it's interested in...
2029- event.data.u32 = fd_names::dispatchee_fd;
2030- event.events = md::fd_event_to_epoll(dispatchee->relevant_events());
2031- epoll_ctl(epoll_fd, EPOLL_CTL_ADD, dispatchee->watch_fd(), &event);
2032-
2033- for (;;)
2034- {
2035- std::array<epoll_event,2> events;
2036- auto const num_available_events =
2037- epoll_wait(epoll_fd, events.data(), events.size(), -1);
2038-
2039- if (num_available_events == 1)
2040- {
2041- if (events[0].data.u32 == fd_names::dispatchee_fd)
2042- {
2043- if (!dispatchee->dispatch(md::epoll_to_fd_event(events[0])))
2044- {
2045- // No need to keep looping, the Dispatchable's not going to produce any more events.
2046- return;
2047- }
2048- }
2049- else if (events[0].data.u32 == fd_names::shutdown)
2050- {
2051- // The only thing we do with the shutdown fd is to close it.
2052- return;
2053- }
2054- }
2055- else if (num_available_events > 1)
2056- {
2057- // Because we only have two fds in the epoll, if there is more than one
2058- // event pending then one of them must be a shutdown event.
2059- // So, shutdown.
2060- return;
2061- }
2062- else if (num_available_events < 0)
2063- {
2064- // Although we have blocked signals in this thread, we can still
2065- // get interrupted by SIGSTOP (which is unblockable and non-fatal).
2066- if (errno != EINTR)
2067- {
2068- BOOST_THROW_EXCEPTION((std::system_error{
2069- errno, std::system_category(), "Failed to wait for epoll events"}));
2070- }
2071- }
2072- }
2073-}
2074-
2075-}
2076-
2077-md::SimpleDispatchThread::SimpleDispatchThread(std::shared_ptr<md::Dispatchable> const& dispatchee)
2078- : SimpleDispatchThread(dispatchee, []{})
2079-{}
2080-
2081-md::SimpleDispatchThread::SimpleDispatchThread(
2082- std::shared_ptr<md::Dispatchable> const& dispatchee,
2083- std::function<void()> const& exception_handler)
2084-{
2085- int pipefds[2];
2086- if (pipe(pipefds) < 0)
2087- {
2088- BOOST_THROW_EXCEPTION((std::system_error{errno,
2089- std::system_category(),
2090- "Failed to create shutdown pipe for IO thread"}));
2091- }
2092- shutdown_fd = mir::Fd{pipefds[1]};
2093- mir::Fd const terminate_fd = mir::Fd{pipefds[0]};
2094- {
2095- // The newly spawned thread inherits the current signal mask; block everything
2096- // before creating the new thread so that there's no race between thread start
2097- // and signal blocking.
2098- mir::SignalBlocker block_signals;
2099- eventloop = std::thread{
2100- [exception_handler, dispatchee, terminate_fd]()
2101- {
2102- try
2103- {
2104- wait_for_events_forever(dispatchee, terminate_fd);
2105- }
2106- catch(...)
2107- {
2108- exception_handler();
2109- }
2110- }};
2111- }
2112-}
2113-
2114-md::SimpleDispatchThread::~SimpleDispatchThread() noexcept
2115-{
2116- shutdown_fd = mir::Fd{};
2117- if (eventloop.get_id() == std::this_thread::get_id())
2118- {
2119- // We're being destroyed from within the dispatch callback
2120- // Attempting to join the eventloop will result in a trivial deadlock.
2121- //
2122- // The std::thread destructor will call std::terminate() for us, let's
2123- // leave a useful message.
2124- mir::logging::log(mir::logging::Severity::critical,
2125- "Destroying SimpleDispatchThread from within a dispatch callback. This is a programming error.",
2126- "Dispatch");
2127- }
2128- else if (eventloop.joinable())
2129- {
2130- eventloop.join();
2131- }
2132-}
2133
2134=== added file 'src/common/dispatch/threaded_dispatcher.cpp'
2135--- src/common/dispatch/threaded_dispatcher.cpp 1970-01-01 00:00:00 +0000
2136+++ src/common/dispatch/threaded_dispatcher.cpp 2015-05-14 07:50:26 +0000
2137@@ -0,0 +1,294 @@
2138+/*
2139+ * Copyright © 2015 Canonical Ltd.
2140+ *
2141+ * This program is free software: you can redistribute it and/or modify it
2142+ * under the terms of the GNU Lesser General Public License version 3,
2143+ * as published by the Free Software Foundation.
2144+ *
2145+ * This program is distributed in the hope that it will be useful,
2146+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2147+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2148+ * GNU Lesser General Public License for more details.
2149+ *
2150+ * You should have received a copy of the GNU Lesser General Public License
2151+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2152+ *
2153+ * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
2154+ */
2155+
2156+#include "mir/dispatch/threaded_dispatcher.h"
2157+#include "mir/dispatch/dispatchable.h"
2158+#include "mir/thread_name.h"
2159+
2160+#include "mir/raii.h"
2161+#include "mir/logging/logger.h"
2162+
2163+#include <fcntl.h>
2164+#include <poll.h>
2165+#include <unistd.h>
2166+#include <system_error>
2167+#include <signal.h>
2168+#include <boost/exception/all.hpp>
2169+#include <algorithm>
2170+#include <unordered_map>
2171+#include <sys/eventfd.h>
2172+
2173+namespace md = mir::dispatch;
2174+
2175+class md::ThreadedDispatcher::ThreadShutdownRequestHandler : public md::Dispatchable
2176+{
2177+public:
2178+ ThreadShutdownRequestHandler()
2179+ : event_semaphore{eventfd(0, EFD_CLOEXEC | EFD_SEMAPHORE)},
2180+ shutting_down{false}
2181+ {
2182+ if (event_semaphore == mir::Fd::invalid)
2183+ {
2184+ BOOST_THROW_EXCEPTION((std::system_error{errno,
2185+ std::system_category(),
2186+ "Failed to create shutdown eventfd"}));
2187+ }
2188+ }
2189+
2190+ mir::Fd watch_fd() const override
2191+ {
2192+ return event_semaphore;
2193+ }
2194+
2195+ bool dispatch(md::FdEvents events) override
2196+ {
2197+ if (events & md::FdEvent::error)
2198+ {
2199+ return false;
2200+ }
2201+
2202+ eventfd_t dummy;
2203+ if (eventfd_read(event_semaphore, &dummy) < 0)
2204+ {
2205+ BOOST_THROW_EXCEPTION((std::system_error{errno,
2206+ std::system_category(),
2207+ "Failed to clear shutdown notification"}));
2208+ }
2209+ std::lock_guard<decltype(running_flag_guard)> lock{running_flag_guard};
2210+ *running_flags.at(std::this_thread::get_id()) = false;
2211+
2212+ return true;
2213+ }
2214+
2215+ md::FdEvents relevant_events() const override
2216+ {
2217+ return md::FdEvent::readable;
2218+ }
2219+
2220+ std::thread::id terminate_one_thread()
2221+ {
2222+ // First we tell a thread to die, any thread...
2223+ if (eventfd_write(event_semaphore, 1) < 0)
2224+ {
2225+ BOOST_THROW_EXCEPTION((std::system_error{errno,
2226+ std::system_category(),
2227+ "Failed to trigger thread shutdown"}));
2228+ }
2229+
2230+ // ...now we wait for a thread to die and tell us its ID...
2231+ // We wait for a surprisingly long time because our threads are potentially blocked
2232+ // in client code that we don't control.
2233+ //
2234+ // If the client is entirely unresponsive for a whole minute, it deserves to die.
2235+ std::unique_lock<decltype(terminating_thread_mutex)> lock{terminating_thread_mutex};
2236+ if (!thread_terminating.wait_for (lock,
2237+ std::chrono::seconds{60},
2238+ [this]() { return !terminating_threads.empty(); }))
2239+ {
2240+ BOOST_THROW_EXCEPTION((std::runtime_error{"Thread failed to shutdown"}));
2241+ }
2242+
2243+ auto killed_thread_id = terminating_threads.back();
2244+ terminating_threads.pop_back();
2245+ return killed_thread_id;
2246+ }
2247+
2248+ void terminate_all_threads_async()
2249+ {
2250+ eventfd_t thread_count;
2251+ {
2252+ std::lock_guard<std::mutex> lock(running_flag_guard);
2253+ thread_count = running_flags.size();
2254+ shutting_down = true;
2255+ }
2256+ if (eventfd_write(event_semaphore, thread_count) < 0)
2257+ {
2258+ BOOST_THROW_EXCEPTION((std::system_error{errno,
2259+ std::system_category(),
2260+ "Failed to trigger thread shutdown"}));
2261+ }
2262+ }
2263+
2264+ void register_thread(bool& run_flag)
2265+ {
2266+ std::lock_guard<decltype(running_flag_guard)> lock{running_flag_guard};
2267+
2268+ running_flags[std::this_thread::get_id()] = &run_flag;
2269+ if (shutting_down)
2270+ {
2271+ if (eventfd_write(event_semaphore, 1) < 0)
2272+ {
2273+ BOOST_THROW_EXCEPTION((std::system_error{errno,
2274+ std::system_category(),
2275+ "Failed to trigger thread shutdown"}));
2276+ }
2277+ }
2278+ }
2279+
2280+ void unregister_thread()
2281+ {
2282+ {
2283+ std::lock_guard<decltype(terminating_thread_mutex)> lock{terminating_thread_mutex};
2284+ terminating_threads.push_back(std::this_thread::get_id());
2285+ }
2286+ thread_terminating.notify_one();
2287+ {
2288+ std::lock_guard<decltype(running_flag_guard)> lock{running_flag_guard};
2289+
2290+ if (running_flags.erase(std::this_thread::get_id()) != 1)
2291+ {
2292+ BOOST_THROW_EXCEPTION((std::logic_error{"Attempted to unregister a not-registered thread"}));
2293+ }
2294+ }
2295+ }
2296+
2297+private:
2298+ mir::Fd event_semaphore;
2299+
2300+ std::mutex terminating_thread_mutex;
2301+ std::condition_variable thread_terminating;
2302+ std::vector<std::thread::id> terminating_threads;
2303+
2304+ std::mutex running_flag_guard;
2305+ std::unordered_map<std::thread::id, bool*> running_flags;
2306+ bool shutting_down;
2307+};
2308+
2309+md::ThreadedDispatcher::ThreadedDispatcher(std::string const& name, std::shared_ptr<Dispatchable> const& dispatchee)
2310+ : ThreadedDispatcher(name, dispatchee, [](){ throw; })
2311+{
2312+}
2313+
2314+md::ThreadedDispatcher::ThreadedDispatcher(std::string const& name,
2315+ std::shared_ptr<md::Dispatchable> const& dispatchee,
2316+ std::function<void()> const& exception_handler)
2317+ : name_base{name},
2318+ thread_exiter{std::make_shared<ThreadShutdownRequestHandler>()},
2319+ dispatcher{std::make_shared<MultiplexingDispatchable>()},
2320+ exception_handler{exception_handler}
2321+{
2322+
2323+ // We rely on exactly one thread at a time getting a shutdown message
2324+ dispatcher->add_watch(thread_exiter, md::DispatchReentrancy::sequential);
2325+
2326+ // But our target dispatchable is welcome to be dispatched on as many threads
2327+ // as desired.
2328+ dispatcher->add_watch(dispatchee, md::DispatchReentrancy::reentrant);
2329+
2330+ threadpool.emplace_back(&dispatch_loop, name_base, thread_exiter, dispatcher, exception_handler);
2331+}
2332+
2333+md::ThreadedDispatcher::~ThreadedDispatcher() noexcept
2334+{
2335+ std::lock_guard<decltype(thread_pool_mutex)> lock{thread_pool_mutex};
2336+
2337+ thread_exiter->terminate_all_threads_async();
2338+
2339+ for (auto& thread : threadpool)
2340+ {
2341+ if (thread.get_id() == std::this_thread::get_id())
2342+ {
2343+ // We're being destroyed from within the dispatch callback
2344+ // Attempting to join the eventloop will result in a trivial deadlock.
2345+ //
2346+ // The std::thread destructor will call std::terminate() for us, let's
2347+ // leave a useful message.
2348+ mir::logging::log(mir::logging::Severity::critical,
2349+ "Destroying ThreadedDispatcher from within a dispatch callback. This is a programming error.",
2350+ "Dispatch");
2351+ }
2352+ else
2353+ {
2354+ thread.join();
2355+ }
2356+ }
2357+}
2358+
2359+void md::ThreadedDispatcher::add_thread()
2360+{
2361+ std::lock_guard<decltype(thread_pool_mutex)> lock{thread_pool_mutex};
2362+ threadpool.emplace_back(&dispatch_loop, name_base, thread_exiter, dispatcher, exception_handler);
2363+}
2364+
2365+void md::ThreadedDispatcher::remove_thread()
2366+{
2367+ auto terminated_thread_id = thread_exiter->terminate_one_thread();
2368+
2369+ // Find that thread in our vector, join() it, then remove it.
2370+ std::lock_guard<decltype(thread_pool_mutex)> threadpool_lock{thread_pool_mutex};
2371+
2372+ auto dying_thread = std::find_if(threadpool.begin(),
2373+ threadpool.end(),
2374+ [this, &terminated_thread_id](std::thread const& candidate)
2375+ {
2376+ return candidate.get_id() == terminated_thread_id;
2377+ });
2378+ dying_thread->join();
2379+ threadpool.erase(dying_thread);
2380+}
2381+
2382+void md::ThreadedDispatcher::dispatch_loop(std::string const& name,
2383+ std::shared_ptr<ThreadShutdownRequestHandler> thread_register,
2384+ std::shared_ptr<Dispatchable> dispatcher,
2385+ std::function<void()> const& exception_handler)
2386+{
2387+ sigset_t all_signals;
2388+ sigfillset(&all_signals);
2389+
2390+ if (auto error = pthread_sigmask(SIG_BLOCK, &all_signals, NULL))
2391+ BOOST_THROW_EXCEPTION((std::system_error{error,
2392+ std::system_category(),
2393+ "Failed to block signals on IO thread"}));
2394+
2395+ mir::set_thread_name(name);
2396+
2397+ // This does not have to be std::atomic<bool> because thread_register is guaranteed to
2398+ // only ever be dispatch()ed from one thread at a time.
2399+ bool running{true};
2400+
2401+ auto thread_registrar = mir::raii::paired_calls(
2402+ [&running, thread_register]()
2403+ {
2404+ thread_register->register_thread(running);
2405+ },
2406+ [thread_register]()
2407+ {
2408+ thread_register->unregister_thread();
2409+ });
2410+
2411+ try
2412+ {
2413+ struct pollfd waiter;
2414+ waiter.fd = dispatcher->watch_fd();
2415+ waiter.events = POLL_IN;
2416+ while (running)
2417+ {
2418+ if (poll(&waiter, 1, -1) < 0)
2419+ {
2420+ BOOST_THROW_EXCEPTION((std::system_error{errno,
2421+ std::system_category(),
2422+ "Failed to wait for event"}));
2423+ }
2424+ dispatcher->dispatch(md::FdEvent::readable);
2425+ }
2426+ }
2427+ catch(...)
2428+ {
2429+ exception_handler();
2430+ }
2431+}
2432
2433=== modified file 'src/common/symbols.map'
2434--- src/common/symbols.map 2015-04-15 05:07:08 +0000
2435+++ src/common/symbols.map 2015-05-14 07:50:26 +0000
2436@@ -24,8 +24,10 @@
2437 mir::dispatch::MultiplexingDispatchable::relevant_events*;
2438 mir::dispatch::MultiplexingDispatchable::remove_watch*;
2439 mir::dispatch::MultiplexingDispatchable::watch_fd*;
2440- mir::dispatch::SimpleDispatchThread::?SimpleDispatchThread*;
2441- mir::dispatch::SimpleDispatchThread::SimpleDispatchThread*;
2442+ mir::dispatch::ThreadedDispatcher::?ThreadedDispatcher*;
2443+ mir::dispatch::ThreadedDispatcher::ThreadedDispatcher*;
2444+ mir::dispatch::ThreadedDispatcher::add_thread*;
2445+ mir::dispatch::ThreadedDispatcher::remove_thread*;
2446 mir::Fd::Fd*;
2447 mir::Fd::invalid*;
2448 mir::Fd::operator*;
2449@@ -73,7 +75,7 @@
2450 typeinfo?for?mir::dispatch::ActionQueue;
2451 typeinfo?for?mir::dispatch::Dispatchable;
2452 typeinfo?for?mir::dispatch::MultiplexingDispatchable;
2453- typeinfo?for?mir::dispatch::SimpleDispatchThread;
2454+ typeinfo?for?mir::dispatch::ThreadedDispatcher;
2455 typeinfo?for?mir::Fd;
2456 typeinfo?for?mir::geometry::Displacement;
2457 typeinfo?for?mir::geometry::Length;
2458@@ -88,7 +90,7 @@
2459 vtable?for?mir::dispatch::ActionQueue;
2460 vtable?for?mir::dispatch::Dispatchable;
2461 vtable?for?mir::dispatch::MultiplexingDispatchable;
2462- vtable?for?mir::dispatch::SimpleDispatchThread;
2463+ vtable?for?mir::dispatch::ThreadedDispatcher;
2464 vtable?for?mir::Fd;
2465 vtable?for?mir::geometry::Displacement;
2466 vtable?for?mir::geometry::Length;
2467
2468=== modified file 'src/server/input/default_input_manager.cpp'
2469--- src/server/input/default_input_manager.cpp 2015-05-05 13:46:29 +0000
2470+++ src/server/input/default_input_manager.cpp 2015-05-14 07:50:26 +0000
2471@@ -22,7 +22,7 @@
2472 #include "mir/input/platform.h"
2473 #include "mir/dispatch/action_queue.h"
2474 #include "mir/dispatch/multiplexing_dispatchable.h"
2475-#include "mir/dispatch/simple_dispatch_thread.h"
2476+#include "mir/dispatch/threaded_dispatcher.h"
2477
2478 #include "mir/main_loop.h"
2479 #include "mir/thread_name.h"
2480@@ -86,7 +86,6 @@
2481
2482 queue->enqueue([this,weak_started_promise]()
2483 {
2484- mir::set_thread_name("Mir/InputReader");
2485 for (auto const& platform : platforms)
2486 {
2487 platform->start();
2488@@ -100,7 +99,8 @@
2489 started_promise->set_value();
2490 });
2491
2492- input_thread = std::make_unique<dispatch::SimpleDispatchThread>(
2493+ input_thread = std::make_unique<dispatch::ThreadedDispatcher>(
2494+ "Mir/InputReader",
2495 multiplexer,
2496 [this,weak_started_promise]()
2497 {
2498
2499=== modified file 'src/server/input/default_input_manager.h'
2500--- src/server/input/default_input_manager.h 2015-05-01 14:47:55 +0000
2501+++ src/server/input/default_input_manager.h 2015-05-14 07:50:26 +0000
2502@@ -37,7 +37,7 @@
2503 namespace dispatch
2504 {
2505 class MultiplexingDispatchable;
2506-class SimpleDispatchThread;
2507+class ThreadedDispatcher;
2508 class ActionQueue;
2509 }
2510 namespace input
2511@@ -65,7 +65,7 @@
2512 std::shared_ptr<dispatch::MultiplexingDispatchable> const multiplexer;
2513 std::shared_ptr<input::android::InputReaderDispatchable> const legacy_dispatchable;
2514 std::shared_ptr<dispatch::ActionQueue> const queue;
2515- std::unique_ptr<dispatch::SimpleDispatchThread> input_thread;
2516+ std::unique_ptr<dispatch::ThreadedDispatcher> input_thread;
2517
2518 enum class State
2519 {
2520
2521=== modified file 'tests/acceptance-tests/CMakeLists.txt'
2522--- tests/acceptance-tests/CMakeLists.txt 2015-05-05 06:46:30 +0000
2523+++ tests/acceptance-tests/CMakeLists.txt 2015-05-14 07:50:26 +0000
2524@@ -2,6 +2,7 @@
2525
2526 include_directories(
2527 ${CMAKE_SOURCE_DIR}
2528+ ${UMOCKDEV_INCLUDE_DIRS}
2529 )
2530
2531 set(
2532@@ -84,5 +85,5 @@
2533 OFF)
2534
2535 if (MIR_RUN_ACCEPTANCE_TESTS)
2536- mir_discover_tests(mir_acceptance_tests)
2537+ mir_discover_tests(mir_acceptance_tests LD_PRELOAD=libumockdev-preload.so.0 G_SLICE=always-malloc G_DEBUG=gc-friendly)
2538 endif (MIR_RUN_ACCEPTANCE_TESTS)
2539
2540=== modified file 'tests/acceptance-tests/test_client_library.cpp'
2541--- tests/acceptance-tests/test_client_library.cpp 2015-04-30 12:27:33 +0000
2542+++ tests/acceptance-tests/test_client_library.cpp 2015-05-14 07:50:26 +0000
2543@@ -24,6 +24,10 @@
2544 #include "mir_test_framework/using_stub_client_platform.h"
2545 #include "mir_test_framework/any_surface.h"
2546 #include "mir_test/validity_matchers.h"
2547+#include "mir_test/fd_utils.h"
2548+#include "mir_test_framework/udev_environment.h"
2549+#include "mir_test/signal.h"
2550+#include "mir_test/auto_unblock_thread.h"
2551
2552 #include "src/include/client/mir/client_buffer.h"
2553
2554@@ -37,6 +41,8 @@
2555 #include <system/window.h> // for ANativeWindowBuffer AKA MirNativeBuffer
2556 #endif
2557
2558+#include <boost/throw_exception.hpp>
2559+
2560 #include <gtest/gtest.h>
2561 #include <gmock/gmock.h>
2562 #include <chrono>
2563@@ -53,6 +59,8 @@
2564 namespace mc = mir::compositor;
2565 namespace mcl = mir::client;
2566 namespace mtf = mir_test_framework;
2567+namespace mt = mir::test;
2568+
2569 namespace
2570 {
2571 struct ClientLibrary : mtf::HeadlessInProcessServer
2572@@ -61,6 +69,12 @@
2573 MirConnection* connection = nullptr;
2574 MirSurface* surface = nullptr;
2575 int buffers = 0;
2576+ mtf::UdevEnvironment mock_devices;
2577+
2578+ ClientLibrary()
2579+ {
2580+ mock_devices.add_standard_device("laptop-keyboard");
2581+ }
2582
2583 static void connection_callback(MirConnection* connection, void* context)
2584 {
2585@@ -275,111 +289,6 @@
2586 mir_connection_release(connection);
2587 }
2588
2589-TEST_F(ClientLibrary, can_set_surface_min_width)
2590-{
2591- connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
2592-
2593- int const width = 640;
2594- int const height = 480;
2595- auto const format = mir_pixel_format_abgr_8888;
2596- auto const spec =
2597- mir_connection_create_spec_for_normal_surface(connection, width, height, format);
2598-
2599- int const min_width = 480;
2600- EXPECT_TRUE(mir_surface_spec_set_min_width(spec, min_width));
2601-
2602- surface = mir_surface_create_sync(spec);
2603- mir_surface_spec_release(spec);
2604- mir_surface_release_sync(surface);
2605- mir_connection_release(connection);
2606-}
2607-
2608-TEST_F(ClientLibrary, can_set_surface_min_height)
2609-{
2610- connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
2611-
2612- int const width = 640;
2613- int const height = 480;
2614- auto const format = mir_pixel_format_abgr_8888;
2615- auto const spec =
2616- mir_connection_create_spec_for_normal_surface(connection, width, height, format);
2617-
2618- int const min_height = 480;
2619- EXPECT_TRUE(mir_surface_spec_set_min_height(spec, min_height));
2620-
2621- surface = mir_surface_create_sync(spec);
2622- mir_surface_spec_release(spec);
2623- mir_surface_release_sync(surface);
2624- mir_connection_release(connection);
2625-}
2626-
2627-TEST_F(ClientLibrary, can_set_surface_max_width)
2628-{
2629- connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
2630-
2631- int const width = 640;
2632- int const height = 480;
2633- auto const format = mir_pixel_format_abgr_8888;
2634- auto const spec =
2635- mir_connection_create_spec_for_normal_surface(connection, width, height, format);
2636-
2637- int const max_width = 1024;
2638- EXPECT_TRUE(mir_surface_spec_set_max_width(spec, max_width));
2639-
2640- surface = mir_surface_create_sync(spec);
2641- mir_surface_spec_release(spec);
2642- mir_surface_release_sync(surface);
2643- mir_connection_release(connection);
2644-}
2645-
2646-TEST_F(ClientLibrary, can_set_surface_max_height)
2647-{
2648- connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
2649-
2650- int const width = 640;
2651- int const height = 480;
2652- auto const format = mir_pixel_format_abgr_8888;
2653- auto const spec =
2654- mir_connection_create_spec_for_normal_surface(connection, width, height, format);
2655-
2656- int const max_height = 1024;
2657- EXPECT_TRUE(mir_surface_spec_set_max_height(spec, max_height));
2658-
2659- surface = mir_surface_create_sync(spec);
2660- mir_surface_spec_release(spec);
2661- mir_surface_release_sync(surface);
2662- mir_connection_release(connection);
2663-}
2664-
2665-TEST_F(ClientLibrary, min_size_respected_when_placing_surface)
2666-{
2667- connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
2668-
2669- int const width = 6400;
2670- int const height = 4800;
2671- auto const format = mir_pixel_format_abgr_8888;
2672- auto const spec =
2673- mir_connection_create_spec_for_normal_surface(connection, width, height, format);
2674-
2675- int const min_width = 4800;
2676- int const min_height = 3200;
2677-
2678- mir_surface_spec_set_min_width(spec, min_width);
2679- mir_surface_spec_set_min_height(spec, min_height);
2680- surface = mir_surface_create_sync(spec);
2681- mir_surface_spec_release(spec);
2682-
2683- auto const buffer_stream = mir_surface_get_buffer_stream(surface);
2684-
2685- MirGraphicsRegion graphics_region;
2686- mir_buffer_stream_get_graphics_region(buffer_stream, &graphics_region);
2687- EXPECT_THAT(graphics_region.width, Ge(min_width));
2688- EXPECT_THAT(graphics_region.height, Ge(min_height));
2689-
2690- mir_surface_release_sync(surface);
2691- mir_connection_release(connection);
2692-}
2693-
2694 TEST_F(ClientLibrary, receives_surface_dpi_value)
2695 {
2696 connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
2697@@ -930,3 +839,320 @@
2698 mir_surface_release_sync(surface);
2699 mir_connection_release(connection);
2700 }
2701+
2702+namespace
2703+{
2704+struct ThreadTrackingCallbacks
2705+{
2706+ ThreadTrackingCallbacks()
2707+ : client_thread{pthread_self()}
2708+ {
2709+ }
2710+
2711+ static void connection_ready(MirConnection* /*connection*/, void* ctx)
2712+ {
2713+ auto data = reinterpret_cast<ThreadTrackingCallbacks*>(ctx);
2714+ EXPECT_EQ(pthread_self(), data->client_thread);
2715+ data->connection_ready_called.raise();
2716+ }
2717+
2718+ static void event_delegate(MirSurface* /*surf*/, MirEvent const* event, void* ctx)
2719+ {
2720+ auto data = reinterpret_cast<ThreadTrackingCallbacks*>(ctx);
2721+
2722+ EXPECT_THAT(pthread_self(), Eq(data->client_thread));
2723+ data->event_received.raise();
2724+ if (mir_event_get_type(event) == mir_event_type_input)
2725+ {
2726+ data->input_event_received.raise();
2727+ }
2728+ }
2729+
2730+ static void surface_created(MirSurface* surf, void* ctx)
2731+ {
2732+ auto data = reinterpret_cast<ThreadTrackingCallbacks*>(ctx);
2733+ EXPECT_THAT(pthread_self(), Eq(data->client_thread));
2734+ data->surf = surf;
2735+
2736+ mir_surface_set_event_handler(data->surf, &ThreadTrackingCallbacks::event_delegate, data);
2737+ }
2738+
2739+ static void swap_buffers_complete(MirBufferStream* /*stream*/, void* ctx)
2740+ {
2741+ auto data = reinterpret_cast<ThreadTrackingCallbacks*>(ctx);
2742+ EXPECT_EQ(pthread_self(), data->client_thread);
2743+ data->buffers_swapped.raise();
2744+ }
2745+
2746+ pthread_t const client_thread;
2747+ MirSurface* surf{nullptr};
2748+ mt::Signal buffers_swapped;
2749+ mt::Signal connection_ready_called;
2750+ mt::Signal event_received;
2751+ mt::Signal input_event_received;
2752+};
2753+
2754+void pump_eventloop_until(MirConnection* connection, std::function<bool()> predicate, std::chrono::steady_clock::time_point timeout)
2755+{
2756+ using namespace std::literals::chrono_literals;
2757+
2758+ auto fd = mir::Fd{mir::IntOwnedFd{mir_connection_get_event_fd(connection)}};
2759+
2760+ while (!predicate() && (std::chrono::steady_clock::now() < timeout))
2761+ {
2762+ if (mt::fd_becomes_readable(fd, 10ms))
2763+ {
2764+ mir_connection_dispatch(connection);
2765+ }
2766+ }
2767+ if (!predicate())
2768+ {
2769+ BOOST_THROW_EXCEPTION((std::runtime_error{"Timeout waiting for state change"}));
2770+ }
2771+}
2772+}
2773+
2774+TEST_F(ClientLibrary, manual_dispatch_handles_callbacks_in_parent_thread)
2775+{
2776+ using namespace std::literals::chrono_literals;
2777+
2778+ auto const test_timeout = std::chrono::steady_clock::now() + 10min;
2779+
2780+ ThreadTrackingCallbacks data;
2781+
2782+ auto connection = mir_connect_with_manual_dispatch(new_connection().c_str(), __PRETTY_FUNCTION__, &ThreadTrackingCallbacks::connection_ready, &data);
2783+
2784+ ASSERT_THAT(connection, Ne(nullptr));
2785+
2786+ pump_eventloop_until(
2787+ connection,
2788+ [&data]()
2789+ {
2790+ return data.connection_ready_called.raised();
2791+ },
2792+ test_timeout);
2793+
2794+ ASSERT_THAT(connection, IsValid());
2795+
2796+ auto surface_spec = mir_connection_create_spec_for_normal_surface(connection,
2797+ 233, 355,
2798+ mir_pixel_format_argb_8888);
2799+ auto surf_wh = mir_surface_create(surface_spec,
2800+ &ThreadTrackingCallbacks::surface_created,
2801+ &data);
2802+ mir_surface_spec_release(surface_spec);
2803+
2804+ pump_eventloop_until(
2805+ connection,
2806+ [surf_wh]()
2807+ {
2808+ return mir_wait_handle_ready(surf_wh);
2809+ },
2810+ test_timeout);
2811+
2812+ EXPECT_THAT(data.surf, IsValid());
2813+
2814+ auto buffer_stream = mir_surface_get_buffer_stream(data.surf);
2815+ auto swap_wh = mir_buffer_stream_swap_buffers(buffer_stream, ThreadTrackingCallbacks::swap_buffers_complete, &data);
2816+
2817+ pump_eventloop_until(
2818+ connection,
2819+ [swap_wh]()
2820+ {
2821+ return mir_wait_handle_ready(swap_wh);
2822+ },
2823+ test_timeout);
2824+ EXPECT_TRUE(data.buffers_swapped.raised());
2825+
2826+ mir_surface_release_sync(data.surf);
2827+ mir_connection_release(connection);
2828+}
2829+
2830+TEST_F(ClientLibrary, manual_dispatch_handles_events_in_parent_thread)
2831+{
2832+ using namespace testing;
2833+ using namespace std::literals::chrono_literals;
2834+
2835+ auto const test_timeout = std::chrono::steady_clock::now() + 10min;
2836+
2837+ ThreadTrackingCallbacks data;
2838+
2839+ connection = mir_connect_with_manual_dispatch(new_connection().c_str(), __PRETTY_FUNCTION__, &ThreadTrackingCallbacks::connection_ready, &data);
2840+
2841+ ASSERT_THAT(connection, Ne(nullptr));
2842+
2843+ pump_eventloop_until(
2844+ connection,
2845+ [&data]()
2846+ {
2847+ return data.connection_ready_called.raised();
2848+ },
2849+ test_timeout);
2850+
2851+ ASSERT_THAT(connection, IsValid());
2852+
2853+ auto surface_spec = mir_connection_create_spec_for_normal_surface(connection,
2854+ 233, 355,
2855+ mir_pixel_format_argb_8888);
2856+ auto surf_wh = mir_surface_create(surface_spec,
2857+ &ThreadTrackingCallbacks::surface_created,
2858+ &data);
2859+ mir_surface_spec_release(surface_spec);
2860+
2861+
2862+ pump_eventloop_until(
2863+ connection,
2864+ [surf_wh]()
2865+ {
2866+ return mir_wait_handle_ready(surf_wh);
2867+ },
2868+ test_timeout);
2869+
2870+ EXPECT_THAT(data.surf, IsValid());
2871+
2872+ // We need to swap buffers so that the surface is fully realised and
2873+ // will be a valid focus target.
2874+ //
2875+ // The shell will not focus a surface with no content.
2876+ auto buffer_stream = mir_surface_get_buffer_stream(data.surf);
2877+ mir_buffer_stream_swap_buffers_sync(buffer_stream);
2878+
2879+ mir_surface_set_state(data.surf, mir_surface_state_fullscreen);
2880+
2881+ pump_eventloop_until(
2882+ connection,
2883+ [&data]()
2884+ {
2885+ return data.event_received.raised();
2886+ },
2887+ test_timeout);
2888+
2889+ ASSERT_THAT(mir_surface_get_focus(data.surf), Eq(mir_surface_focused));
2890+
2891+ mock_devices.load_device_evemu("laptop-keyboard-hello");
2892+
2893+ pump_eventloop_until(
2894+ connection,
2895+ [&data]()
2896+ {
2897+ return data.input_event_received.raised();
2898+ },
2899+ test_timeout);
2900+
2901+ mir_surface_release_sync(data.surf);
2902+ mir_connection_release(connection);
2903+}
2904+
2905+namespace
2906+{
2907+struct SignalPair
2908+{
2909+ mir::test::Signal now_blocking;
2910+ mir::test::Signal event_received;
2911+};
2912+
2913+void notifying_event_handler(MirSurface*, MirEvent const* ev, void* ctx)
2914+{
2915+ auto signal_pair = *reinterpret_cast<std::shared_ptr<SignalPair>*>(ctx);
2916+ // We trigger an input event once we've noticed the surface callback is blocking
2917+ // so we need to only raise the flag on an input event; otherwise we may spuriously
2918+ // fail if we receive a surface event (like the focus event) before we hit
2919+ // the wait in blocking_surface_callback()
2920+ if (mir_event_get_type(ev) == mir_event_type_input)
2921+ {
2922+ signal_pair->event_received.raise();
2923+ }
2924+}
2925+
2926+void blocking_buffer_stream_callback(MirBufferStream*, void* ctx)
2927+{
2928+ auto signal_pair = *reinterpret_cast<std::shared_ptr<SignalPair>*>(ctx);
2929+ signal_pair->now_blocking.raise();
2930+ EXPECT_TRUE(signal_pair->event_received.wait_for(std::chrono::seconds{5}));
2931+}
2932+}
2933+
2934+TEST_F(ClientLibrary, rpc_blocking_doesnt_block_event_delivery_with_auto_dispatch)
2935+{
2936+ using namespace testing;
2937+
2938+ connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
2939+
2940+ ASSERT_THAT(connection, IsValid());
2941+
2942+ auto surface_spec = mir_connection_create_spec_for_normal_surface(connection,
2943+ 233, 355,
2944+ mir_pixel_format_argb_8888);
2945+
2946+ auto surf = mir_surface_create_sync(surface_spec);
2947+ mir_surface_spec_release(surface_spec);
2948+
2949+ EXPECT_THAT(surf, IsValid());
2950+
2951+ auto signal_pair = std::make_shared<SignalPair>();
2952+ mir_surface_set_event_handler(surf, &notifying_event_handler, &signal_pair);
2953+
2954+ auto buffer_stream = mir_surface_get_buffer_stream(surf);
2955+ auto wh = mir_buffer_stream_swap_buffers(buffer_stream, &blocking_buffer_stream_callback, &signal_pair);
2956+
2957+ EXPECT_TRUE(signal_pair->now_blocking.wait_for(std::chrono::seconds{5}));
2958+ EXPECT_FALSE(signal_pair->event_received.raised());
2959+
2960+ mock_devices.load_device_evemu("laptop-keyboard-hello");
2961+
2962+ EXPECT_TRUE(signal_pair->event_received.wait_for(std::chrono::seconds{5}));
2963+
2964+ mir_wait_for(wh);
2965+ mir_surface_release_sync(surf);
2966+ mir_connection_release(connection);
2967+}
2968+
2969+namespace
2970+{
2971+void async_release_completed(MirSurface*, void* ctx)
2972+{
2973+ auto called = reinterpret_cast<bool*>(ctx);
2974+ *called = true;
2975+}
2976+
2977+void async_creation_completed(MirSurface* surf, void* ctx)
2978+{
2979+ mir_surface_release(surf, &async_release_completed, ctx);
2980+}
2981+}
2982+
2983+TEST_F(ClientLibrary, sync_call_completes_before_previous_undispatched_call)
2984+{
2985+ using namespace std::literals::chrono_literals;
2986+ using namespace testing;
2987+
2988+ auto timeout = std::chrono::steady_clock::now() + 60s;
2989+
2990+ ThreadTrackingCallbacks data;
2991+
2992+ auto connection = mir_connect_with_manual_dispatch(new_connection().c_str(), __PRETTY_FUNCTION__, &ThreadTrackingCallbacks::connection_ready, &data);
2993+ ASSERT_THAT(connection, Ne(nullptr));
2994+
2995+ pump_eventloop_until(connection, [connection]() { return mir_connection_is_valid(connection); }, timeout);
2996+
2997+ bool async_call_completed{false};
2998+
2999+ auto surface_spec = mir_connection_create_spec_for_normal_surface(connection,
3000+ 233, 355,
3001+ mir_pixel_format_argb_8888);
3002+ mir_surface_create(surface_spec, &async_creation_completed, &async_call_completed);
3003+
3004+ EXPECT_FALSE(async_call_completed);
3005+ auto surf = mir_surface_create_sync(surface_spec);
3006+ mir_surface_spec_release(surface_spec);
3007+
3008+ EXPECT_THAT(surf, IsValid());
3009+ EXPECT_FALSE(async_call_completed);
3010+
3011+ mir_surface_release_sync(surf);
3012+ EXPECT_FALSE(async_call_completed);
3013+
3014+ pump_eventloop_until(connection, [&async_call_completed]() { return async_call_completed;}, timeout);
3015+
3016+ mir_connection_release(connection);
3017+}
3018
3019=== modified file 'tests/include/mir_test/test_protobuf_client.h'
3020--- tests/include/mir_test/test_protobuf_client.h 2015-03-31 02:35:42 +0000
3021+++ tests/include/mir_test/test_protobuf_client.h 2015-05-14 07:50:26 +0000
3022@@ -32,7 +32,7 @@
3023 {
3024 namespace dispatch
3025 {
3026-class SimpleDispatchThread;
3027+class ThreadedDispatcher;
3028 }
3029 namespace test
3030 {
3031@@ -46,7 +46,7 @@
3032
3033 std::shared_ptr<doubles::MockRpcReport> rpc_report;
3034 std::shared_ptr<google::protobuf::RpcChannel> channel;
3035- std::shared_ptr<dispatch::SimpleDispatchThread> eventloop;
3036+ std::shared_ptr<dispatch::ThreadedDispatcher> eventloop;
3037 mir::protobuf::DisplayServer::Stub display_server;
3038 mir::protobuf::ConnectParameters connect_parameters;
3039 mir::protobuf::SurfaceParameters surface_parameters;
3040
3041=== modified file 'tests/include/mir_test_doubles/mock_client_buffer_stream.h'
3042--- tests/include/mir_test_doubles/mock_client_buffer_stream.h 2015-04-01 20:00:19 +0000
3043+++ tests/include/mir_test_doubles/mock_client_buffer_stream.h 2015-05-14 07:50:26 +0000
3044@@ -45,6 +45,7 @@
3045 MOCK_METHOD0(get_create_wait_handle, MirWaitHandle*(void));
3046 MOCK_CONST_METHOD0(rpc_id, frontend::BufferStreamId(void));
3047 MOCK_CONST_METHOD0(valid, bool(void));
3048+ MOCK_CONST_METHOD0(get_connection, MirConnection*());
3049 };
3050
3051 }
3052
3053=== modified file 'tests/include/mir_test_doubles/mock_client_buffer_stream_factory.h'
3054--- tests/include/mir_test_doubles/mock_client_buffer_stream_factory.h 2015-04-21 17:22:09 +0000
3055+++ tests/include/mir_test_doubles/mock_client_buffer_stream_factory.h 2015-05-14 07:50:26 +0000
3056@@ -34,12 +34,22 @@
3057
3058 struct MockClientBufferStreamFactory : public client::ClientBufferStreamFactory
3059 {
3060- MOCK_METHOD3(make_consumer_stream, std::shared_ptr<client::ClientBufferStream>(protobuf::DisplayServer&,
3061- protobuf::BufferStream const&, std::string const&));
3062- MOCK_METHOD3(make_producer_stream, std::shared_ptr<client::ClientBufferStream>(protobuf::DisplayServer&,
3063- protobuf::BufferStream const&, std::string const&));
3064- MOCK_METHOD4(make_producer_stream, client::ClientBufferStream*(protobuf::DisplayServer&,
3065- protobuf::BufferStreamParameters const&, mir_buffer_stream_callback callback, void* context));
3066+ MOCK_METHOD4(make_consumer_stream, std::shared_ptr<client::ClientBufferStream>(
3067+ MirConnection*,
3068+ protobuf::DisplayServer&,
3069+ protobuf::BufferStream const&,
3070+ std::string const&));
3071+ MOCK_METHOD4(make_producer_stream, std::shared_ptr<client::ClientBufferStream>(
3072+ MirConnection*,
3073+ protobuf::DisplayServer&,
3074+ protobuf::BufferStream const&,
3075+ std::string const&));
3076+ MOCK_METHOD5(make_producer_stream, client::ClientBufferStream*(
3077+ MirConnection*,
3078+ protobuf::DisplayServer&,
3079+ protobuf::BufferStreamParameters const&,
3080+ mir_buffer_stream_callback callback,
3081+ void* context));
3082 };
3083
3084 }
3085
3086=== modified file 'tests/include/mir_test_doubles/stub_client_buffer_stream_factory.h'
3087--- tests/include/mir_test_doubles/stub_client_buffer_stream_factory.h 2015-04-01 20:00:19 +0000
3088+++ tests/include/mir_test_doubles/stub_client_buffer_stream_factory.h 2015-05-14 07:50:26 +0000
3089@@ -32,6 +32,7 @@
3090 struct StubClientBufferStreamFactory : public client::ClientBufferStreamFactory
3091 {
3092 std::shared_ptr<client::ClientBufferStream> make_consumer_stream(
3093+ MirConnection* /* allocating_connection */,
3094 protobuf::DisplayServer& /* server */,
3095 protobuf::BufferStream const& /* protobuf_bs */,
3096 std::string const& /* surface_name */) override
3097@@ -40,6 +41,7 @@
3098 }
3099
3100 std::shared_ptr<client::ClientBufferStream> make_producer_stream(
3101+ MirConnection* /* allocating_connection */,
3102 protobuf::DisplayServer& /* server */,
3103 protobuf::BufferStream const& /* protobuf_bs */,
3104 std::string const& /* surface_name */) override
3105@@ -47,7 +49,9 @@
3106 return nullptr;
3107 }
3108
3109- client::ClientBufferStream* make_producer_stream(protobuf::DisplayServer& /* server */,
3110+ client::ClientBufferStream* make_producer_stream(
3111+ MirConnection* /* allocating_connection */,
3112+ protobuf::DisplayServer& /* server */,
3113 protobuf::BufferStreamParameters const& /* params */,
3114 mir_buffer_stream_callback /* callback */, void* /* context */) override
3115 {
3116
3117=== modified file 'tests/include/mir_test_framework/udev_environment.h'
3118--- tests/include/mir_test_framework/udev_environment.h 2015-01-21 07:34:50 +0000
3119+++ tests/include/mir_test_framework/udev_environment.h 2015-05-14 07:50:26 +0000
3120@@ -53,6 +53,32 @@
3121 */
3122 void add_standard_device(std::string const& name);
3123
3124+ /**
3125+ * Load an ioctl recording for a UMockdev device
3126+ *
3127+ * Looks for a <tt>name</tt>.ioctl file recorded with umockdev-record --ioctl
3128+ * and adds it to the associated device in the testbed.
3129+ *
3130+ * The udev records for the device these ioctl records will be associated with
3131+ * must already exist in the testbed
3132+ *
3133+ * @param name The unadorned filename for the ioctl records to add
3134+ */
3135+ void load_device_ioctls(std::string const& name);
3136+
3137+ /**
3138+ * Load an evemu evdev recording for a UMockdev device
3139+ *
3140+ * Looks for a <tt>name</tt>.evemu file recorded with umockdev-record --evemu
3141+ * (or evemu-record) and associates it with the udev device it was recorded from.
3142+ *
3143+ * The udev records for the device this recording is associated with
3144+ * must already exist in the testbed
3145+ *
3146+ * @param name The unadorned filename for the ioctl records to add
3147+ */
3148+ void load_device_evemu(std::string const& name);
3149+
3150 UMockdevTestbed *testbed;
3151 std::string const recordings_path;
3152 };
3153
3154=== modified file 'tests/integration-tests/client/test_screencast.cpp'
3155--- tests/integration-tests/client/test_screencast.cpp 2015-03-31 02:35:42 +0000
3156+++ tests/integration-tests/client/test_screencast.cpp 2015-05-14 07:50:26 +0000
3157@@ -18,7 +18,8 @@
3158
3159 #include "mir_protobuf.pb.h"
3160 #include "src/client/default_connection_configuration.h"
3161-#include "mir/dispatch/simple_dispatch_thread.h"
3162+#include "src/client/rpc/mir_basic_rpc_channel.h"
3163+#include "mir/dispatch/threaded_dispatcher.h"
3164 #include "mir/dispatch/dispatchable.h"
3165
3166 #include "mir/frontend/connector.h"
3167@@ -78,16 +79,15 @@
3168 protobuf_server =
3169 std::make_shared<mir::protobuf::DisplayServer::Stub>(rpc_channel.get());
3170 eventloop =
3171- std::make_shared<md::SimpleDispatchThread>(
3172- std::dynamic_pointer_cast<md::Dispatchable>(rpc_channel));
3173+ std::make_shared<md::ThreadedDispatcher>("I/O loop", rpc_channel);
3174 }
3175
3176 char const* const test_socket = "./test_socket_screencast";
3177 std::shared_ptr<StubScreencastServerTool> const server_tool;
3178 std::shared_ptr<mt::TestProtobufServer> test_server;
3179- std::shared_ptr<google::protobuf::RpcChannel> rpc_channel;
3180+ std::shared_ptr<mir::client::rpc::MirBasicRpcChannel> rpc_channel;
3181 std::shared_ptr<mir::protobuf::DisplayServer> protobuf_server;
3182- std::shared_ptr<mir::dispatch::SimpleDispatchThread> eventloop;
3183+ std::shared_ptr<mir::dispatch::ThreadedDispatcher> eventloop;
3184 };
3185
3186 }
3187
3188=== modified file 'tests/mir_test_doubles/test_protobuf_client.cpp'
3189--- tests/mir_test_doubles/test_protobuf_client.cpp 2015-04-01 19:39:19 +0000
3190+++ tests/mir_test_doubles/test_protobuf_client.cpp 2015-05-14 07:50:26 +0000
3191@@ -26,7 +26,7 @@
3192 #include "src/client/rpc/make_rpc_channel.h"
3193 #include "src/client/rpc/mir_basic_rpc_channel.h"
3194 #include "mir/dispatch/dispatchable.h"
3195-#include "mir/dispatch/simple_dispatch_thread.h"
3196+#include "mir/dispatch/threaded_dispatcher.h"
3197 #include "mir/events/event_private.h"
3198
3199 #include <thread>
3200@@ -46,7 +46,7 @@
3201 rpc_report,
3202 std::make_shared<mir::client::LifecycleControl>(),
3203 std::make_shared<mtd::NullClientEventSink>())),
3204- eventloop{std::make_shared<md::SimpleDispatchThread>(std::dynamic_pointer_cast<md::Dispatchable>(channel))},
3205+ eventloop{std::make_shared<md::ThreadedDispatcher>("I/O loop", std::dynamic_pointer_cast<md::Dispatchable>(channel))},
3206 display_server(channel.get(), ::google::protobuf::Service::STUB_DOESNT_OWN_CHANNEL),
3207 maxwait(timeout_ms),
3208 connect_done_called(false),
3209
3210=== modified file 'tests/mir_test_framework/CMakeLists.txt'
3211--- tests/mir_test_framework/CMakeLists.txt 2015-04-30 11:36:36 +0000
3212+++ tests/mir_test_framework/CMakeLists.txt 2015-05-14 07:50:26 +0000
3213@@ -14,6 +14,7 @@
3214 add_definitions(
3215 -DMIR_CLIENT_PLATFORM_PATH="${MIR_CLIENT_PLATFORM_PATH}"
3216 -DMIR_SERVER_PLATFORM_PATH="${MIR_SERVER_PLATFORM_PATH}"
3217+ -DTEST_RECORDING_PATH="${CMAKE_INSTALL_PREFIX}/share/mir/udev_recordings"
3218 -DMIR_CLIENT_PLATFORM_ABI_STRING="${MIR_CLIENT_PLATFORM_ABI}"
3219 -DMIR_SERVER_GRAPHICS_PLATFORM_ABI_STRING="${MIR_SERVER_GRAPHICS_PLATFORM_ABI}"
3220 )
3221@@ -158,3 +159,5 @@
3222 install(TARGETS mirplatformgraphicsstub LIBRARY DESTINATION ${MIR_SERVER_PLATFORM_PATH})
3223
3224 install(TARGETS mirclientplatformstub LIBRARY DESTINATION ${MIR_CLIENT_PLATFORM_PATH})
3225+
3226+install(DIRECTORY udev_recordings DESTINATION ${CMAKE_INSTALL_PREFIX}/share/mir)
3227
3228=== modified file 'tests/mir_test_framework/udev_environment.cpp'
3229--- tests/mir_test_framework/udev_environment.cpp 2015-01-21 07:34:50 +0000
3230+++ tests/mir_test_framework/udev_environment.cpp 2015-05-14 07:50:26 +0000
3231@@ -37,9 +37,33 @@
3232
3233 namespace mtf = mir_test_framework;
3234
3235+namespace
3236+{
3237+std::string find_recording_path()
3238+{
3239+ std::initializer_list<std::string> candidates{mtf::executable_path() + "/udev_recordings", TEST_RECORDING_PATH};
3240+ for (auto candidate : candidates)
3241+ {
3242+ struct stat sb;
3243+ if (stat(candidate.c_str(), &sb) == 0)
3244+ {
3245+ if (S_ISDIR(sb.st_mode))
3246+ {
3247+ return candidate;
3248+ }
3249+ }
3250+ }
3251+ BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to find udev recordings directory"}));
3252+}
3253+}
3254+
3255 mtf::UdevEnvironment::UdevEnvironment()
3256- : recordings_path(mtf::executable_path() + "/udev_recordings")
3257+ : recordings_path{find_recording_path()}
3258 {
3259+ if (!umockdev_in_mock_environment())
3260+ {
3261+ BOOST_THROW_EXCEPTION((std::runtime_error{"Test not running under umockdev!"}));
3262+ }
3263 testbed = umockdev_testbed_new();
3264 }
3265
3266@@ -109,4 +133,45 @@
3267 }
3268 }
3269 }
3270+
3271+ auto script_filename = recordings_path + "/" + name + ".script";
3272+ if (stat(script_filename.c_str(), &sb) == 0)
3273+ {
3274+ if (S_ISREG(sb.st_mode) || S_ISLNK(sb.st_mode))
3275+ {
3276+ if (!umockdev_testbed_load_script(testbed, NULL, script_filename.c_str(), &err))
3277+ {
3278+ BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Failed to load device recording: ") +
3279+ err->message));
3280+ }
3281+ }
3282+ }
3283+}
3284+
3285+void mtf::UdevEnvironment::load_device_ioctls(std::string const& name)
3286+{
3287+ auto ioctls_filename = recordings_path + "/" + name + ".ioctl";
3288+
3289+ GError* err = nullptr;
3290+ if (!umockdev_testbed_load_ioctl(testbed, NULL, ioctls_filename.c_str(), &err))
3291+ {
3292+ std::runtime_error exception{std::string("Failed to load ioctl recording: ") +
3293+ err->message};
3294+ g_error_free(err);
3295+ BOOST_THROW_EXCEPTION(exception);
3296+ }
3297+}
3298+
3299+void mtf::UdevEnvironment::load_device_evemu(std::string const& name)
3300+{
3301+ auto evemu_filename = recordings_path + "/" + name + ".evemu";
3302+
3303+ GError* err = nullptr;
3304+ if (!umockdev_testbed_load_evemu_events(testbed, NULL, evemu_filename.c_str(), &err))
3305+ {
3306+ std::runtime_error exception{std::string{"Failed to load evemu recording: "} +
3307+ err->message};
3308+ g_error_free(err);
3309+ BOOST_THROW_EXCEPTION(exception);
3310+ }
3311 }
3312
3313=== added file 'tests/mir_test_framework/udev_recordings/laptop-keyboard-hello.evemu'
3314--- tests/mir_test_framework/udev_recordings/laptop-keyboard-hello.evemu 1970-01-01 00:00:00 +0000
3315+++ tests/mir_test_framework/udev_recordings/laptop-keyboard-hello.evemu 2015-05-14 07:50:26 +0000
3316@@ -0,0 +1,272 @@
3317+# device /dev/input/event4
3318+# EVEMU 1.2
3319+# Input device name: "AT Translated Set 2 keyboard"
3320+# Input device ID: bus 0x11 vendor 0x01 product 0x01 version 0xab83
3321+# Supported events:
3322+# Event type 0 (EV_SYN)
3323+# Event code 0 (SYN_REPORT)
3324+# Event code 1 (SYN_CONFIG)
3325+# Event code 4 (FF_STATUS_STOPPED)
3326+# Event code 17 ((null))
3327+# Event code 20 ((null))
3328+# Event type 1 (EV_KEY)
3329+# Event code 1 (KEY_ESC)
3330+# Event code 2 (KEY_1)
3331+# Event code 3 (KEY_2)
3332+# Event code 4 (KEY_3)
3333+# Event code 5 (KEY_4)
3334+# Event code 6 (KEY_5)
3335+# Event code 7 (KEY_6)
3336+# Event code 8 (KEY_7)
3337+# Event code 9 (KEY_8)
3338+# Event code 10 (KEY_9)
3339+# Event code 11 (KEY_0)
3340+# Event code 12 (KEY_MINUS)
3341+# Event code 13 (KEY_EQUAL)
3342+# Event code 14 (KEY_BACKSPACE)
3343+# Event code 15 (KEY_TAB)
3344+# Event code 16 (KEY_Q)
3345+# Event code 17 (KEY_W)
3346+# Event code 18 (KEY_E)
3347+# Event code 19 (KEY_R)
3348+# Event code 20 (KEY_T)
3349+# Event code 21 (KEY_Y)
3350+# Event code 22 (KEY_U)
3351+# Event code 23 (KEY_I)
3352+# Event code 24 (KEY_O)
3353+# Event code 25 (KEY_P)
3354+# Event code 26 (KEY_LEFTBRACE)
3355+# Event code 27 (KEY_RIGHTBRACE)
3356+# Event code 28 (KEY_ENTER)
3357+# Event code 29 (KEY_LEFTCTRL)
3358+# Event code 30 (KEY_A)
3359+# Event code 31 (KEY_S)
3360+# Event code 32 (KEY_D)
3361+# Event code 33 (KEY_F)
3362+# Event code 34 (KEY_G)
3363+# Event code 35 (KEY_H)
3364+# Event code 36 (KEY_J)
3365+# Event code 37 (KEY_K)
3366+# Event code 38 (KEY_L)
3367+# Event code 39 (KEY_SEMICOLON)
3368+# Event code 40 (KEY_APOSTROPHE)
3369+# Event code 41 (KEY_GRAVE)
3370+# Event code 42 (KEY_LEFTSHIFT)
3371+# Event code 43 (KEY_BACKSLASH)
3372+# Event code 44 (KEY_Z)
3373+# Event code 45 (KEY_X)
3374+# Event code 46 (KEY_C)
3375+# Event code 47 (KEY_V)
3376+# Event code 48 (KEY_B)
3377+# Event code 49 (KEY_N)
3378+# Event code 50 (KEY_M)
3379+# Event code 51 (KEY_COMMA)
3380+# Event code 52 (KEY_DOT)
3381+# Event code 53 (KEY_SLASH)
3382+# Event code 54 (KEY_RIGHTSHIFT)
3383+# Event code 55 (KEY_KPASTERISK)
3384+# Event code 56 (KEY_LEFTALT)
3385+# Event code 57 (KEY_SPACE)
3386+# Event code 58 (KEY_CAPSLOCK)
3387+# Event code 59 (KEY_F1)
3388+# Event code 60 (KEY_F2)
3389+# Event code 61 (KEY_F3)
3390+# Event code 62 (KEY_F4)
3391+# Event code 63 (KEY_F5)
3392+# Event code 64 (KEY_F6)
3393+# Event code 65 (KEY_F7)
3394+# Event code 66 (KEY_F8)
3395+# Event code 67 (KEY_F9)
3396+# Event code 68 (KEY_F10)
3397+# Event code 69 (KEY_NUMLOCK)
3398+# Event code 70 (KEY_SCROLLLOCK)
3399+# Event code 71 (KEY_KP7)
3400+# Event code 72 (KEY_KP8)
3401+# Event code 73 (KEY_KP9)
3402+# Event code 74 (KEY_KPMINUS)
3403+# Event code 75 (KEY_KP4)
3404+# Event code 76 (KEY_KP5)
3405+# Event code 77 (KEY_KP6)
3406+# Event code 78 (KEY_KPPLUS)
3407+# Event code 79 (KEY_KP1)
3408+# Event code 80 (KEY_KP2)
3409+# Event code 81 (KEY_KP3)
3410+# Event code 82 (KEY_KP0)
3411+# Event code 83 (KEY_KPDOT)
3412+# Event code 85 (KEY_ZENKAKUHANKAKU)
3413+# Event code 86 (KEY_102ND)
3414+# Event code 87 (KEY_F11)
3415+# Event code 88 (KEY_F12)
3416+# Event code 89 (KEY_RO)
3417+# Event code 90 (KEY_KATAKANA)
3418+# Event code 91 (KEY_HIRAGANA)
3419+# Event code 92 (KEY_HENKAN)
3420+# Event code 93 (KEY_KATAKANAHIRAGANA)
3421+# Event code 94 (KEY_MUHENKAN)
3422+# Event code 95 (KEY_KPJPCOMMA)
3423+# Event code 96 (KEY_KPENTER)
3424+# Event code 97 (KEY_RIGHTCTRL)
3425+# Event code 98 (KEY_KPSLASH)
3426+# Event code 99 (KEY_SYSRQ)
3427+# Event code 100 (KEY_RIGHTALT)
3428+# Event code 102 (KEY_HOME)
3429+# Event code 103 (KEY_UP)
3430+# Event code 104 (KEY_PAGEUP)
3431+# Event code 105 (KEY_LEFT)
3432+# Event code 106 (KEY_RIGHT)
3433+# Event code 107 (KEY_END)
3434+# Event code 108 (KEY_DOWN)
3435+# Event code 109 (KEY_PAGEDOWN)
3436+# Event code 110 (KEY_INSERT)
3437+# Event code 111 (KEY_DELETE)
3438+# Event code 112 (KEY_MACRO)
3439+# Event code 113 (KEY_MUTE)
3440+# Event code 114 (KEY_VOLUMEDOWN)
3441+# Event code 115 (KEY_VOLUMEUP)
3442+# Event code 116 (KEY_POWER)
3443+# Event code 117 (KEY_KPEQUAL)
3444+# Event code 118 (KEY_KPPLUSMINUS)
3445+# Event code 119 (KEY_PAUSE)
3446+# Event code 121 (KEY_KPCOMMA)
3447+# Event code 122 (KEY_HANGEUL)
3448+# Event code 123 (KEY_HANJA)
3449+# Event code 124 (KEY_YEN)
3450+# Event code 125 (KEY_LEFTMETA)
3451+# Event code 126 (KEY_RIGHTMETA)
3452+# Event code 127 (KEY_COMPOSE)
3453+# Event code 128 (KEY_STOP)
3454+# Event code 140 (KEY_CALC)
3455+# Event code 142 (KEY_SLEEP)
3456+# Event code 143 (KEY_WAKEUP)
3457+# Event code 155 (KEY_MAIL)
3458+# Event code 156 (KEY_BOOKMARKS)
3459+# Event code 157 (KEY_COMPUTER)
3460+# Event code 158 (KEY_BACK)
3461+# Event code 159 (KEY_FORWARD)
3462+# Event code 163 (KEY_NEXTSONG)
3463+# Event code 164 (KEY_PLAYPAUSE)
3464+# Event code 165 (KEY_PREVIOUSSONG)
3465+# Event code 166 (KEY_STOPCD)
3466+# Event code 172 (KEY_HOMEPAGE)
3467+# Event code 173 (KEY_REFRESH)
3468+# Event code 183 (KEY_F13)
3469+# Event code 184 (KEY_F14)
3470+# Event code 185 (KEY_F15)
3471+# Event code 217 (KEY_SEARCH)
3472+# Event code 226 (KEY_MEDIA)
3473+# Event type 4 (EV_MSC)
3474+# Event code 4 (MSC_SCAN)
3475+# Event type 17 (EV_LED)
3476+# Event code 0 (LED_NUML)
3477+# Event code 1 (LED_CAPSL)
3478+# Event code 2 (LED_SCROLLL)
3479+# Event type 20 (EV_REP)
3480+# Properties:
3481+# N: AT Translated Set 2 keyboard
3482+# I: 0011 0001 0001 ab83
3483+# P: 00 00 00 00 00 00 00 00
3484+# B: 00 13 00 12 00 00 00 00 00
3485+# B: 01 fe ff ff ff ff ff ff ff
3486+# B: 01 ff ff ef ff df ff ff fe
3487+# B: 01 01 d0 00 f8 78 30 80 03
3488+# B: 01 00 00 00 02 04 00 00 00
3489+# B: 01 00 00 00 00 00 00 00 00
3490+# B: 01 00 00 00 00 00 00 00 00
3491+# B: 01 00 00 00 00 00 00 00 00
3492+# B: 01 00 00 00 00 00 00 00 00
3493+# B: 01 00 00 00 00 00 00 00 00
3494+# B: 01 00 00 00 00 00 00 00 00
3495+# B: 01 00 00 00 00 00 00 00 00
3496+# B: 01 00 00 00 00 00 00 00 00
3497+# B: 02 00 00 00 00 00 00 00 00
3498+# B: 03 00 00 00 00 00 00 00 00
3499+# B: 04 10 00 00 00 00 00 00 00
3500+# B: 05 00 00 00 00 00 00 00 00
3501+# B: 11 07 00 00 00 00 00 00 00
3502+# B: 12 00 00 00 00 00 00 00 00
3503+# B: 15 00 00 00 00 00 00 00 00
3504+# B: 15 00 00 00 00 00 00 00 00
3505+################################
3506+# Waiting for events #
3507+################################
3508+E: 0.000000 0004 0004 0028 # EV_MSC / MSC_SCAN 28
3509+E: 0.000000 0001 001c 0000 # EV_KEY / KEY_ENTER 0
3510+E: 0.000000 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3511+E: 0.825945 0004 0004 0054 # EV_MSC / MSC_SCAN 54
3512+E: 0.825945 0001 0036 0001 # EV_KEY / KEY_RIGHTSHIFT 1
3513+E: 0.825945 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3514+E: 1.101404 0004 0004 0054 # EV_MSC / MSC_SCAN 54
3515+E: 1.101404 0001 0036 0002 # EV_KEY / KEY_RIGHTSHIFT 2
3516+E: 1.101404 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3517+E: 1.131449 0004 0004 0054 # EV_MSC / MSC_SCAN 54
3518+E: 1.131449 0001 0036 0002 # EV_KEY / KEY_RIGHTSHIFT 2
3519+E: 1.131449 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3520+E: 1.161472 0004 0004 0036 # EV_MSC / MSC_SCAN 36
3521+E: 1.161472 0001 0024 0001 # EV_KEY / KEY_J 1
3522+E: 1.161472 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3523+E: 1.221981 0004 0004 0036 # EV_MSC / MSC_SCAN 36
3524+E: 1.221981 0001 0024 0000 # EV_KEY / KEY_J 0
3525+E: 1.221981 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3526+E: 1.241906 0004 0004 0054 # EV_MSC / MSC_SCAN 54
3527+E: 1.241906 0001 0036 0000 # EV_KEY / KEY_RIGHTSHIFT 0
3528+E: 1.241906 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3529+E: 1.346773 0004 0004 0032 # EV_MSC / MSC_SCAN 32
3530+E: 1.346773 0001 0020 0001 # EV_KEY / KEY_D 1
3531+E: 1.346773 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3532+E: 1.432213 0004 0004 0032 # EV_MSC / MSC_SCAN 32
3533+E: 1.432213 0001 0020 0000 # EV_KEY / KEY_D 0
3534+E: 1.432213 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3535+E: 1.562060 0004 0004 0025 # EV_MSC / MSC_SCAN 25
3536+E: 1.562060 0001 0019 0001 # EV_KEY / KEY_P 1
3537+E: 1.562060 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3538+E: 1.622477 0004 0004 0025 # EV_MSC / MSC_SCAN 25
3539+E: 1.622477 0001 0019 0000 # EV_KEY / KEY_P 0
3540+E: 1.622477 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3541+E: 1.687305 0004 0004 0025 # EV_MSC / MSC_SCAN 25
3542+E: 1.687305 0001 0019 0001 # EV_KEY / KEY_P 1
3543+E: 1.687305 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3544+E: 1.777807 0004 0004 0025 # EV_MSC / MSC_SCAN 25
3545+E: 1.777807 0001 0019 0000 # EV_KEY / KEY_P 0
3546+E: 1.777807 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3547+E: 1.867577 0004 0004 0031 # EV_MSC / MSC_SCAN 31
3548+E: 1.867577 0001 001f 0001 # EV_KEY / KEY_S 1
3549+E: 1.867577 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3550+E: 1.953019 0004 0004 0031 # EV_MSC / MSC_SCAN 31
3551+E: 1.953019 0001 001f 0000 # EV_KEY / KEY_S 0
3552+E: 1.953019 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3553+E: 2.118004 0004 0004 0054 # EV_MSC / MSC_SCAN 54
3554+E: 2.118004 0001 0036 0001 # EV_KEY / KEY_RIGHTSHIFT 1
3555+E: 2.118004 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3556+E: 2.348316 0004 0004 0002 # EV_MSC / MSC_SCAN 2
3557+E: 2.348316 0001 0002 0001 # EV_KEY / KEY_1 1
3558+E: 2.348316 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3559+E: 2.489561 0004 0004 0002 # EV_MSC / MSC_SCAN 2
3560+E: 2.489561 0001 0002 0000 # EV_KEY / KEY_1 0
3561+E: 2.489561 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3562+E: 2.523740 0004 0004 0054 # EV_MSC / MSC_SCAN 54
3563+E: 2.523740 0001 0036 0000 # EV_KEY / KEY_RIGHTSHIFT 0
3564+E: 2.523740 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3565+E: 6.714286 0004 0004 0029 # EV_MSC / MSC_SCAN 29
3566+E: 6.714286 0001 001d 0001 # EV_KEY / KEY_LEFTCTRL 1
3567+E: 6.714286 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3568+E: 6.989728 0004 0004 0029 # EV_MSC / MSC_SCAN 29
3569+E: 6.989728 0001 001d 0002 # EV_KEY / KEY_LEFTCTRL 2
3570+E: 6.989728 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3571+E: 7.019767 0004 0004 0029 # EV_MSC / MSC_SCAN 29
3572+E: 7.019767 0001 001d 0002 # EV_KEY / KEY_LEFTCTRL 2
3573+E: 7.019767 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3574+E: 7.049847 0004 0004 0029 # EV_MSC / MSC_SCAN 29
3575+E: 7.049847 0001 001d 0002 # EV_KEY / KEY_LEFTCTRL 2
3576+E: 7.049847 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3577+E: 7.079889 0004 0004 0029 # EV_MSC / MSC_SCAN 29
3578+E: 7.079889 0001 001d 0002 # EV_KEY / KEY_LEFTCTRL 2
3579+E: 7.079889 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3580+E: 7.109934 0004 0004 0029 # EV_MSC / MSC_SCAN 29
3581+E: 7.109934 0001 001d 0002 # EV_KEY / KEY_LEFTCTRL 2
3582+E: 7.109934 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3583+E: 7.139943 0004 0004 0029 # EV_MSC / MSC_SCAN 29
3584+E: 7.139943 0001 001d 0002 # EV_KEY / KEY_LEFTCTRL 2
3585+E: 7.139943 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3586+E: 7.170005 0004 0004 0023 # EV_MSC / MSC_SCAN 23
3587+E: 7.170005 0001 0017 0001 # EV_KEY / KEY_I 1
3588+E: 7.170005 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
3589
3590=== modified file 'tests/unit-tests/client/test_client_buffer_stream.cpp'
3591--- tests/unit-tests/client/test_client_buffer_stream.cpp 2015-04-16 19:56:08 +0000
3592+++ tests/unit-tests/client/test_client_buffer_stream.cpp 2015-05-14 07:50:26 +0000
3593@@ -132,7 +132,7 @@
3594 mcl::ClientBufferFactory& buffer_factory,
3595 mcl::BufferStreamMode mode=mcl::BufferStreamMode::Producer)
3596 {
3597- return std::make_shared<mcl::BufferStream>(mock_protobuf_server, mode,
3598+ return std::make_shared<mcl::BufferStream>(nullptr, mock_protobuf_server, mode,
3599 std::make_shared<StubClientPlatform>(mt::fake_shared(buffer_factory)), protobuf_bs, perf_report, "");
3600 }
3601 };
3602@@ -448,7 +448,7 @@
3603
3604 EXPECT_CALL(mock_perf_report, name_surface(StrEq(name))).Times(1);
3605
3606- auto bs = std::make_shared<mcl::BufferStream>(mock_protobuf_server, mcl::BufferStreamMode::Producer,
3607+ auto bs = std::make_shared<mcl::BufferStream>(nullptr, mock_protobuf_server, mcl::BufferStreamMode::Producer,
3608 std::make_shared<StubClientPlatform>(mt::fake_shared(stub_client_buffer_factory)),
3609 protobuf_bs, mt::fake_shared(mock_perf_report), name);
3610 }
3611
3612=== modified file 'tests/unit-tests/client/test_client_mir_surface.cpp'
3613--- tests/unit-tests/client/test_client_mir_surface.cpp 2015-04-16 20:03:39 +0000
3614+++ tests/unit-tests/client/test_client_mir_surface.cpp 2015-05-14 07:50:26 +0000
3615@@ -30,8 +30,8 @@
3616 #include "src/client/mir_connection.h"
3617 #include "src/client/default_connection_configuration.h"
3618 #include "src/client/rpc/null_rpc_report.h"
3619-#include "mir/dispatch/simple_dispatch_thread.h"
3620 #include "mir/dispatch/dispatchable.h"
3621+#include "mir/dispatch/threaded_dispatcher.h"
3622
3623 #include "mir/frontend/connector.h"
3624 #include "mir/input/input_platform.h"
3625
3626=== modified file 'tests/unit-tests/client/test_mir_connection.cpp'
3627--- tests/unit-tests/client/test_mir_connection.cpp 2015-03-31 02:35:42 +0000
3628+++ tests/unit-tests/client/test_mir_connection.cpp 2015-05-14 07:50:26 +0000
3629@@ -34,6 +34,10 @@
3630 #include "mir_test/test_protobuf_server.h"
3631 #include "mir_test/stub_server_tool.h"
3632 #include "mir_test_doubles/stub_client_buffer_factory.h"
3633+#include "mir_test/pipe.h"
3634+#include "mir_test/signal.h"
3635+#include "mir_test/fd_utils.h"
3636+#include "mir_test/test_dispatchable.h"
3637
3638 #include "mir_protobuf.pb.h"
3639
3640@@ -49,16 +53,19 @@
3641 namespace md = mir::dispatch;
3642 namespace geom = mir::geometry;
3643 namespace mtd = mir::test::doubles;
3644+namespace md = mir::dispatch;
3645+namespace mt = mir::test;
3646
3647 namespace
3648 {
3649
3650-struct MockRpcChannel : public mir::client::rpc::MirBasicRpcChannel,
3651- public mir::dispatch::Dispatchable
3652+struct MockRpcChannel : public mir::client::rpc::MirBasicRpcChannel
3653 {
3654 MockRpcChannel()
3655 {
3656- ON_CALL(*this, watch_fd()).WillByDefault(testing::Return(mir::Fd{}));
3657+ using namespace testing;
3658+ ON_CALL(*this, watch_fd()).WillByDefault(Return(fake_epoll.read_fd()));
3659+ ON_CALL(*this, relevant_events()).WillByDefault(Return(md::FdEvent::readable));
3660 }
3661
3662 void CallMethod(const google::protobuf::MethodDescriptor* method,
3663@@ -86,14 +93,35 @@
3664 complete->Run();
3665 }
3666
3667+ // MirBasicRpcChannel
3668+ MOCK_METHOD0(process_next_request_first,void());
3669+
3670+ // Dispatchable
3671+ MOCK_CONST_METHOD0(watch_fd, mir::Fd());
3672+ MOCK_METHOD1(dispatch, bool(md::FdEvents));
3673+ MOCK_CONST_METHOD0(relevant_events, md::FdEvents());
3674+
3675 MOCK_METHOD2(connect, void(mp::ConnectParameters const*,mp::Connection*));
3676 MOCK_METHOD1(configure_display_sent, void(mp::DisplayConfiguration const*));
3677 MOCK_METHOD2(platform_operation,
3678 void(mp::PlatformOperationMessage const*, mp::PlatformOperationMessage*));
3679
3680- MOCK_CONST_METHOD0(watch_fd, mir::Fd());
3681- MOCK_METHOD1(dispatch, bool(md::FdEvents));
3682- MOCK_CONST_METHOD0(relevant_events, md::FdEvents());
3683+ void trigger()
3684+ {
3685+ using namespace testing;
3686+ int dummy{0};
3687+ EXPECT_THAT(write(fake_epoll.write_fd(), &dummy, sizeof(dummy)), Eq(sizeof(dummy)));
3688+ }
3689+
3690+ void untrigger()
3691+ {
3692+ using namespace testing;
3693+ int dummy{0};
3694+ EXPECT_THAT(read(fake_epoll.read_fd(), &dummy, sizeof(dummy)), Eq(sizeof(dummy)));
3695+ }
3696+
3697+private:
3698+ mt::Pipe fake_epoll;
3699 };
3700
3701 struct MockClientPlatform : public mcl::ClientPlatform
3702@@ -167,7 +195,7 @@
3703 {
3704 }
3705
3706- std::shared_ptr<::google::protobuf::RpcChannel> the_rpc_channel() override
3707+ std::shared_ptr<mcl::rpc::MirBasicRpcChannel> the_rpc_channel() override
3708 {
3709 return channel;
3710 }
3711@@ -194,22 +222,21 @@
3712 MirConnectionTest()
3713 : mock_platform{std::make_shared<testing::NiceMock<MockClientPlatform>>()},
3714 mock_channel{std::make_shared<testing::NiceMock<MockRpcChannel>>()},
3715- conf{mock_platform, mock_channel},
3716- connection{std::make_shared<MirConnection>(conf)}
3717+ conf{mock_platform, mock_channel}
3718 {
3719- mock_platform->set_client_context(connection.get());
3720 }
3721
3722 std::shared_ptr<testing::NiceMock<MockClientPlatform>> const mock_platform;
3723 std::shared_ptr<testing::NiceMock<MockRpcChannel>> const mock_channel;
3724 TestConnectionConfiguration conf;
3725- std::shared_ptr<MirConnection> const connection;
3726 };
3727
3728 TEST_F(MirConnectionTest, returns_correct_egl_native_display)
3729 {
3730 using namespace testing;
3731
3732+ auto connection = std::make_shared<MirConnection>(conf);
3733+
3734 EGLNativeDisplayType native_display_raw = reinterpret_cast<EGLNativeDisplayType>(0xabcdef);
3735 auto native_display = std::make_shared<EGLNativeDisplayType>();
3736 *native_display = native_display_raw;
3737@@ -278,6 +305,8 @@
3738 {
3739 using namespace testing;
3740
3741+ auto connection = std::make_shared<MirConnection>(conf);
3742+
3743 EXPECT_CALL(*mock_channel, connect(_,_))
3744 .WillOnce(Invoke(fill_display_configuration));
3745
3746@@ -317,6 +346,8 @@
3747 {
3748 using namespace testing;
3749
3750+ auto connection = std::make_shared<MirConnection>(conf);
3751+
3752 EXPECT_CALL(*mock_channel, connect(_,_))
3753 .WillOnce(Invoke(fill_display_configuration));
3754
3755@@ -362,6 +393,8 @@
3756 {
3757 using namespace testing;
3758
3759+ auto connection = std::make_shared<MirConnection>(conf);
3760+
3761 EXPECT_CALL(*mock_channel, connect(_,_))
3762 .WillOnce(Invoke(fill_display_configuration));
3763
3764@@ -383,6 +416,8 @@
3765 {
3766 using namespace testing;
3767
3768+ auto connection = std::make_shared<MirConnection>(conf);
3769+
3770 EXPECT_CALL(*mock_channel, connect(_,_))
3771 .WillOnce(Invoke(fill_display_configuration));
3772
3773@@ -420,6 +455,8 @@
3774 {
3775 using namespace testing;
3776
3777+ auto connection = std::make_shared<MirConnection>(conf);
3778+
3779 EXPECT_CALL(*mock_channel, connect(_,_))
3780 .WillOnce(Invoke(fill_surface_pixel_formats));
3781 MirWaitHandle* wait_handle = connection->connect("MirClientSurfaceTest",
3782@@ -443,6 +480,8 @@
3783 {
3784 using namespace testing;
3785
3786+ auto connection = std::make_shared<MirConnection>(conf);
3787+
3788 EXPECT_CALL(*mock_channel, connect(_,_))
3789 .WillOnce(Invoke(fill_display_configuration));
3790
3791@@ -509,6 +548,8 @@
3792 {
3793 using namespace testing;
3794
3795+ auto connection = std::make_shared<MirConnection>(conf);
3796+
3797 MirSurfaceSpec params{nullptr, 640, 480, mir_pixel_format_abgr_8888};
3798 params.surface_name = __PRETTY_FUNCTION__;
3799
3800@@ -537,6 +578,8 @@
3801 {
3802 using namespace testing;
3803
3804+ auto connection = std::make_shared<MirConnection>(conf);
3805+
3806 MirSurfaceSpec params{nullptr, 640, 480, mir_pixel_format_abgr_8888};
3807 params.surface_name = __PRETTY_FUNCTION__;
3808
3809@@ -588,6 +631,8 @@
3810 mir_platform_message_create(opcode),
3811 &mir_platform_message_release);
3812
3813+ auto connection = std::make_shared<MirConnection>(conf);
3814+
3815 EXPECT_CALL(*mock_platform, platform_operation(request.get()))
3816 .WillOnce(Return(response.get()));
3817 EXPECT_CALL(*mock_channel, platform_operation(_,_))
3818@@ -620,6 +665,8 @@
3819 EXPECT_CALL(*mock_channel, platform_operation(_,_))
3820 .WillOnce(CopyRequestToResponse());
3821
3822+ auto connection = std::make_shared<MirConnection>(conf);
3823+
3824 auto connect_wh =
3825 connection->connect("MirClientSurfaceTest", &connected_callback, nullptr);
3826 mir_wait_for(connect_wh);
3827@@ -633,3 +680,107 @@
3828 EXPECT_THAT(mir_platform_message_get_opcode(returned_response), Eq(opcode));
3829 mir_platform_message_release(returned_response);
3830 }
3831+
3832+TEST_F(MirConnectionTest, dispatch_works_with_automatic_dispatch)
3833+{
3834+ using namespace testing;
3835+
3836+ auto channel = std::dynamic_pointer_cast<MockRpcChannel>(conf.the_rpc_channel());
3837+
3838+ auto dispatched = std::make_shared<mir::test::Signal>();
3839+ ON_CALL(*channel, dispatch(_))
3840+ .WillByDefault(Invoke([dispatched](md::FdEvents) { dispatched->raise(); return true; }));
3841+
3842+ auto connection = std::make_shared<MirConnection>(conf, DispatchType::automatic);
3843+
3844+ channel->trigger();
3845+
3846+ EXPECT_TRUE(dispatched->wait_for(std::chrono::seconds{1}));
3847+}
3848+
3849+TEST_F(MirConnectionTest, manual_dispatch_is_not_automatically_dispatched)
3850+{
3851+ using namespace testing;
3852+
3853+ auto channel = std::dynamic_pointer_cast<MockRpcChannel>(conf.the_rpc_channel());
3854+
3855+ auto dispatched = std::make_shared<mir::test::Signal>();
3856+ ON_CALL(*channel, dispatch(_))
3857+ .WillByDefault(Invoke([dispatched](md::FdEvents) { dispatched->raise(); return true; }));
3858+
3859+ auto connection = std::make_shared<MirConnection>(conf, DispatchType::manual);
3860+
3861+ channel->trigger();
3862+
3863+ EXPECT_FALSE(dispatched->wait_for(std::chrono::seconds{1}));
3864+}
3865+
3866+TEST_F(MirConnectionTest, returns_invalid_watch_fd_when_using_automatic_dispatch)
3867+{
3868+ using namespace testing;
3869+
3870+ auto connection = std::make_shared<MirConnection>(conf, DispatchType::automatic);
3871+
3872+ EXPECT_THAT(connection->watch_fd(), Lt(0));
3873+}
3874+
3875+TEST_F(MirConnectionTest, returns_pollable_watch_fd_when_using_manual_dispatch)
3876+{
3877+ using namespace testing;
3878+
3879+ auto channel = std::dynamic_pointer_cast<MockRpcChannel>(conf.the_rpc_channel());
3880+
3881+ auto connection = std::make_shared<MirConnection>(conf, DispatchType::manual);
3882+
3883+ pollfd fd_readable;
3884+ fd_readable.events = POLLIN;
3885+ fd_readable.fd = connection->watch_fd();
3886+ EXPECT_TRUE(mt::std_call_succeeded(poll(&fd_readable, 1, 0)));
3887+}
3888+
3889+TEST_F(MirConnectionTest, can_add_dispatchables_to_connection)
3890+{
3891+ using namespace testing;
3892+ auto connection = std::make_shared<MirConnection>(conf, DispatchType::manual);
3893+
3894+ auto dispatchee = std::make_shared<mt::TestDispatchable>([](){});
3895+ connection->add_dispatchee(dispatchee);
3896+}
3897+
3898+TEST_F(MirConnectionTest, added_dispatchees_are_dispatched)
3899+{
3900+ using namespace testing;
3901+ auto connection = std::make_shared<MirConnection>(conf, DispatchType::manual);
3902+
3903+ bool dispatched{false};
3904+ auto dispatchee = std::make_shared<mt::TestDispatchable>([&dispatched](){ dispatched = true; });
3905+
3906+ connection->add_dispatchee(dispatchee);
3907+
3908+ dispatchee->trigger();
3909+
3910+ EXPECT_TRUE(mt::fd_is_readable(connection->watch_fd()));
3911+ connection->dispatch();
3912+
3913+ EXPECT_TRUE(dispatched);
3914+}
3915+
3916+TEST_F(MirConnectionTest, removed_dispatchees_are_no_longer_dispatched)
3917+{
3918+ using namespace testing;
3919+ auto connection = std::make_shared<MirConnection>(conf, DispatchType::manual);
3920+
3921+ bool dispatched{false};
3922+ auto dispatchee = std::make_shared<mt::TestDispatchable>([&dispatched](){ dispatched = true; });
3923+
3924+ connection->add_dispatchee(dispatchee);
3925+ connection->remove_dispatchee(dispatchee);
3926+
3927+ dispatchee->trigger();
3928+
3929+ EXPECT_FALSE(mir::test::fd_is_readable(connection->watch_fd()));
3930+
3931+ connection->dispatch();
3932+
3933+ EXPECT_FALSE(dispatched);
3934+}
3935
3936=== modified file 'tests/unit-tests/client/test_mir_screencast.cpp'
3937--- tests/unit-tests/client/test_mir_screencast.cpp 2015-04-16 19:56:08 +0000
3938+++ tests/unit-tests/client/test_mir_screencast.cpp 2015-05-14 07:50:26 +0000
3939@@ -166,7 +166,7 @@
3940 using namespace ::testing;
3941
3942 ON_CALL(*mock_buffer_stream_factory,
3943- make_consumer_stream(_,_,_)).WillByDefault(
3944+ make_consumer_stream(_,_,_,_)).WillByDefault(
3945 Return(mt::fake_shared(mock_bs)));
3946 }
3947
3948@@ -191,6 +191,7 @@
3949 .WillOnce(RunClosure());
3950
3951 MirScreencast screencast{
3952+ nullptr,
3953 default_region,
3954 default_size,
3955 default_pixel_format, mock_server,
3956@@ -215,6 +216,7 @@
3957 .WillOnce(RunClosure());
3958
3959 MirScreencast screencast{
3960+ nullptr,
3961 default_region,
3962 default_size,
3963 default_pixel_format, mock_server,
3964@@ -232,6 +234,7 @@
3965 EXPECT_CALL(mock_cb, call(_, &mock_cb));
3966
3967 MirScreencast screencast{
3968+ nullptr,
3969 default_region,
3970 default_size,
3971 default_pixel_format, stub_server,
3972@@ -246,6 +249,7 @@
3973 using namespace testing;
3974
3975 MirScreencast screencast{
3976+ nullptr,
3977 default_region,
3978 default_size,
3979 default_pixel_format, stub_server,
3980@@ -268,6 +272,7 @@
3981
3982 EXPECT_THROW({
3983 MirScreencast screencast(
3984+ nullptr,
3985 default_region,
3986 invalid_size,
3987 default_pixel_format, stub_server,
3988@@ -277,6 +282,7 @@
3989
3990 EXPECT_THROW({
3991 MirScreencast screencast(
3992+ nullptr,
3993 invalid_region,
3994 default_size,
3995 default_pixel_format, stub_server,
3996@@ -286,6 +292,7 @@
3997
3998 EXPECT_THROW({
3999 MirScreencast screencast(
4000+ nullptr,
4001 default_region,
4002 default_size,
4003 mir_pixel_format_invalid, stub_server,
4004@@ -302,6 +309,7 @@
4005 .WillOnce(DoAll(SetCreateError(), RunClosure()));
4006
4007 MirScreencast screencast{
4008+ nullptr,
4009 default_region,
4010 default_size,
4011 default_pixel_format, mock_server,
4012@@ -323,6 +331,7 @@
4013 EXPECT_CALL(mock_cb, call(_,&mock_cb));
4014
4015 MirScreencast screencast{
4016+ nullptr,
4017 default_region,
4018 default_size,
4019 default_pixel_format, mock_server,
4020
4021=== modified file 'tests/unit-tests/client/test_protobuf_rpc_channel.cpp'
4022--- tests/unit-tests/client/test_protobuf_rpc_channel.cpp 2015-04-13 05:50:18 +0000
4023+++ tests/unit-tests/client/test_protobuf_rpc_channel.cpp 2015-05-14 07:50:26 +0000
4024@@ -27,12 +27,16 @@
4025 #include "mir_protobuf_wire.pb.h"
4026
4027 #include "mir_test_doubles/null_client_event_sink.h"
4028+#include "mir_test/fd_utils.h"
4029
4030 #include <list>
4031 #include <endian.h>
4032
4033+#include <sys/eventfd.h>
4034 #include <fcntl.h>
4035
4036+#include <boost/throw_exception.hpp>
4037+
4038 #include <google/protobuf/descriptor.h>
4039
4040 #include <gtest/gtest.h>
4041@@ -42,6 +46,9 @@
4042 namespace mclr = mir::client::rpc;
4043 namespace mtd = mir::test::doubles;
4044 namespace md = mir::dispatch;
4045+namespace mt = mir::test;
4046+
4047+namespace mt = mir::test;
4048
4049 namespace
4050 {
4051@@ -58,7 +65,15 @@
4052 {
4053 public:
4054 MockStreamTransport()
4055+ : event_fd{eventfd(0, EFD_CLOEXEC)}
4056 {
4057+ if (event_fd == mir::Fd::invalid)
4058+ {
4059+ BOOST_THROW_EXCEPTION((std::system_error{errno,
4060+ std::system_category(),
4061+ "Failed to create event fd"}));
4062+ }
4063+
4064 using namespace testing;
4065 ON_CALL(*this, register_observer(_))
4066 .WillByDefault(Invoke(std::bind(&MockStreamTransport::register_observer_default,
4067@@ -82,6 +97,8 @@
4068
4069 void add_server_message(std::vector<uint8_t> const& message)
4070 {
4071+ eventfd_t data_added{message.size()};
4072+ eventfd_write(event_fd, data_added);
4073 received_data.insert(received_data.end(), message.begin(), message.end());
4074 }
4075 void add_server_message(std::vector<uint8_t> const& message, std::initializer_list<mir::Fd> fds)
4076@@ -95,24 +112,30 @@
4077 return received_data.empty() && received_fds.empty();
4078 }
4079
4080- void notify_data_received()
4081- {
4082- do
4083- {
4084- for(auto& observer : observers)
4085- observer->on_data_available();
4086- }
4087- while (!all_data_consumed());
4088- }
4089-
4090 MOCK_METHOD1(register_observer, void(std::shared_ptr<Observer> const&));
4091 MOCK_METHOD1(unregister_observer, void(std::shared_ptr<Observer> const&));
4092 MOCK_METHOD2(receive_data, void(void*, size_t));
4093 MOCK_METHOD3(receive_data, void(void*, size_t, std::vector<mir::Fd>&));
4094 MOCK_METHOD2(send_message, void(std::vector<uint8_t> const&, std::vector<mir::Fd> const&));
4095- MOCK_CONST_METHOD0(watch_fd, mir::Fd());
4096- MOCK_METHOD1(dispatch, bool(md::FdEvents));
4097- MOCK_CONST_METHOD0(relevant_events, md::FdEvents());
4098+
4099+ mir::Fd watch_fd() const override
4100+ {
4101+ return event_fd;
4102+ }
4103+
4104+ bool dispatch(md::FdEvents /*events*/) override
4105+ {
4106+ for (auto& observer : observers)
4107+ {
4108+ observer->on_data_available();
4109+ }
4110+ return true;
4111+ }
4112+
4113+ md::FdEvents relevant_events() const override
4114+ {
4115+ return md::FdEvent::readable;
4116+ }
4117
4118 // Transport interface
4119 void register_observer_default(std::shared_ptr<Observer> const& observer)
4120@@ -128,6 +151,9 @@
4121
4122 void receive_data_default(void* buffer, size_t read_bytes, std::vector<mir::Fd>& fds)
4123 {
4124+ if (read_bytes == 0)
4125+ return;
4126+
4127 auto num_fds = fds.size();
4128 if (read_bytes > received_data.size())
4129 {
4130@@ -143,6 +169,11 @@
4131
4132 received_data.erase(received_data.begin(), received_data.begin() + read_bytes);
4133 received_fds.erase(received_fds.begin(), received_fds.begin() + num_fds);
4134+
4135+ eventfd_t remaining_bytes;
4136+ eventfd_read(event_fd, &remaining_bytes);
4137+ remaining_bytes -= read_bytes;
4138+ eventfd_write(event_fd, remaining_bytes);
4139 }
4140
4141 void send_message_default(std::vector<uint8_t> const& buffer)
4142@@ -156,6 +187,9 @@
4143 std::vector<uint8_t> received_data;
4144 std::vector<mir::Fd> received_fds;
4145 std::list<std::vector<uint8_t>> sent_messages;
4146+
4147+private:
4148+ mir::Fd event_fd;
4149 };
4150
4151 class MirProtobufRpcChannelTest : public testing::Test
4152@@ -176,53 +210,56 @@
4153
4154 MockStreamTransport* transport;
4155 std::shared_ptr<mcl::LifecycleControl> lifecycle;
4156- std::shared_ptr<::google::protobuf::RpcChannel> channel;
4157+ std::shared_ptr<mclr::MirProtobufRpcChannel> channel;
4158 };
4159
4160 }
4161
4162-TEST_F(MirProtobufRpcChannelTest, ReadsFullMessages)
4163-{
4164- std::vector<uint8_t> empty_message(sizeof(uint16_t));
4165- std::vector<uint8_t> small_message(sizeof(uint16_t) + 8);
4166- std::vector<uint8_t> large_message(sizeof(uint16_t) + 4096);
4167-
4168- *reinterpret_cast<uint16_t*>(empty_message.data()) = htobe16(0);
4169- *reinterpret_cast<uint16_t*>(small_message.data()) = htobe16(8);
4170- *reinterpret_cast<uint16_t*>(large_message.data()) = htobe16(4096);
4171-
4172- transport->add_server_message(empty_message);
4173- transport->notify_data_received();
4174- EXPECT_TRUE(transport->all_data_consumed());
4175-
4176- transport->add_server_message(small_message);
4177- transport->notify_data_received();
4178- EXPECT_TRUE(transport->all_data_consumed());
4179-
4180- transport->add_server_message(large_message);
4181- transport->notify_data_received();
4182- EXPECT_TRUE(transport->all_data_consumed());
4183-}
4184-
4185-TEST_F(MirProtobufRpcChannelTest, ReadsAllQueuedMessages)
4186-{
4187- std::vector<uint8_t> empty_message(sizeof(uint16_t));
4188- std::vector<uint8_t> small_message(sizeof(uint16_t) + 8);
4189- std::vector<uint8_t> large_message(sizeof(uint16_t) + 4096);
4190-
4191- *reinterpret_cast<uint16_t*>(empty_message.data()) = htobe16(0);
4192- *reinterpret_cast<uint16_t*>(small_message.data()) = htobe16(8);
4193- *reinterpret_cast<uint16_t*>(large_message.data()) = htobe16(4096);
4194-
4195- transport->add_server_message(empty_message);
4196- transport->add_server_message(small_message);
4197- transport->add_server_message(large_message);
4198-
4199- transport->notify_data_received();
4200- EXPECT_TRUE(transport->all_data_consumed());
4201-}
4202-
4203-TEST_F(MirProtobufRpcChannelTest, SendsMessagesAtomically)
4204+TEST_F(MirProtobufRpcChannelTest, reads_full_messages)
4205+{
4206+ std::vector<uint8_t> empty_message(sizeof(uint16_t));
4207+ std::vector<uint8_t> small_message(sizeof(uint16_t) + 8);
4208+ std::vector<uint8_t> large_message(sizeof(uint16_t) + 4096);
4209+
4210+ *reinterpret_cast<uint16_t*>(empty_message.data()) = htobe16(0);
4211+ *reinterpret_cast<uint16_t*>(small_message.data()) = htobe16(8);
4212+ *reinterpret_cast<uint16_t*>(large_message.data()) = htobe16(4096);
4213+
4214+ transport->add_server_message(empty_message);
4215+ transport->dispatch(md::FdEvent::readable);
4216+ EXPECT_TRUE(transport->all_data_consumed());
4217+
4218+ transport->add_server_message(small_message);
4219+ transport->dispatch(md::FdEvent::readable);
4220+ EXPECT_TRUE(transport->all_data_consumed());
4221+
4222+ transport->add_server_message(large_message);
4223+ transport->dispatch(md::FdEvent::readable);
4224+ EXPECT_TRUE(transport->all_data_consumed());
4225+}
4226+
4227+TEST_F(MirProtobufRpcChannelTest, reads_all_queued_messages)
4228+{
4229+ std::vector<uint8_t> empty_message(sizeof(uint16_t));
4230+ std::vector<uint8_t> small_message(sizeof(uint16_t) + 8);
4231+ std::vector<uint8_t> large_message(sizeof(uint16_t) + 4096);
4232+
4233+ *reinterpret_cast<uint16_t*>(empty_message.data()) = htobe16(0);
4234+ *reinterpret_cast<uint16_t*>(small_message.data()) = htobe16(8);
4235+ *reinterpret_cast<uint16_t*>(large_message.data()) = htobe16(4096);
4236+
4237+ transport->add_server_message(empty_message);
4238+ transport->add_server_message(small_message);
4239+ transport->add_server_message(large_message);
4240+
4241+ while(mt::fd_is_readable(channel->watch_fd()))
4242+ {
4243+ channel->dispatch(md::FdEvent::readable);
4244+ }
4245+ EXPECT_TRUE(transport->all_data_consumed());
4246+}
4247+
4248+TEST_F(MirProtobufRpcChannelTest, sends_messages_atomically)
4249 {
4250 mir::protobuf::DisplayServer::Stub channel_user{channel.get(), mir::protobuf::DisplayServer::STUB_DOESNT_OWN_CHANNEL};
4251 mir::protobuf::ConnectParameters message;
4252@@ -233,7 +270,7 @@
4253 EXPECT_EQ(transport->sent_messages.size(), 1);
4254 }
4255
4256-TEST_F(MirProtobufRpcChannelTest, SetsCorrectSizeWhenSendingMessage)
4257+TEST_F(MirProtobufRpcChannelTest, sets_correct_size_when_sending_message)
4258 {
4259 mir::protobuf::DisplayServer::Stub channel_user{channel.get(), mir::protobuf::DisplayServer::STUB_DOESNT_OWN_CHANNEL};
4260 mir::protobuf::ConnectParameters message;
4261@@ -246,7 +283,7 @@
4262 EXPECT_EQ(transport->sent_messages.front().size() - sizeof(uint16_t), message_header);
4263 }
4264
4265-TEST_F(MirProtobufRpcChannelTest, ReadsFds)
4266+TEST_F(MirProtobufRpcChannelTest, reads_fds)
4267 {
4268 mir::protobuf::DisplayServer::Stub channel_user{channel.get(), mir::protobuf::DisplayServer::STUB_DOESNT_OWN_CHANNEL};
4269 mir::protobuf::Buffer reply;
4270@@ -288,7 +325,10 @@
4271 std::vector<uint8_t> dummy = {1};
4272 transport->add_server_message(dummy, fds);
4273
4274- transport->notify_data_received();
4275+ while(mt::fd_is_readable(channel->watch_fd()))
4276+ {
4277+ channel->dispatch(md::FdEvent::readable);
4278+ }
4279 }
4280
4281 ASSERT_EQ(reply.fd_size(), fds.size());
4282@@ -300,7 +340,7 @@
4283 }
4284 }
4285
4286-TEST_F(MirProtobufRpcChannelTest, NotifiesOfDisconnectOnWriteError)
4287+TEST_F(MirProtobufRpcChannelTest, notifies_of_disconnect_on_write_error)
4288 {
4289 using namespace ::testing;
4290
4291@@ -328,7 +368,7 @@
4292 EXPECT_TRUE(disconnected);
4293 }
4294
4295-TEST_F(MirProtobufRpcChannelTest, ForwardsDisconnectNotification)
4296+TEST_F(MirProtobufRpcChannelTest, forwards_disconnect_notification)
4297 {
4298 using namespace ::testing;
4299
4300@@ -350,7 +390,7 @@
4301 EXPECT_TRUE(disconnected);
4302 }
4303
4304-TEST_F(MirProtobufRpcChannelTest, NotifiesOfDisconnectOnlyOnce)
4305+TEST_F(MirProtobufRpcChannelTest, notifies_of_disconnect_only_once)
4306 {
4307 using namespace ::testing;
4308
4309@@ -388,3 +428,196 @@
4310
4311 EXPECT_TRUE(disconnected);
4312 }
4313+
4314+namespace
4315+{
4316+void set_flag(bool* flag)
4317+{
4318+ *flag = true;
4319+}
4320+}
4321+
4322+TEST_F(MirProtobufRpcChannelTest, delays_messages_not_requested)
4323+{
4324+ using namespace ::testing;
4325+
4326+ auto typed_channel = std::dynamic_pointer_cast<mclr::MirProtobufRpcChannel>(channel);
4327+
4328+ mir::protobuf::DisplayServer::Stub channel_user{channel.get(), mir::protobuf::DisplayServer::STUB_DOESNT_OWN_CHANNEL};
4329+ mir::protobuf::DRMMagic request;
4330+ mir::protobuf::DRMAuthMagicStatus reply;
4331+
4332+ bool first_response_called{false};
4333+ bool second_response_called{false};
4334+ channel_user.drm_auth_magic(nullptr,
4335+ &request,
4336+ &reply,
4337+ google::protobuf::NewCallback(&set_flag, &first_response_called));
4338+
4339+ typed_channel->process_next_request_first();
4340+ channel_user.drm_auth_magic(nullptr,
4341+ &request,
4342+ &reply,
4343+ google::protobuf::NewCallback(&set_flag, &second_response_called));
4344+
4345+ mir::protobuf::wire::Invocation wire_request;
4346+ mir::protobuf::wire::Result wire_reply;
4347+
4348+ wire_request.ParseFromArray(transport->sent_messages.front().data() + sizeof(uint16_t),
4349+ transport->sent_messages.front().size() - sizeof(uint16_t));
4350+
4351+ transport->sent_messages.pop_front();
4352+
4353+ wire_reply.set_id(wire_request.id());
4354+ wire_reply.set_response(reply.SerializeAsString());
4355+
4356+ std::vector<uint8_t> buffer(wire_reply.ByteSize() + sizeof(uint16_t));
4357+ *reinterpret_cast<uint16_t*>(buffer.data()) = htobe16(wire_reply.ByteSize());
4358+ ASSERT_TRUE(wire_reply.SerializeToArray(buffer.data() + sizeof(uint16_t), buffer.size() - sizeof(uint16_t)));
4359+
4360+ transport->add_server_message(buffer);
4361+
4362+ wire_request.ParseFromArray(transport->sent_messages.front().data() + sizeof(uint16_t),
4363+ transport->sent_messages.front().size() - sizeof(uint16_t));
4364+
4365+ transport->sent_messages.pop_front();
4366+
4367+ wire_reply.set_id(wire_request.id());
4368+ wire_reply.set_response(reply.SerializeAsString());
4369+
4370+ buffer.resize(wire_reply.ByteSize() + sizeof(uint16_t));
4371+ *reinterpret_cast<uint16_t*>(buffer.data()) = htobe16(wire_reply.ByteSize());
4372+ ASSERT_TRUE(wire_reply.SerializeToArray(buffer.data() + sizeof(uint16_t), buffer.size() - sizeof(uint16_t)));
4373+
4374+ transport->add_server_message(buffer);
4375+
4376+ // Read the first message; this should be queued for later processing...
4377+ EXPECT_TRUE(mt::fd_is_readable(typed_channel->watch_fd()));
4378+ typed_channel->dispatch(md::FdEvent::readable);
4379+
4380+ EXPECT_FALSE(first_response_called);
4381+ EXPECT_FALSE(second_response_called);
4382+
4383+ // Read the second message; this should be processed immediately...
4384+ EXPECT_TRUE(mt::fd_is_readable(typed_channel->watch_fd()));
4385+ typed_channel->dispatch(md::FdEvent::readable);
4386+
4387+ EXPECT_FALSE(first_response_called);
4388+ EXPECT_TRUE(second_response_called);
4389+
4390+ // Now, the first message should be ready to be processed...
4391+ EXPECT_TRUE(mt::fd_is_readable(typed_channel->watch_fd()));
4392+ typed_channel->dispatch(md::FdEvent::readable);
4393+
4394+ EXPECT_TRUE(first_response_called);
4395+ EXPECT_TRUE(second_response_called);
4396+}
4397+
4398+TEST_F(MirProtobufRpcChannelTest, delays_messages_with_fds_not_requested)
4399+{
4400+ using namespace ::testing;
4401+
4402+ auto typed_channel = std::dynamic_pointer_cast<mclr::MirProtobufRpcChannel>(channel);
4403+
4404+ mir::protobuf::DisplayServer::Stub channel_user{channel.get(), mir::protobuf::DisplayServer::STUB_DOESNT_OWN_CHANNEL};
4405+ mir::protobuf::DRMMagic drm_request;
4406+ mir::protobuf::DRMAuthMagicStatus drm_reply;
4407+
4408+ mir::protobuf::Buffer buffer_reply;
4409+ mir::protobuf::BufferRequest buffer_request;
4410+
4411+ bool first_response_called{false};
4412+ bool second_response_called{false};
4413+
4414+
4415+ channel_user.exchange_buffer(nullptr,
4416+ &buffer_request,
4417+ &buffer_reply,
4418+ google::protobuf::NewCallback(&set_flag, &first_response_called));
4419+
4420+ typed_channel->process_next_request_first();
4421+ channel_user.drm_auth_magic(nullptr,
4422+ &drm_request,
4423+ &drm_reply,
4424+ google::protobuf::NewCallback(&set_flag, &second_response_called));
4425+
4426+
4427+ std::initializer_list<mir::Fd> fds = {mir::Fd{open("/dev/null", O_RDONLY)},
4428+ mir::Fd{open("/dev/null", O_RDONLY)},
4429+ mir::Fd{open("/dev/null", O_RDONLY)}};
4430+
4431+ {
4432+ mir::protobuf::Buffer reply_message;
4433+
4434+ for (auto fd : fds)
4435+ reply_message.add_fd(fd);
4436+ reply_message.set_fds_on_side_channel(fds.size());
4437+
4438+ mir::protobuf::wire::Invocation request;
4439+ mir::protobuf::wire::Result reply;
4440+
4441+ request.ParseFromArray(transport->sent_messages.front().data() + sizeof(uint16_t),
4442+ transport->sent_messages.front().size() - sizeof(uint16_t));
4443+
4444+ transport->sent_messages.pop_front();
4445+
4446+ reply.set_id(request.id());
4447+ reply.set_response(reply_message.SerializeAsString());
4448+
4449+ ASSERT_TRUE(reply.has_id());
4450+ ASSERT_TRUE(reply.has_response());
4451+
4452+ std::vector<uint8_t> buffer(reply.ByteSize() + sizeof(uint16_t));
4453+ *reinterpret_cast<uint16_t*>(buffer.data()) = htobe16(reply.ByteSize());
4454+ ASSERT_TRUE(reply.SerializeToArray(buffer.data() + sizeof(uint16_t), buffer.size() - sizeof(uint16_t)));
4455+
4456+ transport->add_server_message(buffer);
4457+
4458+ // Because our protocol is a bit silly...
4459+ std::vector<uint8_t> dummy = {1};
4460+ transport->add_server_message(dummy, fds);
4461+ }
4462+
4463+ {
4464+ mir::protobuf::DRMAuthMagicStatus reply;
4465+
4466+ mir::protobuf::wire::Invocation wire_request;
4467+ mir::protobuf::wire::Result wire_reply;
4468+
4469+ wire_request.ParseFromArray(transport->sent_messages.front().data() + sizeof(uint16_t),
4470+ transport->sent_messages.front().size() - sizeof(uint16_t));
4471+
4472+ transport->sent_messages.pop_front();
4473+
4474+ wire_reply.set_id(wire_request.id());
4475+ wire_reply.set_response(reply.SerializeAsString());
4476+
4477+ std::vector<uint8_t> buffer(wire_reply.ByteSize() + sizeof(uint16_t));
4478+ *reinterpret_cast<uint16_t*>(buffer.data()) = htobe16(wire_reply.ByteSize());
4479+ ASSERT_TRUE(wire_reply.SerializeToArray(buffer.data() + sizeof(uint16_t), buffer.size() - sizeof(uint16_t)));
4480+
4481+ transport->add_server_message(buffer);
4482+ }
4483+
4484+ // Read the first message; this should be queued for later processing...
4485+ EXPECT_TRUE(mt::fd_is_readable(typed_channel->watch_fd()));
4486+ typed_channel->dispatch(md::FdEvent::readable);
4487+
4488+ EXPECT_FALSE(first_response_called);
4489+ EXPECT_FALSE(second_response_called);
4490+
4491+ // Read the second message; this should be processed immediately...
4492+ EXPECT_TRUE(mt::fd_is_readable(typed_channel->watch_fd()));
4493+ typed_channel->dispatch(md::FdEvent::readable);
4494+
4495+ EXPECT_FALSE(first_response_called);
4496+ EXPECT_TRUE(second_response_called);
4497+
4498+ // Now, the first message should be ready to be processed...
4499+ EXPECT_TRUE(mt::fd_is_readable(typed_channel->watch_fd()));
4500+ typed_channel->dispatch(md::FdEvent::readable);
4501+
4502+ EXPECT_TRUE(first_response_called);
4503+ EXPECT_TRUE(second_response_called);
4504+}
4505+
4506
4507=== modified file 'tests/unit-tests/dispatch/CMakeLists.txt'
4508--- tests/unit-tests/dispatch/CMakeLists.txt 2015-03-31 02:35:42 +0000
4509+++ tests/unit-tests/dispatch/CMakeLists.txt 2015-05-14 07:50:26 +0000
4510@@ -1,6 +1,6 @@
4511 list(APPEND UNIT_TEST_SOURCES
4512+ ${CMAKE_CURRENT_SOURCE_DIR}/test_threaded_dispatcher.cpp
4513 ${CMAKE_CURRENT_SOURCE_DIR}/test_action_queue.cpp
4514- ${CMAKE_CURRENT_SOURCE_DIR}/test_simple_dispatch_thread.cpp
4515 ${CMAKE_CURRENT_SOURCE_DIR}/test_multiplexing_dispatchable.cpp
4516 ${CMAKE_CURRENT_SOURCE_DIR}/test_dispatch_utils.cpp
4517 )
4518
4519=== modified file 'tests/unit-tests/dispatch/test_multiplexing_dispatchable.cpp'
4520--- tests/unit-tests/dispatch/test_multiplexing_dispatchable.cpp 2015-02-11 23:15:33 +0000
4521+++ tests/unit-tests/dispatch/test_multiplexing_dispatchable.cpp 2015-05-14 07:50:26 +0000
4522@@ -17,7 +17,7 @@
4523 */
4524
4525 #include "mir/dispatch/multiplexing_dispatchable.h"
4526-#include "mir/dispatch/simple_dispatch_thread.h"
4527+#include "mir/dispatch/threaded_dispatcher.h"
4528 #include "mir/fd.h"
4529 #include "mir_test/pipe.h"
4530 #include "mir_test/signal.h"
4531@@ -217,8 +217,8 @@
4532 auto dispatcher = std::make_shared<md::MultiplexingDispatchable>();
4533 dispatcher->add_watch(dispatchee);
4534
4535- md::SimpleDispatchThread first_loop{dispatcher};
4536- md::SimpleDispatchThread second_loop{dispatcher};
4537+ md::ThreadedDispatcher eventloop{"Fools!", dispatcher};
4538+ eventloop.add_thread();
4539
4540 EXPECT_TRUE(second_dispatch->wait_for(std::chrono::seconds{5}));
4541 }
4542@@ -315,15 +315,15 @@
4543
4544 dispatchee->trigger();
4545
4546- md::SimpleDispatchThread eventloop{dispatcher};
4547+ md::ThreadedDispatcher eventloop{"I'll destroy", dispatcher};
4548
4549- EXPECT_TRUE(in_dispatch->wait_for(std::chrono::seconds{1}));
4550+ EXPECT_TRUE(in_dispatch->wait_for(std::chrono::seconds{5}));
4551
4552 dispatcher->remove_watch(dispatchee);
4553 dispatchee.reset();
4554 canary.reset();
4555
4556- EXPECT_TRUE(canary_killed->wait_for(std::chrono::seconds{2}));
4557+ EXPECT_TRUE(canary_killed->wait_for(std::chrono::seconds{5}));
4558 }
4559
4560 TEST(MultiplexingDispatchableTest, destruction_is_threadsafe)
4561@@ -348,28 +348,27 @@
4562
4563 mt::AutoJoinThread dispatch_thread{[dispatcher]() { dispatcher->dispatch(md::FdEvent::readable); }};
4564
4565- EXPECT_TRUE(in_dispatch->wait_for(std::chrono::seconds{1}));
4566+ EXPECT_TRUE(in_dispatch->wait_for(std::chrono::seconds{5}));
4567
4568 dispatcher->remove_watch(dispatchee);
4569 dispatcher.reset();
4570 dispatchee.reset();
4571 canary.reset();
4572
4573- EXPECT_TRUE(canary_killed->wait_for(std::chrono::seconds{2}));
4574+ EXPECT_TRUE(canary_killed->wait_for(std::chrono::seconds{5}));
4575 }
4576
4577 TEST(MultiplexingDispatchableTest, stress_test_threading)
4578 {
4579 using namespace testing;
4580-
4581 int const dispatchee_count{20};
4582
4583 auto dispatcher = std::make_shared<md::MultiplexingDispatchable>();
4584
4585- std::vector<std::shared_ptr<md::SimpleDispatchThread>> eventloops;
4586+ auto event_dispatcher = std::make_shared<md::ThreadedDispatcher>(__func__, dispatcher);
4587 for (int i = 0 ; i < dispatchee_count + 5 ; ++i)
4588 {
4589- eventloops.push_back(std::make_shared<md::SimpleDispatchThread>(dispatcher));
4590+ event_dispatcher->add_thread();
4591 }
4592
4593 std::vector<std::shared_ptr<mt::Signal>> canary_tomb;
4594@@ -397,12 +396,12 @@
4595
4596 dispatchees.clear();
4597 dispatcher.reset();
4598- eventloops.clear();
4599+ event_dispatcher.reset();
4600
4601 for (auto headstone : canary_tomb)
4602 {
4603 // Use assert so as to not block for *ages* on failure
4604- ASSERT_TRUE(headstone->wait_for(std::chrono::seconds{2}));
4605+ ASSERT_TRUE(headstone->wait_for(std::chrono::seconds{5}));
4606 }
4607 }
4608
4609@@ -456,10 +455,10 @@
4610
4611 first_dispatchee->trigger();
4612
4613- md::SimpleDispatchThread eventloop_one{dispatcher};
4614- md::SimpleDispatchThread eventloop_two{dispatcher};
4615+ md::ThreadedDispatcher eventloop{"them all!", dispatcher};
4616+ eventloop.add_thread();
4617
4618- EXPECT_TRUE(in_dispatch->wait_for(std::chrono::seconds{1}));
4619+ EXPECT_TRUE(in_dispatch->wait_for(std::chrono::seconds{5}));
4620
4621 dispatcher->remove_watch(dummy_dispatchee);
4622 dispatcher->remove_watch(first_dispatchee);
4623@@ -470,7 +469,7 @@
4624
4625 unblock_dispatchee->raise();
4626
4627- EXPECT_TRUE(canary_killed->wait_for(std::chrono::seconds{2}));
4628+ EXPECT_TRUE(canary_killed->wait_for(std::chrono::seconds{5}));
4629 }
4630
4631 TEST(MultiplexingDispatchableTest, automatic_removals_are_threadsafe)
4632@@ -481,7 +480,11 @@
4633
4634 dispatcher->add_watch(dispatchee, md::DispatchReentrancy::reentrant);
4635
4636- md::SimpleDispatchThread one{dispatcher}, two{dispatcher}, three{dispatcher}, four{dispatcher};
4637+ md::ThreadedDispatcher eventloop{"Eeelo", dispatcher};
4638+
4639+ eventloop.add_thread();
4640+ eventloop.add_thread();
4641+ eventloop.add_thread();
4642
4643 dispatchee->trigger();
4644 }
4645
4646=== removed file 'tests/unit-tests/dispatch/test_simple_dispatch_thread.cpp'
4647--- tests/unit-tests/dispatch/test_simple_dispatch_thread.cpp 2015-05-01 14:47:55 +0000
4648+++ tests/unit-tests/dispatch/test_simple_dispatch_thread.cpp 1970-01-01 00:00:00 +0000
4649@@ -1,291 +0,0 @@
4650-/*
4651- * Copyright © 2015 Canonical Ltd.
4652- *
4653- * This program is free software: you can redistribute it and/or modify
4654- * it under the terms of the GNU General Public License version 3 as
4655- * published by the Free Software Foundation.
4656- *
4657- * This program is distributed in the hope that it will be useful,
4658- * but WITHOUT ANY WARRANTY; without even the implied warranty of
4659- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4660- * GNU General Public License for more details.
4661- *
4662- * You should have received a copy of the GNU General Public License
4663- * along with this program. If not, see <http://www.gnu.org/licenses/>.
4664- *
4665- * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
4666- */
4667-
4668-#include "mir/dispatch/simple_dispatch_thread.h"
4669-#include "mir/dispatch/dispatchable.h"
4670-#include "mir/fd.h"
4671-#include "mir_test/pipe.h"
4672-#include "mir_test/signal.h"
4673-#include "mir_test/test_dispatchable.h"
4674-#include "mir_test_framework/process.h"
4675-#include "mir_test/cross_process_action.h"
4676-
4677-#include <fcntl.h>
4678-
4679-#include <atomic>
4680-#include <exception>
4681-#include <thread>
4682-
4683-#include <gtest/gtest.h>
4684-#include <gmock/gmock.h>
4685-
4686-namespace md = mir::dispatch;
4687-namespace mt = mir::test;
4688-
4689-namespace
4690-{
4691-class SimpleDispatchThreadTest : public ::testing::Test
4692-{
4693-public:
4694- SimpleDispatchThreadTest()
4695- {
4696- mt::Pipe pipe{O_NONBLOCK};
4697- watch_fd = pipe.read_fd();
4698- test_fd = pipe.write_fd();
4699- }
4700-
4701- mir::Fd watch_fd;
4702- mir::Fd test_fd;
4703-};
4704-
4705-class MockDispatchable : public md::Dispatchable
4706-{
4707-public:
4708- MOCK_CONST_METHOD0(watch_fd, mir::Fd());
4709- MOCK_METHOD1(dispatch, bool(md::FdEvents));
4710- MOCK_CONST_METHOD0(relevant_events, md::FdEvents());
4711-};
4712-}
4713-
4714-TEST_F(SimpleDispatchThreadTest, calls_dispatch_when_fd_is_readable)
4715-{
4716- using namespace testing;
4717-
4718- auto dispatched = std::make_shared<mt::Signal>();
4719- auto dispatchable = std::make_shared<mt::TestDispatchable>([dispatched]()
4720- {
4721- dispatched->raise();
4722- });
4723-
4724- md::SimpleDispatchThread dispatcher{dispatchable};
4725-
4726- dispatchable->trigger();
4727-
4728- EXPECT_TRUE(dispatched->wait_for(std::chrono::seconds{1}));
4729-}
4730-
4731-TEST_F(SimpleDispatchThreadTest, stops_calling_dispatch_once_fd_is_not_readable)
4732-{
4733- using namespace testing;
4734-
4735- std::atomic<int> dispatch_count{0};
4736- auto dispatchable = std::make_shared<mt::TestDispatchable>([&dispatch_count]()
4737- {
4738- ++dispatch_count;
4739- });
4740-
4741- md::SimpleDispatchThread dispatcher{dispatchable};
4742-
4743- dispatchable->trigger();
4744-
4745- std::this_thread::sleep_for(std::chrono::seconds{1});
4746-
4747- EXPECT_THAT(dispatch_count, Eq(1));
4748-}
4749-
4750-TEST_F(SimpleDispatchThreadTest, passes_dispatch_events_through)
4751-{
4752- using namespace testing;
4753-
4754- auto dispatched_with_only_readable = std::make_shared<mt::Signal>();
4755- auto dispatched_with_hangup = std::make_shared<mt::Signal>();
4756- auto delegate = [dispatched_with_only_readable, dispatched_with_hangup](md::FdEvents events)
4757- {
4758- if (events == md::FdEvent::readable)
4759- {
4760- dispatched_with_only_readable->raise();
4761- }
4762- if (events & md::FdEvent::remote_closed)
4763- {
4764- dispatched_with_hangup->raise();
4765- return false;
4766- }
4767- return true;
4768- };
4769- auto dispatchable =
4770- std::make_shared<mt::TestDispatchable>(delegate, md::FdEvent::readable | md::FdEvent::remote_closed);
4771-
4772- md::SimpleDispatchThread dispatcher{dispatchable};
4773-
4774- dispatchable->trigger();
4775- EXPECT_TRUE(dispatched_with_only_readable->wait_for(std::chrono::seconds{1}));
4776-
4777- dispatchable->hangup();
4778- EXPECT_TRUE(dispatched_with_hangup->wait_for(std::chrono::seconds{1}));
4779-}
4780-
4781-TEST_F(SimpleDispatchThreadTest, doesnt_call_dispatch_after_first_false_return)
4782-{
4783- using namespace testing;
4784- using namespace std::chrono_literals;
4785-
4786- int constexpr expected_count{10};
4787- auto const dispatched_more_than_enough = std::make_shared<mt::Signal>();
4788-
4789- auto delegate =
4790- [dispatched_more_than_enough, dispatch_count = 0](md::FdEvents) mutable
4791- {
4792- if (++dispatch_count == expected_count)
4793- {
4794- return false;
4795- }
4796- if (dispatch_count > expected_count)
4797- {
4798- dispatched_more_than_enough->raise();
4799- }
4800- return true;
4801- };
4802- auto const dispatchable = std::make_shared<mt::TestDispatchable>(delegate);
4803-
4804- md::SimpleDispatchThread dispatcher{dispatchable};
4805-
4806- for (int i = 0; i < expected_count + 1; ++i)
4807- {
4808- dispatchable->trigger();
4809- }
4810-
4811- EXPECT_FALSE(dispatched_more_than_enough->wait_for(1s));
4812-}
4813-
4814-TEST_F(SimpleDispatchThreadTest, only_calls_dispatch_with_remote_closed_when_relevant)
4815-{
4816- using namespace testing;
4817-
4818- auto dispatchable = std::make_shared<NiceMock<MockDispatchable>>();
4819- ON_CALL(*dispatchable, watch_fd()).WillByDefault(Return(test_fd));
4820- ON_CALL(*dispatchable, relevant_events()).WillByDefault(Return(md::FdEvent::writable));
4821- auto dispatched_writable = std::make_shared<mt::Signal>();
4822- auto dispatched_closed = std::make_shared<mt::Signal>();
4823-
4824- ON_CALL(*dispatchable, dispatch(_)).WillByDefault(Invoke([=](md::FdEvents events)
4825- {
4826- if (events & md::FdEvent::writable)
4827- {
4828- dispatched_writable->raise();
4829- }
4830- if (events & md::FdEvent::remote_closed)
4831- {
4832- dispatched_closed->raise();
4833- }
4834- return true;
4835- }));
4836-
4837- md::SimpleDispatchThread dispatcher{dispatchable};
4838-
4839- EXPECT_TRUE(dispatched_writable->wait_for(std::chrono::seconds{1}));
4840-
4841- // Make the fd remote-closed...
4842- watch_fd = mir::Fd{};
4843- EXPECT_FALSE(dispatched_closed->wait_for(std::chrono::seconds{1}));
4844-}
4845-
4846-// Regression test for: lp #1439719
4847-// The bug involves uninitialized memory and is also sensitive to signal
4848-// timings, so this test does not always catch the problem. However, repeated
4849-// runs (~300, YMMV) consistently fail when run against the problematic code.
4850-TEST_F(SimpleDispatchThreadTest, keeps_dispatching_after_signal_interruption)
4851-{
4852- using namespace std::chrono_literals;
4853- mt::CrossProcessAction stop_and_restart_process;
4854-
4855- auto child = mir_test_framework::fork_and_run_in_a_different_process(
4856- [&]
4857- {
4858- auto dispatched = std::make_shared<mt::Signal>();
4859- auto dispatchable = std::make_shared<mt::TestDispatchable>(
4860- [dispatched]() { dispatched->raise(); });
4861-
4862- md::SimpleDispatchThread dispatcher{dispatchable};
4863- // Ensure the dispatcher has started
4864- dispatchable->trigger();
4865- EXPECT_TRUE(dispatched->wait_for(1s));
4866-
4867- stop_and_restart_process();
4868-
4869- dispatched->reset();
4870- // The dispatcher shouldn't have been affected by the signal
4871- dispatchable->trigger();
4872- EXPECT_TRUE(dispatched->wait_for(1s));
4873- exit(HasFailure() ? EXIT_FAILURE : EXIT_SUCCESS);
4874- },
4875- []{ return 1; });
4876-
4877- stop_and_restart_process.exec(
4878- [child]
4879- {
4880- // Increase chances of interrupting the dispatch mechanism
4881- for (int i = 0; i < 100; ++i)
4882- {
4883- child->stop();
4884- child->cont();
4885- }
4886- });
4887-
4888- auto const result = child->wait_for_termination(10s);
4889- EXPECT_TRUE(result.succeeded());
4890-}
4891-
4892-using SimpleDispatchThreadDeathTest = SimpleDispatchThreadTest;
4893-
4894-TEST_F(SimpleDispatchThreadDeathTest, destroying_dispatcher_from_a_callback_is_an_error)
4895-{
4896- using namespace testing;
4897- using namespace std::literals::chrono_literals;
4898-
4899- EXPECT_EXIT(
4900- {
4901- std::mutex mutex;
4902- md::SimpleDispatchThread* dispatcher;
4903-
4904- auto dispatchable = std::make_shared<mt::TestDispatchable>([&dispatcher, &mutex]{
4905- std::lock_guard<decltype(mutex)> lock{mutex};
4906- delete dispatcher;
4907- });
4908-
4909- {
4910- std::lock_guard<decltype(mutex)> lock{mutex};
4911- dispatchable->trigger();
4912- dispatcher = new md::SimpleDispatchThread{dispatchable};
4913- }
4914- std::this_thread::sleep_for(10s);
4915- }, KilledBySignal(SIGABRT), ".*Destroying SimpleDispatchThread.*");
4916-}
4917-
4918-TEST_F(SimpleDispatchThreadTest, executes_exception_handler_with_current_exception)
4919-{
4920- using namespace std::chrono_literals;
4921- auto dispatched = std::make_shared<mt::Signal>();
4922- std::exception_ptr exception;
4923-
4924- auto dispatchable = std::make_shared<mt::TestDispatchable>(
4925- []()
4926- {
4927- throw std::runtime_error("thrown");
4928- });
4929-
4930- md::SimpleDispatchThread dispatcher{dispatchable,
4931- [&dispatched,&exception]()
4932- {
4933- exception = std::current_exception();
4934- if (exception)
4935- dispatched->raise();
4936- }};
4937- dispatchable->trigger();
4938- EXPECT_TRUE(dispatched->wait_for(10s));
4939- EXPECT_TRUE(exception!=nullptr);
4940-}
4941
4942=== added file 'tests/unit-tests/dispatch/test_threaded_dispatcher.cpp'
4943--- tests/unit-tests/dispatch/test_threaded_dispatcher.cpp 1970-01-01 00:00:00 +0000
4944+++ tests/unit-tests/dispatch/test_threaded_dispatcher.cpp 2015-05-14 07:50:26 +0000
4945@@ -0,0 +1,403 @@
4946+/*
4947+ * Copyright © 2015 Canonical Ltd.
4948+ *
4949+ * This program is free software: you can redistribute it and/or modify
4950+ * it under the terms of the GNU General Public License version 3 as
4951+ * published by the Free Software Foundation.
4952+ *
4953+ * This program is distributed in the hope that it will be useful,
4954+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4955+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4956+ * GNU General Public License for more details.
4957+ *
4958+ * You should have received a copy of the GNU General Public License
4959+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4960+ *
4961+ * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
4962+ */
4963+
4964+#include "mir/dispatch/threaded_dispatcher.h"
4965+#include "mir/dispatch/dispatchable.h"
4966+#include "mir/fd.h"
4967+#include "mir_test/pipe.h"
4968+#include "mir_test/signal.h"
4969+#include "mir_test/test_dispatchable.h"
4970+#include "mir_test_framework/process.h"
4971+#include "mir_test/cross_process_action.h"
4972+
4973+#include <fcntl.h>
4974+
4975+#include <atomic>
4976+#include <thread>
4977+
4978+#include <gtest/gtest.h>
4979+#include <gmock/gmock.h>
4980+
4981+namespace md = mir::dispatch;
4982+namespace mt = mir::test;
4983+
4984+namespace
4985+{
4986+class ThreadedDispatcherTest : public ::testing::Test
4987+{
4988+public:
4989+ ThreadedDispatcherTest()
4990+ {
4991+ mt::Pipe pipe{O_NONBLOCK};
4992+ watch_fd = pipe.read_fd();
4993+ test_fd = pipe.write_fd();
4994+ }
4995+
4996+ mir::Fd watch_fd;
4997+ mir::Fd test_fd;
4998+};
4999+
5000+class MockDispatchable : public md::Dispatchable
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches