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
=== modified file 'benchmarks/benchmark_multiplexing_dispatchable.cpp'
--- benchmarks/benchmark_multiplexing_dispatchable.cpp 2015-04-24 15:36:12 +0000
+++ benchmarks/benchmark_multiplexing_dispatchable.cpp 2015-05-14 07:50:26 +0000
@@ -17,9 +17,9 @@
17 */17 */
1818
19#include "mir/dispatch/multiplexing_dispatchable.h"19#include "mir/dispatch/multiplexing_dispatchable.h"
20#include "mir/dispatch/simple_dispatch_thread.h"
2120
22#include <iostream>21#include <iostream>
22#include <thread>
23#include <vector>23#include <vector>
24#include <memory>24#include <memory>
25#include <chrono>25#include <chrono>
2626
=== modified file 'debian/control'
--- debian/control 2015-05-08 15:40:20 +0000
+++ debian/control 2015-05-14 07:50:26 +0000
@@ -245,6 +245,7 @@
245Pre-Depends: ${misc:Pre-Depends}245Pre-Depends: ${misc:Pre-Depends}
246Depends: ${misc:Depends},246Depends: ${misc:Depends},
247 ${shlibs:Depends},247 ${shlibs:Depends},
248 umockdev,
248Recommends: mir-demos,249Recommends: mir-demos,
249Description: Display Server for Ubuntu - stress tests and other test tools250Description: Display Server for Ubuntu - stress tests and other test tools
250 Mir is a display server running on linux systems, with a focus on efficiency,251 Mir is a display server running on linux systems, with a focus on efficiency,
251252
=== modified file 'debian/mir-test-tools.install'
--- debian/mir-test-tools.install 2015-04-09 06:20:31 +0000
+++ debian/mir-test-tools.install 2015-05-14 07:50:26 +0000
@@ -8,3 +8,4 @@
8usr/lib/*/mir/server-platform/graphics-dummy.so8usr/lib/*/mir/server-platform/graphics-dummy.so
9usr/lib/*/mir/server-platform/input-stub.so9usr/lib/*/mir/server-platform/input-stub.so
10usr/lib/*/mir/client-platform/dummy.so10usr/lib/*/mir/client-platform/dummy.so
11usr/share/mir/udev_recordings
1112
=== modified file 'include/client/mir_toolkit/mir_connection.h'
--- include/client/mir_toolkit/mir_connection.h 2015-03-31 02:35:42 +0000
+++ include/client/mir_toolkit/mir_connection.h 2015-05-14 07:50:26 +0000
@@ -63,6 +63,35 @@
63MirConnection *mir_connect_sync(char const *server, char const *app_name);63MirConnection *mir_connect_sync(char const *server, char const *app_name);
6464
65/**65/**
66 * Request a connection to the Mir server.
67 *
68 * The client is responsible for handling event consumption via
69 * mir_connection_dispatch(). Events will be available when the fd returned
70 * by mir_connection_get_event_fd() becomes readable.
71 *
72 * All callbacks for this MirConnection and any objects created on it will be
73 * called from the thread calling mir_connection_dispatch().
74 *
75 * \param [in] server A string specifying the server to connect to.
76 * Connection strings can either be path to the socket file on
77 * the filesystem, or an open file descriptor fd://
78 * \param [in] app_name A name referring to the application
79 * \param [in] callback Callback function to be invoked when request
80 * completes. mir_connection_is_valid() will return false
81 * until this callback has completed.
82 * \param [in,out] context User data passed to the callback function
83 * \returns The resulting MirConnection
84 * \todo Currently manual dispatch interacts awkwardly with *_sync() calls;
85 * Synchronous calls do *not* automatically dispatch, so unless client
86 * code has a separate thread running dispatch, any *_sync() call will
87 * deadlock. This restriction will be fixed in a later release.
88 */
89MirConnection* mir_connect_with_manual_dispatch(char const* server,
90 char const* app_name,
91 mir_connected_callback callback,
92 void* context);
93
94/**
66 * Test for a valid connection95 * Test for a valid connection
67 * \param [in] connection The connection96 * \param [in] connection The connection
68 * \return True if the supplied connection is valid, or97 * \return True if the supplied connection is valid, or
@@ -87,6 +116,28 @@
87void mir_connection_release(MirConnection *connection);116void mir_connection_release(MirConnection *connection);
88117
89/**118/**
119 * \brief Get the notification fd for this connection
120 * \param [in] connection The connection. This connection must have been created by
121 * mir_connect_with_manual_dispatch().
122 * \return A file descriptor that supports select/poll/epoll and similar
123 * APIs that becomes readable when there are events to dispatch,
124 * or -1 if \ref connection was not created by
125 * mir_connect_with_manual_dispatch();
126 * \note This fd is owned by the MirConnection. Do not close the fd yourself.
127 * The fd remains valid until mir_connection_release() returns.
128 */
129int mir_connection_get_event_fd(MirConnection* connection);
130
131/**
132 * \brief Dispatch a single pending event on the connection
133 * \param [in] connection The connection. This connection must have been created by
134 * mir_connect_with_manual_dispatch().
135 * \note It is an error to call this on a \ref connection not created by
136 * mir_connect_with_manual_dispatch().
137 */
138void mir_connection_dispatch(MirConnection* connection);
139
140/**
90 * Query platform-specific data and/or file descriptors that are required to141 * Query platform-specific data and/or file descriptors that are required to
91 * initialize GL/EGL features.142 * initialize GL/EGL features.
92 * \param [in] connection The connection143 * \param [in] connection The connection
93144
=== modified file 'include/client/mir_toolkit/mir_wait.h'
--- include/client/mir_toolkit/mir_wait.h 2014-03-31 14:36:08 +0000
+++ include/client/mir_toolkit/mir_wait.h 2015-05-14 07:50:26 +0000
@@ -46,6 +46,7 @@
46 */46 */
47void mir_wait_for_one(MirWaitHandle *wait_handle);47void mir_wait_for_one(MirWaitHandle *wait_handle);
4848
49bool mir_wait_handle_ready(MirWaitHandle* wait_handle);
4950
50#ifdef __cplusplus51#ifdef __cplusplus
51}52}
5253
=== removed file 'include/common/mir/dispatch/simple_dispatch_thread.h'
--- include/common/mir/dispatch/simple_dispatch_thread.h 2015-04-23 18:29:35 +0000
+++ include/common/mir/dispatch/simple_dispatch_thread.h 1970-01-01 00:00:00 +0000
@@ -1,50 +0,0 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#ifndef MIR_DISPATCH_SIMPLE_DISPATCH_THREAD_H_
20#define MIR_DISPATCH_SIMPLE_DISPATCH_THREAD_H_
21
22#include <memory>
23#include <thread>
24#include "mir/fd.h"
25
26namespace mir
27{
28namespace dispatch
29{
30class Dispatchable;
31
32
33class SimpleDispatchThread
34{
35public:
36 SimpleDispatchThread(std::shared_ptr<Dispatchable> const& dispatchee);
37 SimpleDispatchThread(std::shared_ptr<Dispatchable> const& dispatchee,
38 std::function<void()> const& exception_handler);
39 ~SimpleDispatchThread() noexcept;
40
41private:
42 Fd shutdown_fd;
43 std::thread eventloop;
44};
45
46}
47}
48
49
50#endif // MIR_DISPATCH_SIMPLE_DISPATCH_THREAD_H_
510
=== added file 'include/common/mir/dispatch/threaded_dispatcher.h'
--- include/common/mir/dispatch/threaded_dispatcher.h 1970-01-01 00:00:00 +0000
+++ include/common/mir/dispatch/threaded_dispatcher.h 2015-05-14 07:50:26 +0000
@@ -0,0 +1,75 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#ifndef MIR_DISPATCH_SIMPLE_DISPATCH_THREAD_H_
20#define MIR_DISPATCH_SIMPLE_DISPATCH_THREAD_H_
21
22#include <string>
23#include <memory>
24#include <thread>
25#include <vector>
26#include <mutex>
27#include <condition_variable>
28
29#include "mir/dispatch/multiplexing_dispatchable.h"
30#include "mir/fd.h"
31
32namespace mir
33{
34namespace dispatch
35{
36class Dispatchable;
37
38class ThreadedDispatcher
39{
40public:
41 ThreadedDispatcher(std::string const& name, std::shared_ptr<Dispatchable> const& dispatchee);
42 ThreadedDispatcher(std::string const& name,
43 std::shared_ptr<Dispatchable> const& dispatchee,
44 std::function<void()> const& exception_handler);
45 ~ThreadedDispatcher() noexcept;
46
47 void add_thread();
48 void remove_thread();
49
50private:
51 class ThreadShutdownRequestHandler;
52 friend class ThreadShutdownRequestHandler;
53
54 std::string const name_base;
55
56 std::shared_ptr<ThreadShutdownRequestHandler> thread_exiter;
57 std::shared_ptr<MultiplexingDispatchable> dispatcher;
58
59 std::mutex thread_pool_mutex;
60 std::vector<std::thread> threadpool;
61
62 std::function<void()> const exception_handler;
63
64 static void dispatch_loop(std::string const& name,
65 std::shared_ptr<ThreadShutdownRequestHandler> thread_register,
66 std::shared_ptr<Dispatchable> dispatcher,
67 std::function<void()> const& exception_handler);
68
69};
70
71}
72}
73
74
75#endif // MIR_DISPATCH_SIMPLE_DISPATCH_THREAD_H_
076
=== modified file 'src/client/CMakeLists.txt'
--- src/client/CMakeLists.txt 2015-05-08 09:01:55 +0000
+++ src/client/CMakeLists.txt 2015-05-14 07:50:26 +0000
@@ -70,6 +70,7 @@
70 buffer_stream.cpp70 buffer_stream.cpp
71 mir_buffer_stream_api.cpp71 mir_buffer_stream_api.cpp
72 default_client_buffer_stream_factory.cpp72 default_client_buffer_stream_factory.cpp
73 synchronous_helper.cpp
73 ${MIR_CLIENT_SOURCES}74 ${MIR_CLIENT_SOURCES}
74)75)
7576
7677
=== modified file 'src/client/buffer_stream.cpp'
--- src/client/buffer_stream.cpp 2015-04-23 14:09:57 +0000
+++ src/client/buffer_stream.cpp 2015-05-14 07:50:26 +0000
@@ -87,13 +87,16 @@
87// TODO: It seems like a bit of a wart that we have to pass the Logger specifically here...perhaps87// TODO: It seems like a bit of a wart that we have to pass the Logger specifically here...perhaps
88// due to the lack of an easily mockable client configuration interface (passing around88// due to the lack of an easily mockable client configuration interface (passing around
89// connection can complicate unit tests ala MirSurface and test_client_mir_surface.cpp)89// connection can complicate unit tests ala MirSurface and test_client_mir_surface.cpp)
90mcl::BufferStream::BufferStream(mp::DisplayServer& server,90mcl::BufferStream::BufferStream(
91 MirConnection* allocating_connection,
92 mp::DisplayServer& server,
91 mcl::BufferStreamMode mode,93 mcl::BufferStreamMode mode,
92 std::shared_ptr<mcl::ClientPlatform> const& client_platform,94 std::shared_ptr<mcl::ClientPlatform> const& client_platform,
93 mp::BufferStream const& protobuf_bs,95 mp::BufferStream const& protobuf_bs,
94 std::shared_ptr<mcl::PerfReport> const& perf_report,96 std::shared_ptr<mcl::PerfReport> const& perf_report,
95 std::string const& surface_name)97 std::string const& surface_name)
96 : display_server(server),98 : connection{allocating_connection},
99 display_server(server),
97 mode(mode),100 mode(mode),
98 client_platform(client_platform),101 client_platform(client_platform),
99 protobuf_bs(protobuf_bs),102 protobuf_bs(protobuf_bs),
@@ -106,13 +109,16 @@
106 perf_report->name_surface(surface_name.c_str());109 perf_report->name_surface(surface_name.c_str());
107}110}
108111
109mcl::BufferStream::BufferStream(mp::DisplayServer& server,112mcl::BufferStream::BufferStream(
113 MirConnection* allocating_connection,
114 mp::DisplayServer& server,
110 std::shared_ptr<mcl::ClientPlatform> const& client_platform,115 std::shared_ptr<mcl::ClientPlatform> const& client_platform,
111 mp::BufferStreamParameters const& parameters,116 mp::BufferStreamParameters const& parameters,
112 std::shared_ptr<mcl::PerfReport> const& perf_report,117 std::shared_ptr<mcl::PerfReport> const& perf_report,
113 mir_buffer_stream_callback callback,118 mir_buffer_stream_callback callback,
114 void *context)119 void *context)
115 : display_server(server),120 : connection{allocating_connection},
121 display_server(server),
116 mode(BufferStreamMode::Producer),122 mode(BufferStreamMode::Producer),
117 client_platform(client_platform),123 client_platform(client_platform),
118 buffer_depository{client_platform->create_buffer_factory(), mir::frontend::client_buffer_cache_size},124 buffer_depository{client_platform->create_buffer_factory(), mir::frontend::client_buffer_cache_size},
@@ -146,7 +152,11 @@
146 egl_native_window_ = client_platform->create_egl_native_window(this);152 egl_native_window_ = client_platform->create_egl_native_window(this);
147153
148 if (callback)154 if (callback)
149 callback(reinterpret_cast<MirBufferStream*>(this), context);155 {
156 // Fun fact! The offset of mcl::ClientBufferStream's vtable in *this is *not* 0!
157 auto cbs_object = static_cast<mcl::ClientBufferStream*>(this);
158 callback(reinterpret_cast<MirBufferStream*>(cbs_object), context);
159 }
150 create_wait_handle.result_received();160 create_wait_handle.result_received();
151}161}
152162
@@ -382,7 +392,11 @@
382 mir_buffer_stream_callback callback, void* context)392 mir_buffer_stream_callback callback, void* context)
383{393{
384 if (callback)394 if (callback)
385 callback(reinterpret_cast<MirBufferStream*>(this), context);395 {
396 // Fun fact! The offset of mcl::ClientBufferStream's vtable in *this is *not* 0!
397 auto cbs_object = static_cast<mcl::ClientBufferStream*>(this);
398 callback(reinterpret_cast<MirBufferStream*>(cbs_object), context);
399 }
386 release_wait_handle.result_received();400 release_wait_handle.result_received();
387}401}
388402
@@ -403,3 +417,8 @@
403{417{
404 buffer_depository.set_max_buffers(cache_size);418 buffer_depository.set_max_buffers(cache_size);
405}419}
420
421MirConnection* mcl::BufferStream::get_connection() const
422{
423 return connection;
424}
406425
=== modified file 'src/client/buffer_stream.h'
--- src/client/buffer_stream.h 2015-04-23 14:09:57 +0000
+++ src/client/buffer_stream.h 2015-05-14 07:50:26 +0000
@@ -57,14 +57,18 @@
57class BufferStream : public EGLNativeSurface, public ClientBufferStream57class BufferStream : public EGLNativeSurface, public ClientBufferStream
58{58{
59public:59public:
60 BufferStream(mir::protobuf::DisplayServer& server,60 BufferStream(
61 MirConnection* allocating_connection,
62 mir::protobuf::DisplayServer& server,
61 BufferStreamMode mode,63 BufferStreamMode mode,
62 std::shared_ptr<ClientPlatform> const& native_window_factory,64 std::shared_ptr<ClientPlatform> const& native_window_factory,
63 protobuf::BufferStream const& protobuf_bs,65 protobuf::BufferStream const& protobuf_bs,
64 std::shared_ptr<PerfReport> const& perf_report,66 std::shared_ptr<PerfReport> const& perf_report,
65 std::string const& surface_name);67 std::string const& surface_name);
66 // For surfaceless buffer streams68 // For surfaceless buffer streams
67 BufferStream(mir::protobuf::DisplayServer& server,69 BufferStream(
70 MirConnection* allocating_connection,
71 mir::protobuf::DisplayServer& server,
68 std::shared_ptr<ClientPlatform> const& native_window_factory,72 std::shared_ptr<ClientPlatform> const& native_window_factory,
69 mir::protobuf::BufferStreamParameters const& parameters,73 mir::protobuf::BufferStreamParameters const& parameters,
70 std::shared_ptr<PerfReport> const& perf_report,74 std::shared_ptr<PerfReport> const& perf_report,
@@ -100,6 +104,8 @@
100104
101 frontend::BufferStreamId rpc_id() const override;105 frontend::BufferStreamId rpc_id() const override;
102 bool valid() const override;106 bool valid() const override;
107
108 MirConnection* get_connection() const override;
103 109
104protected:110protected:
105 BufferStream(BufferStream const&) = delete;111 BufferStream(BufferStream const&) = delete;
@@ -116,6 +122,8 @@
116122
117 mutable std::mutex mutex; // Protects all members of *this123 mutable std::mutex mutex; // Protects all members of *this
118124
125 MirConnection* const connection;
126
119 mir::protobuf::DisplayServer& display_server;127 mir::protobuf::DisplayServer& display_server;
120128
121 BufferStreamMode const mode;129 BufferStreamMode const mode;
122130
=== modified file 'src/client/client_buffer_stream.h'
--- src/client/client_buffer_stream.h 2015-04-01 20:00:19 +0000
+++ src/client/client_buffer_stream.h 2015-05-14 07:50:26 +0000
@@ -63,6 +63,8 @@
63 virtual MirWaitHandle* release(mir_buffer_stream_callback callback, void* context) = 0;63 virtual MirWaitHandle* release(mir_buffer_stream_callback callback, void* context) = 0;
6464
65 virtual bool valid() const = 0;65 virtual bool valid() const = 0;
66
67 virtual MirConnection* get_connection() const = 0;
66 68
67protected:69protected:
68 ClientBufferStream() = default;70 ClientBufferStream() = default;
6971
=== modified file 'src/client/client_buffer_stream_factory.h'
--- src/client/client_buffer_stream_factory.h 2015-04-01 20:00:19 +0000
+++ src/client/client_buffer_stream_factory.h 2015-05-14 07:50:26 +0000
@@ -25,6 +25,8 @@
2525
26#include <memory>26#include <memory>
2727
28class MirConnection;
29
28namespace mir30namespace mir
29{31{
30namespace client32namespace client
@@ -34,13 +36,21 @@
34class ClientBufferStreamFactory36class ClientBufferStreamFactory
35{37{
36public:38public:
37 virtual std::shared_ptr<ClientBufferStream> make_consumer_stream(protobuf::DisplayServer& server,39 virtual std::shared_ptr<ClientBufferStream> make_consumer_stream(
38 protobuf::BufferStream const& protobuf_bs, std::string const& surface_name) = 0;40 MirConnection* allocating_connection,
39 virtual std::shared_ptr<ClientBufferStream> make_producer_stream(protobuf::DisplayServer& server,41 protobuf::DisplayServer& server,
40 protobuf::BufferStream const& protobuf_bs, std::string const& surface_name) = 0;42 protobuf::BufferStream const& protobuf_bs,
43 std::string const& surface_name) = 0;
44 virtual std::shared_ptr<ClientBufferStream> make_producer_stream(
45 MirConnection* allocating_connection,
46 protobuf::DisplayServer& server,
47 protobuf::BufferStream const& protobuf_bs,
48 std::string const& surface_name) = 0;
4149
42 // For creating buffer stream owned by client.50 // For creating buffer stream owned by client.
43 virtual ClientBufferStream* make_producer_stream(protobuf::DisplayServer& server,51 virtual ClientBufferStream* make_producer_stream(
52 MirConnection* allocating_connection,
53 protobuf::DisplayServer& server,
44 protobuf::BufferStreamParameters const& params,54 protobuf::BufferStreamParameters const& params,
45 mir_buffer_stream_callback callback, void* context) = 0;55 mir_buffer_stream_callback callback, void* context) = 0;
4656
4757
=== modified file 'src/client/connection_configuration.h'
--- src/client/connection_configuration.h 2015-01-21 07:34:50 +0000
+++ src/client/connection_configuration.h 2015-05-14 07:50:26 +0000
@@ -49,13 +49,18 @@
49class EventSink;49class EventSink;
50class EventHandlerRegister;50class EventHandlerRegister;
5151
52namespace rpc
53{
54class MirBasicRpcChannel;
55}
56
52class ConnectionConfiguration57class ConnectionConfiguration
53{58{
54public:59public:
55 virtual ~ConnectionConfiguration() = default;60 virtual ~ConnectionConfiguration() = default;
5661
57 virtual std::shared_ptr<ConnectionSurfaceMap> the_surface_map() = 0;62 virtual std::shared_ptr<ConnectionSurfaceMap> the_surface_map() = 0;
58 virtual std::shared_ptr<google::protobuf::RpcChannel> the_rpc_channel() = 0;63 virtual std::shared_ptr<rpc::MirBasicRpcChannel> the_rpc_channel() = 0;
59 virtual std::shared_ptr<mir::logging::Logger> the_logger() = 0;64 virtual std::shared_ptr<mir::logging::Logger> the_logger() = 0;
60 virtual std::shared_ptr<ClientPlatformFactory> the_client_platform_factory() = 0;65 virtual std::shared_ptr<ClientPlatformFactory> the_client_platform_factory() = 0;
61 virtual std::shared_ptr<input::receiver::InputPlatform> the_input_platform() = 0;66 virtual std::shared_ptr<input::receiver::InputPlatform> the_input_platform() = 0;
6267
=== modified file 'src/client/default_client_buffer_stream_factory.cpp'
--- src/client/default_client_buffer_stream_factory.cpp 2015-04-01 20:00:19 +0000
+++ src/client/default_client_buffer_stream_factory.cpp 2015-05-14 07:50:26 +0000
@@ -57,21 +57,31 @@
57{57{
58}58}
5959
60std::shared_ptr<mcl::ClientBufferStream> mcl::DefaultClientBufferStreamFactory::make_consumer_stream(mp::DisplayServer& server,60std::shared_ptr<mcl::ClientBufferStream> mcl::DefaultClientBufferStreamFactory::make_consumer_stream(
61 mp::BufferStream const& protobuf_bs, std::string const& surface_name)61 MirConnection* allocating_connection,
62{62 mp::DisplayServer& server,
63 return std::make_shared<mcl::BufferStream>(server, mcl::BufferStreamMode::Consumer, client_platform, protobuf_bs, make_perf_report(logger), surface_name);63 mp::BufferStream const& protobuf_bs,
64}64 std::string const& surface_name)
6565{
66std::shared_ptr<mcl::ClientBufferStream> mcl::DefaultClientBufferStreamFactory::make_producer_stream(mp::DisplayServer& server,66 return std::make_shared<mcl::BufferStream>(allocating_connection, server, mcl::BufferStreamMode::Consumer, client_platform, protobuf_bs, make_perf_report(logger), surface_name);
67 mp::BufferStream const& protobuf_bs, std::string const& surface_name)67}
68{68
69 return std::make_shared<mcl::BufferStream>(server, mcl::BufferStreamMode::Producer, client_platform, protobuf_bs, make_perf_report(logger), surface_name);69std::shared_ptr<mcl::ClientBufferStream> mcl::DefaultClientBufferStreamFactory::make_producer_stream(
70}70 MirConnection* allocating_connection,
7171 mp::DisplayServer& server,
7272 mp::BufferStream const& protobuf_bs,
73mcl::ClientBufferStream* mcl::DefaultClientBufferStreamFactory::make_producer_stream(mp::DisplayServer& server,73 std::string const& surface_name)
74 mp::BufferStreamParameters const& params, mir_buffer_stream_callback callback, void* context)74{
75{75 return std::make_shared<mcl::BufferStream>(allocating_connection, server, mcl::BufferStreamMode::Producer, client_platform, protobuf_bs, make_perf_report(logger), surface_name);
76 return new mcl::BufferStream(server, client_platform, params, make_perf_report(logger), callback, context);76}
77
78
79mcl::ClientBufferStream* mcl::DefaultClientBufferStreamFactory::make_producer_stream(
80 MirConnection* allocating_connection,
81 mp::DisplayServer& server,
82 mp::BufferStreamParameters const& params,
83 mir_buffer_stream_callback callback,
84 void* context)
85{
86 return new mcl::BufferStream(allocating_connection, server, client_platform, params, make_perf_report(logger), callback, context);
77}87}
7888
=== modified file 'src/client/default_client_buffer_stream_factory.h'
--- src/client/default_client_buffer_stream_factory.h 2015-04-01 20:00:19 +0000
+++ src/client/default_client_buffer_stream_factory.h 2015-05-14 07:50:26 +0000
@@ -21,6 +21,8 @@
2121
22#include "client_buffer_stream_factory.h"22#include "client_buffer_stream_factory.h"
2323
24class MirConnection;
25
24namespace mir26namespace mir
25{27{
26namespace logging28namespace logging
@@ -40,14 +42,22 @@
40 std::shared_ptr<logging::Logger> const& logger);42 std::shared_ptr<logging::Logger> const& logger);
41 virtual ~DefaultClientBufferStreamFactory() = default;43 virtual ~DefaultClientBufferStreamFactory() = default;
4244
43 std::shared_ptr<ClientBufferStream> make_consumer_stream(protobuf::DisplayServer& server,45 std::shared_ptr<ClientBufferStream> make_consumer_stream(
44 protobuf::BufferStream const& protobuf_bs, std::string const& surface_name);46 MirConnection* allocating_connection,
45 std::shared_ptr<ClientBufferStream> make_producer_stream(protobuf::DisplayServer& server,47 protobuf::DisplayServer& server,
46 protobuf::BufferStream const& protobuf_bs, std::string const& surface_name);48 protobuf::BufferStream const& protobuf_bs,
49 std::string const& surface_name) override;
50 std::shared_ptr<ClientBufferStream> make_producer_stream(
51 MirConnection* allocating_connection,
52 protobuf::DisplayServer& server,
53 protobuf::BufferStream const& protobuf_bs,
54 std::string const& surface_name) override;
4755
48 ClientBufferStream* make_producer_stream(protobuf::DisplayServer& server,56 ClientBufferStream* make_producer_stream(
57 MirConnection* allocating_connection,
58 protobuf::DisplayServer& server,
49 protobuf::BufferStreamParameters const& params,59 protobuf::BufferStreamParameters const& params,
50 mir_buffer_stream_callback callback, void* context);60 mir_buffer_stream_callback callback, void* context) override;
5161
52private:62private:
53 std::shared_ptr<ClientPlatform> const client_platform;63 std::shared_ptr<ClientPlatform> const client_platform;
5464
=== modified file 'src/client/default_connection_configuration.cpp'
--- src/client/default_connection_configuration.cpp 2015-03-31 02:35:42 +0000
+++ src/client/default_connection_configuration.cpp 2015-05-14 07:50:26 +0000
@@ -85,7 +85,7 @@
85 });85 });
86}86}
8787
88std::shared_ptr<google::protobuf::RpcChannel>88std::shared_ptr<mcl::rpc::MirBasicRpcChannel>
89mcl::DefaultConnectionConfiguration::the_rpc_channel()89mcl::DefaultConnectionConfiguration::the_rpc_channel()
90{90{
91 return rpc_channel(91 return rpc_channel(
9292
=== modified file 'src/client/default_connection_configuration.h'
--- src/client/default_connection_configuration.h 2015-01-21 07:34:50 +0000
+++ src/client/default_connection_configuration.h 2015-05-14 07:50:26 +0000
@@ -51,7 +51,7 @@
51 DefaultConnectionConfiguration(std::string const& socket_file);51 DefaultConnectionConfiguration(std::string const& socket_file);
5252
53 std::shared_ptr<ConnectionSurfaceMap> the_surface_map() override;53 std::shared_ptr<ConnectionSurfaceMap> the_surface_map() override;
54 std::shared_ptr<google::protobuf::RpcChannel> the_rpc_channel() override;54 std::shared_ptr<rpc::MirBasicRpcChannel> the_rpc_channel() override;
55 std::shared_ptr<mir::logging::Logger> the_logger() override;55 std::shared_ptr<mir::logging::Logger> the_logger() override;
56 std::shared_ptr<ClientPlatformFactory> the_client_platform_factory() override;56 std::shared_ptr<ClientPlatformFactory> the_client_platform_factory() override;
57 std::shared_ptr<input::receiver::InputPlatform> the_input_platform() override;57 std::shared_ptr<input::receiver::InputPlatform> the_input_platform() override;
@@ -66,7 +66,7 @@
66 virtual std::shared_ptr<input::receiver::InputReceiverReport> the_input_receiver_report();66 virtual std::shared_ptr<input::receiver::InputReceiverReport> the_input_receiver_report();
6767
68protected:68protected:
69 CachedPtr<google::protobuf::RpcChannel> rpc_channel;69 CachedPtr<rpc::MirBasicRpcChannel> rpc_channel;
70 CachedPtr<mir::logging::Logger> logger;70 CachedPtr<mir::logging::Logger> logger;
71 CachedPtr<ClientPlatformFactory> client_platform_factory;71 CachedPtr<ClientPlatformFactory> client_platform_factory;
72 CachedPtr<input::receiver::InputPlatform> input_platform;72 CachedPtr<input::receiver::InputPlatform> input_platform;
7373
=== modified file 'src/client/mir_buffer_stream_api.cpp'
--- src/client/mir_buffer_stream_api.cpp 2015-03-31 02:35:42 +0000
+++ src/client/mir_buffer_stream_api.cpp 2015-05-14 07:50:26 +0000
@@ -28,6 +28,8 @@
2828
29#include "mir/uncaught.h"29#include "mir/uncaught.h"
3030
31#include "synchronous_helper.h"
32
31#include <stdexcept>33#include <stdexcept>
32#include <boost/throw_exception.hpp>34#include <boost/throw_exception.hpp>
3335
@@ -49,13 +51,6 @@
49 mcl::ClientBufferStream *bs = reinterpret_cast<mcl::ClientBufferStream*>(stream);51 mcl::ClientBufferStream *bs = reinterpret_cast<mcl::ClientBufferStream*>(stream);
50 delete bs;52 delete bs;
51}53}
52// assign_result is compatible with all 2-parameter callbacks
53void assign_result(void* result, void** context)
54{
55 if (context)
56 *context = result;
57}
58
59}54}
6055
6156
@@ -74,7 +69,7 @@
74 params.set_pixel_format(format);69 params.set_pixel_format(format);
75 params.set_buffer_usage(buffer_usage);70 params.set_buffer_usage(buffer_usage);
7671
77 return connection->get_client_buffer_stream_factory()->make_producer_stream(connection->display_server(), params, callback, context)72 return connection->get_client_buffer_stream_factory()->make_producer_stream(connection, connection->display_server(), params, callback, context)
78 ->get_create_wait_handle();73 ->get_create_wait_handle();
79}74}
80catch (std::exception const& ex)75catch (std::exception const& ex)
@@ -89,10 +84,14 @@
89 MirBufferUsage buffer_usage)84 MirBufferUsage buffer_usage)
90try85try
91{86{
92 mcl::BufferStream *stream = nullptr;87 MirBufferStream* stream = nullptr;
93 mir_connection_create_buffer_stream(connection, width, height, format, buffer_usage,88 make_synchronous_call(connection,
94 reinterpret_cast<mir_buffer_stream_callback>(assign_result), &stream)->wait_for_all();89 mir_connection_create_buffer_stream,
95 return reinterpret_cast<MirBufferStream*>(dynamic_cast<mcl::ClientBufferStream*>(stream));90 connection,
91 width, height, format, buffer_usage,
92 &assign_result<MirBufferStream>,
93 &stream);
94 return stream;
96}95}
97catch (std::exception const& ex)96catch (std::exception const& ex)
98{97{
@@ -113,7 +112,11 @@
113void mir_buffer_stream_release_sync(MirBufferStream *buffer_stream)112void mir_buffer_stream_release_sync(MirBufferStream *buffer_stream)
114{113{
115 mcl::ClientBufferStream *bs = reinterpret_cast<mcl::ClientBufferStream*>(buffer_stream);114 mcl::ClientBufferStream *bs = reinterpret_cast<mcl::ClientBufferStream*>(buffer_stream);
116 bs->release(nullptr, nullptr)->wait_for_all();115 make_synchronous_call(bs->get_connection(),
116 std::mem_fn(&mcl::ClientBufferStream::release),
117 bs,
118 &assign_result<MirBufferStream>,
119 static_cast<void*>(nullptr));
117 delete bs;120 delete bs;
118}121}
119122
@@ -148,9 +151,13 @@
148151
149void mir_buffer_stream_swap_buffers_sync(MirBufferStream* buffer_stream)152void mir_buffer_stream_swap_buffers_sync(MirBufferStream* buffer_stream)
150{153{
151 mir_wait_for(mir_buffer_stream_swap_buffers(buffer_stream,154 mcl::ClientBufferStream *bs = reinterpret_cast<mcl::ClientBufferStream*>(buffer_stream);
152 reinterpret_cast<mir_buffer_stream_callback>(assign_result),155 make_synchronous_call(
153 nullptr));156 bs->get_connection(),
157 &mir_buffer_stream_swap_buffers,
158 buffer_stream,
159 &assign_result<MirBufferStream>,
160 static_cast<void*>(nullptr));
154}161}
155162
156void mir_buffer_stream_get_graphics_region(163void mir_buffer_stream_get_graphics_region(
157164
=== modified file 'src/client/mir_connection.cpp'
--- src/client/mir_connection.cpp 2015-04-01 19:39:19 +0000
+++ src/client/mir_connection.cpp 2015-05-14 07:50:26 +0000
@@ -24,8 +24,10 @@
24#include "mir/client_platform.h"24#include "mir/client_platform.h"
25#include "mir/client_platform_factory.h"25#include "mir/client_platform_factory.h"
26#include "rpc/mir_basic_rpc_channel.h"26#include "rpc/mir_basic_rpc_channel.h"
27#include "rpc/mir_protobuf_rpc_channel.h"
27#include "mir/dispatch/dispatchable.h"28#include "mir/dispatch/dispatchable.h"
28#include "mir/dispatch/simple_dispatch_thread.h"29#include "mir/dispatch/multiplexing_dispatchable.h"
30#include "mir/dispatch/threaded_dispatcher.h"
29#include "connection_configuration.h"31#include "connection_configuration.h"
30#include "display_configuration.h"32#include "display_configuration.h"
31#include "connection_surface_map.h"33#include "connection_surface_map.h"
@@ -33,6 +35,7 @@
3335
34#include "mir/events/event_builders.h"36#include "mir/events/event_builders.h"
35#include "mir/logging/logger.h"37#include "mir/logging/logger.h"
38#include "mir/require.h"
3639
37#include <algorithm>40#include <algorithm>
38#include <cstddef>41#include <cstddef>
@@ -42,6 +45,7 @@
42#include <boost/exception/diagnostic_information.hpp>45#include <boost/exception/diagnostic_information.hpp>
4346
44namespace mcl = mir::client;47namespace mcl = mir::client;
48namespace mclr = mir::client::rpc;
45namespace md = mir::dispatch;49namespace md = mir::dispatch;
46namespace mircv = mir::input::receiver;50namespace mircv = mir::input::receiver;
47namespace mev = mir::events;51namespace mev = mir::events;
@@ -99,10 +103,15 @@
99{103{
100}104}
101105
106MirConnection::MirConnection(mir::client::ConnectionConfiguration& conf) :
107 MirConnection(conf, DispatchType::automatic)
108{
109}
110
102MirConnection::MirConnection(111MirConnection::MirConnection(
103 mir::client::ConnectionConfiguration& conf) :112 mir::client::ConnectionConfiguration& conf, DispatchType dispatch) :
104 deregisterer{this},113 deregisterer{this},
105 channel(conf.the_rpc_channel()),114 channel{conf.the_rpc_channel()},
106 server(channel.get(), ::google::protobuf::Service::STUB_DOESNT_OWN_CHANNEL),115 server(channel.get(), ::google::protobuf::Service::STUB_DOESNT_OWN_CHANNEL),
107 debug(channel.get(), ::google::protobuf::Service::STUB_DOESNT_OWN_CHANNEL),116 debug(channel.get(), ::google::protobuf::Service::STUB_DOESNT_OWN_CHANNEL),
108 logger(conf.the_logger()),117 logger(conf.the_logger()),
@@ -113,7 +122,11 @@
113 lifecycle_control(conf.the_lifecycle_control()),122 lifecycle_control(conf.the_lifecycle_control()),
114 surface_map(conf.the_surface_map()),123 surface_map(conf.the_surface_map()),
115 event_handler_register(conf.the_event_handler_register()),124 event_handler_register(conf.the_event_handler_register()),
116 eventloop{new md::SimpleDispatchThread{std::dynamic_pointer_cast<md::Dispatchable>(channel)}}125 dispatcher{std::shared_ptr<md::MultiplexingDispatchable>(new md::MultiplexingDispatchable{channel})},
126 eventloop{dispatch == DispatchType::automatic ?
127 new md::ThreadedDispatcher{"I/O loop", dispatcher} :
128 nullptr
129 }
117{130{
118 connect_result.set_error("connect not called");131 connect_result.set_error("connect not called");
119 {132 {
@@ -325,6 +338,36 @@
325 return &disconnect_wait_handle;338 return &disconnect_wait_handle;
326}339}
327340
341mir::Fd MirConnection::watch_fd() const
342{
343 return eventloop ? mir::Fd{} : dispatcher->watch_fd();
344}
345
346void MirConnection::dispatch()
347{
348 mir::require(!eventloop);
349
350 dispatcher->dispatch(md::FdEvent::readable);
351}
352
353void MirConnection::add_dispatchee(std::shared_ptr<mir::dispatch::Dispatchable> const& dispatchee)
354{
355 dispatcher->add_watch(dispatchee);
356 if (eventloop)
357 {
358 eventloop->add_thread();
359 }
360}
361
362void MirConnection::remove_dispatchee(std::shared_ptr<mir::dispatch::Dispatchable> const& dispatchee)
363{
364 dispatcher->remove_watch(dispatchee);
365 if (eventloop)
366 {
367 eventloop->remove_thread();
368 }
369}
370
328void MirConnection::done_platform_operation(371void MirConnection::done_platform_operation(
329 mir_platform_operation_callback callback, void* context)372 mir_platform_operation_callback callback, void* context)
330{373{
@@ -568,3 +611,8 @@
568{611{
569 return logger;612 return logger;
570}613}
614
615void MirConnection::process_next_request_first()
616{
617 channel->process_next_request_first();
618}
571619
=== modified file 'src/client/mir_connection.h'
--- src/client/mir_connection.h 2015-03-31 02:35:42 +0000
+++ src/client/mir_connection.h 2015-05-14 07:50:26 +0000
@@ -26,6 +26,8 @@
2626
27#include <mutex>27#include <mutex>
2828
29#include "rpc/mir_basic_rpc_channel.h"
30
29#include "mir_protobuf.pb.h"31#include "mir_protobuf.pb.h"
3032
31#include "mir_toolkit/mir_client_library.h"33#include "mir_toolkit/mir_client_library.h"
@@ -36,6 +38,8 @@
3638
37#include "mir_wait_handle.h"39#include "mir_wait_handle.h"
3840
41#include "mir/fd.h"
42
39namespace mir43namespace mir
40{44{
41class SharedLibrary;45class SharedLibrary;
@@ -51,10 +55,6 @@
51class LifecycleControl;55class LifecycleControl;
52class EventHandlerRegister;56class EventHandlerRegister;
5357
54namespace rpc
55{
56class MirBasicRpcChannel;
57}
58}58}
5959
60namespace input60namespace input
@@ -72,9 +72,17 @@
7272
73namespace dispatch73namespace dispatch
74{74{
75class SimpleDispatchThread;75class ThreadedDispatcher;
76}76class Dispatchable;
77}77class MultiplexingDispatchable;
78}
79}
80
81enum class DispatchType
82{
83 automatic,
84 manual
85};
7886
79struct MirConnection : mir::client::ClientContext87struct MirConnection : mir::client::ClientContext
80{88{
@@ -82,6 +90,7 @@
82 MirConnection(std::string const& error_message);90 MirConnection(std::string const& error_message);
8391
84 MirConnection(mir::client::ConnectionConfiguration& conf);92 MirConnection(mir::client::ConnectionConfiguration& conf);
93 MirConnection(mir::client::ConnectionConfiguration &conf, DispatchType dispatch);
85 ~MirConnection() noexcept;94 ~MirConnection() noexcept;
8695
87 MirConnection(MirConnection const &) = delete;96 MirConnection(MirConnection const &) = delete;
@@ -107,6 +116,12 @@
107116
108 MirWaitHandle* disconnect();117 MirWaitHandle* disconnect();
109118
119 mir::Fd watch_fd() const;
120 void dispatch();
121
122 void add_dispatchee(std::shared_ptr<mir::dispatch::Dispatchable> const& dispatchee);
123 void remove_dispatchee(std::shared_ptr<mir::dispatch::Dispatchable> const& dispatchee);
124
110 MirWaitHandle* platform_operation(125 MirWaitHandle* platform_operation(
111 MirPlatformMessage const* request,126 MirPlatformMessage const* request,
112 mir_platform_operation_callback callback, void* context);127 mir_platform_operation_callback callback, void* context);
@@ -140,6 +155,7 @@
140 mir::protobuf::DisplayServer& display_server();155 mir::protobuf::DisplayServer& display_server();
141 std::shared_ptr<mir::logging::Logger> const& the_logger() const;156 std::shared_ptr<mir::logging::Logger> const& the_logger() const;
142157
158 void process_next_request_first();
143private:159private:
144 void populate_server_package(MirPlatformPackage& platform_package) override;160 void populate_server_package(MirPlatformPackage& platform_package) override;
145 // MUST be first data member so it is destroyed last.161 // MUST be first data member so it is destroyed last.
@@ -152,7 +168,7 @@
152168
153 std::mutex mutex; // Protects all members of *this (except release_wait_handles)169 std::mutex mutex; // Protects all members of *this (except release_wait_handles)
154170
155 std::shared_ptr<google::protobuf::RpcChannel> const channel;171 std::shared_ptr<mir::client::rpc::MirBasicRpcChannel> const channel;
156 mir::protobuf::DisplayServer::Stub server;172 mir::protobuf::DisplayServer::Stub server;
157 mir::protobuf::Debug::Stub debug;173 mir::protobuf::Debug::Stub debug;
158 std::shared_ptr<mir::logging::Logger> const logger;174 std::shared_ptr<mir::logging::Logger> const logger;
@@ -189,7 +205,9 @@
189205
190 std::shared_ptr<mir::client::EventHandlerRegister> const event_handler_register;206 std::shared_ptr<mir::client::EventHandlerRegister> const event_handler_register;
191207
192 std::unique_ptr<mir::dispatch::SimpleDispatchThread> const eventloop;208 std::shared_ptr<mir::dispatch::MultiplexingDispatchable> const dispatcher;
209 std::unique_ptr<mir::dispatch::ThreadedDispatcher> const eventloop;
210
193 211
194 std::shared_ptr<mir::client::ClientBufferStreamFactory> buffer_stream_factory;212 std::shared_ptr<mir::client::ClientBufferStreamFactory> buffer_stream_factory;
195213
196214
=== modified file 'src/client/mir_connection_api.cpp'
--- src/client/mir_connection_api.cpp 2015-03-31 02:35:42 +0000
+++ src/client/mir_connection_api.cpp 2015-05-14 07:50:26 +0000
@@ -39,6 +39,7 @@
39#include <unordered_set>39#include <unordered_set>
40#include <cstddef>40#include <cstddef>
41#include <cstring>41#include <cstring>
42#include <poll.h>
4243
43namespace mcl = mir::client;44namespace mcl = mir::client;
4445
@@ -98,7 +99,20 @@
98 try99 try
99 {100 {
100 auto wait_handle = connection->disconnect();101 auto wait_handle = connection->disconnect();
101 wait_handle->wait_for_all();102 if (connection->watch_fd() != mir::Fd::invalid)
103 {
104 pollfd fd;
105 fd.fd = connection->watch_fd();
106 fd.events = POLLIN;
107 while(!wait_handle->ready() && (poll(&fd, 1, -1) > 0))
108 {
109 connection->dispatch();
110 }
111 }
112 else
113 {
114 wait_handle->wait_for_all();
115 }
102 }116 }
103 catch (std::exception const& ex)117 catch (std::exception const& ex)
104 {118 {
@@ -165,6 +179,41 @@
165 return conn;179 return conn;
166}180}
167181
182MirConnection* mir_connect_with_manual_dispatch(
183 char const* server,
184 char const* app_name,
185 mir_connected_callback callback,
186 void* context)
187{
188 try
189 {
190 std::string sock;
191 if (server)
192 sock = server;
193 else
194 {
195 auto socket_env = getenv("MIR_SOCKET");
196 if (socket_env)
197 sock = socket_env;
198 else
199 sock = mir::default_server_socket;
200 }
201
202 mcl::DefaultConnectionConfiguration conf{sock};
203
204 std::unique_ptr<MirConnection> connection{new MirConnection(conf, DispatchType::manual)};
205 connection->connect(app_name, callback, context);
206 return connection.release();
207 }
208 catch (std::exception const& x)
209 {
210 MirConnection* error_connection = new MirConnection(x.what());
211 mcl::ErrorConnections::instance().insert(error_connection);
212 callback(error_connection, context);
213 return error_connection;
214 }
215}
216
168bool mir_connection_is_valid(MirConnection* connection)217bool mir_connection_is_valid(MirConnection* connection)
169{218{
170 return MirConnection::is_valid(connection);219 return MirConnection::is_valid(connection);
@@ -187,6 +236,16 @@
187 }236 }
188}237}
189238
239int mir_connection_get_event_fd(MirConnection* connection)
240{
241 return connection->watch_fd();
242}
243
244void mir_connection_dispatch(MirConnection* connection)
245{
246 connection->dispatch();
247}
248
190void mir_connection_get_platform(249void mir_connection_get_platform(
191 MirConnection* connection,250 MirConnection* connection,
192 MirPlatformPackage* platform_package)251 MirPlatformPackage* platform_package)
193252
=== modified file 'src/client/mir_prompt_session_api.cpp'
--- src/client/mir_prompt_session_api.cpp 2015-03-31 02:35:42 +0000
+++ src/client/mir_prompt_session_api.cpp 2015-05-14 07:50:26 +0000
@@ -24,6 +24,8 @@
2424
25#include "mir/uncaught.h"25#include "mir/uncaught.h"
2626
27#include "synchronous_helper.h"
28
27#include <stdexcept>29#include <stdexcept>
28#include <boost/throw_exception.hpp>30#include <boost/throw_exception.hpp>
2931
@@ -44,9 +46,14 @@
44 if (state_change_callback)46 if (state_change_callback)
45 prompt_session->register_prompt_session_state_change_callback(state_change_callback, context);47 prompt_session->register_prompt_session_state_change_callback(state_change_callback, context);
4648
47 mir_wait_for(prompt_session->start(application_pid,49 make_synchronous_call(
48 null_callback,50 connection,
49 nullptr));51 std::mem_fn(&MirPromptSession::start),
52 prompt_session,
53 application_pid,
54 &assign_result<MirPromptSession>,
55 static_cast<void*>(nullptr));
56
50 return prompt_session;57 return prompt_session;
51 }58 }
52 catch (std::exception const& ex)59 catch (std::exception const& ex)
5360
=== modified file 'src/client/mir_screencast.cpp'
--- src/client/mir_screencast.cpp 2015-04-25 11:38:31 +0000
+++ src/client/mir_screencast.cpp 2015-05-14 07:50:26 +0000
@@ -30,13 +30,15 @@
30namespace geom = mir::geometry;30namespace geom = mir::geometry;
3131
32MirScreencast::MirScreencast(32MirScreencast::MirScreencast(
33 MirConnection* allocating_connection,
33 geom::Rectangle const& region,34 geom::Rectangle const& region,
34 geom::Size const& size,35 geom::Size const& size,
35 MirPixelFormat pixel_format,36 MirPixelFormat pixel_format,
36 mir::protobuf::DisplayServer& server,37 mir::protobuf::DisplayServer& server,
37 std::shared_ptr<mcl::ClientBufferStreamFactory> const& buffer_stream_factory,38 std::shared_ptr<mcl::ClientBufferStreamFactory> const& buffer_stream_factory,
38 mir_screencast_callback callback, void* context)39 mir_screencast_callback callback, void* context)
39 : server(server),40 : allocating_connection{allocating_connection},
41 server(server),
40 output_size{size},42 output_size{size},
41 buffer_stream_factory{buffer_stream_factory}43 buffer_stream_factory{buffer_stream_factory}
42{44{
@@ -102,12 +104,16 @@
102}104}
103105
104void MirScreencast::screencast_created(106void MirScreencast::screencast_created(
105 mir_screencast_callback callback, void* context)107 mir_screencast_callback callback,
108 void* context)
106{109{
107 if (!protobuf_screencast.has_error())110 if (!protobuf_screencast.has_error())
108 {111 {
109 buffer_stream = buffer_stream_factory->make_consumer_stream(server,112 buffer_stream = buffer_stream_factory->make_consumer_stream(
110 protobuf_screencast.buffer_stream(), "MirScreencast");113 allocating_connection,
114 server,
115 protobuf_screencast.buffer_stream(),
116 "MirScreencast");
111 }117 }
112118
113 callback(this, context);119 callback(this, context);
@@ -125,3 +131,8 @@
125{131{
126 return buffer_stream.get();132 return buffer_stream.get();
127}133}
134
135MirConnection* MirScreencast::get_connection()
136{
137 return allocating_connection;
138}
128139
=== modified file 'src/client/mir_screencast.h'
--- src/client/mir_screencast.h 2015-03-31 02:35:42 +0000
+++ src/client/mir_screencast.h 2015-05-14 07:50:26 +0000
@@ -41,6 +41,7 @@
41{41{
42public:42public:
43 MirScreencast(43 MirScreencast(
44 MirConnection* allocating_connection,
44 mir::geometry::Rectangle const& region,45 mir::geometry::Rectangle const& region,
45 mir::geometry::Size const& size,46 mir::geometry::Size const& size,
46 MirPixelFormat pixel_format,47 MirPixelFormat pixel_format,
@@ -60,12 +61,14 @@
6061
61 mir::client::ClientBufferStream* get_buffer_stream();62 mir::client::ClientBufferStream* get_buffer_stream();
6263
64 MirConnection* get_connection();
63private:65private:
64 void screencast_created(66 void screencast_created(
65 mir_screencast_callback callback, void* context);67 mir_screencast_callback callback, void* context);
66 void released(68 void released(
67 mir_screencast_callback callback, void* context);69 mir_screencast_callback callback, void* context);
6870
71 MirConnection* const allocating_connection;
69 mir::protobuf::DisplayServer& server;72 mir::protobuf::DisplayServer& server;
70 mir::geometry::Size const output_size;73 mir::geometry::Size const output_size;
71 std::shared_ptr<mir::client::ClientBufferStreamFactory> const buffer_stream_factory;74 std::shared_ptr<mir::client::ClientBufferStreamFactory> const buffer_stream_factory;
7275
=== modified file 'src/client/mir_screencast_api.cpp'
--- src/client/mir_screencast_api.cpp 2015-03-31 02:35:42 +0000
+++ src/client/mir_screencast_api.cpp 2015-05-14 07:50:26 +0000
@@ -25,6 +25,8 @@
2525
26#include "mir/uncaught.h"26#include "mir/uncaught.h"
2727
28#include "synchronous_helper.h"
29
28#include <stdexcept>30#include <stdexcept>
29#include <boost/throw_exception.hpp>31#include <boost/throw_exception.hpp>
3032
@@ -56,6 +58,7 @@
5658
57 std::unique_ptr<MirScreencast> screencast_uptr{59 std::unique_ptr<MirScreencast> screencast_uptr{
58 new MirScreencast{60 new MirScreencast{
61 connection,
59 region,62 region,
60 size,63 size,
61 parameters->pixel_format,64 parameters->pixel_format,
@@ -63,7 +66,18 @@
63 connection->get_client_buffer_stream_factory(),66 connection->get_client_buffer_stream_factory(),
64 null_callback, nullptr}};67 null_callback, nullptr}};
6568
66 screencast_uptr->creation_wait_handle()->wait_for_all();69
70 if (connection->watch_fd() != mir::Fd::invalid)
71 {
72 dispatch_connection_until(connection, [&screencast_uptr]()
73 {
74 return screencast_uptr->creation_wait_handle()->ready();
75 });
76 }
77 else
78 {
79 screencast_uptr->creation_wait_handle()->wait_for_all();
80 }
6781
68 if (screencast_uptr->valid())82 if (screencast_uptr->valid())
69 {83 {
@@ -82,7 +96,11 @@
8296
83void mir_screencast_release_sync(MirScreencast* screencast)97void mir_screencast_release_sync(MirScreencast* screencast)
84{98{
85 screencast->release(null_callback, nullptr)->wait_for_all();99 make_synchronous_call(screencast->get_connection(),
100 std::mem_fn(&MirScreencast::release),
101 screencast,
102 &assign_result<MirScreencast>,
103 static_cast<void*>(nullptr));
86 delete screencast;104 delete screencast;
87}105}
88106
89107
=== modified file 'src/client/mir_surface.cpp'
--- src/client/mir_surface.cpp 2015-04-28 10:30:44 +0000
+++ src/client/mir_surface.cpp 2015-05-14 07:50:26 +0000
@@ -23,7 +23,7 @@
23#include "cursor_configuration.h"23#include "cursor_configuration.h"
24#include "client_buffer_stream_factory.h"24#include "client_buffer_stream_factory.h"
25#include "mir_connection.h"25#include "mir_connection.h"
26#include "mir/dispatch/simple_dispatch_thread.h"26#include "mir/dispatch/threaded_dispatcher.h"
27#include "mir/input/input_platform.h"27#include "mir/input/input_platform.h"
28#include "mir/input/xkb_mapper.h"28#include "mir/input/xkb_mapper.h"
2929
@@ -179,7 +179,10 @@
179179
180 std::lock_guard<decltype(mutex)> lock(mutex);180 std::lock_guard<decltype(mutex)> lock(mutex);
181181
182 input_thread.reset();182 if (input_dispatcher)
183 {
184 connection->remove_dispatchee(input_dispatcher);
185 }
183186
184 for (auto i = 0, end = surface.fd_size(); i != end; ++i)187 for (auto i = 0, end = surface.fd_size(); i != end; ++i)
185 close(surface.fd(i));188 close(surface.fd(i));
@@ -220,6 +223,11 @@
220 return false;223 return false;
221}224}
222225
226MirConnection* MirSurface::get_connection() const
227{
228 return connection;
229}
230
223MirWaitHandle* MirSurface::get_create_wait_handle()231MirWaitHandle* MirSurface::get_create_wait_handle()
224{232{
225 return &create_wait_handle;233 return &create_wait_handle;
@@ -252,7 +260,7 @@
252 std::lock_guard<decltype(mutex)> lock(mutex);260 std::lock_guard<decltype(mutex)> lock(mutex);
253261
254 buffer_stream = buffer_stream_factory->262 buffer_stream = buffer_stream_factory->
255 make_producer_stream(*server, surface.buffer_stream(), name);263 make_producer_stream(connection, *server, surface.buffer_stream(), name);
256264
257 for(int i = 0; i < surface.attributes_size(); i++)265 for(int i = 0; i < surface.attributes_size(); i++)
258 {266 {
@@ -458,7 +466,11 @@
458{466{
459 std::lock_guard<decltype(mutex)> lock(mutex);467 std::lock_guard<decltype(mutex)> lock(mutex);
460468
461 input_thread.reset();469 if (input_dispatcher)
470 {
471 connection->remove_dispatchee(input_dispatcher);
472 input_dispatcher.reset();
473 }
462474
463 if (callback)475 if (callback)
464 {476 {
@@ -468,10 +480,10 @@
468480
469 if (surface.fd_size() > 0 && handle_event_callback)481 if (surface.fd_size() > 0 && handle_event_callback)
470 {482 {
471 auto input_dispatcher = input_platform->create_input_receiver(surface.fd(0),483 input_dispatcher = input_platform->create_input_receiver(surface.fd(0),
472 keymapper,484 keymapper,
473 handle_event_callback);485 handle_event_callback);
474 input_thread = std::make_shared<md::SimpleDispatchThread>(input_dispatcher);486 connection->add_dispatchee(input_dispatcher);
475 }487 }
476 }488 }
477}489}
478490
=== modified file 'src/client/mir_surface.h'
--- src/client/mir_surface.h 2015-04-28 08:59:22 +0000
+++ src/client/mir_surface.h 2015-05-14 07:50:26 +0000
@@ -39,7 +39,7 @@
39{39{
40namespace dispatch40namespace dispatch
41{41{
42class SimpleDispatchThread;42class Dispatchable;
43}43}
44namespace input44namespace input
45{45{
@@ -153,6 +153,8 @@
153 MirWaitHandle* modify(MirSurfaceSpec const& changes);153 MirWaitHandle* modify(MirSurfaceSpec const& changes);
154154
155 static bool is_valid(MirSurface* query);155 static bool is_valid(MirSurface* query);
156
157 MirConnection* get_connection() const;
156private:158private:
157 mutable std::mutex mutex; // Protects all members of *this159 mutable std::mutex mutex; // Protects all members of *this
158160
@@ -191,7 +193,7 @@
191 MirOrientation orientation = mir_orientation_normal;193 MirOrientation orientation = mir_orientation_normal;
192194
193 std::function<void(MirEvent const*)> handle_event_callback;195 std::function<void(MirEvent const*)> handle_event_callback;
194 std::shared_ptr<mir::dispatch::SimpleDispatchThread> input_thread;196 std::shared_ptr<mir::dispatch::Dispatchable> input_dispatcher;
195};197};
196198
197#endif /* MIR_CLIENT_PRIVATE_MIR_WAIT_HANDLE_H_ */199#endif /* MIR_CLIENT_PRIVATE_MIR_WAIT_HANDLE_H_ */
198200
=== modified file 'src/client/mir_surface_api.cpp'
--- src/client/mir_surface_api.cpp 2015-04-30 12:27:33 +0000
+++ src/client/mir_surface_api.cpp 2015-05-14 07:50:26 +0000
@@ -26,24 +26,13 @@
26#include "mir_surface.h"26#include "mir_surface.h"
27#include "error_connections.h"27#include "error_connections.h"
28#include "mir/uncaught.h"28#include "mir/uncaught.h"
29#include "synchronous_helper.h"
2930
30#include <boost/exception/diagnostic_information.hpp>31#include <boost/exception/diagnostic_information.hpp>
31#include <functional>32#include <functional>
3233
33namespace mcl = mir::client;34namespace mcl = mir::client;
3435
35namespace
36{
37
38// assign_result is compatible with all 2-parameter callbacks
39void assign_result(void* result, void** context)
40{
41 if (context)
42 *context = result;
43}
44
45}
46
47MirSurfaceSpec* mir_connection_create_spec_for_normal_surface(MirConnection* connection,36MirSurfaceSpec* mir_connection_create_spec_for_normal_surface(MirConnection* connection,
48 int width, int height,37 int width, int height,
49 MirPixelFormat format)38 MirPixelFormat format)
@@ -128,9 +117,12 @@
128{117{
129 MirSurface* surface = nullptr;118 MirSurface* surface = nullptr;
130119
131 mir_wait_for(mir_surface_create(requested_specification,120 make_synchronous_call(
132 reinterpret_cast<mir_surface_callback>(assign_result),121 requested_specification->connection,
133 &surface));122 &mir_surface_create,
123 requested_specification,
124 &assign_result<MirSurface>,
125 &surface);
134126
135 return surface;127 return surface;
136}128}
@@ -364,9 +356,11 @@
364356
365void mir_surface_release_sync(MirSurface* surface)357void mir_surface_release_sync(MirSurface* surface)
366{358{
367 mir_wait_for(mir_surface_release(surface,359 make_synchronous_call(surface->get_connection(),
368 reinterpret_cast<mir_surface_callback>(assign_result),360 mir_surface_release,
369 nullptr));361 surface,
362 &assign_result<MirSurface>,
363 static_cast<void*>(nullptr));
370}364}
371365
372int mir_surface_get_id(MirSurface* /*surface*/)366int mir_surface_get_id(MirSurface* /*surface*/)
373367
=== modified file 'src/client/mir_wait_api.cpp'
--- src/client/mir_wait_api.cpp 2014-03-31 14:36:08 +0000
+++ src/client/mir_wait_api.cpp 2015-05-14 07:50:26 +0000
@@ -30,3 +30,12 @@
30 if (wait_handle)30 if (wait_handle)
31 wait_handle->wait_for_one();31 wait_handle->wait_for_one();
32}32}
33
34bool mir_wait_handle_ready(MirWaitHandle* wait_handle)
35{
36 if (wait_handle)
37 {
38 return wait_handle->ready();
39 }
40 return true;
41}
3342
=== modified file 'src/client/mir_wait_handle.cpp'
--- src/client/mir_wait_handle.cpp 2015-01-21 07:34:50 +0000
+++ src/client/mir_wait_handle.cpp 2015-05-14 07:50:26 +0000
@@ -74,3 +74,9 @@
74 --expecting;74 --expecting;
75}75}
7676
77bool MirWaitHandle::ready()
78{
79 std::lock_guard<std::mutex> lock{guard};
80
81 return received == expecting;
82}
7783
=== modified file 'src/client/mir_wait_handle.h'
--- src/client/mir_wait_handle.h 2013-10-03 03:44:08 +0000
+++ src/client/mir_wait_handle.h 2015-05-14 07:50:26 +0000
@@ -39,6 +39,7 @@
39 void wait_for_all();39 void wait_for_all();
40 void wait_for_one();40 void wait_for_one();
41 void wait_for_pending(std::chrono::milliseconds limit);41 void wait_for_pending(std::chrono::milliseconds limit);
42 bool ready();
4243
43private:44private:
44 std::mutex guard;45 std::mutex guard;
4546
=== modified file 'src/client/rpc/make_rpc_channel.h'
--- src/client/rpc/make_rpc_channel.h 2015-01-21 07:34:50 +0000
+++ src/client/rpc/make_rpc_channel.h 2015-05-14 07:50:26 +0000
@@ -20,8 +20,6 @@
2020
21#include <memory>21#include <memory>
2222
23namespace google { namespace protobuf { class RpcChannel; } }
24
25namespace mir23namespace mir
26{24{
27namespace client25namespace client
@@ -34,8 +32,9 @@
34namespace rpc32namespace rpc
35{33{
36class RpcReport;34class RpcReport;
35class MirBasicRpcChannel;
3736
38std::shared_ptr<google::protobuf::RpcChannel>37std::shared_ptr<MirBasicRpcChannel>
39make_rpc_channel(std::string const& name,38make_rpc_channel(std::string const& name,
40 std::shared_ptr<SurfaceMap> const& map,39 std::shared_ptr<SurfaceMap> const& map,
41 std::shared_ptr<DisplayConfiguration> const& disp_conf,40 std::shared_ptr<DisplayConfiguration> const& disp_conf,
4241
=== modified file 'src/client/rpc/make_socket_rpc_channel.cpp'
--- src/client/rpc/make_socket_rpc_channel.cpp 2015-03-31 02:35:42 +0000
+++ src/client/rpc/make_socket_rpc_channel.cpp 2015-05-14 07:50:26 +0000
@@ -40,7 +40,7 @@
40} const fd_prefix("fd://");40} const fd_prefix("fd://");
41}41}
4242
43std::shared_ptr<google::protobuf::RpcChannel>43std::shared_ptr<mclr::MirBasicRpcChannel>
44mclr::make_rpc_channel(std::string const& name,44mclr::make_rpc_channel(std::string const& name,
45 std::shared_ptr<mcl::SurfaceMap> const& map,45 std::shared_ptr<mcl::SurfaceMap> const& map,
46 std::shared_ptr<mcl::DisplayConfiguration> const& disp_conf,46 std::shared_ptr<mcl::DisplayConfiguration> const& disp_conf,
4747
=== modified file 'src/client/rpc/mir_basic_rpc_channel.cpp'
--- src/client/rpc/mir_basic_rpc_channel.cpp 2015-01-21 07:34:50 +0000
+++ src/client/rpc/mir_basic_rpc_channel.cpp 2015-05-14 07:50:26 +0000
@@ -38,13 +38,19 @@
38void mclrd::PendingCallCache::save_completion_details(38void mclrd::PendingCallCache::save_completion_details(
39 mir::protobuf::wire::Invocation const& invoke,39 mir::protobuf::wire::Invocation const& invoke,
40 google::protobuf::Message* response,40 google::protobuf::Message* response,
41 std::shared_ptr<google::protobuf::Closure> const& complete)41 google::protobuf::Closure* complete)
42{42{
43 std::unique_lock<std::mutex> lock(mutex);43 std::unique_lock<std::mutex> lock(mutex);
4444
45 pending_calls[invoke.id()] = PendingCall(response, complete);45 pending_calls[invoke.id()] = PendingCall(response, complete);
46}46}
4747
48google::protobuf::Message* mclrd::PendingCallCache::message_for_result(mir::protobuf::wire::Result& result)
49{
50 std::unique_lock<std::mutex> lock(mutex);
51 return pending_calls.at(result.id()).response;
52}
53
48void mclrd::PendingCallCache::complete_response(mir::protobuf::wire::Result& result)54void mclrd::PendingCallCache::complete_response(mir::protobuf::wire::Result& result)
49{55{
50 PendingCall completion;56 PendingCall completion;
@@ -66,7 +72,6 @@
66 else72 else
67 {73 {
68 rpc_report->complete_response(result);74 rpc_report->complete_response(result);
69 completion.response->ParseFromString(result.response());
70 completion.complete->Run();75 completion.complete->Run();
71 }76 }
72}77}
7378
=== modified file 'src/client/rpc/mir_basic_rpc_channel.h'
--- src/client/rpc/mir_basic_rpc_channel.h 2015-01-21 07:34:50 +0000
+++ src/client/rpc/mir_basic_rpc_channel.h 2015-05-14 07:50:26 +0000
@@ -19,6 +19,8 @@
19#ifndef MIR_CLIENT_RPC_MIR_BASIC_RPC_CHANNEL_H_19#ifndef MIR_CLIENT_RPC_MIR_BASIC_RPC_CHANNEL_H_
20#define MIR_CLIENT_RPC_MIR_BASIC_RPC_CHANNEL_H_20#define MIR_CLIENT_RPC_MIR_BASIC_RPC_CHANNEL_H_
2121
22#include "mir/dispatch/dispatchable.h"
23
22#include <google/protobuf/service.h>24#include <google/protobuf/service.h>
23#include <google/protobuf/descriptor.h>25#include <google/protobuf/descriptor.h>
2426
@@ -57,8 +59,10 @@
57 void save_completion_details(59 void save_completion_details(
58 mir::protobuf::wire::Invocation const& invoke,60 mir::protobuf::wire::Invocation const& invoke,
59 google::protobuf::Message* response,61 google::protobuf::Message* response,
60 std::shared_ptr<google::protobuf::Closure> const& complete);62 google::protobuf::Closure* complete);
6163
64
65 google::protobuf::Message* message_for_result(mir::protobuf::wire::Result& result);
6266
63 void complete_response(mir::protobuf::wire::Result& result);67 void complete_response(mir::protobuf::wire::Result& result);
6468
@@ -72,14 +76,14 @@
72 {76 {
73 PendingCall(77 PendingCall(
74 google::protobuf::Message* response,78 google::protobuf::Message* response,
75 std::shared_ptr<google::protobuf::Closure> const& target)79 google::protobuf::Closure* target)
76 : response(response), complete(target) {}80 : response(response), complete(target) {}
7781
78 PendingCall()82 PendingCall()
79 : response(0), complete() {}83 : response(0), complete() {}
8084
81 google::protobuf::Message* response;85 google::protobuf::Message* response;
82 std::shared_ptr<google::protobuf::Closure> complete;86 google::protobuf::Closure* complete;
83 };87 };
8488
85 std::mutex mutable mutex;89 std::mutex mutable mutex;
@@ -88,12 +92,15 @@
88};92};
89}93}
9094
91class MirBasicRpcChannel : public google::protobuf::RpcChannel95class MirBasicRpcChannel
96 : public google::protobuf::RpcChannel,
97 public mir::dispatch::Dispatchable
92{98{
93public:99public:
94 MirBasicRpcChannel();100 MirBasicRpcChannel();
95 ~MirBasicRpcChannel();101 ~MirBasicRpcChannel();
96102
103 virtual void process_next_request_first() = 0;
97protected:104protected:
98 mir::protobuf::wire::Invocation invocation_for(105 mir::protobuf::wire::Invocation invocation_for(
99 google::protobuf::MethodDescriptor const* method,106 google::protobuf::MethodDescriptor const* method,
100107
=== modified file 'src/client/rpc/mir_protobuf_rpc_channel.cpp'
--- src/client/rpc/mir_protobuf_rpc_channel.cpp 2015-05-01 14:47:55 +0000
+++ src/client/rpc/mir_protobuf_rpc_channel.cpp 2015-05-14 07:50:26 +0000
@@ -60,7 +60,9 @@
60 lifecycle_control(lifecycle_control),60 lifecycle_control(lifecycle_control),
61 event_sink(event_sink),61 event_sink(event_sink),
62 disconnected(false),62 disconnected(false),
63 transport{std::move(transport)}63 transport{std::move(transport)},
64 delayed_processor{std::make_shared<md::ActionQueue>()},
65 multiplexer{this->transport, delayed_processor}
64{66{
65 class NullDeleter67 class NullDeleter
66 {68 {
@@ -105,8 +107,7 @@
105 }107 }
106}108}
107109
108void mclr::MirProtobufRpcChannel::receive_file_descriptors(google::protobuf::Message* response,110void mclr::MirProtobufRpcChannel::receive_file_descriptors(google::protobuf::Message* response)
109 google::protobuf::Closure* complete)
110{111{
111 auto const message_type = response->GetTypeName();112 auto const message_type = response->GetTypeName();
112113
@@ -163,7 +164,6 @@
163 receive_any_file_descriptors_for(platform);164 receive_any_file_descriptors_for(platform);
164 receive_any_file_descriptors_for(socket_fd);165 receive_any_file_descriptors_for(socket_fd);
165 receive_any_file_descriptors_for(platform_operation_message);166 receive_any_file_descriptors_for(platform_operation_message);
166 complete->Run();
167}167}
168168
169void mclr::MirProtobufRpcChannel::CallMethod(169void mclr::MirProtobufRpcChannel::CallMethod(
@@ -193,10 +193,13 @@
193193
194 rpc_report->invocation_requested(invocation);194 rpc_report->invocation_requested(invocation);
195195
196 std::shared_ptr<google::protobuf::Closure> callback(196 pending_calls.save_completion_details(invocation, response, complete);
197 google::protobuf::NewPermanentCallback(this, &MirProtobufRpcChannel::receive_file_descriptors, response, complete));
198197
199 pending_calls.save_completion_details(invocation, response, callback);198 if (prioritise_next_request)
199 {
200 id_to_wait_for = invocation.id();
201 prioritise_next_request = false;
202 }
200203
201 send_message(invocation, invocation, fds);204 send_message(invocation, invocation, fds);
202}205}
@@ -317,7 +320,7 @@
317 */320 */
318 std::lock_guard<decltype(read_mutex)> lock(read_mutex);321 std::lock_guard<decltype(read_mutex)> lock(read_mutex);
319322
320 mir::protobuf::wire::Result result;323 auto result = std::make_unique<mir::protobuf::wire::Result>();
321 try324 try
322 {325 {
323 uint16_t message_size;326 uint16_t message_size;
@@ -327,9 +330,9 @@
327 body_bytes.resize(message_size);330 body_bytes.resize(message_size);
328 transport->receive_data(body_bytes.data(), message_size);331 transport->receive_data(body_bytes.data(), message_size);
329332
330 result.ParseFromArray(body_bytes.data(), message_size);333 result->ParseFromArray(body_bytes.data(), message_size);
331334
332 rpc_report->result_receipt_succeeded(result);335 rpc_report->result_receipt_succeeded(*result);
333 }336 }
334 catch (std::exception const& x)337 catch (std::exception const& x)
335 {338 {
@@ -339,14 +342,39 @@
339342
340 try343 try
341 {344 {
342 for (int i = 0; i != result.events_size(); ++i)345 for (int i = 0; i != result->events_size(); ++i)
343 {346 {
344 process_event_sequence(result.events(i));347 process_event_sequence(result->events(i));
345 }348 }
346349
347 if (result.has_id())350 if (result->has_id())
348 {351 {
349 pending_calls.complete_response(result);352 auto result_message = pending_calls.message_for_result(*result);
353 result_message->ParseFromString(result->response());
354 receive_file_descriptors(result_message);
355
356 if (id_to_wait_for)
357 {
358 if (result->id() == id_to_wait_for.value())
359 {
360 pending_calls.complete_response(*result);
361 multiplexer.add_watch(delayed_processor);
362 }
363 else
364 {
365 // It's too difficult to convince C++ to move this lambda everywhere, so
366 // just give up and let it pretend its a shared_ptr.
367 std::shared_ptr<mir::protobuf::wire::Result> appeaser{std::move(result)};
368 delayed_processor->enqueue([delayed_result = std::move(appeaser), this]() mutable
369 {
370 pending_calls.complete_response(*delayed_result);
371 });
372 }
373 }
374 else
375 {
376 pending_calls.complete_response(*result);
377 }
350 }378 }
351 }379 }
352 catch (std::exception const& x)380 catch (std::exception const& x)
@@ -354,7 +382,7 @@
354 // TODO: This is dangerous as an error in result processing could cause a wait handle382 // TODO: This is dangerous as an error in result processing could cause a wait handle
355 // to never fire. Could perhaps fix by catching and setting error on the response before invoking383 // to never fire. Could perhaps fix by catching and setting error on the response before invoking
356 // callback ~racarr384 // callback ~racarr
357 rpc_report->result_processing_failed(result, x);385 rpc_report->result_processing_failed(*result, x);
358 }386 }
359}387}
360388
@@ -365,15 +393,21 @@
365393
366mir::Fd mir::client::rpc::MirProtobufRpcChannel::watch_fd() const394mir::Fd mir::client::rpc::MirProtobufRpcChannel::watch_fd() const
367{395{
368 return transport->watch_fd();396 return multiplexer.watch_fd();
369}397}
370398
371bool mir::client::rpc::MirProtobufRpcChannel::dispatch(md::FdEvents events)399bool mir::client::rpc::MirProtobufRpcChannel::dispatch(md::FdEvents events)
372{400{
373 return transport->dispatch(events);401 return multiplexer.dispatch(events);
374}402}
375403
376md::FdEvents mclr::MirProtobufRpcChannel::relevant_events() const404md::FdEvents mclr::MirProtobufRpcChannel::relevant_events() const
377{405{
378 return transport->relevant_events();406 return multiplexer.relevant_events();
407}
408
409void mclr::MirProtobufRpcChannel::process_next_request_first()
410{
411 prioritise_next_request = true;
412 multiplexer.remove_watch(delayed_processor);
379}413}
380414
=== modified file 'src/client/rpc/mir_protobuf_rpc_channel.h'
--- src/client/rpc/mir_protobuf_rpc_channel.h 2015-03-31 02:35:42 +0000
+++ src/client/rpc/mir_protobuf_rpc_channel.h 2015-05-14 07:50:26 +0000
@@ -22,23 +22,21 @@
22#include "mir_basic_rpc_channel.h"22#include "mir_basic_rpc_channel.h"
23#include "stream_transport.h"23#include "stream_transport.h"
24#include "mir/dispatch/dispatchable.h"24#include "mir/dispatch/dispatchable.h"
25#include "mir/dispatch/multiplexing_dispatchable.h"
26#include "mir/dispatch/action_queue.h"
27
28#include "mir_protobuf_wire.pb.h"
2529
26#include <google/protobuf/service.h>30#include <google/protobuf/service.h>
27#include <google/protobuf/descriptor.h>31#include <google/protobuf/descriptor.h>
2832
29#include <thread>33#include <thread>
30#include <atomic>34#include <atomic>
35#include <list>
36#include <experimental/optional>
3137
32namespace mir38namespace mir
33{39{
34namespace protobuf
35{
36namespace wire
37{
38class Invocation;
39class Result;
40}
41}
4240
43namespace client41namespace client
44{42{
@@ -53,8 +51,7 @@
5351
54class MirProtobufRpcChannel :52class MirProtobufRpcChannel :
55 public MirBasicRpcChannel,53 public MirBasicRpcChannel,
56 public StreamTransport::Observer,54 public StreamTransport::Observer
57 public dispatch::Dispatchable
58{55{
59public:56public:
60 MirProtobufRpcChannel(std::unique_ptr<StreamTransport> transport,57 MirProtobufRpcChannel(std::unique_ptr<StreamTransport> transport,
@@ -74,6 +71,20 @@
74 Fd watch_fd() const override;71 Fd watch_fd() const override;
75 bool dispatch(mir::dispatch::FdEvents events) override;72 bool dispatch(mir::dispatch::FdEvents events) override;
76 mir::dispatch::FdEvents relevant_events() const override;73 mir::dispatch::FdEvents relevant_events() const override;
74
75 /**
76 * \brief Switch the RpcChannel into out-of-order mode
77 *
78 * The first CallMethod after this method is called will be processed
79 * out of order - no server responses will be processed until the response
80 * for the next CallMethod is processed.
81 *
82 * After the response for the next CallMethod is processed, normal processing
83 * is resumed.
84 *
85 * No messages are discarded, only delayed.
86 */
87 void process_next_request_first() override;
77private:88private:
78 virtual void CallMethod(const google::protobuf::MethodDescriptor* method, google::protobuf::RpcController*,89 virtual void CallMethod(const google::protobuf::MethodDescriptor* method, google::protobuf::RpcController*,
79 const google::protobuf::Message* parameters, google::protobuf::Message* response,90 const google::protobuf::Message* parameters, google::protobuf::Message* response,
@@ -86,7 +97,7 @@
86 detail::SendBuffer header_bytes;97 detail::SendBuffer header_bytes;
87 detail::SendBuffer body_bytes;98 detail::SendBuffer body_bytes;
8899
89 void receive_file_descriptors(google::protobuf::Message* response, google::protobuf::Closure* complete);100 void receive_file_descriptors(google::protobuf::Message* response);
90 template<class MessageType>101 template<class MessageType>
91 void receive_any_file_descriptors_for(MessageType* response);102 void receive_any_file_descriptors_for(MessageType* response);
92 void send_message(mir::protobuf::wire::Invocation const& body,103 void send_message(mir::protobuf::wire::Invocation const& body,
@@ -106,14 +117,22 @@
106 std::mutex read_mutex;117 std::mutex read_mutex;
107 std::mutex write_mutex;118 std::mutex write_mutex;
108119
120 bool prioritise_next_request{false};
121 std::experimental::optional<uint32_t> id_to_wait_for;
122
109 /* We use the guarantee that the transport's destructor blocks until123 /* We use the guarantee that the transport's destructor blocks until
110 * pending processing has finished to ensure that on_data_available()124 * pending processing has finished to ensure that on_data_available()
111 * isn't called after the members it relies on are destroyed.125 * isn't called after the members it relies on are destroyed.
112 *126 *
113 * This means the transport field must appear after any field used127 * This means that anything that owns a reference to the transport
114 * by on_data_available. For simplicity, put it last.128 * needs to be after anything that can be accessed from on_data_available().
129 *
130 * For simplicity's sake keep all of the dispatch infrastructure at the
131 * end to guarantee this.
115 */132 */
116 std::unique_ptr<StreamTransport> transport;133 std::shared_ptr<StreamTransport> const transport;
134 std::shared_ptr<mir::dispatch::ActionQueue> const delayed_processor;
135 mir::dispatch::MultiplexingDispatchable multiplexer;
117};136};
118137
119}138}
120139
=== modified file 'src/client/symbols.map'
--- src/client/symbols.map 2015-05-07 19:41:08 +0000
+++ src/client/symbols.map 2015-05-14 07:50:26 +0000
@@ -196,6 +196,10 @@
196 mir_buffer_stream_release;196 mir_buffer_stream_release;
197 mir_buffer_stream_release_sync;197 mir_buffer_stream_release_sync;
198 mir_buffer_stream_is_valid;198 mir_buffer_stream_is_valid;
199 mir_connect_with_manual_dispatch;
200 mir_connection_get_event_fd;
201 mir_connection_dispatch;
202 mir_wait_handle_ready;
199} MIR_CLIENT_8.3;203} MIR_CLIENT_8.3;
200204
201MIR_CLIENT_DETAIL_8 {205MIR_CLIENT_DETAIL_8 {
202206
=== added file 'src/client/synchronous_helper.cpp'
--- src/client/synchronous_helper.cpp 1970-01-01 00:00:00 +0000
+++ src/client/synchronous_helper.cpp 2015-05-14 07:50:26 +0000
@@ -0,0 +1,33 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#include "synchronous_helper.h"
20
21#include <poll.h>
22
23void dispatch_connection_until(MirConnection* connection, std::function<bool()> predicate)
24{
25 pollfd fd;
26 fd.fd = connection->watch_fd();
27 fd.events = POLLIN;
28 while(!predicate() && (poll(&fd, 1, -1) > 0))
29 {
30 connection->dispatch();
31 }
32}
33
034
=== added file 'src/client/synchronous_helper.h'
--- src/client/synchronous_helper.h 1970-01-01 00:00:00 +0000
+++ src/client/synchronous_helper.h 2015-05-14 07:50:26 +0000
@@ -0,0 +1,160 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#ifndef MIR_CLIENT_SYNCHRONOUS_H_
20#define MIR_CLIENT_SYNCHRONOUS_H_
21
22#include "mir_connection.h"
23#include "mir_wait_handle.h"
24
25#include <functional>
26#include <type_traits>
27
28template<typename Result>
29void assign_result(Result* result, void* ctx)
30{
31 auto assignee = reinterpret_cast<Result**>(ctx);
32 if (assignee)
33 {
34 *assignee = result;
35 }
36}
37
38template<typename Callback>
39struct SynchronousContext
40{
41 Callback real_callback;
42 bool complete;
43 void* userdata;
44};
45
46template<typename Callable, class Tuple, std::size_t...I>
47void apply_substituting_last_arg(
48 Callable&& function,
49 Tuple&& args,
50 std::index_sequence<I...>,
51 void* context)
52{
53 return std::forward<Callable>(function)(
54 std::get<I>(std::forward<Tuple>(args))...,
55 context);
56}
57
58
59template<typename... Args>
60void synchronous_wrapper(Args... args)
61{
62 std::tuple<Args...> arguments{args...};
63 constexpr std::size_t arg_count = sizeof...(Args);
64
65 auto wrapped_context = reinterpret_cast<SynchronousContext<void(*)(Args...)>*>(std::get<arg_count - 1>(arguments));
66 if (wrapped_context->real_callback)
67 {
68 apply_substituting_last_arg(
69 wrapped_context->real_callback,
70 std::forward_as_tuple(args...),
71 std::make_index_sequence<arg_count - 1>(),
72 wrapped_context->userdata);
73 }
74 wrapped_context->complete = true;
75}
76
77
78template<typename Callable, typename Tuple, std::size_t...I>
79constexpr decltype(auto) call_impl(Callable&& function, Tuple&& args, std::index_sequence<I...>)
80{
81 return std::forward<Callable>(function)(std::get<I>(std::forward<Tuple>((args)))...);
82}
83
84template<typename Callable, typename Tuple>
85constexpr decltype(auto) apply(Callable&& function, Tuple&& args)
86{
87 return call_impl(
88 std::forward<Callable>(function),
89 std::forward<Tuple>(args),
90 std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>{}>{});
91}
92
93void dispatch_connection_until(MirConnection* connection, std::function<bool()> predicate);
94
95
96/**
97 * \brief Make a synchronous RPC call
98 *
99 * This wrapper takes care of manually dispatching the MirConnection if it is in
100 * manual dispatch mode, or waiting for the operation to complete if in automatic
101 * dispatch mode.
102 *
103 * Given a function mir_foo_do_thing(MirFoo* target, int arg, foo_callback callback, void* context)
104 * the correct way to call this function is
105 * make_synchronous_call(connection,
106 * &mir_foo_do_thing,
107 * target,
108 * arg,
109 * callback,
110 * static_cast<void*>(context));
111 *
112 * The last two parameters must be the callback function and the void* to pass to that callback.
113 * The last argument must have a pointer type; NULL or nullptr must be explicitly cast to
114 * void*.
115 */
116template<typename Callable, typename... Args>
117void make_synchronous_call(MirConnection* connection,
118 Callable&& function,
119 Args&&... args)
120{
121 static_assert(
122 std::is_same<typename std::result_of<Callable(Args...)>::type, MirWaitHandle*>::value,
123 "Second parameter must be a function that returns a MirWaitHandle*");
124
125 if (connection->watch_fd() != mir::Fd::invalid)
126 {
127 std::tuple<Args...> arguments{args...};
128 constexpr int arg_count = sizeof...(Args);
129 auto callback = std::get<arg_count - 2>(arguments);
130 auto context = std::get<arg_count - 1>(arguments);
131
132 static_assert(
133 std::is_pointer<typename std::tuple_element<arg_count - 1, std::tuple<Args...>>::type>::value,
134 "The final argument must be a pointer type");
135 static_assert(
136 std::is_pointer<typename std::tuple_element<arg_count - 2, std::tuple<Args...>>::type>::value,
137 "The second last argument must be a function pointer");
138
139 SynchronousContext<decltype(callback)> wrapper_context {
140 callback,
141 false,
142 context
143 };
144
145 std::get<arg_count - 2>(arguments) = &synchronous_wrapper;
146 std::get<arg_count - 1>(arguments) =
147 reinterpret_cast<typename std::tuple_element<arg_count - 1, std::tuple<Args...>>::type>(&wrapper_context);
148
149 connection->process_next_request_first();
150 apply(std::forward<Callable>(function), arguments);
151
152 dispatch_connection_until(connection, [&wrapper_context](){ return wrapper_context.complete; });
153 }
154 else
155 {
156 mir_wait_for(std::forward<Callable>(function)(std::forward<Args>(args)...));
157 }
158}
159
160#endif // MIR_CLIENT_SYNCHRONOUS_H_
0161
=== modified file 'src/common/dispatch/CMakeLists.txt'
--- src/common/dispatch/CMakeLists.txt 2015-04-09 06:20:31 +0000
+++ src/common/dispatch/CMakeLists.txt 2015-05-14 07:50:26 +0000
@@ -17,10 +17,9 @@
17list(17list(
18 APPEND MIR_COMMON_SOURCES18 APPEND MIR_COMMON_SOURCES
19 ${CMAKE_CURRENT_SOURCE_DIR}/action_queue.cpp19 ${CMAKE_CURRENT_SOURCE_DIR}/action_queue.cpp
20 ${CMAKE_CURRENT_SOURCE_DIR}/simple_dispatch_thread.cpp
21 ${CMAKE_CURRENT_SOURCE_DIR}/multiplexing_dispatchable.cpp20 ${CMAKE_CURRENT_SOURCE_DIR}/multiplexing_dispatchable.cpp
22 ${CMAKE_CURRENT_SOURCE_DIR}/utils.cpp21 ${CMAKE_CURRENT_SOURCE_DIR}/utils.cpp
23 ${CMAKE_CURRENT_SOURCE_DIR}/action_queue.cpp22 ${CMAKE_CURRENT_SOURCE_DIR}/threaded_dispatcher.cpp
24)23)
2524
26set(MIR_COMMON_SOURCES ${MIR_COMMON_SOURCES} PARENT_SCOPE)25set(MIR_COMMON_SOURCES ${MIR_COMMON_SOURCES} PARENT_SCOPE)
2726
=== removed file 'src/common/dispatch/simple_dispatch_thread.cpp'
--- src/common/dispatch/simple_dispatch_thread.cpp 2015-04-30 17:41:03 +0000
+++ src/common/dispatch/simple_dispatch_thread.cpp 1970-01-01 00:00:00 +0000
@@ -1,163 +0,0 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#include "mir/dispatch/simple_dispatch_thread.h"
20#include "mir/dispatch/dispatchable.h"
21#include "mir/logging/logger.h"
22#include "utils.h"
23#include "mir/signal_blocker.h"
24
25#include <sys/epoll.h>
26#include <unistd.h>
27#include <system_error>
28#include <array>
29#include <signal.h>
30#include <boost/exception/all.hpp>
31
32namespace md = mir::dispatch;
33
34namespace
35{
36
37void wait_for_events_forever(std::shared_ptr<md::Dispatchable> const& dispatchee, mir::Fd shutdown_fd)
38{
39 auto epoll_fd = mir::Fd{epoll_create1(0)};
40 if (epoll_fd == mir::Fd::invalid)
41 {
42 BOOST_THROW_EXCEPTION((std::system_error{errno,
43 std::system_category(),
44 "Failed to create epoll IO monitor"}));
45 }
46 epoll_event event;
47 memset(&event, 0, sizeof(event));
48
49 enum fd_names : uint32_t {
50 shutdown,
51 dispatchee_fd
52 };
53
54 // We only care when the shutdown pipe has been closed
55 event.data.u32 = fd_names::shutdown;
56 event.events = EPOLLRDHUP;
57 epoll_ctl(epoll_fd, EPOLL_CTL_ADD, shutdown_fd, &event);
58
59 // Ask the dispatchee what it events it's interested in...
60 event.data.u32 = fd_names::dispatchee_fd;
61 event.events = md::fd_event_to_epoll(dispatchee->relevant_events());
62 epoll_ctl(epoll_fd, EPOLL_CTL_ADD, dispatchee->watch_fd(), &event);
63
64 for (;;)
65 {
66 std::array<epoll_event,2> events;
67 auto const num_available_events =
68 epoll_wait(epoll_fd, events.data(), events.size(), -1);
69
70 if (num_available_events == 1)
71 {
72 if (events[0].data.u32 == fd_names::dispatchee_fd)
73 {
74 if (!dispatchee->dispatch(md::epoll_to_fd_event(events[0])))
75 {
76 // No need to keep looping, the Dispatchable's not going to produce any more events.
77 return;
78 }
79 }
80 else if (events[0].data.u32 == fd_names::shutdown)
81 {
82 // The only thing we do with the shutdown fd is to close it.
83 return;
84 }
85 }
86 else if (num_available_events > 1)
87 {
88 // Because we only have two fds in the epoll, if there is more than one
89 // event pending then one of them must be a shutdown event.
90 // So, shutdown.
91 return;
92 }
93 else if (num_available_events < 0)
94 {
95 // Although we have blocked signals in this thread, we can still
96 // get interrupted by SIGSTOP (which is unblockable and non-fatal).
97 if (errno != EINTR)
98 {
99 BOOST_THROW_EXCEPTION((std::system_error{
100 errno, std::system_category(), "Failed to wait for epoll events"}));
101 }
102 }
103 }
104}
105
106}
107
108md::SimpleDispatchThread::SimpleDispatchThread(std::shared_ptr<md::Dispatchable> const& dispatchee)
109 : SimpleDispatchThread(dispatchee, []{})
110{}
111
112md::SimpleDispatchThread::SimpleDispatchThread(
113 std::shared_ptr<md::Dispatchable> const& dispatchee,
114 std::function<void()> const& exception_handler)
115{
116 int pipefds[2];
117 if (pipe(pipefds) < 0)
118 {
119 BOOST_THROW_EXCEPTION((std::system_error{errno,
120 std::system_category(),
121 "Failed to create shutdown pipe for IO thread"}));
122 }
123 shutdown_fd = mir::Fd{pipefds[1]};
124 mir::Fd const terminate_fd = mir::Fd{pipefds[0]};
125 {
126 // The newly spawned thread inherits the current signal mask; block everything
127 // before creating the new thread so that there's no race between thread start
128 // and signal blocking.
129 mir::SignalBlocker block_signals;
130 eventloop = std::thread{
131 [exception_handler, dispatchee, terminate_fd]()
132 {
133 try
134 {
135 wait_for_events_forever(dispatchee, terminate_fd);
136 }
137 catch(...)
138 {
139 exception_handler();
140 }
141 }};
142 }
143}
144
145md::SimpleDispatchThread::~SimpleDispatchThread() noexcept
146{
147 shutdown_fd = mir::Fd{};
148 if (eventloop.get_id() == std::this_thread::get_id())
149 {
150 // We're being destroyed from within the dispatch callback
151 // Attempting to join the eventloop will result in a trivial deadlock.
152 //
153 // The std::thread destructor will call std::terminate() for us, let's
154 // leave a useful message.
155 mir::logging::log(mir::logging::Severity::critical,
156 "Destroying SimpleDispatchThread from within a dispatch callback. This is a programming error.",
157 "Dispatch");
158 }
159 else if (eventloop.joinable())
160 {
161 eventloop.join();
162 }
163}
1640
=== added file 'src/common/dispatch/threaded_dispatcher.cpp'
--- src/common/dispatch/threaded_dispatcher.cpp 1970-01-01 00:00:00 +0000
+++ src/common/dispatch/threaded_dispatcher.cpp 2015-05-14 07:50:26 +0000
@@ -0,0 +1,294 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#include "mir/dispatch/threaded_dispatcher.h"
20#include "mir/dispatch/dispatchable.h"
21#include "mir/thread_name.h"
22
23#include "mir/raii.h"
24#include "mir/logging/logger.h"
25
26#include <fcntl.h>
27#include <poll.h>
28#include <unistd.h>
29#include <system_error>
30#include <signal.h>
31#include <boost/exception/all.hpp>
32#include <algorithm>
33#include <unordered_map>
34#include <sys/eventfd.h>
35
36namespace md = mir::dispatch;
37
38class md::ThreadedDispatcher::ThreadShutdownRequestHandler : public md::Dispatchable
39{
40public:
41 ThreadShutdownRequestHandler()
42 : event_semaphore{eventfd(0, EFD_CLOEXEC | EFD_SEMAPHORE)},
43 shutting_down{false}
44 {
45 if (event_semaphore == mir::Fd::invalid)
46 {
47 BOOST_THROW_EXCEPTION((std::system_error{errno,
48 std::system_category(),
49 "Failed to create shutdown eventfd"}));
50 }
51 }
52
53 mir::Fd watch_fd() const override
54 {
55 return event_semaphore;
56 }
57
58 bool dispatch(md::FdEvents events) override
59 {
60 if (events & md::FdEvent::error)
61 {
62 return false;
63 }
64
65 eventfd_t dummy;
66 if (eventfd_read(event_semaphore, &dummy) < 0)
67 {
68 BOOST_THROW_EXCEPTION((std::system_error{errno,
69 std::system_category(),
70 "Failed to clear shutdown notification"}));
71 }
72 std::lock_guard<decltype(running_flag_guard)> lock{running_flag_guard};
73 *running_flags.at(std::this_thread::get_id()) = false;
74
75 return true;
76 }
77
78 md::FdEvents relevant_events() const override
79 {
80 return md::FdEvent::readable;
81 }
82
83 std::thread::id terminate_one_thread()
84 {
85 // First we tell a thread to die, any thread...
86 if (eventfd_write(event_semaphore, 1) < 0)
87 {
88 BOOST_THROW_EXCEPTION((std::system_error{errno,
89 std::system_category(),
90 "Failed to trigger thread shutdown"}));
91 }
92
93 // ...now we wait for a thread to die and tell us its ID...
94 // We wait for a surprisingly long time because our threads are potentially blocked
95 // in client code that we don't control.
96 //
97 // If the client is entirely unresponsive for a whole minute, it deserves to die.
98 std::unique_lock<decltype(terminating_thread_mutex)> lock{terminating_thread_mutex};
99 if (!thread_terminating.wait_for (lock,
100 std::chrono::seconds{60},
101 [this]() { return !terminating_threads.empty(); }))
102 {
103 BOOST_THROW_EXCEPTION((std::runtime_error{"Thread failed to shutdown"}));
104 }
105
106 auto killed_thread_id = terminating_threads.back();
107 terminating_threads.pop_back();
108 return killed_thread_id;
109 }
110
111 void terminate_all_threads_async()
112 {
113 eventfd_t thread_count;
114 {
115 std::lock_guard<std::mutex> lock(running_flag_guard);
116 thread_count = running_flags.size();
117 shutting_down = true;
118 }
119 if (eventfd_write(event_semaphore, thread_count) < 0)
120 {
121 BOOST_THROW_EXCEPTION((std::system_error{errno,
122 std::system_category(),
123 "Failed to trigger thread shutdown"}));
124 }
125 }
126
127 void register_thread(bool& run_flag)
128 {
129 std::lock_guard<decltype(running_flag_guard)> lock{running_flag_guard};
130
131 running_flags[std::this_thread::get_id()] = &run_flag;
132 if (shutting_down)
133 {
134 if (eventfd_write(event_semaphore, 1) < 0)
135 {
136 BOOST_THROW_EXCEPTION((std::system_error{errno,
137 std::system_category(),
138 "Failed to trigger thread shutdown"}));
139 }
140 }
141 }
142
143 void unregister_thread()
144 {
145 {
146 std::lock_guard<decltype(terminating_thread_mutex)> lock{terminating_thread_mutex};
147 terminating_threads.push_back(std::this_thread::get_id());
148 }
149 thread_terminating.notify_one();
150 {
151 std::lock_guard<decltype(running_flag_guard)> lock{running_flag_guard};
152
153 if (running_flags.erase(std::this_thread::get_id()) != 1)
154 {
155 BOOST_THROW_EXCEPTION((std::logic_error{"Attempted to unregister a not-registered thread"}));
156 }
157 }
158 }
159
160private:
161 mir::Fd event_semaphore;
162
163 std::mutex terminating_thread_mutex;
164 std::condition_variable thread_terminating;
165 std::vector<std::thread::id> terminating_threads;
166
167 std::mutex running_flag_guard;
168 std::unordered_map<std::thread::id, bool*> running_flags;
169 bool shutting_down;
170};
171
172md::ThreadedDispatcher::ThreadedDispatcher(std::string const& name, std::shared_ptr<Dispatchable> const& dispatchee)
173 : ThreadedDispatcher(name, dispatchee, [](){ throw; })
174{
175}
176
177md::ThreadedDispatcher::ThreadedDispatcher(std::string const& name,
178 std::shared_ptr<md::Dispatchable> const& dispatchee,
179 std::function<void()> const& exception_handler)
180 : name_base{name},
181 thread_exiter{std::make_shared<ThreadShutdownRequestHandler>()},
182 dispatcher{std::make_shared<MultiplexingDispatchable>()},
183 exception_handler{exception_handler}
184{
185
186 // We rely on exactly one thread at a time getting a shutdown message
187 dispatcher->add_watch(thread_exiter, md::DispatchReentrancy::sequential);
188
189 // But our target dispatchable is welcome to be dispatched on as many threads
190 // as desired.
191 dispatcher->add_watch(dispatchee, md::DispatchReentrancy::reentrant);
192
193 threadpool.emplace_back(&dispatch_loop, name_base, thread_exiter, dispatcher, exception_handler);
194}
195
196md::ThreadedDispatcher::~ThreadedDispatcher() noexcept
197{
198 std::lock_guard<decltype(thread_pool_mutex)> lock{thread_pool_mutex};
199
200 thread_exiter->terminate_all_threads_async();
201
202 for (auto& thread : threadpool)
203 {
204 if (thread.get_id() == std::this_thread::get_id())
205 {
206 // We're being destroyed from within the dispatch callback
207 // Attempting to join the eventloop will result in a trivial deadlock.
208 //
209 // The std::thread destructor will call std::terminate() for us, let's
210 // leave a useful message.
211 mir::logging::log(mir::logging::Severity::critical,
212 "Destroying ThreadedDispatcher from within a dispatch callback. This is a programming error.",
213 "Dispatch");
214 }
215 else
216 {
217 thread.join();
218 }
219 }
220}
221
222void md::ThreadedDispatcher::add_thread()
223{
224 std::lock_guard<decltype(thread_pool_mutex)> lock{thread_pool_mutex};
225 threadpool.emplace_back(&dispatch_loop, name_base, thread_exiter, dispatcher, exception_handler);
226}
227
228void md::ThreadedDispatcher::remove_thread()
229{
230 auto terminated_thread_id = thread_exiter->terminate_one_thread();
231
232 // Find that thread in our vector, join() it, then remove it.
233 std::lock_guard<decltype(thread_pool_mutex)> threadpool_lock{thread_pool_mutex};
234
235 auto dying_thread = std::find_if(threadpool.begin(),
236 threadpool.end(),
237 [this, &terminated_thread_id](std::thread const& candidate)
238 {
239 return candidate.get_id() == terminated_thread_id;
240 });
241 dying_thread->join();
242 threadpool.erase(dying_thread);
243}
244
245void md::ThreadedDispatcher::dispatch_loop(std::string const& name,
246 std::shared_ptr<ThreadShutdownRequestHandler> thread_register,
247 std::shared_ptr<Dispatchable> dispatcher,
248 std::function<void()> const& exception_handler)
249{
250 sigset_t all_signals;
251 sigfillset(&all_signals);
252
253 if (auto error = pthread_sigmask(SIG_BLOCK, &all_signals, NULL))
254 BOOST_THROW_EXCEPTION((std::system_error{error,
255 std::system_category(),
256 "Failed to block signals on IO thread"}));
257
258 mir::set_thread_name(name);
259
260 // This does not have to be std::atomic<bool> because thread_register is guaranteed to
261 // only ever be dispatch()ed from one thread at a time.
262 bool running{true};
263
264 auto thread_registrar = mir::raii::paired_calls(
265 [&running, thread_register]()
266 {
267 thread_register->register_thread(running);
268 },
269 [thread_register]()
270 {
271 thread_register->unregister_thread();
272 });
273
274 try
275 {
276 struct pollfd waiter;
277 waiter.fd = dispatcher->watch_fd();
278 waiter.events = POLL_IN;
279 while (running)
280 {
281 if (poll(&waiter, 1, -1) < 0)
282 {
283 BOOST_THROW_EXCEPTION((std::system_error{errno,
284 std::system_category(),
285 "Failed to wait for event"}));
286 }
287 dispatcher->dispatch(md::FdEvent::readable);
288 }
289 }
290 catch(...)
291 {
292 exception_handler();
293 }
294}
0295
=== modified file 'src/common/symbols.map'
--- src/common/symbols.map 2015-04-15 05:07:08 +0000
+++ src/common/symbols.map 2015-05-14 07:50:26 +0000
@@ -24,8 +24,10 @@
24 mir::dispatch::MultiplexingDispatchable::relevant_events*;24 mir::dispatch::MultiplexingDispatchable::relevant_events*;
25 mir::dispatch::MultiplexingDispatchable::remove_watch*;25 mir::dispatch::MultiplexingDispatchable::remove_watch*;
26 mir::dispatch::MultiplexingDispatchable::watch_fd*;26 mir::dispatch::MultiplexingDispatchable::watch_fd*;
27 mir::dispatch::SimpleDispatchThread::?SimpleDispatchThread*;27 mir::dispatch::ThreadedDispatcher::?ThreadedDispatcher*;
28 mir::dispatch::SimpleDispatchThread::SimpleDispatchThread*;28 mir::dispatch::ThreadedDispatcher::ThreadedDispatcher*;
29 mir::dispatch::ThreadedDispatcher::add_thread*;
30 mir::dispatch::ThreadedDispatcher::remove_thread*;
29 mir::Fd::Fd*;31 mir::Fd::Fd*;
30 mir::Fd::invalid*;32 mir::Fd::invalid*;
31 mir::Fd::operator*;33 mir::Fd::operator*;
@@ -73,7 +75,7 @@
73 typeinfo?for?mir::dispatch::ActionQueue;75 typeinfo?for?mir::dispatch::ActionQueue;
74 typeinfo?for?mir::dispatch::Dispatchable;76 typeinfo?for?mir::dispatch::Dispatchable;
75 typeinfo?for?mir::dispatch::MultiplexingDispatchable;77 typeinfo?for?mir::dispatch::MultiplexingDispatchable;
76 typeinfo?for?mir::dispatch::SimpleDispatchThread;78 typeinfo?for?mir::dispatch::ThreadedDispatcher;
77 typeinfo?for?mir::Fd;79 typeinfo?for?mir::Fd;
78 typeinfo?for?mir::geometry::Displacement;80 typeinfo?for?mir::geometry::Displacement;
79 typeinfo?for?mir::geometry::Length;81 typeinfo?for?mir::geometry::Length;
@@ -88,7 +90,7 @@
88 vtable?for?mir::dispatch::ActionQueue;90 vtable?for?mir::dispatch::ActionQueue;
89 vtable?for?mir::dispatch::Dispatchable;91 vtable?for?mir::dispatch::Dispatchable;
90 vtable?for?mir::dispatch::MultiplexingDispatchable;92 vtable?for?mir::dispatch::MultiplexingDispatchable;
91 vtable?for?mir::dispatch::SimpleDispatchThread;93 vtable?for?mir::dispatch::ThreadedDispatcher;
92 vtable?for?mir::Fd;94 vtable?for?mir::Fd;
93 vtable?for?mir::geometry::Displacement;95 vtable?for?mir::geometry::Displacement;
94 vtable?for?mir::geometry::Length;96 vtable?for?mir::geometry::Length;
9597
=== modified file 'src/server/input/default_input_manager.cpp'
--- src/server/input/default_input_manager.cpp 2015-05-05 13:46:29 +0000
+++ src/server/input/default_input_manager.cpp 2015-05-14 07:50:26 +0000
@@ -22,7 +22,7 @@
22#include "mir/input/platform.h"22#include "mir/input/platform.h"
23#include "mir/dispatch/action_queue.h"23#include "mir/dispatch/action_queue.h"
24#include "mir/dispatch/multiplexing_dispatchable.h"24#include "mir/dispatch/multiplexing_dispatchable.h"
25#include "mir/dispatch/simple_dispatch_thread.h"25#include "mir/dispatch/threaded_dispatcher.h"
2626
27#include "mir/main_loop.h"27#include "mir/main_loop.h"
28#include "mir/thread_name.h"28#include "mir/thread_name.h"
@@ -86,7 +86,6 @@
8686
87 queue->enqueue([this,weak_started_promise]()87 queue->enqueue([this,weak_started_promise]()
88 {88 {
89 mir::set_thread_name("Mir/InputReader");
90 for (auto const& platform : platforms)89 for (auto const& platform : platforms)
91 {90 {
92 platform->start();91 platform->start();
@@ -100,7 +99,8 @@
100 started_promise->set_value();99 started_promise->set_value();
101 });100 });
102101
103 input_thread = std::make_unique<dispatch::SimpleDispatchThread>(102 input_thread = std::make_unique<dispatch::ThreadedDispatcher>(
103 "Mir/InputReader",
104 multiplexer,104 multiplexer,
105 [this,weak_started_promise]()105 [this,weak_started_promise]()
106 {106 {
107107
=== modified file 'src/server/input/default_input_manager.h'
--- src/server/input/default_input_manager.h 2015-05-01 14:47:55 +0000
+++ src/server/input/default_input_manager.h 2015-05-14 07:50:26 +0000
@@ -37,7 +37,7 @@
37namespace dispatch37namespace dispatch
38{38{
39class MultiplexingDispatchable;39class MultiplexingDispatchable;
40class SimpleDispatchThread;40class ThreadedDispatcher;
41class ActionQueue;41class ActionQueue;
42}42}
43namespace input43namespace input
@@ -65,7 +65,7 @@
65 std::shared_ptr<dispatch::MultiplexingDispatchable> const multiplexer;65 std::shared_ptr<dispatch::MultiplexingDispatchable> const multiplexer;
66 std::shared_ptr<input::android::InputReaderDispatchable> const legacy_dispatchable;66 std::shared_ptr<input::android::InputReaderDispatchable> const legacy_dispatchable;
67 std::shared_ptr<dispatch::ActionQueue> const queue;67 std::shared_ptr<dispatch::ActionQueue> const queue;
68 std::unique_ptr<dispatch::SimpleDispatchThread> input_thread;68 std::unique_ptr<dispatch::ThreadedDispatcher> input_thread;
6969
70 enum class State70 enum class State
71 {71 {
7272
=== modified file 'tests/acceptance-tests/CMakeLists.txt'
--- tests/acceptance-tests/CMakeLists.txt 2015-05-05 06:46:30 +0000
+++ tests/acceptance-tests/CMakeLists.txt 2015-05-14 07:50:26 +0000
@@ -2,6 +2,7 @@
22
3include_directories(3include_directories(
4 ${CMAKE_SOURCE_DIR}4 ${CMAKE_SOURCE_DIR}
5 ${UMOCKDEV_INCLUDE_DIRS}
5)6)
67
7set(8set(
@@ -84,5 +85,5 @@
84 OFF)85 OFF)
8586
86if (MIR_RUN_ACCEPTANCE_TESTS)87if (MIR_RUN_ACCEPTANCE_TESTS)
87 mir_discover_tests(mir_acceptance_tests)88 mir_discover_tests(mir_acceptance_tests LD_PRELOAD=libumockdev-preload.so.0 G_SLICE=always-malloc G_DEBUG=gc-friendly)
88endif (MIR_RUN_ACCEPTANCE_TESTS)89endif (MIR_RUN_ACCEPTANCE_TESTS)
8990
=== modified file 'tests/acceptance-tests/test_client_library.cpp'
--- tests/acceptance-tests/test_client_library.cpp 2015-04-30 12:27:33 +0000
+++ tests/acceptance-tests/test_client_library.cpp 2015-05-14 07:50:26 +0000
@@ -24,6 +24,10 @@
24#include "mir_test_framework/using_stub_client_platform.h"24#include "mir_test_framework/using_stub_client_platform.h"
25#include "mir_test_framework/any_surface.h"25#include "mir_test_framework/any_surface.h"
26#include "mir_test/validity_matchers.h"26#include "mir_test/validity_matchers.h"
27#include "mir_test/fd_utils.h"
28#include "mir_test_framework/udev_environment.h"
29#include "mir_test/signal.h"
30#include "mir_test/auto_unblock_thread.h"
2731
28#include "src/include/client/mir/client_buffer.h"32#include "src/include/client/mir/client_buffer.h"
2933
@@ -37,6 +41,8 @@
37#include <system/window.h> // for ANativeWindowBuffer AKA MirNativeBuffer41#include <system/window.h> // for ANativeWindowBuffer AKA MirNativeBuffer
38#endif42#endif
3943
44#include <boost/throw_exception.hpp>
45
40#include <gtest/gtest.h>46#include <gtest/gtest.h>
41#include <gmock/gmock.h>47#include <gmock/gmock.h>
42#include <chrono>48#include <chrono>
@@ -53,6 +59,8 @@
53namespace mc = mir::compositor;59namespace mc = mir::compositor;
54namespace mcl = mir::client;60namespace mcl = mir::client;
55namespace mtf = mir_test_framework;61namespace mtf = mir_test_framework;
62namespace mt = mir::test;
63
56namespace64namespace
57{65{
58struct ClientLibrary : mtf::HeadlessInProcessServer66struct ClientLibrary : mtf::HeadlessInProcessServer
@@ -61,6 +69,12 @@
61 MirConnection* connection = nullptr;69 MirConnection* connection = nullptr;
62 MirSurface* surface = nullptr;70 MirSurface* surface = nullptr;
63 int buffers = 0;71 int buffers = 0;
72 mtf::UdevEnvironment mock_devices;
73
74 ClientLibrary()
75 {
76 mock_devices.add_standard_device("laptop-keyboard");
77 }
6478
65 static void connection_callback(MirConnection* connection, void* context)79 static void connection_callback(MirConnection* connection, void* context)
66 {80 {
@@ -275,111 +289,6 @@
275 mir_connection_release(connection);289 mir_connection_release(connection);
276}290}
277291
278TEST_F(ClientLibrary, can_set_surface_min_width)
279{
280 connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
281
282 int const width = 640;
283 int const height = 480;
284 auto const format = mir_pixel_format_abgr_8888;
285 auto const spec =
286 mir_connection_create_spec_for_normal_surface(connection, width, height, format);
287
288 int const min_width = 480;
289 EXPECT_TRUE(mir_surface_spec_set_min_width(spec, min_width));
290
291 surface = mir_surface_create_sync(spec);
292 mir_surface_spec_release(spec);
293 mir_surface_release_sync(surface);
294 mir_connection_release(connection);
295}
296
297TEST_F(ClientLibrary, can_set_surface_min_height)
298{
299 connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
300
301 int const width = 640;
302 int const height = 480;
303 auto const format = mir_pixel_format_abgr_8888;
304 auto const spec =
305 mir_connection_create_spec_for_normal_surface(connection, width, height, format);
306
307 int const min_height = 480;
308 EXPECT_TRUE(mir_surface_spec_set_min_height(spec, min_height));
309
310 surface = mir_surface_create_sync(spec);
311 mir_surface_spec_release(spec);
312 mir_surface_release_sync(surface);
313 mir_connection_release(connection);
314}
315
316TEST_F(ClientLibrary, can_set_surface_max_width)
317{
318 connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
319
320 int const width = 640;
321 int const height = 480;
322 auto const format = mir_pixel_format_abgr_8888;
323 auto const spec =
324 mir_connection_create_spec_for_normal_surface(connection, width, height, format);
325
326 int const max_width = 1024;
327 EXPECT_TRUE(mir_surface_spec_set_max_width(spec, max_width));
328
329 surface = mir_surface_create_sync(spec);
330 mir_surface_spec_release(spec);
331 mir_surface_release_sync(surface);
332 mir_connection_release(connection);
333}
334
335TEST_F(ClientLibrary, can_set_surface_max_height)
336{
337 connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
338
339 int const width = 640;
340 int const height = 480;
341 auto const format = mir_pixel_format_abgr_8888;
342 auto const spec =
343 mir_connection_create_spec_for_normal_surface(connection, width, height, format);
344
345 int const max_height = 1024;
346 EXPECT_TRUE(mir_surface_spec_set_max_height(spec, max_height));
347
348 surface = mir_surface_create_sync(spec);
349 mir_surface_spec_release(spec);
350 mir_surface_release_sync(surface);
351 mir_connection_release(connection);
352}
353
354TEST_F(ClientLibrary, min_size_respected_when_placing_surface)
355{
356 connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
357
358 int const width = 6400;
359 int const height = 4800;
360 auto const format = mir_pixel_format_abgr_8888;
361 auto const spec =
362 mir_connection_create_spec_for_normal_surface(connection, width, height, format);
363
364 int const min_width = 4800;
365 int const min_height = 3200;
366
367 mir_surface_spec_set_min_width(spec, min_width);
368 mir_surface_spec_set_min_height(spec, min_height);
369 surface = mir_surface_create_sync(spec);
370 mir_surface_spec_release(spec);
371
372 auto const buffer_stream = mir_surface_get_buffer_stream(surface);
373
374 MirGraphicsRegion graphics_region;
375 mir_buffer_stream_get_graphics_region(buffer_stream, &graphics_region);
376 EXPECT_THAT(graphics_region.width, Ge(min_width));
377 EXPECT_THAT(graphics_region.height, Ge(min_height));
378
379 mir_surface_release_sync(surface);
380 mir_connection_release(connection);
381}
382
383TEST_F(ClientLibrary, receives_surface_dpi_value)292TEST_F(ClientLibrary, receives_surface_dpi_value)
384{293{
385 connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);294 connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
@@ -930,3 +839,320 @@
930 mir_surface_release_sync(surface);839 mir_surface_release_sync(surface);
931 mir_connection_release(connection);840 mir_connection_release(connection);
932}841}
842
843namespace
844{
845struct ThreadTrackingCallbacks
846{
847 ThreadTrackingCallbacks()
848 : client_thread{pthread_self()}
849 {
850 }
851
852 static void connection_ready(MirConnection* /*connection*/, void* ctx)
853 {
854 auto data = reinterpret_cast<ThreadTrackingCallbacks*>(ctx);
855 EXPECT_EQ(pthread_self(), data->client_thread);
856 data->connection_ready_called.raise();
857 }
858
859 static void event_delegate(MirSurface* /*surf*/, MirEvent const* event, void* ctx)
860 {
861 auto data = reinterpret_cast<ThreadTrackingCallbacks*>(ctx);
862
863 EXPECT_THAT(pthread_self(), Eq(data->client_thread));
864 data->event_received.raise();
865 if (mir_event_get_type(event) == mir_event_type_input)
866 {
867 data->input_event_received.raise();
868 }
869 }
870
871 static void surface_created(MirSurface* surf, void* ctx)
872 {
873 auto data = reinterpret_cast<ThreadTrackingCallbacks*>(ctx);
874 EXPECT_THAT(pthread_self(), Eq(data->client_thread));
875 data->surf = surf;
876
877 mir_surface_set_event_handler(data->surf, &ThreadTrackingCallbacks::event_delegate, data);
878 }
879
880 static void swap_buffers_complete(MirBufferStream* /*stream*/, void* ctx)
881 {
882 auto data = reinterpret_cast<ThreadTrackingCallbacks*>(ctx);
883 EXPECT_EQ(pthread_self(), data->client_thread);
884 data->buffers_swapped.raise();
885 }
886
887 pthread_t const client_thread;
888 MirSurface* surf{nullptr};
889 mt::Signal buffers_swapped;
890 mt::Signal connection_ready_called;
891 mt::Signal event_received;
892 mt::Signal input_event_received;
893};
894
895void pump_eventloop_until(MirConnection* connection, std::function<bool()> predicate, std::chrono::steady_clock::time_point timeout)
896{
897 using namespace std::literals::chrono_literals;
898
899 auto fd = mir::Fd{mir::IntOwnedFd{mir_connection_get_event_fd(connection)}};
900
901 while (!predicate() && (std::chrono::steady_clock::now() < timeout))
902 {
903 if (mt::fd_becomes_readable(fd, 10ms))
904 {
905 mir_connection_dispatch(connection);
906 }
907 }
908 if (!predicate())
909 {
910 BOOST_THROW_EXCEPTION((std::runtime_error{"Timeout waiting for state change"}));
911 }
912}
913}
914
915TEST_F(ClientLibrary, manual_dispatch_handles_callbacks_in_parent_thread)
916{
917 using namespace std::literals::chrono_literals;
918
919 auto const test_timeout = std::chrono::steady_clock::now() + 10min;
920
921 ThreadTrackingCallbacks data;
922
923 auto connection = mir_connect_with_manual_dispatch(new_connection().c_str(), __PRETTY_FUNCTION__, &ThreadTrackingCallbacks::connection_ready, &data);
924
925 ASSERT_THAT(connection, Ne(nullptr));
926
927 pump_eventloop_until(
928 connection,
929 [&data]()
930 {
931 return data.connection_ready_called.raised();
932 },
933 test_timeout);
934
935 ASSERT_THAT(connection, IsValid());
936
937 auto surface_spec = mir_connection_create_spec_for_normal_surface(connection,
938 233, 355,
939 mir_pixel_format_argb_8888);
940 auto surf_wh = mir_surface_create(surface_spec,
941 &ThreadTrackingCallbacks::surface_created,
942 &data);
943 mir_surface_spec_release(surface_spec);
944
945 pump_eventloop_until(
946 connection,
947 [surf_wh]()
948 {
949 return mir_wait_handle_ready(surf_wh);
950 },
951 test_timeout);
952
953 EXPECT_THAT(data.surf, IsValid());
954
955 auto buffer_stream = mir_surface_get_buffer_stream(data.surf);
956 auto swap_wh = mir_buffer_stream_swap_buffers(buffer_stream, ThreadTrackingCallbacks::swap_buffers_complete, &data);
957
958 pump_eventloop_until(
959 connection,
960 [swap_wh]()
961 {
962 return mir_wait_handle_ready(swap_wh);
963 },
964 test_timeout);
965 EXPECT_TRUE(data.buffers_swapped.raised());
966
967 mir_surface_release_sync(data.surf);
968 mir_connection_release(connection);
969}
970
971TEST_F(ClientLibrary, manual_dispatch_handles_events_in_parent_thread)
972{
973 using namespace testing;
974 using namespace std::literals::chrono_literals;
975
976 auto const test_timeout = std::chrono::steady_clock::now() + 10min;
977
978 ThreadTrackingCallbacks data;
979
980 connection = mir_connect_with_manual_dispatch(new_connection().c_str(), __PRETTY_FUNCTION__, &ThreadTrackingCallbacks::connection_ready, &data);
981
982 ASSERT_THAT(connection, Ne(nullptr));
983
984 pump_eventloop_until(
985 connection,
986 [&data]()
987 {
988 return data.connection_ready_called.raised();
989 },
990 test_timeout);
991
992 ASSERT_THAT(connection, IsValid());
993
994 auto surface_spec = mir_connection_create_spec_for_normal_surface(connection,
995 233, 355,
996 mir_pixel_format_argb_8888);
997 auto surf_wh = mir_surface_create(surface_spec,
998 &ThreadTrackingCallbacks::surface_created,
999 &data);
1000 mir_surface_spec_release(surface_spec);
1001
1002
1003 pump_eventloop_until(
1004 connection,
1005 [surf_wh]()
1006 {
1007 return mir_wait_handle_ready(surf_wh);
1008 },
1009 test_timeout);
1010
1011 EXPECT_THAT(data.surf, IsValid());
1012
1013 // We need to swap buffers so that the surface is fully realised and
1014 // will be a valid focus target.
1015 //
1016 // The shell will not focus a surface with no content.
1017 auto buffer_stream = mir_surface_get_buffer_stream(data.surf);
1018 mir_buffer_stream_swap_buffers_sync(buffer_stream);
1019
1020 mir_surface_set_state(data.surf, mir_surface_state_fullscreen);
1021
1022 pump_eventloop_until(
1023 connection,
1024 [&data]()
1025 {
1026 return data.event_received.raised();
1027 },
1028 test_timeout);
1029
1030 ASSERT_THAT(mir_surface_get_focus(data.surf), Eq(mir_surface_focused));
1031
1032 mock_devices.load_device_evemu("laptop-keyboard-hello");
1033
1034 pump_eventloop_until(
1035 connection,
1036 [&data]()
1037 {
1038 return data.input_event_received.raised();
1039 },
1040 test_timeout);
1041
1042 mir_surface_release_sync(data.surf);
1043 mir_connection_release(connection);
1044}
1045
1046namespace
1047{
1048struct SignalPair
1049{
1050 mir::test::Signal now_blocking;
1051 mir::test::Signal event_received;
1052};
1053
1054void notifying_event_handler(MirSurface*, MirEvent const* ev, void* ctx)
1055{
1056 auto signal_pair = *reinterpret_cast<std::shared_ptr<SignalPair>*>(ctx);
1057 // We trigger an input event once we've noticed the surface callback is blocking
1058 // so we need to only raise the flag on an input event; otherwise we may spuriously
1059 // fail if we receive a surface event (like the focus event) before we hit
1060 // the wait in blocking_surface_callback()
1061 if (mir_event_get_type(ev) == mir_event_type_input)
1062 {
1063 signal_pair->event_received.raise();
1064 }
1065}
1066
1067void blocking_buffer_stream_callback(MirBufferStream*, void* ctx)
1068{
1069 auto signal_pair = *reinterpret_cast<std::shared_ptr<SignalPair>*>(ctx);
1070 signal_pair->now_blocking.raise();
1071 EXPECT_TRUE(signal_pair->event_received.wait_for(std::chrono::seconds{5}));
1072}
1073}
1074
1075TEST_F(ClientLibrary, rpc_blocking_doesnt_block_event_delivery_with_auto_dispatch)
1076{
1077 using namespace testing;
1078
1079 connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
1080
1081 ASSERT_THAT(connection, IsValid());
1082
1083 auto surface_spec = mir_connection_create_spec_for_normal_surface(connection,
1084 233, 355,
1085 mir_pixel_format_argb_8888);
1086
1087 auto surf = mir_surface_create_sync(surface_spec);
1088 mir_surface_spec_release(surface_spec);
1089
1090 EXPECT_THAT(surf, IsValid());
1091
1092 auto signal_pair = std::make_shared<SignalPair>();
1093 mir_surface_set_event_handler(surf, &notifying_event_handler, &signal_pair);
1094
1095 auto buffer_stream = mir_surface_get_buffer_stream(surf);
1096 auto wh = mir_buffer_stream_swap_buffers(buffer_stream, &blocking_buffer_stream_callback, &signal_pair);
1097
1098 EXPECT_TRUE(signal_pair->now_blocking.wait_for(std::chrono::seconds{5}));
1099 EXPECT_FALSE(signal_pair->event_received.raised());
1100
1101 mock_devices.load_device_evemu("laptop-keyboard-hello");
1102
1103 EXPECT_TRUE(signal_pair->event_received.wait_for(std::chrono::seconds{5}));
1104
1105 mir_wait_for(wh);
1106 mir_surface_release_sync(surf);
1107 mir_connection_release(connection);
1108}
1109
1110namespace
1111{
1112void async_release_completed(MirSurface*, void* ctx)
1113{
1114 auto called = reinterpret_cast<bool*>(ctx);
1115 *called = true;
1116}
1117
1118void async_creation_completed(MirSurface* surf, void* ctx)
1119{
1120 mir_surface_release(surf, &async_release_completed, ctx);
1121}
1122}
1123
1124TEST_F(ClientLibrary, sync_call_completes_before_previous_undispatched_call)
1125{
1126 using namespace std::literals::chrono_literals;
1127 using namespace testing;
1128
1129 auto timeout = std::chrono::steady_clock::now() + 60s;
1130
1131 ThreadTrackingCallbacks data;
1132
1133 auto connection = mir_connect_with_manual_dispatch(new_connection().c_str(), __PRETTY_FUNCTION__, &ThreadTrackingCallbacks::connection_ready, &data);
1134 ASSERT_THAT(connection, Ne(nullptr));
1135
1136 pump_eventloop_until(connection, [connection]() { return mir_connection_is_valid(connection); }, timeout);
1137
1138 bool async_call_completed{false};
1139
1140 auto surface_spec = mir_connection_create_spec_for_normal_surface(connection,
1141 233, 355,
1142 mir_pixel_format_argb_8888);
1143 mir_surface_create(surface_spec, &async_creation_completed, &async_call_completed);
1144
1145 EXPECT_FALSE(async_call_completed);
1146 auto surf = mir_surface_create_sync(surface_spec);
1147 mir_surface_spec_release(surface_spec);
1148
1149 EXPECT_THAT(surf, IsValid());
1150 EXPECT_FALSE(async_call_completed);
1151
1152 mir_surface_release_sync(surf);
1153 EXPECT_FALSE(async_call_completed);
1154
1155 pump_eventloop_until(connection, [&async_call_completed]() { return async_call_completed;}, timeout);
1156
1157 mir_connection_release(connection);
1158}
9331159
=== modified file 'tests/include/mir_test/test_protobuf_client.h'
--- tests/include/mir_test/test_protobuf_client.h 2015-03-31 02:35:42 +0000
+++ tests/include/mir_test/test_protobuf_client.h 2015-05-14 07:50:26 +0000
@@ -32,7 +32,7 @@
32{32{
33namespace dispatch33namespace dispatch
34{34{
35class SimpleDispatchThread;35class ThreadedDispatcher;
36}36}
37namespace test37namespace test
38{38{
@@ -46,7 +46,7 @@
4646
47 std::shared_ptr<doubles::MockRpcReport> rpc_report;47 std::shared_ptr<doubles::MockRpcReport> rpc_report;
48 std::shared_ptr<google::protobuf::RpcChannel> channel;48 std::shared_ptr<google::protobuf::RpcChannel> channel;
49 std::shared_ptr<dispatch::SimpleDispatchThread> eventloop;49 std::shared_ptr<dispatch::ThreadedDispatcher> eventloop;
50 mir::protobuf::DisplayServer::Stub display_server;50 mir::protobuf::DisplayServer::Stub display_server;
51 mir::protobuf::ConnectParameters connect_parameters;51 mir::protobuf::ConnectParameters connect_parameters;
52 mir::protobuf::SurfaceParameters surface_parameters;52 mir::protobuf::SurfaceParameters surface_parameters;
5353
=== modified file 'tests/include/mir_test_doubles/mock_client_buffer_stream.h'
--- tests/include/mir_test_doubles/mock_client_buffer_stream.h 2015-04-01 20:00:19 +0000
+++ tests/include/mir_test_doubles/mock_client_buffer_stream.h 2015-05-14 07:50:26 +0000
@@ -45,6 +45,7 @@
45 MOCK_METHOD0(get_create_wait_handle, MirWaitHandle*(void));45 MOCK_METHOD0(get_create_wait_handle, MirWaitHandle*(void));
46 MOCK_CONST_METHOD0(rpc_id, frontend::BufferStreamId(void));46 MOCK_CONST_METHOD0(rpc_id, frontend::BufferStreamId(void));
47 MOCK_CONST_METHOD0(valid, bool(void));47 MOCK_CONST_METHOD0(valid, bool(void));
48 MOCK_CONST_METHOD0(get_connection, MirConnection*());
48};49};
4950
50}51}
5152
=== modified file 'tests/include/mir_test_doubles/mock_client_buffer_stream_factory.h'
--- tests/include/mir_test_doubles/mock_client_buffer_stream_factory.h 2015-04-21 17:22:09 +0000
+++ tests/include/mir_test_doubles/mock_client_buffer_stream_factory.h 2015-05-14 07:50:26 +0000
@@ -34,12 +34,22 @@
3434
35struct MockClientBufferStreamFactory : public client::ClientBufferStreamFactory35struct MockClientBufferStreamFactory : public client::ClientBufferStreamFactory
36{36{
37 MOCK_METHOD3(make_consumer_stream, std::shared_ptr<client::ClientBufferStream>(protobuf::DisplayServer&,37 MOCK_METHOD4(make_consumer_stream, std::shared_ptr<client::ClientBufferStream>(
38 protobuf::BufferStream const&, std::string const&));38 MirConnection*,
39 MOCK_METHOD3(make_producer_stream, std::shared_ptr<client::ClientBufferStream>(protobuf::DisplayServer&,39 protobuf::DisplayServer&,
40 protobuf::BufferStream const&, std::string const&));40 protobuf::BufferStream const&,
41 MOCK_METHOD4(make_producer_stream, client::ClientBufferStream*(protobuf::DisplayServer&,41 std::string const&));
42 protobuf::BufferStreamParameters const&, mir_buffer_stream_callback callback, void* context));42 MOCK_METHOD4(make_producer_stream, std::shared_ptr<client::ClientBufferStream>(
43 MirConnection*,
44 protobuf::DisplayServer&,
45 protobuf::BufferStream const&,
46 std::string const&));
47 MOCK_METHOD5(make_producer_stream, client::ClientBufferStream*(
48 MirConnection*,
49 protobuf::DisplayServer&,
50 protobuf::BufferStreamParameters const&,
51 mir_buffer_stream_callback callback,
52 void* context));
43};53};
4454
45}55}
4656
=== modified file 'tests/include/mir_test_doubles/stub_client_buffer_stream_factory.h'
--- tests/include/mir_test_doubles/stub_client_buffer_stream_factory.h 2015-04-01 20:00:19 +0000
+++ tests/include/mir_test_doubles/stub_client_buffer_stream_factory.h 2015-05-14 07:50:26 +0000
@@ -32,6 +32,7 @@
32struct StubClientBufferStreamFactory : public client::ClientBufferStreamFactory32struct StubClientBufferStreamFactory : public client::ClientBufferStreamFactory
33{33{
34 std::shared_ptr<client::ClientBufferStream> make_consumer_stream(34 std::shared_ptr<client::ClientBufferStream> make_consumer_stream(
35 MirConnection* /* allocating_connection */,
35 protobuf::DisplayServer& /* server */,36 protobuf::DisplayServer& /* server */,
36 protobuf::BufferStream const& /* protobuf_bs */,37 protobuf::BufferStream const& /* protobuf_bs */,
37 std::string const& /* surface_name */) override38 std::string const& /* surface_name */) override
@@ -40,6 +41,7 @@
40 }41 }
4142
42 std::shared_ptr<client::ClientBufferStream> make_producer_stream(43 std::shared_ptr<client::ClientBufferStream> make_producer_stream(
44 MirConnection* /* allocating_connection */,
43 protobuf::DisplayServer& /* server */,45 protobuf::DisplayServer& /* server */,
44 protobuf::BufferStream const& /* protobuf_bs */,46 protobuf::BufferStream const& /* protobuf_bs */,
45 std::string const& /* surface_name */) override47 std::string const& /* surface_name */) override
@@ -47,7 +49,9 @@
47 return nullptr;49 return nullptr;
48 }50 }
4951
50 client::ClientBufferStream* make_producer_stream(protobuf::DisplayServer& /* server */,52 client::ClientBufferStream* make_producer_stream(
53 MirConnection* /* allocating_connection */,
54 protobuf::DisplayServer& /* server */,
51 protobuf::BufferStreamParameters const& /* params */,55 protobuf::BufferStreamParameters const& /* params */,
52 mir_buffer_stream_callback /* callback */, void* /* context */) override56 mir_buffer_stream_callback /* callback */, void* /* context */) override
53 {57 {
5458
=== modified file 'tests/include/mir_test_framework/udev_environment.h'
--- tests/include/mir_test_framework/udev_environment.h 2015-01-21 07:34:50 +0000
+++ tests/include/mir_test_framework/udev_environment.h 2015-05-14 07:50:26 +0000
@@ -53,6 +53,32 @@
53 */53 */
54 void add_standard_device(std::string const& name);54 void add_standard_device(std::string const& name);
5555
56 /**
57 * Load an ioctl recording for a UMockdev device
58 *
59 * Looks for a <tt>name</tt>.ioctl file recorded with umockdev-record --ioctl
60 * and adds it to the associated device in the testbed.
61 *
62 * The udev records for the device these ioctl records will be associated with
63 * must already exist in the testbed
64 *
65 * @param name The unadorned filename for the ioctl records to add
66 */
67 void load_device_ioctls(std::string const& name);
68
69 /**
70 * Load an evemu evdev recording for a UMockdev device
71 *
72 * Looks for a <tt>name</tt>.evemu file recorded with umockdev-record --evemu
73 * (or evemu-record) and associates it with the udev device it was recorded from.
74 *
75 * The udev records for the device this recording is associated with
76 * must already exist in the testbed
77 *
78 * @param name The unadorned filename for the ioctl records to add
79 */
80 void load_device_evemu(std::string const& name);
81
56 UMockdevTestbed *testbed;82 UMockdevTestbed *testbed;
57 std::string const recordings_path;83 std::string const recordings_path;
58};84};
5985
=== modified file 'tests/integration-tests/client/test_screencast.cpp'
--- tests/integration-tests/client/test_screencast.cpp 2015-03-31 02:35:42 +0000
+++ tests/integration-tests/client/test_screencast.cpp 2015-05-14 07:50:26 +0000
@@ -18,7 +18,8 @@
1818
19#include "mir_protobuf.pb.h"19#include "mir_protobuf.pb.h"
20#include "src/client/default_connection_configuration.h"20#include "src/client/default_connection_configuration.h"
21#include "mir/dispatch/simple_dispatch_thread.h"21#include "src/client/rpc/mir_basic_rpc_channel.h"
22#include "mir/dispatch/threaded_dispatcher.h"
22#include "mir/dispatch/dispatchable.h"23#include "mir/dispatch/dispatchable.h"
2324
24#include "mir/frontend/connector.h"25#include "mir/frontend/connector.h"
@@ -78,16 +79,15 @@
78 protobuf_server =79 protobuf_server =
79 std::make_shared<mir::protobuf::DisplayServer::Stub>(rpc_channel.get());80 std::make_shared<mir::protobuf::DisplayServer::Stub>(rpc_channel.get());
80 eventloop =81 eventloop =
81 std::make_shared<md::SimpleDispatchThread>(82 std::make_shared<md::ThreadedDispatcher>("I/O loop", rpc_channel);
82 std::dynamic_pointer_cast<md::Dispatchable>(rpc_channel));
83 }83 }
8484
85 char const* const test_socket = "./test_socket_screencast";85 char const* const test_socket = "./test_socket_screencast";
86 std::shared_ptr<StubScreencastServerTool> const server_tool;86 std::shared_ptr<StubScreencastServerTool> const server_tool;
87 std::shared_ptr<mt::TestProtobufServer> test_server;87 std::shared_ptr<mt::TestProtobufServer> test_server;
88 std::shared_ptr<google::protobuf::RpcChannel> rpc_channel;88 std::shared_ptr<mir::client::rpc::MirBasicRpcChannel> rpc_channel;
89 std::shared_ptr<mir::protobuf::DisplayServer> protobuf_server;89 std::shared_ptr<mir::protobuf::DisplayServer> protobuf_server;
90 std::shared_ptr<mir::dispatch::SimpleDispatchThread> eventloop;90 std::shared_ptr<mir::dispatch::ThreadedDispatcher> eventloop;
91};91};
9292
93}93}
9494
=== modified file 'tests/mir_test_doubles/test_protobuf_client.cpp'
--- tests/mir_test_doubles/test_protobuf_client.cpp 2015-04-01 19:39:19 +0000
+++ tests/mir_test_doubles/test_protobuf_client.cpp 2015-05-14 07:50:26 +0000
@@ -26,7 +26,7 @@
26#include "src/client/rpc/make_rpc_channel.h"26#include "src/client/rpc/make_rpc_channel.h"
27#include "src/client/rpc/mir_basic_rpc_channel.h"27#include "src/client/rpc/mir_basic_rpc_channel.h"
28#include "mir/dispatch/dispatchable.h"28#include "mir/dispatch/dispatchable.h"
29#include "mir/dispatch/simple_dispatch_thread.h"29#include "mir/dispatch/threaded_dispatcher.h"
30#include "mir/events/event_private.h"30#include "mir/events/event_private.h"
3131
32#include <thread>32#include <thread>
@@ -46,7 +46,7 @@
46 rpc_report,46 rpc_report,
47 std::make_shared<mir::client::LifecycleControl>(),47 std::make_shared<mir::client::LifecycleControl>(),
48 std::make_shared<mtd::NullClientEventSink>())),48 std::make_shared<mtd::NullClientEventSink>())),
49 eventloop{std::make_shared<md::SimpleDispatchThread>(std::dynamic_pointer_cast<md::Dispatchable>(channel))},49 eventloop{std::make_shared<md::ThreadedDispatcher>("I/O loop", std::dynamic_pointer_cast<md::Dispatchable>(channel))},
50 display_server(channel.get(), ::google::protobuf::Service::STUB_DOESNT_OWN_CHANNEL),50 display_server(channel.get(), ::google::protobuf::Service::STUB_DOESNT_OWN_CHANNEL),
51 maxwait(timeout_ms),51 maxwait(timeout_ms),
52 connect_done_called(false),52 connect_done_called(false),
5353
=== modified file 'tests/mir_test_framework/CMakeLists.txt'
--- tests/mir_test_framework/CMakeLists.txt 2015-04-30 11:36:36 +0000
+++ tests/mir_test_framework/CMakeLists.txt 2015-05-14 07:50:26 +0000
@@ -14,6 +14,7 @@
14add_definitions(14add_definitions(
15 -DMIR_CLIENT_PLATFORM_PATH="${MIR_CLIENT_PLATFORM_PATH}"15 -DMIR_CLIENT_PLATFORM_PATH="${MIR_CLIENT_PLATFORM_PATH}"
16 -DMIR_SERVER_PLATFORM_PATH="${MIR_SERVER_PLATFORM_PATH}"16 -DMIR_SERVER_PLATFORM_PATH="${MIR_SERVER_PLATFORM_PATH}"
17 -DTEST_RECORDING_PATH="${CMAKE_INSTALL_PREFIX}/share/mir/udev_recordings"
17 -DMIR_CLIENT_PLATFORM_ABI_STRING="${MIR_CLIENT_PLATFORM_ABI}"18 -DMIR_CLIENT_PLATFORM_ABI_STRING="${MIR_CLIENT_PLATFORM_ABI}"
18 -DMIR_SERVER_GRAPHICS_PLATFORM_ABI_STRING="${MIR_SERVER_GRAPHICS_PLATFORM_ABI}"19 -DMIR_SERVER_GRAPHICS_PLATFORM_ABI_STRING="${MIR_SERVER_GRAPHICS_PLATFORM_ABI}"
19 )20 )
@@ -158,3 +159,5 @@
158install(TARGETS mirplatformgraphicsstub LIBRARY DESTINATION ${MIR_SERVER_PLATFORM_PATH})159install(TARGETS mirplatformgraphicsstub LIBRARY DESTINATION ${MIR_SERVER_PLATFORM_PATH})
159160
160install(TARGETS mirclientplatformstub LIBRARY DESTINATION ${MIR_CLIENT_PLATFORM_PATH})161install(TARGETS mirclientplatformstub LIBRARY DESTINATION ${MIR_CLIENT_PLATFORM_PATH})
162
163install(DIRECTORY udev_recordings DESTINATION ${CMAKE_INSTALL_PREFIX}/share/mir)
161164
=== modified file 'tests/mir_test_framework/udev_environment.cpp'
--- tests/mir_test_framework/udev_environment.cpp 2015-01-21 07:34:50 +0000
+++ tests/mir_test_framework/udev_environment.cpp 2015-05-14 07:50:26 +0000
@@ -37,9 +37,33 @@
3737
38namespace mtf = mir_test_framework;38namespace mtf = mir_test_framework;
3939
40namespace
41{
42std::string find_recording_path()
43{
44 std::initializer_list<std::string> candidates{mtf::executable_path() + "/udev_recordings", TEST_RECORDING_PATH};
45 for (auto candidate : candidates)
46 {
47 struct stat sb;
48 if (stat(candidate.c_str(), &sb) == 0)
49 {
50 if (S_ISDIR(sb.st_mode))
51 {
52 return candidate;
53 }
54 }
55 }
56 BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to find udev recordings directory"}));
57}
58}
59
40mtf::UdevEnvironment::UdevEnvironment()60mtf::UdevEnvironment::UdevEnvironment()
41 : recordings_path(mtf::executable_path() + "/udev_recordings")61 : recordings_path{find_recording_path()}
42{62{
63 if (!umockdev_in_mock_environment())
64 {
65 BOOST_THROW_EXCEPTION((std::runtime_error{"Test not running under umockdev!"}));
66 }
43 testbed = umockdev_testbed_new();67 testbed = umockdev_testbed_new();
44}68}
4569
@@ -109,4 +133,45 @@
109 }133 }
110 }134 }
111 }135 }
136
137 auto script_filename = recordings_path + "/" + name + ".script";
138 if (stat(script_filename.c_str(), &sb) == 0)
139 {
140 if (S_ISREG(sb.st_mode) || S_ISLNK(sb.st_mode))
141 {
142 if (!umockdev_testbed_load_script(testbed, NULL, script_filename.c_str(), &err))
143 {
144 BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Failed to load device recording: ") +
145 err->message));
146 }
147 }
148 }
149}
150
151void mtf::UdevEnvironment::load_device_ioctls(std::string const& name)
152{
153 auto ioctls_filename = recordings_path + "/" + name + ".ioctl";
154
155 GError* err = nullptr;
156 if (!umockdev_testbed_load_ioctl(testbed, NULL, ioctls_filename.c_str(), &err))
157 {
158 std::runtime_error exception{std::string("Failed to load ioctl recording: ") +
159 err->message};
160 g_error_free(err);
161 BOOST_THROW_EXCEPTION(exception);
162 }
163}
164
165void mtf::UdevEnvironment::load_device_evemu(std::string const& name)
166{
167 auto evemu_filename = recordings_path + "/" + name + ".evemu";
168
169 GError* err = nullptr;
170 if (!umockdev_testbed_load_evemu_events(testbed, NULL, evemu_filename.c_str(), &err))
171 {
172 std::runtime_error exception{std::string{"Failed to load evemu recording: "} +
173 err->message};
174 g_error_free(err);
175 BOOST_THROW_EXCEPTION(exception);
176 }
112}177}
113178
=== added file 'tests/mir_test_framework/udev_recordings/laptop-keyboard-hello.evemu'
--- tests/mir_test_framework/udev_recordings/laptop-keyboard-hello.evemu 1970-01-01 00:00:00 +0000
+++ tests/mir_test_framework/udev_recordings/laptop-keyboard-hello.evemu 2015-05-14 07:50:26 +0000
@@ -0,0 +1,272 @@
1# device /dev/input/event4
2# EVEMU 1.2
3# Input device name: "AT Translated Set 2 keyboard"
4# Input device ID: bus 0x11 vendor 0x01 product 0x01 version 0xab83
5# Supported events:
6# Event type 0 (EV_SYN)
7# Event code 0 (SYN_REPORT)
8# Event code 1 (SYN_CONFIG)
9# Event code 4 (FF_STATUS_STOPPED)
10# Event code 17 ((null))
11# Event code 20 ((null))
12# Event type 1 (EV_KEY)
13# Event code 1 (KEY_ESC)
14# Event code 2 (KEY_1)
15# Event code 3 (KEY_2)
16# Event code 4 (KEY_3)
17# Event code 5 (KEY_4)
18# Event code 6 (KEY_5)
19# Event code 7 (KEY_6)
20# Event code 8 (KEY_7)
21# Event code 9 (KEY_8)
22# Event code 10 (KEY_9)
23# Event code 11 (KEY_0)
24# Event code 12 (KEY_MINUS)
25# Event code 13 (KEY_EQUAL)
26# Event code 14 (KEY_BACKSPACE)
27# Event code 15 (KEY_TAB)
28# Event code 16 (KEY_Q)
29# Event code 17 (KEY_W)
30# Event code 18 (KEY_E)
31# Event code 19 (KEY_R)
32# Event code 20 (KEY_T)
33# Event code 21 (KEY_Y)
34# Event code 22 (KEY_U)
35# Event code 23 (KEY_I)
36# Event code 24 (KEY_O)
37# Event code 25 (KEY_P)
38# Event code 26 (KEY_LEFTBRACE)
39# Event code 27 (KEY_RIGHTBRACE)
40# Event code 28 (KEY_ENTER)
41# Event code 29 (KEY_LEFTCTRL)
42# Event code 30 (KEY_A)
43# Event code 31 (KEY_S)
44# Event code 32 (KEY_D)
45# Event code 33 (KEY_F)
46# Event code 34 (KEY_G)
47# Event code 35 (KEY_H)
48# Event code 36 (KEY_J)
49# Event code 37 (KEY_K)
50# Event code 38 (KEY_L)
51# Event code 39 (KEY_SEMICOLON)
52# Event code 40 (KEY_APOSTROPHE)
53# Event code 41 (KEY_GRAVE)
54# Event code 42 (KEY_LEFTSHIFT)
55# Event code 43 (KEY_BACKSLASH)
56# Event code 44 (KEY_Z)
57# Event code 45 (KEY_X)
58# Event code 46 (KEY_C)
59# Event code 47 (KEY_V)
60# Event code 48 (KEY_B)
61# Event code 49 (KEY_N)
62# Event code 50 (KEY_M)
63# Event code 51 (KEY_COMMA)
64# Event code 52 (KEY_DOT)
65# Event code 53 (KEY_SLASH)
66# Event code 54 (KEY_RIGHTSHIFT)
67# Event code 55 (KEY_KPASTERISK)
68# Event code 56 (KEY_LEFTALT)
69# Event code 57 (KEY_SPACE)
70# Event code 58 (KEY_CAPSLOCK)
71# Event code 59 (KEY_F1)
72# Event code 60 (KEY_F2)
73# Event code 61 (KEY_F3)
74# Event code 62 (KEY_F4)
75# Event code 63 (KEY_F5)
76# Event code 64 (KEY_F6)
77# Event code 65 (KEY_F7)
78# Event code 66 (KEY_F8)
79# Event code 67 (KEY_F9)
80# Event code 68 (KEY_F10)
81# Event code 69 (KEY_NUMLOCK)
82# Event code 70 (KEY_SCROLLLOCK)
83# Event code 71 (KEY_KP7)
84# Event code 72 (KEY_KP8)
85# Event code 73 (KEY_KP9)
86# Event code 74 (KEY_KPMINUS)
87# Event code 75 (KEY_KP4)
88# Event code 76 (KEY_KP5)
89# Event code 77 (KEY_KP6)
90# Event code 78 (KEY_KPPLUS)
91# Event code 79 (KEY_KP1)
92# Event code 80 (KEY_KP2)
93# Event code 81 (KEY_KP3)
94# Event code 82 (KEY_KP0)
95# Event code 83 (KEY_KPDOT)
96# Event code 85 (KEY_ZENKAKUHANKAKU)
97# Event code 86 (KEY_102ND)
98# Event code 87 (KEY_F11)
99# Event code 88 (KEY_F12)
100# Event code 89 (KEY_RO)
101# Event code 90 (KEY_KATAKANA)
102# Event code 91 (KEY_HIRAGANA)
103# Event code 92 (KEY_HENKAN)
104# Event code 93 (KEY_KATAKANAHIRAGANA)
105# Event code 94 (KEY_MUHENKAN)
106# Event code 95 (KEY_KPJPCOMMA)
107# Event code 96 (KEY_KPENTER)
108# Event code 97 (KEY_RIGHTCTRL)
109# Event code 98 (KEY_KPSLASH)
110# Event code 99 (KEY_SYSRQ)
111# Event code 100 (KEY_RIGHTALT)
112# Event code 102 (KEY_HOME)
113# Event code 103 (KEY_UP)
114# Event code 104 (KEY_PAGEUP)
115# Event code 105 (KEY_LEFT)
116# Event code 106 (KEY_RIGHT)
117# Event code 107 (KEY_END)
118# Event code 108 (KEY_DOWN)
119# Event code 109 (KEY_PAGEDOWN)
120# Event code 110 (KEY_INSERT)
121# Event code 111 (KEY_DELETE)
122# Event code 112 (KEY_MACRO)
123# Event code 113 (KEY_MUTE)
124# Event code 114 (KEY_VOLUMEDOWN)
125# Event code 115 (KEY_VOLUMEUP)
126# Event code 116 (KEY_POWER)
127# Event code 117 (KEY_KPEQUAL)
128# Event code 118 (KEY_KPPLUSMINUS)
129# Event code 119 (KEY_PAUSE)
130# Event code 121 (KEY_KPCOMMA)
131# Event code 122 (KEY_HANGEUL)
132# Event code 123 (KEY_HANJA)
133# Event code 124 (KEY_YEN)
134# Event code 125 (KEY_LEFTMETA)
135# Event code 126 (KEY_RIGHTMETA)
136# Event code 127 (KEY_COMPOSE)
137# Event code 128 (KEY_STOP)
138# Event code 140 (KEY_CALC)
139# Event code 142 (KEY_SLEEP)
140# Event code 143 (KEY_WAKEUP)
141# Event code 155 (KEY_MAIL)
142# Event code 156 (KEY_BOOKMARKS)
143# Event code 157 (KEY_COMPUTER)
144# Event code 158 (KEY_BACK)
145# Event code 159 (KEY_FORWARD)
146# Event code 163 (KEY_NEXTSONG)
147# Event code 164 (KEY_PLAYPAUSE)
148# Event code 165 (KEY_PREVIOUSSONG)
149# Event code 166 (KEY_STOPCD)
150# Event code 172 (KEY_HOMEPAGE)
151# Event code 173 (KEY_REFRESH)
152# Event code 183 (KEY_F13)
153# Event code 184 (KEY_F14)
154# Event code 185 (KEY_F15)
155# Event code 217 (KEY_SEARCH)
156# Event code 226 (KEY_MEDIA)
157# Event type 4 (EV_MSC)
158# Event code 4 (MSC_SCAN)
159# Event type 17 (EV_LED)
160# Event code 0 (LED_NUML)
161# Event code 1 (LED_CAPSL)
162# Event code 2 (LED_SCROLLL)
163# Event type 20 (EV_REP)
164# Properties:
165# N: AT Translated Set 2 keyboard
166# I: 0011 0001 0001 ab83
167# P: 00 00 00 00 00 00 00 00
168# B: 00 13 00 12 00 00 00 00 00
169# B: 01 fe ff ff ff ff ff ff ff
170# B: 01 ff ff ef ff df ff ff fe
171# B: 01 01 d0 00 f8 78 30 80 03
172# B: 01 00 00 00 02 04 00 00 00
173# B: 01 00 00 00 00 00 00 00 00
174# B: 01 00 00 00 00 00 00 00 00
175# B: 01 00 00 00 00 00 00 00 00
176# B: 01 00 00 00 00 00 00 00 00
177# B: 01 00 00 00 00 00 00 00 00
178# B: 01 00 00 00 00 00 00 00 00
179# B: 01 00 00 00 00 00 00 00 00
180# B: 01 00 00 00 00 00 00 00 00
181# B: 02 00 00 00 00 00 00 00 00
182# B: 03 00 00 00 00 00 00 00 00
183# B: 04 10 00 00 00 00 00 00 00
184# B: 05 00 00 00 00 00 00 00 00
185# B: 11 07 00 00 00 00 00 00 00
186# B: 12 00 00 00 00 00 00 00 00
187# B: 15 00 00 00 00 00 00 00 00
188# B: 15 00 00 00 00 00 00 00 00
189################################
190# Waiting for events #
191################################
192E: 0.000000 0004 0004 0028 # EV_MSC / MSC_SCAN 28
193E: 0.000000 0001 001c 0000 # EV_KEY / KEY_ENTER 0
194E: 0.000000 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
195E: 0.825945 0004 0004 0054 # EV_MSC / MSC_SCAN 54
196E: 0.825945 0001 0036 0001 # EV_KEY / KEY_RIGHTSHIFT 1
197E: 0.825945 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
198E: 1.101404 0004 0004 0054 # EV_MSC / MSC_SCAN 54
199E: 1.101404 0001 0036 0002 # EV_KEY / KEY_RIGHTSHIFT 2
200E: 1.101404 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
201E: 1.131449 0004 0004 0054 # EV_MSC / MSC_SCAN 54
202E: 1.131449 0001 0036 0002 # EV_KEY / KEY_RIGHTSHIFT 2
203E: 1.131449 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
204E: 1.161472 0004 0004 0036 # EV_MSC / MSC_SCAN 36
205E: 1.161472 0001 0024 0001 # EV_KEY / KEY_J 1
206E: 1.161472 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
207E: 1.221981 0004 0004 0036 # EV_MSC / MSC_SCAN 36
208E: 1.221981 0001 0024 0000 # EV_KEY / KEY_J 0
209E: 1.221981 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
210E: 1.241906 0004 0004 0054 # EV_MSC / MSC_SCAN 54
211E: 1.241906 0001 0036 0000 # EV_KEY / KEY_RIGHTSHIFT 0
212E: 1.241906 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
213E: 1.346773 0004 0004 0032 # EV_MSC / MSC_SCAN 32
214E: 1.346773 0001 0020 0001 # EV_KEY / KEY_D 1
215E: 1.346773 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
216E: 1.432213 0004 0004 0032 # EV_MSC / MSC_SCAN 32
217E: 1.432213 0001 0020 0000 # EV_KEY / KEY_D 0
218E: 1.432213 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
219E: 1.562060 0004 0004 0025 # EV_MSC / MSC_SCAN 25
220E: 1.562060 0001 0019 0001 # EV_KEY / KEY_P 1
221E: 1.562060 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
222E: 1.622477 0004 0004 0025 # EV_MSC / MSC_SCAN 25
223E: 1.622477 0001 0019 0000 # EV_KEY / KEY_P 0
224E: 1.622477 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
225E: 1.687305 0004 0004 0025 # EV_MSC / MSC_SCAN 25
226E: 1.687305 0001 0019 0001 # EV_KEY / KEY_P 1
227E: 1.687305 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
228E: 1.777807 0004 0004 0025 # EV_MSC / MSC_SCAN 25
229E: 1.777807 0001 0019 0000 # EV_KEY / KEY_P 0
230E: 1.777807 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
231E: 1.867577 0004 0004 0031 # EV_MSC / MSC_SCAN 31
232E: 1.867577 0001 001f 0001 # EV_KEY / KEY_S 1
233E: 1.867577 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
234E: 1.953019 0004 0004 0031 # EV_MSC / MSC_SCAN 31
235E: 1.953019 0001 001f 0000 # EV_KEY / KEY_S 0
236E: 1.953019 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
237E: 2.118004 0004 0004 0054 # EV_MSC / MSC_SCAN 54
238E: 2.118004 0001 0036 0001 # EV_KEY / KEY_RIGHTSHIFT 1
239E: 2.118004 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
240E: 2.348316 0004 0004 0002 # EV_MSC / MSC_SCAN 2
241E: 2.348316 0001 0002 0001 # EV_KEY / KEY_1 1
242E: 2.348316 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
243E: 2.489561 0004 0004 0002 # EV_MSC / MSC_SCAN 2
244E: 2.489561 0001 0002 0000 # EV_KEY / KEY_1 0
245E: 2.489561 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
246E: 2.523740 0004 0004 0054 # EV_MSC / MSC_SCAN 54
247E: 2.523740 0001 0036 0000 # EV_KEY / KEY_RIGHTSHIFT 0
248E: 2.523740 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
249E: 6.714286 0004 0004 0029 # EV_MSC / MSC_SCAN 29
250E: 6.714286 0001 001d 0001 # EV_KEY / KEY_LEFTCTRL 1
251E: 6.714286 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
252E: 6.989728 0004 0004 0029 # EV_MSC / MSC_SCAN 29
253E: 6.989728 0001 001d 0002 # EV_KEY / KEY_LEFTCTRL 2
254E: 6.989728 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
255E: 7.019767 0004 0004 0029 # EV_MSC / MSC_SCAN 29
256E: 7.019767 0001 001d 0002 # EV_KEY / KEY_LEFTCTRL 2
257E: 7.019767 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
258E: 7.049847 0004 0004 0029 # EV_MSC / MSC_SCAN 29
259E: 7.049847 0001 001d 0002 # EV_KEY / KEY_LEFTCTRL 2
260E: 7.049847 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
261E: 7.079889 0004 0004 0029 # EV_MSC / MSC_SCAN 29
262E: 7.079889 0001 001d 0002 # EV_KEY / KEY_LEFTCTRL 2
263E: 7.079889 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
264E: 7.109934 0004 0004 0029 # EV_MSC / MSC_SCAN 29
265E: 7.109934 0001 001d 0002 # EV_KEY / KEY_LEFTCTRL 2
266E: 7.109934 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
267E: 7.139943 0004 0004 0029 # EV_MSC / MSC_SCAN 29
268E: 7.139943 0001 001d 0002 # EV_KEY / KEY_LEFTCTRL 2
269E: 7.139943 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
270E: 7.170005 0004 0004 0023 # EV_MSC / MSC_SCAN 23
271E: 7.170005 0001 0017 0001 # EV_KEY / KEY_I 1
272E: 7.170005 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
0273
=== modified file 'tests/unit-tests/client/test_client_buffer_stream.cpp'
--- tests/unit-tests/client/test_client_buffer_stream.cpp 2015-04-16 19:56:08 +0000
+++ tests/unit-tests/client/test_client_buffer_stream.cpp 2015-05-14 07:50:26 +0000
@@ -132,7 +132,7 @@
132 mcl::ClientBufferFactory& buffer_factory,132 mcl::ClientBufferFactory& buffer_factory,
133 mcl::BufferStreamMode mode=mcl::BufferStreamMode::Producer)133 mcl::BufferStreamMode mode=mcl::BufferStreamMode::Producer)
134 {134 {
135 return std::make_shared<mcl::BufferStream>(mock_protobuf_server, mode,135 return std::make_shared<mcl::BufferStream>(nullptr, mock_protobuf_server, mode,
136 std::make_shared<StubClientPlatform>(mt::fake_shared(buffer_factory)), protobuf_bs, perf_report, "");136 std::make_shared<StubClientPlatform>(mt::fake_shared(buffer_factory)), protobuf_bs, perf_report, "");
137 }137 }
138};138};
@@ -448,7 +448,7 @@
448448
449 EXPECT_CALL(mock_perf_report, name_surface(StrEq(name))).Times(1);449 EXPECT_CALL(mock_perf_report, name_surface(StrEq(name))).Times(1);
450450
451 auto bs = std::make_shared<mcl::BufferStream>(mock_protobuf_server, mcl::BufferStreamMode::Producer,451 auto bs = std::make_shared<mcl::BufferStream>(nullptr, mock_protobuf_server, mcl::BufferStreamMode::Producer,
452 std::make_shared<StubClientPlatform>(mt::fake_shared(stub_client_buffer_factory)),452 std::make_shared<StubClientPlatform>(mt::fake_shared(stub_client_buffer_factory)),
453 protobuf_bs, mt::fake_shared(mock_perf_report), name);453 protobuf_bs, mt::fake_shared(mock_perf_report), name);
454}454}
455455
=== modified file 'tests/unit-tests/client/test_client_mir_surface.cpp'
--- tests/unit-tests/client/test_client_mir_surface.cpp 2015-04-16 20:03:39 +0000
+++ tests/unit-tests/client/test_client_mir_surface.cpp 2015-05-14 07:50:26 +0000
@@ -30,8 +30,8 @@
30#include "src/client/mir_connection.h"30#include "src/client/mir_connection.h"
31#include "src/client/default_connection_configuration.h"31#include "src/client/default_connection_configuration.h"
32#include "src/client/rpc/null_rpc_report.h"32#include "src/client/rpc/null_rpc_report.h"
33#include "mir/dispatch/simple_dispatch_thread.h"
34#include "mir/dispatch/dispatchable.h"33#include "mir/dispatch/dispatchable.h"
34#include "mir/dispatch/threaded_dispatcher.h"
3535
36#include "mir/frontend/connector.h"36#include "mir/frontend/connector.h"
37#include "mir/input/input_platform.h"37#include "mir/input/input_platform.h"
3838
=== modified file 'tests/unit-tests/client/test_mir_connection.cpp'
--- tests/unit-tests/client/test_mir_connection.cpp 2015-03-31 02:35:42 +0000
+++ tests/unit-tests/client/test_mir_connection.cpp 2015-05-14 07:50:26 +0000
@@ -34,6 +34,10 @@
34#include "mir_test/test_protobuf_server.h"34#include "mir_test/test_protobuf_server.h"
35#include "mir_test/stub_server_tool.h"35#include "mir_test/stub_server_tool.h"
36#include "mir_test_doubles/stub_client_buffer_factory.h"36#include "mir_test_doubles/stub_client_buffer_factory.h"
37#include "mir_test/pipe.h"
38#include "mir_test/signal.h"
39#include "mir_test/fd_utils.h"
40#include "mir_test/test_dispatchable.h"
3741
38#include "mir_protobuf.pb.h"42#include "mir_protobuf.pb.h"
3943
@@ -49,16 +53,19 @@
49namespace md = mir::dispatch;53namespace md = mir::dispatch;
50namespace geom = mir::geometry;54namespace geom = mir::geometry;
51namespace mtd = mir::test::doubles;55namespace mtd = mir::test::doubles;
56namespace md = mir::dispatch;
57namespace mt = mir::test;
5258
53namespace59namespace
54{60{
5561
56struct MockRpcChannel : public mir::client::rpc::MirBasicRpcChannel,62struct MockRpcChannel : public mir::client::rpc::MirBasicRpcChannel
57 public mir::dispatch::Dispatchable
58{63{
59 MockRpcChannel()64 MockRpcChannel()
60 {65 {
61 ON_CALL(*this, watch_fd()).WillByDefault(testing::Return(mir::Fd{}));66 using namespace testing;
67 ON_CALL(*this, watch_fd()).WillByDefault(Return(fake_epoll.read_fd()));
68 ON_CALL(*this, relevant_events()).WillByDefault(Return(md::FdEvent::readable));
62 }69 }
6370
64 void CallMethod(const google::protobuf::MethodDescriptor* method,71 void CallMethod(const google::protobuf::MethodDescriptor* method,
@@ -86,14 +93,35 @@
86 complete->Run();93 complete->Run();
87 }94 }
8895
96 // MirBasicRpcChannel
97 MOCK_METHOD0(process_next_request_first,void());
98
99 // Dispatchable
100 MOCK_CONST_METHOD0(watch_fd, mir::Fd());
101 MOCK_METHOD1(dispatch, bool(md::FdEvents));
102 MOCK_CONST_METHOD0(relevant_events, md::FdEvents());
103
89 MOCK_METHOD2(connect, void(mp::ConnectParameters const*,mp::Connection*));104 MOCK_METHOD2(connect, void(mp::ConnectParameters const*,mp::Connection*));
90 MOCK_METHOD1(configure_display_sent, void(mp::DisplayConfiguration const*));105 MOCK_METHOD1(configure_display_sent, void(mp::DisplayConfiguration const*));
91 MOCK_METHOD2(platform_operation,106 MOCK_METHOD2(platform_operation,
92 void(mp::PlatformOperationMessage const*, mp::PlatformOperationMessage*));107 void(mp::PlatformOperationMessage const*, mp::PlatformOperationMessage*));
93108
94 MOCK_CONST_METHOD0(watch_fd, mir::Fd());109 void trigger()
95 MOCK_METHOD1(dispatch, bool(md::FdEvents));110 {
96 MOCK_CONST_METHOD0(relevant_events, md::FdEvents());111 using namespace testing;
112 int dummy{0};
113 EXPECT_THAT(write(fake_epoll.write_fd(), &dummy, sizeof(dummy)), Eq(sizeof(dummy)));
114 }
115
116 void untrigger()
117 {
118 using namespace testing;
119 int dummy{0};
120 EXPECT_THAT(read(fake_epoll.read_fd(), &dummy, sizeof(dummy)), Eq(sizeof(dummy)));
121 }
122
123private:
124 mt::Pipe fake_epoll;
97};125};
98126
99struct MockClientPlatform : public mcl::ClientPlatform127struct MockClientPlatform : public mcl::ClientPlatform
@@ -167,7 +195,7 @@
167 {195 {
168 }196 }
169197
170 std::shared_ptr<::google::protobuf::RpcChannel> the_rpc_channel() override198 std::shared_ptr<mcl::rpc::MirBasicRpcChannel> the_rpc_channel() override
171 {199 {
172 return channel;200 return channel;
173 }201 }
@@ -194,22 +222,21 @@
194 MirConnectionTest()222 MirConnectionTest()
195 : mock_platform{std::make_shared<testing::NiceMock<MockClientPlatform>>()},223 : mock_platform{std::make_shared<testing::NiceMock<MockClientPlatform>>()},
196 mock_channel{std::make_shared<testing::NiceMock<MockRpcChannel>>()},224 mock_channel{std::make_shared<testing::NiceMock<MockRpcChannel>>()},
197 conf{mock_platform, mock_channel},225 conf{mock_platform, mock_channel}
198 connection{std::make_shared<MirConnection>(conf)}
199 {226 {
200 mock_platform->set_client_context(connection.get());
201 }227 }
202228
203 std::shared_ptr<testing::NiceMock<MockClientPlatform>> const mock_platform;229 std::shared_ptr<testing::NiceMock<MockClientPlatform>> const mock_platform;
204 std::shared_ptr<testing::NiceMock<MockRpcChannel>> const mock_channel;230 std::shared_ptr<testing::NiceMock<MockRpcChannel>> const mock_channel;
205 TestConnectionConfiguration conf;231 TestConnectionConfiguration conf;
206 std::shared_ptr<MirConnection> const connection;
207};232};
208233
209TEST_F(MirConnectionTest, returns_correct_egl_native_display)234TEST_F(MirConnectionTest, returns_correct_egl_native_display)
210{235{
211 using namespace testing;236 using namespace testing;
212237
238 auto connection = std::make_shared<MirConnection>(conf);
239
213 EGLNativeDisplayType native_display_raw = reinterpret_cast<EGLNativeDisplayType>(0xabcdef);240 EGLNativeDisplayType native_display_raw = reinterpret_cast<EGLNativeDisplayType>(0xabcdef);
214 auto native_display = std::make_shared<EGLNativeDisplayType>();241 auto native_display = std::make_shared<EGLNativeDisplayType>();
215 *native_display = native_display_raw;242 *native_display = native_display_raw;
@@ -278,6 +305,8 @@
278{305{
279 using namespace testing;306 using namespace testing;
280307
308 auto connection = std::make_shared<MirConnection>(conf);
309
281 EXPECT_CALL(*mock_channel, connect(_,_))310 EXPECT_CALL(*mock_channel, connect(_,_))
282 .WillOnce(Invoke(fill_display_configuration));311 .WillOnce(Invoke(fill_display_configuration));
283312
@@ -317,6 +346,8 @@
317{346{
318 using namespace testing;347 using namespace testing;
319348
349 auto connection = std::make_shared<MirConnection>(conf);
350
320 EXPECT_CALL(*mock_channel, connect(_,_))351 EXPECT_CALL(*mock_channel, connect(_,_))
321 .WillOnce(Invoke(fill_display_configuration));352 .WillOnce(Invoke(fill_display_configuration));
322353
@@ -362,6 +393,8 @@
362{393{
363 using namespace testing;394 using namespace testing;
364395
396 auto connection = std::make_shared<MirConnection>(conf);
397
365 EXPECT_CALL(*mock_channel, connect(_,_))398 EXPECT_CALL(*mock_channel, connect(_,_))
366 .WillOnce(Invoke(fill_display_configuration));399 .WillOnce(Invoke(fill_display_configuration));
367400
@@ -383,6 +416,8 @@
383{416{
384 using namespace testing;417 using namespace testing;
385418
419 auto connection = std::make_shared<MirConnection>(conf);
420
386 EXPECT_CALL(*mock_channel, connect(_,_))421 EXPECT_CALL(*mock_channel, connect(_,_))
387 .WillOnce(Invoke(fill_display_configuration));422 .WillOnce(Invoke(fill_display_configuration));
388423
@@ -420,6 +455,8 @@
420{455{
421 using namespace testing;456 using namespace testing;
422457
458 auto connection = std::make_shared<MirConnection>(conf);
459
423 EXPECT_CALL(*mock_channel, connect(_,_))460 EXPECT_CALL(*mock_channel, connect(_,_))
424 .WillOnce(Invoke(fill_surface_pixel_formats));461 .WillOnce(Invoke(fill_surface_pixel_formats));
425 MirWaitHandle* wait_handle = connection->connect("MirClientSurfaceTest",462 MirWaitHandle* wait_handle = connection->connect("MirClientSurfaceTest",
@@ -443,6 +480,8 @@
443{480{
444 using namespace testing;481 using namespace testing;
445482
483 auto connection = std::make_shared<MirConnection>(conf);
484
446 EXPECT_CALL(*mock_channel, connect(_,_))485 EXPECT_CALL(*mock_channel, connect(_,_))
447 .WillOnce(Invoke(fill_display_configuration));486 .WillOnce(Invoke(fill_display_configuration));
448487
@@ -509,6 +548,8 @@
509{548{
510 using namespace testing;549 using namespace testing;
511550
551 auto connection = std::make_shared<MirConnection>(conf);
552
512 MirSurfaceSpec params{nullptr, 640, 480, mir_pixel_format_abgr_8888};553 MirSurfaceSpec params{nullptr, 640, 480, mir_pixel_format_abgr_8888};
513 params.surface_name = __PRETTY_FUNCTION__;554 params.surface_name = __PRETTY_FUNCTION__;
514555
@@ -537,6 +578,8 @@
537{578{
538 using namespace testing;579 using namespace testing;
539580
581 auto connection = std::make_shared<MirConnection>(conf);
582
540 MirSurfaceSpec params{nullptr, 640, 480, mir_pixel_format_abgr_8888};583 MirSurfaceSpec params{nullptr, 640, 480, mir_pixel_format_abgr_8888};
541 params.surface_name = __PRETTY_FUNCTION__;584 params.surface_name = __PRETTY_FUNCTION__;
542585
@@ -588,6 +631,8 @@
588 mir_platform_message_create(opcode),631 mir_platform_message_create(opcode),
589 &mir_platform_message_release);632 &mir_platform_message_release);
590633
634 auto connection = std::make_shared<MirConnection>(conf);
635
591 EXPECT_CALL(*mock_platform, platform_operation(request.get()))636 EXPECT_CALL(*mock_platform, platform_operation(request.get()))
592 .WillOnce(Return(response.get()));637 .WillOnce(Return(response.get()));
593 EXPECT_CALL(*mock_channel, platform_operation(_,_))638 EXPECT_CALL(*mock_channel, platform_operation(_,_))
@@ -620,6 +665,8 @@
620 EXPECT_CALL(*mock_channel, platform_operation(_,_))665 EXPECT_CALL(*mock_channel, platform_operation(_,_))
621 .WillOnce(CopyRequestToResponse());666 .WillOnce(CopyRequestToResponse());
622667
668 auto connection = std::make_shared<MirConnection>(conf);
669
623 auto connect_wh =670 auto connect_wh =
624 connection->connect("MirClientSurfaceTest", &connected_callback, nullptr);671 connection->connect("MirClientSurfaceTest", &connected_callback, nullptr);
625 mir_wait_for(connect_wh);672 mir_wait_for(connect_wh);
@@ -633,3 +680,107 @@
633 EXPECT_THAT(mir_platform_message_get_opcode(returned_response), Eq(opcode));680 EXPECT_THAT(mir_platform_message_get_opcode(returned_response), Eq(opcode));
634 mir_platform_message_release(returned_response);681 mir_platform_message_release(returned_response);
635}682}
683
684TEST_F(MirConnectionTest, dispatch_works_with_automatic_dispatch)
685{
686 using namespace testing;
687
688 auto channel = std::dynamic_pointer_cast<MockRpcChannel>(conf.the_rpc_channel());
689
690 auto dispatched = std::make_shared<mir::test::Signal>();
691 ON_CALL(*channel, dispatch(_))
692 .WillByDefault(Invoke([dispatched](md::FdEvents) { dispatched->raise(); return true; }));
693
694 auto connection = std::make_shared<MirConnection>(conf, DispatchType::automatic);
695
696 channel->trigger();
697
698 EXPECT_TRUE(dispatched->wait_for(std::chrono::seconds{1}));
699}
700
701TEST_F(MirConnectionTest, manual_dispatch_is_not_automatically_dispatched)
702{
703 using namespace testing;
704
705 auto channel = std::dynamic_pointer_cast<MockRpcChannel>(conf.the_rpc_channel());
706
707 auto dispatched = std::make_shared<mir::test::Signal>();
708 ON_CALL(*channel, dispatch(_))
709 .WillByDefault(Invoke([dispatched](md::FdEvents) { dispatched->raise(); return true; }));
710
711 auto connection = std::make_shared<MirConnection>(conf, DispatchType::manual);
712
713 channel->trigger();
714
715 EXPECT_FALSE(dispatched->wait_for(std::chrono::seconds{1}));
716}
717
718TEST_F(MirConnectionTest, returns_invalid_watch_fd_when_using_automatic_dispatch)
719{
720 using namespace testing;
721
722 auto connection = std::make_shared<MirConnection>(conf, DispatchType::automatic);
723
724 EXPECT_THAT(connection->watch_fd(), Lt(0));
725}
726
727TEST_F(MirConnectionTest, returns_pollable_watch_fd_when_using_manual_dispatch)
728{
729 using namespace testing;
730
731 auto channel = std::dynamic_pointer_cast<MockRpcChannel>(conf.the_rpc_channel());
732
733 auto connection = std::make_shared<MirConnection>(conf, DispatchType::manual);
734
735 pollfd fd_readable;
736 fd_readable.events = POLLIN;
737 fd_readable.fd = connection->watch_fd();
738 EXPECT_TRUE(mt::std_call_succeeded(poll(&fd_readable, 1, 0)));
739}
740
741TEST_F(MirConnectionTest, can_add_dispatchables_to_connection)
742{
743 using namespace testing;
744 auto connection = std::make_shared<MirConnection>(conf, DispatchType::manual);
745
746 auto dispatchee = std::make_shared<mt::TestDispatchable>([](){});
747 connection->add_dispatchee(dispatchee);
748}
749
750TEST_F(MirConnectionTest, added_dispatchees_are_dispatched)
751{
752 using namespace testing;
753 auto connection = std::make_shared<MirConnection>(conf, DispatchType::manual);
754
755 bool dispatched{false};
756 auto dispatchee = std::make_shared<mt::TestDispatchable>([&dispatched](){ dispatched = true; });
757
758 connection->add_dispatchee(dispatchee);
759
760 dispatchee->trigger();
761
762 EXPECT_TRUE(mt::fd_is_readable(connection->watch_fd()));
763 connection->dispatch();
764
765 EXPECT_TRUE(dispatched);
766}
767
768TEST_F(MirConnectionTest, removed_dispatchees_are_no_longer_dispatched)
769{
770 using namespace testing;
771 auto connection = std::make_shared<MirConnection>(conf, DispatchType::manual);
772
773 bool dispatched{false};
774 auto dispatchee = std::make_shared<mt::TestDispatchable>([&dispatched](){ dispatched = true; });
775
776 connection->add_dispatchee(dispatchee);
777 connection->remove_dispatchee(dispatchee);
778
779 dispatchee->trigger();
780
781 EXPECT_FALSE(mir::test::fd_is_readable(connection->watch_fd()));
782
783 connection->dispatch();
784
785 EXPECT_FALSE(dispatched);
786}
636787
=== modified file 'tests/unit-tests/client/test_mir_screencast.cpp'
--- tests/unit-tests/client/test_mir_screencast.cpp 2015-04-16 19:56:08 +0000
+++ tests/unit-tests/client/test_mir_screencast.cpp 2015-05-14 07:50:26 +0000
@@ -166,7 +166,7 @@
166 using namespace ::testing;166 using namespace ::testing;
167167
168 ON_CALL(*mock_buffer_stream_factory,168 ON_CALL(*mock_buffer_stream_factory,
169 make_consumer_stream(_,_,_)).WillByDefault(169 make_consumer_stream(_,_,_,_)).WillByDefault(
170 Return(mt::fake_shared(mock_bs)));170 Return(mt::fake_shared(mock_bs)));
171 }171 }
172172
@@ -191,6 +191,7 @@
191 .WillOnce(RunClosure());191 .WillOnce(RunClosure());
192192
193 MirScreencast screencast{193 MirScreencast screencast{
194 nullptr,
194 default_region,195 default_region,
195 default_size,196 default_size,
196 default_pixel_format, mock_server,197 default_pixel_format, mock_server,
@@ -215,6 +216,7 @@
215 .WillOnce(RunClosure());216 .WillOnce(RunClosure());
216217
217 MirScreencast screencast{218 MirScreencast screencast{
219 nullptr,
218 default_region,220 default_region,
219 default_size,221 default_size,
220 default_pixel_format, mock_server,222 default_pixel_format, mock_server,
@@ -232,6 +234,7 @@
232 EXPECT_CALL(mock_cb, call(_, &mock_cb));234 EXPECT_CALL(mock_cb, call(_, &mock_cb));
233235
234 MirScreencast screencast{236 MirScreencast screencast{
237 nullptr,
235 default_region,238 default_region,
236 default_size,239 default_size,
237 default_pixel_format, stub_server,240 default_pixel_format, stub_server,
@@ -246,6 +249,7 @@
246 using namespace testing;249 using namespace testing;
247250
248 MirScreencast screencast{251 MirScreencast screencast{
252 nullptr,
249 default_region,253 default_region,
250 default_size,254 default_size,
251 default_pixel_format, stub_server,255 default_pixel_format, stub_server,
@@ -268,6 +272,7 @@
268272
269 EXPECT_THROW({273 EXPECT_THROW({
270 MirScreencast screencast(274 MirScreencast screencast(
275 nullptr,
271 default_region,276 default_region,
272 invalid_size,277 invalid_size,
273 default_pixel_format, stub_server,278 default_pixel_format, stub_server,
@@ -277,6 +282,7 @@
277282
278 EXPECT_THROW({283 EXPECT_THROW({
279 MirScreencast screencast(284 MirScreencast screencast(
285 nullptr,
280 invalid_region,286 invalid_region,
281 default_size,287 default_size,
282 default_pixel_format, stub_server,288 default_pixel_format, stub_server,
@@ -286,6 +292,7 @@
286292
287 EXPECT_THROW({293 EXPECT_THROW({
288 MirScreencast screencast(294 MirScreencast screencast(
295 nullptr,
289 default_region,296 default_region,
290 default_size,297 default_size,
291 mir_pixel_format_invalid, stub_server,298 mir_pixel_format_invalid, stub_server,
@@ -302,6 +309,7 @@
302 .WillOnce(DoAll(SetCreateError(), RunClosure()));309 .WillOnce(DoAll(SetCreateError(), RunClosure()));
303310
304 MirScreencast screencast{311 MirScreencast screencast{
312 nullptr,
305 default_region,313 default_region,
306 default_size,314 default_size,
307 default_pixel_format, mock_server,315 default_pixel_format, mock_server,
@@ -323,6 +331,7 @@
323 EXPECT_CALL(mock_cb, call(_,&mock_cb));331 EXPECT_CALL(mock_cb, call(_,&mock_cb));
324332
325 MirScreencast screencast{333 MirScreencast screencast{
334 nullptr,
326 default_region,335 default_region,
327 default_size,336 default_size,
328 default_pixel_format, mock_server,337 default_pixel_format, mock_server,
329338
=== modified file 'tests/unit-tests/client/test_protobuf_rpc_channel.cpp'
--- tests/unit-tests/client/test_protobuf_rpc_channel.cpp 2015-04-13 05:50:18 +0000
+++ tests/unit-tests/client/test_protobuf_rpc_channel.cpp 2015-05-14 07:50:26 +0000
@@ -27,12 +27,16 @@
27#include "mir_protobuf_wire.pb.h"27#include "mir_protobuf_wire.pb.h"
2828
29#include "mir_test_doubles/null_client_event_sink.h"29#include "mir_test_doubles/null_client_event_sink.h"
30#include "mir_test/fd_utils.h"
3031
31#include <list>32#include <list>
32#include <endian.h>33#include <endian.h>
3334
35#include <sys/eventfd.h>
34#include <fcntl.h>36#include <fcntl.h>
3537
38#include <boost/throw_exception.hpp>
39
36#include <google/protobuf/descriptor.h>40#include <google/protobuf/descriptor.h>
3741
38#include <gtest/gtest.h>42#include <gtest/gtest.h>
@@ -42,6 +46,9 @@
42namespace mclr = mir::client::rpc;46namespace mclr = mir::client::rpc;
43namespace mtd = mir::test::doubles;47namespace mtd = mir::test::doubles;
44namespace md = mir::dispatch;48namespace md = mir::dispatch;
49namespace mt = mir::test;
50
51namespace mt = mir::test;
4552
46namespace53namespace
47{54{
@@ -58,7 +65,15 @@
58{65{
59public:66public:
60 MockStreamTransport()67 MockStreamTransport()
68 : event_fd{eventfd(0, EFD_CLOEXEC)}
61 {69 {
70 if (event_fd == mir::Fd::invalid)
71 {
72 BOOST_THROW_EXCEPTION((std::system_error{errno,
73 std::system_category(),
74 "Failed to create event fd"}));
75 }
76
62 using namespace testing;77 using namespace testing;
63 ON_CALL(*this, register_observer(_))78 ON_CALL(*this, register_observer(_))
64 .WillByDefault(Invoke(std::bind(&MockStreamTransport::register_observer_default,79 .WillByDefault(Invoke(std::bind(&MockStreamTransport::register_observer_default,
@@ -82,6 +97,8 @@
8297
83 void add_server_message(std::vector<uint8_t> const& message)98 void add_server_message(std::vector<uint8_t> const& message)
84 {99 {
100 eventfd_t data_added{message.size()};
101 eventfd_write(event_fd, data_added);
85 received_data.insert(received_data.end(), message.begin(), message.end());102 received_data.insert(received_data.end(), message.begin(), message.end());
86 }103 }
87 void add_server_message(std::vector<uint8_t> const& message, std::initializer_list<mir::Fd> fds)104 void add_server_message(std::vector<uint8_t> const& message, std::initializer_list<mir::Fd> fds)
@@ -95,24 +112,30 @@
95 return received_data.empty() && received_fds.empty();112 return received_data.empty() && received_fds.empty();
96 }113 }
97114
98 void notify_data_received()
99 {
100 do
101 {
102 for(auto& observer : observers)
103 observer->on_data_available();
104 }
105 while (!all_data_consumed());
106 }
107
108 MOCK_METHOD1(register_observer, void(std::shared_ptr<Observer> const&));115 MOCK_METHOD1(register_observer, void(std::shared_ptr<Observer> const&));
109 MOCK_METHOD1(unregister_observer, void(std::shared_ptr<Observer> const&));116 MOCK_METHOD1(unregister_observer, void(std::shared_ptr<Observer> const&));
110 MOCK_METHOD2(receive_data, void(void*, size_t));117 MOCK_METHOD2(receive_data, void(void*, size_t));
111 MOCK_METHOD3(receive_data, void(void*, size_t, std::vector<mir::Fd>&));118 MOCK_METHOD3(receive_data, void(void*, size_t, std::vector<mir::Fd>&));
112 MOCK_METHOD2(send_message, void(std::vector<uint8_t> const&, std::vector<mir::Fd> const&));119 MOCK_METHOD2(send_message, void(std::vector<uint8_t> const&, std::vector<mir::Fd> const&));
113 MOCK_CONST_METHOD0(watch_fd, mir::Fd());120
114 MOCK_METHOD1(dispatch, bool(md::FdEvents));121 mir::Fd watch_fd() const override
115 MOCK_CONST_METHOD0(relevant_events, md::FdEvents());122 {
123 return event_fd;
124 }
125
126 bool dispatch(md::FdEvents /*events*/) override
127 {
128 for (auto& observer : observers)
129 {
130 observer->on_data_available();
131 }
132 return true;
133 }
134
135 md::FdEvents relevant_events() const override
136 {
137 return md::FdEvent::readable;
138 }
116139
117 // Transport interface140 // Transport interface
118 void register_observer_default(std::shared_ptr<Observer> const& observer)141 void register_observer_default(std::shared_ptr<Observer> const& observer)
@@ -128,6 +151,9 @@
128151
129 void receive_data_default(void* buffer, size_t read_bytes, std::vector<mir::Fd>& fds)152 void receive_data_default(void* buffer, size_t read_bytes, std::vector<mir::Fd>& fds)
130 {153 {
154 if (read_bytes == 0)
155 return;
156
131 auto num_fds = fds.size();157 auto num_fds = fds.size();
132 if (read_bytes > received_data.size())158 if (read_bytes > received_data.size())
133 {159 {
@@ -143,6 +169,11 @@
143169
144 received_data.erase(received_data.begin(), received_data.begin() + read_bytes);170 received_data.erase(received_data.begin(), received_data.begin() + read_bytes);
145 received_fds.erase(received_fds.begin(), received_fds.begin() + num_fds);171 received_fds.erase(received_fds.begin(), received_fds.begin() + num_fds);
172
173 eventfd_t remaining_bytes;
174 eventfd_read(event_fd, &remaining_bytes);
175 remaining_bytes -= read_bytes;
176 eventfd_write(event_fd, remaining_bytes);
146 }177 }
147178
148 void send_message_default(std::vector<uint8_t> const& buffer)179 void send_message_default(std::vector<uint8_t> const& buffer)
@@ -156,6 +187,9 @@
156 std::vector<uint8_t> received_data;187 std::vector<uint8_t> received_data;
157 std::vector<mir::Fd> received_fds;188 std::vector<mir::Fd> received_fds;
158 std::list<std::vector<uint8_t>> sent_messages;189 std::list<std::vector<uint8_t>> sent_messages;
190
191private:
192 mir::Fd event_fd;
159};193};
160194
161class MirProtobufRpcChannelTest : public testing::Test195class MirProtobufRpcChannelTest : public testing::Test
@@ -176,53 +210,56 @@
176210
177 MockStreamTransport* transport;211 MockStreamTransport* transport;
178 std::shared_ptr<mcl::LifecycleControl> lifecycle;212 std::shared_ptr<mcl::LifecycleControl> lifecycle;
179 std::shared_ptr<::google::protobuf::RpcChannel> channel;213 std::shared_ptr<mclr::MirProtobufRpcChannel> channel;
180};214};
181215
182}216}
183217
184TEST_F(MirProtobufRpcChannelTest, ReadsFullMessages)218TEST_F(MirProtobufRpcChannelTest, reads_full_messages)
185{219{
186 std::vector<uint8_t> empty_message(sizeof(uint16_t));220 std::vector<uint8_t> empty_message(sizeof(uint16_t));
187 std::vector<uint8_t> small_message(sizeof(uint16_t) + 8);221 std::vector<uint8_t> small_message(sizeof(uint16_t) + 8);
188 std::vector<uint8_t> large_message(sizeof(uint16_t) + 4096);222 std::vector<uint8_t> large_message(sizeof(uint16_t) + 4096);
189223
190 *reinterpret_cast<uint16_t*>(empty_message.data()) = htobe16(0);224 *reinterpret_cast<uint16_t*>(empty_message.data()) = htobe16(0);
191 *reinterpret_cast<uint16_t*>(small_message.data()) = htobe16(8);225 *reinterpret_cast<uint16_t*>(small_message.data()) = htobe16(8);
192 *reinterpret_cast<uint16_t*>(large_message.data()) = htobe16(4096);226 *reinterpret_cast<uint16_t*>(large_message.data()) = htobe16(4096);
193227
194 transport->add_server_message(empty_message);228 transport->add_server_message(empty_message);
195 transport->notify_data_received();229 transport->dispatch(md::FdEvent::readable);
196 EXPECT_TRUE(transport->all_data_consumed());230 EXPECT_TRUE(transport->all_data_consumed());
197231
198 transport->add_server_message(small_message);232 transport->add_server_message(small_message);
199 transport->notify_data_received();233 transport->dispatch(md::FdEvent::readable);
200 EXPECT_TRUE(transport->all_data_consumed());234 EXPECT_TRUE(transport->all_data_consumed());
201235
202 transport->add_server_message(large_message);236 transport->add_server_message(large_message);
203 transport->notify_data_received();237 transport->dispatch(md::FdEvent::readable);
204 EXPECT_TRUE(transport->all_data_consumed());238 EXPECT_TRUE(transport->all_data_consumed());
205}239}
206240
207TEST_F(MirProtobufRpcChannelTest, ReadsAllQueuedMessages)241TEST_F(MirProtobufRpcChannelTest, reads_all_queued_messages)
208{242{
209 std::vector<uint8_t> empty_message(sizeof(uint16_t));243 std::vector<uint8_t> empty_message(sizeof(uint16_t));
210 std::vector<uint8_t> small_message(sizeof(uint16_t) + 8);244 std::vector<uint8_t> small_message(sizeof(uint16_t) + 8);
211 std::vector<uint8_t> large_message(sizeof(uint16_t) + 4096);245 std::vector<uint8_t> large_message(sizeof(uint16_t) + 4096);
212246
213 *reinterpret_cast<uint16_t*>(empty_message.data()) = htobe16(0);247 *reinterpret_cast<uint16_t*>(empty_message.data()) = htobe16(0);
214 *reinterpret_cast<uint16_t*>(small_message.data()) = htobe16(8);248 *reinterpret_cast<uint16_t*>(small_message.data()) = htobe16(8);
215 *reinterpret_cast<uint16_t*>(large_message.data()) = htobe16(4096);249 *reinterpret_cast<uint16_t*>(large_message.data()) = htobe16(4096);
216250
217 transport->add_server_message(empty_message);251 transport->add_server_message(empty_message);
218 transport->add_server_message(small_message);252 transport->add_server_message(small_message);
219 transport->add_server_message(large_message);253 transport->add_server_message(large_message);
220254
221 transport->notify_data_received();255 while(mt::fd_is_readable(channel->watch_fd()))
222 EXPECT_TRUE(transport->all_data_consumed());256 {
223}257 channel->dispatch(md::FdEvent::readable);
224258 }
225TEST_F(MirProtobufRpcChannelTest, SendsMessagesAtomically)259 EXPECT_TRUE(transport->all_data_consumed());
260}
261
262TEST_F(MirProtobufRpcChannelTest, sends_messages_atomically)
226{263{
227 mir::protobuf::DisplayServer::Stub channel_user{channel.get(), mir::protobuf::DisplayServer::STUB_DOESNT_OWN_CHANNEL};264 mir::protobuf::DisplayServer::Stub channel_user{channel.get(), mir::protobuf::DisplayServer::STUB_DOESNT_OWN_CHANNEL};
228 mir::protobuf::ConnectParameters message;265 mir::protobuf::ConnectParameters message;
@@ -233,7 +270,7 @@
233 EXPECT_EQ(transport->sent_messages.size(), 1);270 EXPECT_EQ(transport->sent_messages.size(), 1);
234}271}
235272
236TEST_F(MirProtobufRpcChannelTest, SetsCorrectSizeWhenSendingMessage)273TEST_F(MirProtobufRpcChannelTest, sets_correct_size_when_sending_message)
237{274{
238 mir::protobuf::DisplayServer::Stub channel_user{channel.get(), mir::protobuf::DisplayServer::STUB_DOESNT_OWN_CHANNEL};275 mir::protobuf::DisplayServer::Stub channel_user{channel.get(), mir::protobuf::DisplayServer::STUB_DOESNT_OWN_CHANNEL};
239 mir::protobuf::ConnectParameters message;276 mir::protobuf::ConnectParameters message;
@@ -246,7 +283,7 @@
246 EXPECT_EQ(transport->sent_messages.front().size() - sizeof(uint16_t), message_header);283 EXPECT_EQ(transport->sent_messages.front().size() - sizeof(uint16_t), message_header);
247}284}
248285
249TEST_F(MirProtobufRpcChannelTest, ReadsFds)286TEST_F(MirProtobufRpcChannelTest, reads_fds)
250{287{
251 mir::protobuf::DisplayServer::Stub channel_user{channel.get(), mir::protobuf::DisplayServer::STUB_DOESNT_OWN_CHANNEL};288 mir::protobuf::DisplayServer::Stub channel_user{channel.get(), mir::protobuf::DisplayServer::STUB_DOESNT_OWN_CHANNEL};
252 mir::protobuf::Buffer reply;289 mir::protobuf::Buffer reply;
@@ -288,7 +325,10 @@
288 std::vector<uint8_t> dummy = {1};325 std::vector<uint8_t> dummy = {1};
289 transport->add_server_message(dummy, fds);326 transport->add_server_message(dummy, fds);
290327
291 transport->notify_data_received();328 while(mt::fd_is_readable(channel->watch_fd()))
329 {
330 channel->dispatch(md::FdEvent::readable);
331 }
292 }332 }
293333
294 ASSERT_EQ(reply.fd_size(), fds.size());334 ASSERT_EQ(reply.fd_size(), fds.size());
@@ -300,7 +340,7 @@
300 }340 }
301}341}
302342
303TEST_F(MirProtobufRpcChannelTest, NotifiesOfDisconnectOnWriteError)343TEST_F(MirProtobufRpcChannelTest, notifies_of_disconnect_on_write_error)
304{344{
305 using namespace ::testing;345 using namespace ::testing;
306346
@@ -328,7 +368,7 @@
328 EXPECT_TRUE(disconnected);368 EXPECT_TRUE(disconnected);
329}369}
330370
331TEST_F(MirProtobufRpcChannelTest, ForwardsDisconnectNotification)371TEST_F(MirProtobufRpcChannelTest, forwards_disconnect_notification)
332{372{
333 using namespace ::testing;373 using namespace ::testing;
334374
@@ -350,7 +390,7 @@
350 EXPECT_TRUE(disconnected);390 EXPECT_TRUE(disconnected);
351}391}
352392
353TEST_F(MirProtobufRpcChannelTest, NotifiesOfDisconnectOnlyOnce)393TEST_F(MirProtobufRpcChannelTest, notifies_of_disconnect_only_once)
354{394{
355 using namespace ::testing;395 using namespace ::testing;
356396
@@ -388,3 +428,196 @@
388428
389 EXPECT_TRUE(disconnected);429 EXPECT_TRUE(disconnected);
390}430}
431
432namespace
433{
434void set_flag(bool* flag)
435{
436 *flag = true;
437}
438}
439
440TEST_F(MirProtobufRpcChannelTest, delays_messages_not_requested)
441{
442 using namespace ::testing;
443
444 auto typed_channel = std::dynamic_pointer_cast<mclr::MirProtobufRpcChannel>(channel);
445
446 mir::protobuf::DisplayServer::Stub channel_user{channel.get(), mir::protobuf::DisplayServer::STUB_DOESNT_OWN_CHANNEL};
447 mir::protobuf::DRMMagic request;
448 mir::protobuf::DRMAuthMagicStatus reply;
449
450 bool first_response_called{false};
451 bool second_response_called{false};
452 channel_user.drm_auth_magic(nullptr,
453 &request,
454 &reply,
455 google::protobuf::NewCallback(&set_flag, &first_response_called));
456
457 typed_channel->process_next_request_first();
458 channel_user.drm_auth_magic(nullptr,
459 &request,
460 &reply,
461 google::protobuf::NewCallback(&set_flag, &second_response_called));
462
463 mir::protobuf::wire::Invocation wire_request;
464 mir::protobuf::wire::Result wire_reply;
465
466 wire_request.ParseFromArray(transport->sent_messages.front().data() + sizeof(uint16_t),
467 transport->sent_messages.front().size() - sizeof(uint16_t));
468
469 transport->sent_messages.pop_front();
470
471 wire_reply.set_id(wire_request.id());
472 wire_reply.set_response(reply.SerializeAsString());
473
474 std::vector<uint8_t> buffer(wire_reply.ByteSize() + sizeof(uint16_t));
475 *reinterpret_cast<uint16_t*>(buffer.data()) = htobe16(wire_reply.ByteSize());
476 ASSERT_TRUE(wire_reply.SerializeToArray(buffer.data() + sizeof(uint16_t), buffer.size() - sizeof(uint16_t)));
477
478 transport->add_server_message(buffer);
479
480 wire_request.ParseFromArray(transport->sent_messages.front().data() + sizeof(uint16_t),
481 transport->sent_messages.front().size() - sizeof(uint16_t));
482
483 transport->sent_messages.pop_front();
484
485 wire_reply.set_id(wire_request.id());
486 wire_reply.set_response(reply.SerializeAsString());
487
488 buffer.resize(wire_reply.ByteSize() + sizeof(uint16_t));
489 *reinterpret_cast<uint16_t*>(buffer.data()) = htobe16(wire_reply.ByteSize());
490 ASSERT_TRUE(wire_reply.SerializeToArray(buffer.data() + sizeof(uint16_t), buffer.size() - sizeof(uint16_t)));
491
492 transport->add_server_message(buffer);
493
494 // Read the first message; this should be queued for later processing...
495 EXPECT_TRUE(mt::fd_is_readable(typed_channel->watch_fd()));
496 typed_channel->dispatch(md::FdEvent::readable);
497
498 EXPECT_FALSE(first_response_called);
499 EXPECT_FALSE(second_response_called);
500
501 // Read the second message; this should be processed immediately...
502 EXPECT_TRUE(mt::fd_is_readable(typed_channel->watch_fd()));
503 typed_channel->dispatch(md::FdEvent::readable);
504
505 EXPECT_FALSE(first_response_called);
506 EXPECT_TRUE(second_response_called);
507
508 // Now, the first message should be ready to be processed...
509 EXPECT_TRUE(mt::fd_is_readable(typed_channel->watch_fd()));
510 typed_channel->dispatch(md::FdEvent::readable);
511
512 EXPECT_TRUE(first_response_called);
513 EXPECT_TRUE(second_response_called);
514}
515
516TEST_F(MirProtobufRpcChannelTest, delays_messages_with_fds_not_requested)
517{
518 using namespace ::testing;
519
520 auto typed_channel = std::dynamic_pointer_cast<mclr::MirProtobufRpcChannel>(channel);
521
522 mir::protobuf::DisplayServer::Stub channel_user{channel.get(), mir::protobuf::DisplayServer::STUB_DOESNT_OWN_CHANNEL};
523 mir::protobuf::DRMMagic drm_request;
524 mir::protobuf::DRMAuthMagicStatus drm_reply;
525
526 mir::protobuf::Buffer buffer_reply;
527 mir::protobuf::BufferRequest buffer_request;
528
529 bool first_response_called{false};
530 bool second_response_called{false};
531
532
533 channel_user.exchange_buffer(nullptr,
534 &buffer_request,
535 &buffer_reply,
536 google::protobuf::NewCallback(&set_flag, &first_response_called));
537
538 typed_channel->process_next_request_first();
539 channel_user.drm_auth_magic(nullptr,
540 &drm_request,
541 &drm_reply,
542 google::protobuf::NewCallback(&set_flag, &second_response_called));
543
544
545 std::initializer_list<mir::Fd> fds = {mir::Fd{open("/dev/null", O_RDONLY)},
546 mir::Fd{open("/dev/null", O_RDONLY)},
547 mir::Fd{open("/dev/null", O_RDONLY)}};
548
549 {
550 mir::protobuf::Buffer reply_message;
551
552 for (auto fd : fds)
553 reply_message.add_fd(fd);
554 reply_message.set_fds_on_side_channel(fds.size());
555
556 mir::protobuf::wire::Invocation request;
557 mir::protobuf::wire::Result reply;
558
559 request.ParseFromArray(transport->sent_messages.front().data() + sizeof(uint16_t),
560 transport->sent_messages.front().size() - sizeof(uint16_t));
561
562 transport->sent_messages.pop_front();
563
564 reply.set_id(request.id());
565 reply.set_response(reply_message.SerializeAsString());
566
567 ASSERT_TRUE(reply.has_id());
568 ASSERT_TRUE(reply.has_response());
569
570 std::vector<uint8_t> buffer(reply.ByteSize() + sizeof(uint16_t));
571 *reinterpret_cast<uint16_t*>(buffer.data()) = htobe16(reply.ByteSize());
572 ASSERT_TRUE(reply.SerializeToArray(buffer.data() + sizeof(uint16_t), buffer.size() - sizeof(uint16_t)));
573
574 transport->add_server_message(buffer);
575
576 // Because our protocol is a bit silly...
577 std::vector<uint8_t> dummy = {1};
578 transport->add_server_message(dummy, fds);
579 }
580
581 {
582 mir::protobuf::DRMAuthMagicStatus reply;
583
584 mir::protobuf::wire::Invocation wire_request;
585 mir::protobuf::wire::Result wire_reply;
586
587 wire_request.ParseFromArray(transport->sent_messages.front().data() + sizeof(uint16_t),
588 transport->sent_messages.front().size() - sizeof(uint16_t));
589
590 transport->sent_messages.pop_front();
591
592 wire_reply.set_id(wire_request.id());
593 wire_reply.set_response(reply.SerializeAsString());
594
595 std::vector<uint8_t> buffer(wire_reply.ByteSize() + sizeof(uint16_t));
596 *reinterpret_cast<uint16_t*>(buffer.data()) = htobe16(wire_reply.ByteSize());
597 ASSERT_TRUE(wire_reply.SerializeToArray(buffer.data() + sizeof(uint16_t), buffer.size() - sizeof(uint16_t)));
598
599 transport->add_server_message(buffer);
600 }
601
602 // Read the first message; this should be queued for later processing...
603 EXPECT_TRUE(mt::fd_is_readable(typed_channel->watch_fd()));
604 typed_channel->dispatch(md::FdEvent::readable);
605
606 EXPECT_FALSE(first_response_called);
607 EXPECT_FALSE(second_response_called);
608
609 // Read the second message; this should be processed immediately...
610 EXPECT_TRUE(mt::fd_is_readable(typed_channel->watch_fd()));
611 typed_channel->dispatch(md::FdEvent::readable);
612
613 EXPECT_FALSE(first_response_called);
614 EXPECT_TRUE(second_response_called);
615
616 // Now, the first message should be ready to be processed...
617 EXPECT_TRUE(mt::fd_is_readable(typed_channel->watch_fd()));
618 typed_channel->dispatch(md::FdEvent::readable);
619
620 EXPECT_TRUE(first_response_called);
621 EXPECT_TRUE(second_response_called);
622}
623
391624
=== modified file 'tests/unit-tests/dispatch/CMakeLists.txt'
--- tests/unit-tests/dispatch/CMakeLists.txt 2015-03-31 02:35:42 +0000
+++ tests/unit-tests/dispatch/CMakeLists.txt 2015-05-14 07:50:26 +0000
@@ -1,6 +1,6 @@
1list(APPEND UNIT_TEST_SOURCES1list(APPEND UNIT_TEST_SOURCES
2 ${CMAKE_CURRENT_SOURCE_DIR}/test_threaded_dispatcher.cpp
2 ${CMAKE_CURRENT_SOURCE_DIR}/test_action_queue.cpp3 ${CMAKE_CURRENT_SOURCE_DIR}/test_action_queue.cpp
3 ${CMAKE_CURRENT_SOURCE_DIR}/test_simple_dispatch_thread.cpp
4 ${CMAKE_CURRENT_SOURCE_DIR}/test_multiplexing_dispatchable.cpp4 ${CMAKE_CURRENT_SOURCE_DIR}/test_multiplexing_dispatchable.cpp
5 ${CMAKE_CURRENT_SOURCE_DIR}/test_dispatch_utils.cpp5 ${CMAKE_CURRENT_SOURCE_DIR}/test_dispatch_utils.cpp
6)6)
77
=== modified file 'tests/unit-tests/dispatch/test_multiplexing_dispatchable.cpp'
--- tests/unit-tests/dispatch/test_multiplexing_dispatchable.cpp 2015-02-11 23:15:33 +0000
+++ tests/unit-tests/dispatch/test_multiplexing_dispatchable.cpp 2015-05-14 07:50:26 +0000
@@ -17,7 +17,7 @@
17 */17 */
1818
19#include "mir/dispatch/multiplexing_dispatchable.h"19#include "mir/dispatch/multiplexing_dispatchable.h"
20#include "mir/dispatch/simple_dispatch_thread.h"20#include "mir/dispatch/threaded_dispatcher.h"
21#include "mir/fd.h"21#include "mir/fd.h"
22#include "mir_test/pipe.h"22#include "mir_test/pipe.h"
23#include "mir_test/signal.h"23#include "mir_test/signal.h"
@@ -217,8 +217,8 @@
217 auto dispatcher = std::make_shared<md::MultiplexingDispatchable>();217 auto dispatcher = std::make_shared<md::MultiplexingDispatchable>();
218 dispatcher->add_watch(dispatchee);218 dispatcher->add_watch(dispatchee);
219219
220 md::SimpleDispatchThread first_loop{dispatcher};220 md::ThreadedDispatcher eventloop{"Fools!", dispatcher};
221 md::SimpleDispatchThread second_loop{dispatcher};221 eventloop.add_thread();
222222
223 EXPECT_TRUE(second_dispatch->wait_for(std::chrono::seconds{5}));223 EXPECT_TRUE(second_dispatch->wait_for(std::chrono::seconds{5}));
224}224}
@@ -315,15 +315,15 @@
315315
316 dispatchee->trigger();316 dispatchee->trigger();
317317
318 md::SimpleDispatchThread eventloop{dispatcher};318 md::ThreadedDispatcher eventloop{"I'll destroy", dispatcher};
319319
320 EXPECT_TRUE(in_dispatch->wait_for(std::chrono::seconds{1}));320 EXPECT_TRUE(in_dispatch->wait_for(std::chrono::seconds{5}));
321321
322 dispatcher->remove_watch(dispatchee);322 dispatcher->remove_watch(dispatchee);
323 dispatchee.reset();323 dispatchee.reset();
324 canary.reset();324 canary.reset();
325325
326 EXPECT_TRUE(canary_killed->wait_for(std::chrono::seconds{2}));326 EXPECT_TRUE(canary_killed->wait_for(std::chrono::seconds{5}));
327}327}
328328
329TEST(MultiplexingDispatchableTest, destruction_is_threadsafe)329TEST(MultiplexingDispatchableTest, destruction_is_threadsafe)
@@ -348,28 +348,27 @@
348348
349 mt::AutoJoinThread dispatch_thread{[dispatcher]() { dispatcher->dispatch(md::FdEvent::readable); }};349 mt::AutoJoinThread dispatch_thread{[dispatcher]() { dispatcher->dispatch(md::FdEvent::readable); }};
350350
351 EXPECT_TRUE(in_dispatch->wait_for(std::chrono::seconds{1}));351 EXPECT_TRUE(in_dispatch->wait_for(std::chrono::seconds{5}));
352352
353 dispatcher->remove_watch(dispatchee);353 dispatcher->remove_watch(dispatchee);
354 dispatcher.reset();354 dispatcher.reset();
355 dispatchee.reset();355 dispatchee.reset();
356 canary.reset();356 canary.reset();
357357
358 EXPECT_TRUE(canary_killed->wait_for(std::chrono::seconds{2}));358 EXPECT_TRUE(canary_killed->wait_for(std::chrono::seconds{5}));
359}359}
360360
361TEST(MultiplexingDispatchableTest, stress_test_threading)361TEST(MultiplexingDispatchableTest, stress_test_threading)
362{362{
363 using namespace testing;363 using namespace testing;
364
365 int const dispatchee_count{20};364 int const dispatchee_count{20};
366365
367 auto dispatcher = std::make_shared<md::MultiplexingDispatchable>();366 auto dispatcher = std::make_shared<md::MultiplexingDispatchable>();
368367
369 std::vector<std::shared_ptr<md::SimpleDispatchThread>> eventloops;368 auto event_dispatcher = std::make_shared<md::ThreadedDispatcher>(__func__, dispatcher);
370 for (int i = 0 ; i < dispatchee_count + 5 ; ++i)369 for (int i = 0 ; i < dispatchee_count + 5 ; ++i)
371 {370 {
372 eventloops.push_back(std::make_shared<md::SimpleDispatchThread>(dispatcher));371 event_dispatcher->add_thread();
373 }372 }
374373
375 std::vector<std::shared_ptr<mt::Signal>> canary_tomb;374 std::vector<std::shared_ptr<mt::Signal>> canary_tomb;
@@ -397,12 +396,12 @@
397396
398 dispatchees.clear();397 dispatchees.clear();
399 dispatcher.reset();398 dispatcher.reset();
400 eventloops.clear();399 event_dispatcher.reset();
401400
402 for (auto headstone : canary_tomb)401 for (auto headstone : canary_tomb)
403 {402 {
404 // Use assert so as to not block for *ages* on failure403 // Use assert so as to not block for *ages* on failure
405 ASSERT_TRUE(headstone->wait_for(std::chrono::seconds{2}));404 ASSERT_TRUE(headstone->wait_for(std::chrono::seconds{5}));
406 }405 }
407}406}
408407
@@ -456,10 +455,10 @@
456455
457 first_dispatchee->trigger();456 first_dispatchee->trigger();
458457
459 md::SimpleDispatchThread eventloop_one{dispatcher};458 md::ThreadedDispatcher eventloop{"them all!", dispatcher};
460 md::SimpleDispatchThread eventloop_two{dispatcher};459 eventloop.add_thread();
461460
462 EXPECT_TRUE(in_dispatch->wait_for(std::chrono::seconds{1}));461 EXPECT_TRUE(in_dispatch->wait_for(std::chrono::seconds{5}));
463462
464 dispatcher->remove_watch(dummy_dispatchee);463 dispatcher->remove_watch(dummy_dispatchee);
465 dispatcher->remove_watch(first_dispatchee);464 dispatcher->remove_watch(first_dispatchee);
@@ -470,7 +469,7 @@
470469
471 unblock_dispatchee->raise();470 unblock_dispatchee->raise();
472471
473 EXPECT_TRUE(canary_killed->wait_for(std::chrono::seconds{2}));472 EXPECT_TRUE(canary_killed->wait_for(std::chrono::seconds{5}));
474}473}
475474
476TEST(MultiplexingDispatchableTest, automatic_removals_are_threadsafe)475TEST(MultiplexingDispatchableTest, automatic_removals_are_threadsafe)
@@ -481,7 +480,11 @@
481480
482 dispatcher->add_watch(dispatchee, md::DispatchReentrancy::reentrant);481 dispatcher->add_watch(dispatchee, md::DispatchReentrancy::reentrant);
483482
484 md::SimpleDispatchThread one{dispatcher}, two{dispatcher}, three{dispatcher}, four{dispatcher};483 md::ThreadedDispatcher eventloop{"Eeelo", dispatcher};
484
485 eventloop.add_thread();
486 eventloop.add_thread();
487 eventloop.add_thread();
485488
486 dispatchee->trigger();489 dispatchee->trigger();
487}490}
488491
=== removed file 'tests/unit-tests/dispatch/test_simple_dispatch_thread.cpp'
--- tests/unit-tests/dispatch/test_simple_dispatch_thread.cpp 2015-05-01 14:47:55 +0000
+++ tests/unit-tests/dispatch/test_simple_dispatch_thread.cpp 1970-01-01 00:00:00 +0000
@@ -1,291 +0,0 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#include "mir/dispatch/simple_dispatch_thread.h"
20#include "mir/dispatch/dispatchable.h"
21#include "mir/fd.h"
22#include "mir_test/pipe.h"
23#include "mir_test/signal.h"
24#include "mir_test/test_dispatchable.h"
25#include "mir_test_framework/process.h"
26#include "mir_test/cross_process_action.h"
27
28#include <fcntl.h>
29
30#include <atomic>
31#include <exception>
32#include <thread>
33
34#include <gtest/gtest.h>
35#include <gmock/gmock.h>
36
37namespace md = mir::dispatch;
38namespace mt = mir::test;
39
40namespace
41{
42class SimpleDispatchThreadTest : public ::testing::Test
43{
44public:
45 SimpleDispatchThreadTest()
46 {
47 mt::Pipe pipe{O_NONBLOCK};
48 watch_fd = pipe.read_fd();
49 test_fd = pipe.write_fd();
50 }
51
52 mir::Fd watch_fd;
53 mir::Fd test_fd;
54};
55
56class MockDispatchable : public md::Dispatchable
57{
58public:
59 MOCK_CONST_METHOD0(watch_fd, mir::Fd());
60 MOCK_METHOD1(dispatch, bool(md::FdEvents));
61 MOCK_CONST_METHOD0(relevant_events, md::FdEvents());
62};
63}
64
65TEST_F(SimpleDispatchThreadTest, calls_dispatch_when_fd_is_readable)
66{
67 using namespace testing;
68
69 auto dispatched = std::make_shared<mt::Signal>();
70 auto dispatchable = std::make_shared<mt::TestDispatchable>([dispatched]()
71 {
72 dispatched->raise();
73 });
74
75 md::SimpleDispatchThread dispatcher{dispatchable};
76
77 dispatchable->trigger();
78
79 EXPECT_TRUE(dispatched->wait_for(std::chrono::seconds{1}));
80}
81
82TEST_F(SimpleDispatchThreadTest, stops_calling_dispatch_once_fd_is_not_readable)
83{
84 using namespace testing;
85
86 std::atomic<int> dispatch_count{0};
87 auto dispatchable = std::make_shared<mt::TestDispatchable>([&dispatch_count]()
88 {
89 ++dispatch_count;
90 });
91
92 md::SimpleDispatchThread dispatcher{dispatchable};
93
94 dispatchable->trigger();
95
96 std::this_thread::sleep_for(std::chrono::seconds{1});
97
98 EXPECT_THAT(dispatch_count, Eq(1));
99}
100
101TEST_F(SimpleDispatchThreadTest, passes_dispatch_events_through)
102{
103 using namespace testing;
104
105 auto dispatched_with_only_readable = std::make_shared<mt::Signal>();
106 auto dispatched_with_hangup = std::make_shared<mt::Signal>();
107 auto delegate = [dispatched_with_only_readable, dispatched_with_hangup](md::FdEvents events)
108 {
109 if (events == md::FdEvent::readable)
110 {
111 dispatched_with_only_readable->raise();
112 }
113 if (events & md::FdEvent::remote_closed)
114 {
115 dispatched_with_hangup->raise();
116 return false;
117 }
118 return true;
119 };
120 auto dispatchable =
121 std::make_shared<mt::TestDispatchable>(delegate, md::FdEvent::readable | md::FdEvent::remote_closed);
122
123 md::SimpleDispatchThread dispatcher{dispatchable};
124
125 dispatchable->trigger();
126 EXPECT_TRUE(dispatched_with_only_readable->wait_for(std::chrono::seconds{1}));
127
128 dispatchable->hangup();
129 EXPECT_TRUE(dispatched_with_hangup->wait_for(std::chrono::seconds{1}));
130}
131
132TEST_F(SimpleDispatchThreadTest, doesnt_call_dispatch_after_first_false_return)
133{
134 using namespace testing;
135 using namespace std::chrono_literals;
136
137 int constexpr expected_count{10};
138 auto const dispatched_more_than_enough = std::make_shared<mt::Signal>();
139
140 auto delegate =
141 [dispatched_more_than_enough, dispatch_count = 0](md::FdEvents) mutable
142 {
143 if (++dispatch_count == expected_count)
144 {
145 return false;
146 }
147 if (dispatch_count > expected_count)
148 {
149 dispatched_more_than_enough->raise();
150 }
151 return true;
152 };
153 auto const dispatchable = std::make_shared<mt::TestDispatchable>(delegate);
154
155 md::SimpleDispatchThread dispatcher{dispatchable};
156
157 for (int i = 0; i < expected_count + 1; ++i)
158 {
159 dispatchable->trigger();
160 }
161
162 EXPECT_FALSE(dispatched_more_than_enough->wait_for(1s));
163}
164
165TEST_F(SimpleDispatchThreadTest, only_calls_dispatch_with_remote_closed_when_relevant)
166{
167 using namespace testing;
168
169 auto dispatchable = std::make_shared<NiceMock<MockDispatchable>>();
170 ON_CALL(*dispatchable, watch_fd()).WillByDefault(Return(test_fd));
171 ON_CALL(*dispatchable, relevant_events()).WillByDefault(Return(md::FdEvent::writable));
172 auto dispatched_writable = std::make_shared<mt::Signal>();
173 auto dispatched_closed = std::make_shared<mt::Signal>();
174
175 ON_CALL(*dispatchable, dispatch(_)).WillByDefault(Invoke([=](md::FdEvents events)
176 {
177 if (events & md::FdEvent::writable)
178 {
179 dispatched_writable->raise();
180 }
181 if (events & md::FdEvent::remote_closed)
182 {
183 dispatched_closed->raise();
184 }
185 return true;
186 }));
187
188 md::SimpleDispatchThread dispatcher{dispatchable};
189
190 EXPECT_TRUE(dispatched_writable->wait_for(std::chrono::seconds{1}));
191
192 // Make the fd remote-closed...
193 watch_fd = mir::Fd{};
194 EXPECT_FALSE(dispatched_closed->wait_for(std::chrono::seconds{1}));
195}
196
197// Regression test for: lp #1439719
198// The bug involves uninitialized memory and is also sensitive to signal
199// timings, so this test does not always catch the problem. However, repeated
200// runs (~300, YMMV) consistently fail when run against the problematic code.
201TEST_F(SimpleDispatchThreadTest, keeps_dispatching_after_signal_interruption)
202{
203 using namespace std::chrono_literals;
204 mt::CrossProcessAction stop_and_restart_process;
205
206 auto child = mir_test_framework::fork_and_run_in_a_different_process(
207 [&]
208 {
209 auto dispatched = std::make_shared<mt::Signal>();
210 auto dispatchable = std::make_shared<mt::TestDispatchable>(
211 [dispatched]() { dispatched->raise(); });
212
213 md::SimpleDispatchThread dispatcher{dispatchable};
214 // Ensure the dispatcher has started
215 dispatchable->trigger();
216 EXPECT_TRUE(dispatched->wait_for(1s));
217
218 stop_and_restart_process();
219
220 dispatched->reset();
221 // The dispatcher shouldn't have been affected by the signal
222 dispatchable->trigger();
223 EXPECT_TRUE(dispatched->wait_for(1s));
224 exit(HasFailure() ? EXIT_FAILURE : EXIT_SUCCESS);
225 },
226 []{ return 1; });
227
228 stop_and_restart_process.exec(
229 [child]
230 {
231 // Increase chances of interrupting the dispatch mechanism
232 for (int i = 0; i < 100; ++i)
233 {
234 child->stop();
235 child->cont();
236 }
237 });
238
239 auto const result = child->wait_for_termination(10s);
240 EXPECT_TRUE(result.succeeded());
241}
242
243using SimpleDispatchThreadDeathTest = SimpleDispatchThreadTest;
244
245TEST_F(SimpleDispatchThreadDeathTest, destroying_dispatcher_from_a_callback_is_an_error)
246{
247 using namespace testing;
248 using namespace std::literals::chrono_literals;
249
250 EXPECT_EXIT(
251 {
252 std::mutex mutex;
253 md::SimpleDispatchThread* dispatcher;
254
255 auto dispatchable = std::make_shared<mt::TestDispatchable>([&dispatcher, &mutex]{
256 std::lock_guard<decltype(mutex)> lock{mutex};
257 delete dispatcher;
258 });
259
260 {
261 std::lock_guard<decltype(mutex)> lock{mutex};
262 dispatchable->trigger();
263 dispatcher = new md::SimpleDispatchThread{dispatchable};
264 }
265 std::this_thread::sleep_for(10s);
266 }, KilledBySignal(SIGABRT), ".*Destroying SimpleDispatchThread.*");
267}
268
269TEST_F(SimpleDispatchThreadTest, executes_exception_handler_with_current_exception)
270{
271 using namespace std::chrono_literals;
272 auto dispatched = std::make_shared<mt::Signal>();
273 std::exception_ptr exception;
274
275 auto dispatchable = std::make_shared<mt::TestDispatchable>(
276 []()
277 {
278 throw std::runtime_error("thrown");
279 });
280
281 md::SimpleDispatchThread dispatcher{dispatchable,
282 [&dispatched,&exception]()
283 {
284 exception = std::current_exception();
285 if (exception)
286 dispatched->raise();
287 }};
288 dispatchable->trigger();
289 EXPECT_TRUE(dispatched->wait_for(10s));
290 EXPECT_TRUE(exception!=nullptr);
291}
2920
=== added file 'tests/unit-tests/dispatch/test_threaded_dispatcher.cpp'
--- tests/unit-tests/dispatch/test_threaded_dispatcher.cpp 1970-01-01 00:00:00 +0000
+++ tests/unit-tests/dispatch/test_threaded_dispatcher.cpp 2015-05-14 07:50:26 +0000
@@ -0,0 +1,403 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#include "mir/dispatch/threaded_dispatcher.h"
20#include "mir/dispatch/dispatchable.h"
21#include "mir/fd.h"
22#include "mir_test/pipe.h"
23#include "mir_test/signal.h"
24#include "mir_test/test_dispatchable.h"
25#include "mir_test_framework/process.h"
26#include "mir_test/cross_process_action.h"
27
28#include <fcntl.h>
29
30#include <atomic>
31#include <thread>
32
33#include <gtest/gtest.h>
34#include <gmock/gmock.h>
35
36namespace md = mir::dispatch;
37namespace mt = mir::test;
38
39namespace
40{
41class ThreadedDispatcherTest : public ::testing::Test
42{
43public:
44 ThreadedDispatcherTest()
45 {
46 mt::Pipe pipe{O_NONBLOCK};
47 watch_fd = pipe.read_fd();
48 test_fd = pipe.write_fd();
49 }
50
51 mir::Fd watch_fd;
52 mir::Fd test_fd;
53};
54
55class MockDispatchable : public md::Dispatchable
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches