Mir

Merge lp:~raof/mir/no-hidden-rpc-in-bufferstream into lp:mir

Proposed by Chris Halse Rogers
Status: Work in progress
Proposed branch: lp:~raof/mir/no-hidden-rpc-in-bufferstream
Merge into: lp:mir
Prerequisite: lp:~raof/mir/fix-non-precompiled-headers-build
Diff against target: 852 lines (+379/-72)
14 files modified
src/client/buffer_factory.cpp (+11/-1)
src/client/buffer_stream.cpp (+57/-21)
src/client/mir_connection.cpp (+22/-6)
src/client/mir_connection.h (+3/-0)
src/client/no_tls_future-inl.h (+76/-6)
src/client/rpc/mir_protobuf_rpc_channel.cpp (+1/-1)
tests/acceptance-tests/test_client_library.cpp (+2/-0)
tests/acceptance-tests/test_client_surfaces.cpp (+37/-0)
tests/include/mir/test/doubles/stub_buffer.h (+8/-6)
tests/integration-tests/test_surfaceloop.cpp (+44/-28)
tests/mir_test_doubles/stub_buffer.cpp (+5/-3)
tests/unit-tests/client/CMakeLists.txt (+1/-0)
tests/unit-tests/client/test_client_buffer_stream.cpp (+1/-0)
tests/unit-tests/client/test_no_tls_future.cpp (+111/-0)
To merge this branch: bzr merge lp:~raof/mir/no-hidden-rpc-in-bufferstream
Reviewer Review Type Date Requested Status
Mir CI Bot continuous-integration Needs Fixing
Kevin DuBois (community) Approve
Review via email: mp+298202@code.launchpad.net

Commit message

Eliminate the hidden RPC waits in MirBufferStream with NBS.

There were a variety of mir_buffer_stream_* calls which would implicitly block on RPC, particularly mir_buffer_stream_get_current_buffer() after
mir_connection_create_buffer_stream().

Resolve this by having the wait handles not signal until the buffer stream is in a valid state.

Description of the change

Another prerequisite for manual event dispatch.

This removes two unit tests from TestMirConnection - I did this because they require a lot of state set up now - surface creation doesn't complete until buffer stream creation is complete, which doesn't complete until the buffer vault receives a buffer in response to its alloc_buffer calls, and this buffer needs to be sufficiently genuine to make it through multiple layers of RPC/MirSurface dispatch.

Instead, there's a new acceptance test for this behaviour in https://code.launchpad.net/~raof/mir/acceptance-test-for-unfocus-on-surface-release/+merge/298098

To post a comment you must log in.
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:3555
https://mir-jenkins.ubuntu.com/job/mir-ci/1177/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/1325/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/1376
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/1367
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/1367
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/1339/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/1339
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/1339/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/1339
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/1339/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/1339
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/1339/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/1339
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/1339/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/1177/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Kevin DuBois (kdub) wrote :

(curiosity) why does manual dispatch need this?

lgtm otherwise. If there's future work in the area, I'm planning on condensing BufferVault and "NewBufferSemantics" into one class.

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

Manual dispatch ran into this because you can't assume that RPC will occur at arbitrary times. Particularly - mir_buffer_stream_swap_buffers_sync() will first call mir_buffer_stream_swap_buffers() and then, in the same thread, pump the event loop until the wait handle resolves.

Except mir_buffer_stream_swap_buffers() calls get_current_buffer(), which blocks waiting for an IPC event that we'll never dispatch because we're blocked :).

Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:3556
https://mir-jenkins.ubuntu.com/job/mir-ci/1185/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/1344
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/1395
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/1386
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/1386
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/1358
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/1358/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/1358
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/1358/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/1358
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/1358/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/1358
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/1358/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/1358
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/1358/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/1185/rebuild

review: Approve (continuous-integration)
Revision history for this message
Chris Halse Rogers (raof) wrote :

Oh, hello! This doesn't actually ensure that the mir_surface_created callback is called only once the MirSurface is valid. Oops!

Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Chris Halse Rogers (raof) wrote :

...and it turns out that I really need to remove the hidden RPC *harder*...

Unmerged revisions

3560. By Chris Halse Rogers

Wait for default BufferStream creation before calling surface created callback.

Punt completion of the operation into the delayed-processing queue, where it can block on
the creation MirWaitHandle of the default BufferStream.

3559. By Chris Halse Rogers

Be more verbose when receiving an unexpected buffer.

This *should* never happen, so being verbose in the error message is low cost and potentially
high reward.

3558. By Chris Halse Rogers

MirProtobufRpcChannel: Don't destroy exception information on error.

3557. By Chris Halse Rogers

Send valid buffers in SurfaceLoop integration test.

This requires making StubBuffer a bit stubbier and actually packing the IPC messages.

3556. By Chris Halse Rogers

Fix NoTLSFuture so that the move-setter is actually accessible.

Because NoTLSPromise only had a set_value(T val) (wat‽) method, PromiseState's set_value(T&& val)
method was unreachable. Which is apparently why g++ didn't notice that it was broken.

3555. By Chris Halse Rogers

BufferStream: Do not signal creation wait handle until fully ready.

With NewBufferSemantics we were signalling the creation wait handle after the construction of the mcl::BufferStream,
but at this point the BufferVault does not necessarily have a buffer yet - that comes later as an event in response
to the allocate_buffer requests made during construction.

Instead, wait until the current buffer future<> has resolved before signalling that the BufferStream
is ready.

3554. By Chris Halse Rogers

Add missing gmock header.

testing::Eq and EXPECT_THAT are defined in Google Mock, not Google Test.

This only builds because MIR_USE_PRECOMPILED_HEADERS means that all our tests effectively
get the same set of headers included.

3553. By Chris Halse Rogers

Remove synthesised-focuse-event unit tests on MirConnection.

These require an ungodly amount of setup, so much so that they're basically
acceptance tests. The change to making sure buffer-streams are completely
constructed before we inform the client that they're completely constructed
results in these tests requiring *even more* setup.

Since we've just added real acceptance tests for this behaviour, just delete
these “unit” tests.

3552. By Chris Halse Rogers

Add NoTLSPromise<T>::or_else()

3551. By Chris Halse Rogers

NoTLSFuture is a class, damnit.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/client/buffer_factory.cpp'
2--- src/client/buffer_factory.cpp 2016-06-06 11:51:31 +0000
3+++ src/client/buffer_factory.cpp 2016-07-05 05:54:27 +0000
4@@ -21,6 +21,7 @@
5 #include <algorithm>
6 #include <boost/throw_exception.hpp>
7 #include "protobuf_to_native_buffer.h"
8+#include <sstream>
9
10 namespace mcl = mir::client;
11 namespace geom = mir::geometry;
12@@ -64,7 +65,16 @@
13 });
14
15 if (request_it == allocation_requests.end())
16- BOOST_THROW_EXCEPTION(std::logic_error("unrequested buffer received"));
17+ {
18+ std::stringstream message;
19+ message << "Unrequested buffer received. Expected a buffer with one of the following sizes:" << std::endl;
20+ for (auto const& request : allocation_requests)
21+ {
22+ message << "\t(" << request->size.width << "×" << request->size.height << ")" << std::endl;
23+ }
24+ message << "Received buffer has size: (" << buffer.width() << "×" << buffer.height() << ")" << std::endl;
25+ BOOST_THROW_EXCEPTION(std::logic_error{message.str()});
26+ }
27
28 auto b = std::make_unique<Buffer>(
29 (*request_it)->cb, (*request_it)->cb_context,
30
31=== modified file 'src/client/buffer_stream.cpp'
32--- src/client/buffer_stream.cpp 2016-06-06 11:51:31 +0000
33+++ src/client/buffer_stream.cpp 2016-07-05 05:54:27 +0000
34@@ -233,6 +233,7 @@
35 MirWaitHandle next_buffer_wait_handle;
36 bool server_connection_lost {false};
37 MirWaitHandle scale_wait_handle;
38+ MirWaitHandle construction_wait_handle;
39 float scale_;
40 geom::Size size_;
41 };
42@@ -300,6 +301,7 @@
43 struct NewBufferSemantics : mcl::ServerBufferSemantics
44 {
45 NewBufferSemantics(
46+ std::shared_ptr<MirWaitHandle> const& construction_wait_handle,
47 std::shared_ptr<mcl::ClientBufferFactory> const& factory,
48 std::shared_ptr<mcl::AsyncBufferFactory> const& mirbuffer_factory,
49 std::shared_ptr<mcl::ServerBufferRequests> const& requests,
50@@ -308,53 +310,82 @@
51 unsigned int initial_nbuffers) :
52 vault(factory, mirbuffer_factory, requests, surface_map, size, format, usage, initial_nbuffers),
53 current(nullptr),
54- future(vault.withdraw()),
55 size_(size)
56 {
57+ construction_wait_handle->expect_result();
58+ future = vault.withdraw();
59+ future.and_then(
60+ [this, construction_wait_handle](std::shared_ptr<mcl::MirBuffer>&& new_buffer)
61+ {
62+ std::lock_guard<std::mutex> lk{current_buffer_mutex};
63+
64+ current = std::move(new_buffer);
65+ construction_wait_handle->result_received();
66+ });
67+ future.or_else(
68+ [this, construction_wait_handle](auto)
69+ {
70+ construction_wait_handle->result_received();
71+ });
72 }
73
74 void deposit(mp::Buffer const&, mir::optional_value<geom::Size>, MirPixelFormat) override
75 {
76 }
77
78- void advance_current_buffer(std::unique_lock<std::mutex>& lk)
79+ void ensure_current_is_valid_locked()
80 {
81- lk.unlock();
82- auto c = future.get();
83- lk.lock();
84- current = c;
85+ if (!current)
86+ {
87+ BOOST_THROW_EXCEPTION(
88+ std::logic_error{
89+ "Attempt made to access current buffer before submit() has completed.\n"
90+ "After calling mir_buffer_stream_swap_buffers a client *must* wait for the operation to complete\n"
91+ "before accessing the current buffer. There is no current buffer until swap_buffers has completed."});
92+ }
93 }
94
95 std::shared_ptr<mir::client::ClientBuffer> current_buffer() override
96 {
97- std::unique_lock<std::mutex> lk(mutex);
98- if (!current)
99- advance_current_buffer(lk);
100+ std::lock_guard<std::mutex> lk(current_buffer_mutex);
101+ ensure_current_is_valid_locked();
102 return current->client_buffer();
103 }
104
105 uint32_t current_buffer_id() override
106 {
107- std::unique_lock<std::mutex> lk(mutex);
108- if (!current)
109- advance_current_buffer(lk);
110+ std::lock_guard<std::mutex> lk(current_buffer_mutex);
111+ ensure_current_is_valid_locked();
112 return current->rpc_id();
113 }
114
115 MirWaitHandle* submit(std::function<void()> const& done, MirPixelFormat, int) override
116 {
117- std::unique_lock<std::mutex> lk(mutex);
118- if (!current)
119- advance_current_buffer(lk);
120- auto c = current;
121- current = nullptr;
122- lk.unlock();
123+ decltype(current) c;
124+ {
125+ std::lock_guard<std::mutex> lk(current_buffer_mutex);
126+ ensure_current_is_valid_locked();
127+ c = current;
128+ current = nullptr;
129+ }
130
131 vault.deposit(c);
132 auto wh = vault.wire_transfer_outbound(c, done);
133 auto f = vault.withdraw();
134- lk.lock();
135- future = std::move(f);
136+
137+ {
138+ std::lock_guard<std::mutex> lk{mutex};
139+ future = std::move(f);
140+
141+ future.and_then(
142+ [this](std::shared_ptr<mcl::MirBuffer>&& new_buffer)
143+ {
144+ std::lock_guard<std::mutex> lk{current_buffer_mutex};
145+
146+ current = std::move(new_buffer);
147+ });
148+ }
149+
150 return wh;
151 }
152
153@@ -403,10 +434,13 @@
154 current_swap_interval = interval;
155 }
156
157+ // Future must be destroyed after vault to ensure any broken promise is handled
158+ mir::client::NoTLSFuture<std::shared_ptr<mcl::MirBuffer>> future;
159+
160 mcl::BufferVault vault;
161 std::mutex mutable mutex;
162+ std::mutex current_buffer_mutex;
163 std::shared_ptr<mcl::MirBuffer> current{nullptr};
164- mir::client::NoTLSFuture<std::shared_ptr<mcl::MirBuffer>> future;
165 MirWaitHandle scale_wait_handle;
166 int current_swap_interval = 1;
167 geom::Size size_;
168@@ -464,6 +498,7 @@
169 else
170 {
171 buffer_depository = std::make_unique<NewBufferSemantics>(
172+ creation_wait_handle,
173 client_platform->create_buffer_factory(), factory,
174 std::make_shared<Requests>(display_server, protobuf_bs->id().value()), map,
175 ideal_buffer_size, static_cast<MirPixelFormat>(protobuf_bs->pixel_format()),
176@@ -552,6 +587,7 @@
177 else
178 {
179 buffer_depository = std::make_unique<NewBufferSemantics>(
180+ creation_wait_handle,
181 client_platform->create_buffer_factory(), factory,
182 std::make_shared<Requests>(display_server, protobuf_bs->id().value()), map,
183 ideal_buffer_size, static_cast<MirPixelFormat>(protobuf_bs->pixel_format()), 0, nbuffers);
184
185=== modified file 'src/client/mir_connection.cpp'
186--- src/client/mir_connection.cpp 2016-06-27 22:13:21 +0000
187+++ src/client/mir_connection.cpp 2016-07-05 05:54:27 +0000
188@@ -27,6 +27,8 @@
189 #include "rpc/mir_basic_rpc_channel.h"
190 #include "mir/dispatch/dispatchable.h"
191 #include "mir/dispatch/threaded_dispatcher.h"
192+#include "mir/dispatch/action_queue.h"
193+#include "mir/dispatch/multiplexing_dispatchable.h"
194 #include "mir/input/input_devices.h"
195 #include "connection_configuration.h"
196 #include "display_configuration.h"
197@@ -287,9 +289,18 @@
198 ping_handler{conf.the_ping_handler()},
199 event_handler_register(conf.the_event_handler_register()),
200 pong_callback(google::protobuf::NewPermanentCallback(&google::protobuf::DoNothing)),
201- eventloop{new md::ThreadedDispatcher{"RPC Thread", std::dynamic_pointer_cast<md::Dispatchable>(channel)}},
202+ delayed_queue{std::make_unique<md::ActionQueue>()},
203+ eventloop{std::make_unique<md::ThreadedDispatcher>(
204+ "RPC Thread",
205+ std::make_shared<md::MultiplexingDispatchable>(
206+ std::initializer_list<std::shared_ptr<md::Dispatchable>>{
207+ std::dynamic_pointer_cast<md::Dispatchable>(channel),
208+ delayed_queue}))},
209 nbuffers(get_nbuffers_from_env())
210 {
211+ // Need a second thread ensure RPC is not blocked if the delayed_queue blocks,
212+ // as it does when waiting for surface creation.
213+ eventloop->add_thread();
214 connect_result->set_error("connect not called");
215 {
216 std::lock_guard<std::mutex> lock(connection_guard);
217@@ -371,19 +382,19 @@
218 return;
219
220 auto surface_proto = request->response;
221- auto callback = request->cb;
222- auto context = request->context;
223 auto const& spec = request->spec;
224 std::string name{spec.surface_name.is_set() ?
225 spec.surface_name.value() : ""};
226
227 std::shared_ptr<mcl::ClientBufferStream> default_stream {nullptr};
228+ std::shared_ptr<MirWaitHandle> buffer_stream_wh;
229 if (surface_proto->buffer_stream().has_id())
230 {
231 try
232 {
233+ buffer_stream_wh = std::make_shared<MirWaitHandle>();
234 default_stream = std::make_shared<mcl::BufferStream>(
235- this, request->wh, server, platform, surface_map, buffer_factory,
236+ this, buffer_stream_wh, server, platform, surface_map, buffer_factory,
237 surface_proto->buffer_stream(), make_perf_report(logger), name,
238 mir::geometry::Size{surface_proto->width(), surface_proto->height()}, nbuffers);
239 surface_map->insert(default_stream->rpc_id(), default_stream);
240@@ -422,8 +433,13 @@
241 surface_map->insert(mf::SurfaceId{surface_proto->id().value()}, surf);
242 }
243
244- callback(surf.get(), context);
245- request->wh->result_received();
246+ delayed_queue->enqueue(
247+ [buffer_stream_wh, callback = request->cb, context = request->context, done = request->wh, surf, loop = eventloop.get()]()
248+ {
249+ mir_wait_for(buffer_stream_wh.get());
250+ callback(surf.get(), context);
251+ done->result_received();
252+ });
253
254 surface_requests.erase(request_it);
255 }
256
257=== modified file 'src/client/mir_connection.h'
258--- src/client/mir_connection.h 2016-06-16 19:13:53 +0000
259+++ src/client/mir_connection.h 2016-07-05 05:54:27 +0000
260@@ -89,6 +89,7 @@
261 namespace dispatch
262 {
263 class ThreadedDispatcher;
264+class ActionQueue;
265 }
266 }
267
268@@ -308,6 +309,8 @@
269
270 std::unique_ptr<google::protobuf::Closure> const pong_callback;
271
272+ std::shared_ptr<mir::dispatch::ActionQueue> const delayed_queue;
273+
274 std::unique_ptr<mir::dispatch::ThreadedDispatcher> const eventloop;
275
276 mir::client::AtomicCallback<MirError const*> error_handler;
277
278=== modified file 'src/client/no_tls_future-inl.h'
279--- src/client/no_tls_future-inl.h 2016-05-03 06:55:25 +0000
280+++ src/client/no_tls_future-inl.h 2016-07-05 05:54:27 +0000
281@@ -35,6 +35,9 @@
282 namespace client
283 {
284 template<typename T>
285+class NoTLSFuture;
286+
287+template<typename T>
288 class PromiseState
289 {
290 public:
291@@ -51,15 +54,30 @@
292 {
293 std::lock_guard<std::mutex> lk(mutex);
294 set = true;
295- value = val;
296+ if (continuation)
297+ {
298+ value = val;
299+ continuation(std::move(value));
300+ }
301+ else
302+ {
303+ value = val;
304+ }
305 cv.notify_all();
306 }
307
308- void set_value(T && val)
309+ void set_value(T&& val)
310 {
311 std::lock_guard<std::mutex> lk(mutex);
312 set = true;
313- value = std::move(val);
314+ if (continuation)
315+ {
316+ continuation(std::move(val));
317+ }
318+ else
319+ {
320+ value = val;
321+ }
322 cv.notify_all();
323 }
324
325@@ -79,7 +97,13 @@
326 {
327 std::lock_guard<std::mutex> lk(mutex);
328 if (!set)
329+ {
330 broken = true;
331+ if (exception_continuation)
332+ {
333+ exception_continuation(std::make_exception_ptr(std::runtime_error("broken_promise")));
334+ }
335+ }
336 cv.notify_all();
337 }
338
339@@ -95,11 +119,35 @@
340 bool set{false};
341 bool broken{false};
342 T value;
343+ std::function<void(typename std::add_rvalue_reference<T>::type)> continuation;
344+ std::function<void(std::exception_ptr const&)> exception_continuation;
345+
346+ friend class NoTLSFuture<T>;
347+ void set_continuation(std::function<void(typename std::add_rvalue_reference<T>::type)> const& continuation)
348+ {
349+ std::lock_guard<std::mutex> lk{mutex};
350+ if (set)
351+ {
352+ continuation(std::move(value));
353+ }
354+ this->continuation = continuation;
355+ }
356+
357+ void set_exception_continuation(std::function<void(std::exception_ptr const&)> const& exception_continuation)
358+ {
359+ std::lock_guard<std::mutex> lk{mutex};
360+ if (broken)
361+ {
362+ exception_continuation(std::make_exception_ptr(std::runtime_error("broken_promise")));
363+ }
364+ this->exception_continuation = exception_continuation;
365+ }
366 };
367
368 template<typename T>
369-struct NoTLSFuture
370+class NoTLSFuture
371 {
372+public:
373 NoTLSFuture() :
374 state(nullptr)
375 {
376@@ -138,6 +186,16 @@
377 return value;
378 }
379
380+ void and_then(std::function<void(typename std::add_rvalue_reference<T>::type)> const& continuation)
381+ {
382+ state->set_continuation(continuation);
383+ }
384+
385+ void or_else(std::function<void(std::exception_ptr const&)> const& handler)
386+ {
387+ state->set_exception_continuation(handler);
388+ }
389+
390 template<class Rep, class Period>
391 std::future_status wait_for(std::chrono::duration<Rep, Period> const& timeout_duration) const
392 {
393@@ -182,18 +240,30 @@
394 NoTLSPromise(NoTLSPromise const&) = delete;
395 NoTLSPromise operator=(NoTLSPromise const&) = delete;
396
397- void set_value(T value)
398+ void set_value(T const& value)
399 {
400 state->set_value(value);
401 }
402
403+ void set_value(T&& value)
404+ {
405+ state->set_value(std::move(value));
406+ }
407+
408 NoTLSFuture<T> get_future()
409 {
410+ if (future_retrieved)
411+ {
412+ //clang has problems with std::future_error::what() on vivid+overlay
413+ BOOST_THROW_EXCEPTION(std::runtime_error{"future_already_retrieved"});
414+ }
415+ future_retrieved = true;
416 return NoTLSFuture<T>(state);
417 }
418
419 private:
420- std::shared_ptr<PromiseState<T>> state;
421+ std::shared_ptr<PromiseState<T>> state;
422+ bool future_retrieved{false};
423 };
424 }
425 }
426
427=== modified file 'src/client/rpc/mir_protobuf_rpc_channel.cpp'
428--- src/client/rpc/mir_protobuf_rpc_channel.cpp 2016-06-02 05:33:50 +0000
429+++ src/client/rpc/mir_protobuf_rpc_channel.cpp 2016-07-05 05:54:27 +0000
430@@ -345,7 +345,7 @@
431 {
432 for(auto i = 0; i < seq.buffer_request().buffer().fd_size(); i++)
433 close(seq.buffer_request().buffer().fd(i));
434- throw e;
435+ throw;
436 }
437 }
438 else
439
440=== modified file 'tests/acceptance-tests/test_client_library.cpp'
441--- tests/acceptance-tests/test_client_library.cpp 2016-06-08 20:40:53 +0000
442+++ tests/acceptance-tests/test_client_library.cpp 2016-07-05 05:54:27 +0000
443@@ -24,6 +24,7 @@
444 #include "mir_test_framework/using_stub_client_platform.h"
445 #include "mir_test_framework/any_surface.h"
446 #include "mir/test/validity_matchers.h"
447+#include "mir/test/signal.h"
448 #include "src/include/common/mir/protobuf/protocol_version.h"
449
450 #include "mir_protobuf.pb.h"
451@@ -52,6 +53,7 @@
452 namespace mf = mir::frontend;
453 namespace mc = mir::compositor;
454 namespace mtf = mir_test_framework;
455+namespace mt = mir::test;
456 namespace
457 {
458 struct ClientLibrary : mtf::HeadlessInProcessServer
459
460=== modified file 'tests/acceptance-tests/test_client_surfaces.cpp'
461--- tests/acceptance-tests/test_client_surfaces.cpp 2016-05-03 06:55:25 +0000
462+++ tests/acceptance-tests/test_client_surfaces.cpp 2016-07-05 05:54:27 +0000
463@@ -28,6 +28,7 @@
464 #include "mir_test_framework/any_surface.h"
465 #include "mir/test/validity_matchers.h"
466 #include "mir/test/fake_shared.h"
467+#include "mir/test/signal.h"
468
469 #include <gmock/gmock.h>
470 #include <gtest/gtest.h>
471@@ -393,3 +394,39 @@
472
473 mir_connection_release(im_connection);
474 }
475+
476+TEST_F(ClientSurfaces, default_buffer_stream_is_fully_constructed_in_surface_created_callback)
477+{
478+ using namespace testing;
479+ using namespace std::chrono_literals;
480+
481+ auto surface_spec = mir_connection_create_spec_for_normal_surface(
482+ connection,
483+ 800, 600,
484+ mir_pixel_format_argb_8888);
485+
486+ mt::Signal surface_released;
487+ mir_surface_create(
488+ surface_spec,
489+ [](MirSurface* surf, void* ctx)
490+ {
491+ EXPECT_THAT(surf, IsValid());
492+ auto buffer_stream = mir_surface_get_buffer_stream(surf);
493+ MirGraphicsRegion region;
494+ mir_buffer_stream_get_graphics_region(buffer_stream, &region);
495+ EXPECT_THAT(region.vaddr, NotNull());
496+
497+ mir_surface_release(
498+ surf,
499+ [](MirSurface*, void* ctx)
500+ {
501+ auto& done = *reinterpret_cast<mt::Signal*>(ctx);
502+ done.raise();
503+ },
504+ ctx);
505+ },
506+ &surface_released);
507+ mir_surface_spec_release(surface_spec);
508+
509+ EXPECT_TRUE(surface_released.wait_for(10s));
510+}
511
512=== modified file 'tests/include/mir/test/doubles/stub_buffer.h'
513--- tests/include/mir/test/doubles/stub_buffer.h 2016-01-29 08:18:22 +0000
514+++ tests/include/mir/test/doubles/stub_buffer.h 2016-07-05 05:54:27 +0000
515@@ -25,6 +25,7 @@
516 #include "mir/graphics/buffer_id.h"
517 #include <vector>
518 #include <string.h>
519+#include <atomic>
520
521 namespace mir
522 {
523@@ -38,7 +39,7 @@
524 public:
525 StubBuffer()
526 : StubBuffer{
527- create_native_buffer(),
528+ create_native_buffer(geometry::Size{}),
529 graphics::BufferProperties{
530 geometry::Size{},
531 mir_pixel_format_abgr_8888,
532@@ -50,7 +51,7 @@
533
534 StubBuffer(geometry::Size const& size)
535 : StubBuffer{
536- create_native_buffer(),
537+ create_native_buffer(size),
538 graphics::BufferProperties{
539 size,
540 mir_pixel_format_abgr_8888,
541@@ -78,12 +79,12 @@
542 }
543
544 StubBuffer(graphics::BufferProperties const& properties)
545- : StubBuffer{create_native_buffer(), properties, geometry::Stride{properties.size.width.as_int() * MIR_BYTES_PER_PIXEL(properties.format)}}
546+ : StubBuffer{create_native_buffer(properties.size), properties, geometry::Stride{properties.size.width.as_int() * MIR_BYTES_PER_PIXEL(properties.format)}}
547 {
548 }
549
550 StubBuffer(graphics::BufferID id)
551- : native_buffer(create_native_buffer()),
552+ : native_buffer(create_native_buffer(geometry::Size{})),
553 buf_size{},
554 buf_pixel_format{mir_pixel_format_abgr_8888},
555 buf_stride{},
556@@ -98,7 +99,7 @@
557 buf_size{properties.size},
558 buf_pixel_format{properties.format},
559 buf_stride{stride},
560- buf_id{graphics::BufferBasic::id()}
561+ buf_id{++next_id}
562 {
563 }
564
565@@ -138,8 +139,9 @@
566 geometry::Stride const buf_stride;
567 graphics::BufferID const buf_id;
568 std::vector<unsigned char> written_pixels;
569+ static std::atomic<unsigned int> next_id;
570
571- std::shared_ptr<graphics::NativeBuffer> create_native_buffer();
572+ std::shared_ptr<graphics::NativeBuffer> create_native_buffer(geometry::Size const& size);
573 };
574 }
575 }
576
577=== modified file 'tests/integration-tests/test_surfaceloop.cpp'
578--- tests/integration-tests/test_surfaceloop.cpp 2016-05-03 06:55:25 +0000
579+++ tests/integration-tests/test_surfaceloop.cpp 2016-07-05 05:54:27 +0000
580@@ -17,6 +17,7 @@
581 */
582
583 #include "mir_toolkit/mir_client_library.h"
584+#include "mir/graphics/buffer_ipc_message.h"
585
586 #include "mir/test/doubles/stub_buffer.h"
587 #include "mir/test/doubles/stub_buffer_allocator.h"
588@@ -49,30 +50,6 @@
589 mg::BufferUsage const usage{mg::BufferUsage::hardware};
590 mg::BufferProperties const buffer_properties{size, format, usage};
591
592-
593-class MockGraphicBufferAllocator : public mtd::StubBufferAllocator
594-{
595- public:
596- MockGraphicBufferAllocator()
597- {
598- using testing::_;
599- ON_CALL(*this, alloc_buffer(_))
600- .WillByDefault(testing::Invoke(this, &MockGraphicBufferAllocator::on_create_swapper));
601- }
602-
603- MOCK_METHOD1(
604- alloc_buffer,
605- std::shared_ptr<mg::Buffer> (mg::BufferProperties const&));
606-
607-
608- std::shared_ptr<mg::Buffer> on_create_swapper(mg::BufferProperties const&)
609- {
610- return std::make_shared<mtd::StubBuffer>(::buffer_properties);
611- }
612-
613- ~MockGraphicBufferAllocator() noexcept {}
614-};
615-
616 class StubDisplay : public mtd::NullDisplay
617 {
618 public:
619@@ -151,11 +128,13 @@
620 {
621 public:
622
623- CountingStubBuffer()
624+ CountingStubBuffer(mg::BufferProperties const& properties)
625+ : mtd::StubBuffer(properties)
626 {
627 std::lock_guard<std::mutex> lock{buffers_mutex};
628 ++buffers_created;
629 buffers_cv.notify_one();
630+ std::cout << "Created buffer with size " << size() << std::endl;
631 }
632 ~CountingStubBuffer()
633 {
634@@ -173,9 +152,40 @@
635 class StubGraphicBufferAllocator : public mtd::StubBufferAllocator
636 {
637 public:
638- std::shared_ptr<mg::Buffer> alloc_buffer(mg::BufferProperties const&) override
639- {
640- return std::make_shared<CountingStubBuffer>();
641+ std::shared_ptr<mg::Buffer> alloc_buffer(mg::BufferProperties const& props) override
642+ {
643+ return std::make_shared<CountingStubBuffer>(props);
644+ }
645+ };
646+
647+ class StubIPCOperations : public mg::PlatformIpcOperations
648+ {
649+ public:
650+ void pack_buffer(
651+ mg::BufferIpcMessage& message,
652+ mg::Buffer const& buffer,
653+ mg::BufferIpcMsgType /*msg_type*/) const override
654+ {
655+ message.pack_size(buffer.size());
656+ }
657+
658+ void
659+ unpack_buffer(mg::BufferIpcMessage& /*message*/, mg::Buffer const& /*buffer*/) const override
660+ {
661+
662+ }
663+
664+ std::shared_ptr<mir::graphics::PlatformIPCPackage> connection_ipc_package() override
665+ {
666+ return std::make_shared<mg::PlatformIPCPackage>();
667+ }
668+
669+ mg::PlatformOperationMessage
670+ platform_operation(
671+ unsigned int const /*opcode*/,
672+ mg::PlatformOperationMessage const& /*message*/) override
673+ {
674+ return mg::PlatformOperationMessage{};
675 }
676 };
677
678@@ -193,6 +203,12 @@
679 {
680 return mir::make_module_ptr<StubDisplay>();
681 }
682+
683+ mir::UniqueModulePtr<mg::PlatformIpcOperations> make_ipc_operations() const override
684+ {
685+ return mir::make_module_ptr<StubIPCOperations>();
686+ }
687+
688 };
689
690 std::shared_ptr<mg::Platform> the_graphics_platform()
691
692=== modified file 'tests/mir_test_doubles/stub_buffer.cpp'
693--- tests/mir_test_doubles/stub_buffer.cpp 2016-01-29 08:18:22 +0000
694+++ tests/mir_test_doubles/stub_buffer.cpp 2016-07-05 05:54:27 +0000
695@@ -26,12 +26,14 @@
696
697 namespace mtd=mir::test::doubles;
698
699-auto mtd::StubBuffer::create_native_buffer()
700+auto mtd::StubBuffer::create_native_buffer(geometry::Size const& size)
701 -> std::shared_ptr<graphics::NativeBuffer>
702 {
703 #if defined(MESA_KMS) || defined(MESA_X11)
704- return std::make_shared<StubGBMNativeBuffer>(geometry::Size{0,0});
705+ return std::make_shared<StubGBMNativeBuffer>(size);
706 #elif ANDROID
707- return std::make_shared<StubAndroidNativeBuffer>();
708+ return std::make_shared<StubAndroidNativeBuffer>(size);
709 #endif
710 }
711+
712+std::atomic<unsigned int> mtd::StubBuffer::next_id{1};
713
714=== modified file 'tests/unit-tests/client/CMakeLists.txt'
715--- tests/unit-tests/client/CMakeLists.txt 2016-06-06 16:08:53 +0000
716+++ tests/unit-tests/client/CMakeLists.txt 2016-07-05 05:54:27 +0000
717@@ -23,6 +23,7 @@
718 ${CMAKE_CURRENT_SOURCE_DIR}/test_mir_buffer.cpp
719 ${CMAKE_CURRENT_SOURCE_DIR}/test_error_buffer.cpp
720 ${CMAKE_CURRENT_SOURCE_DIR}/test_client_mir_error.cpp
721+ ${CMAKE_CURRENT_SOURCE_DIR}/test_no_tls_future.cpp
722 )
723
724 if(MIR_TEST_PLATFORM STREQUAL "android")
725
726=== modified file 'tests/unit-tests/client/test_client_buffer_stream.cpp'
727--- tests/unit-tests/client/test_client_buffer_stream.cpp 2016-06-02 11:19:25 +0000
728+++ tests/unit-tests/client/test_client_buffer_stream.cpp 2016-07-05 05:54:27 +0000
729@@ -150,6 +150,7 @@
730 struct ClientBufferStream : TestWithParam<bool>
731 {
732 ClientBufferStream()
733+ : wait_handle{std::make_shared<MirWaitHandle>()}
734 {
735 ON_CALL(mock_factory, create_buffer(_,_,_))
736 .WillByDefault(Return(std::make_shared<mtd::NullClientBuffer>()));
737
738=== added file 'tests/unit-tests/client/test_no_tls_future.cpp'
739--- tests/unit-tests/client/test_no_tls_future.cpp 1970-01-01 00:00:00 +0000
740+++ tests/unit-tests/client/test_no_tls_future.cpp 2016-07-05 05:54:27 +0000
741@@ -0,0 +1,111 @@
742+/*
743+ * Copyright © 2016 Canonical Ltd.
744+ *
745+ * This program is free software: you can redistribute it and/or modify
746+ * it under the terms of the GNU General Public License version 3 as
747+ * published by the Free Software Foundation.
748+ *
749+ * This program is distributed in the hope that it will be useful,
750+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
751+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
752+ * GNU General Public License for more details.
753+ *
754+ * You should have received a copy of the GNU General Public License
755+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
756+ *
757+ * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
758+ */
759+
760+#include "src/client/no_tls_future-inl.h"
761+
762+#include <gmock/gmock.h>
763+#include <gtest/gtest.h>
764+
765+namespace mcl = mir::client;
766+
767+TEST(NoTLSFuture, and_then_calls_back_immediately_when_promise_is_already_fulfilled)
768+{
769+ mcl::NoTLSPromise<int> promise;
770+ promise.set_value(1);
771+
772+ auto future = promise.get_future();
773+ bool callback_called{false};
774+
775+ future.and_then([&callback_called](int) { callback_called = true;});
776+ EXPECT_TRUE(callback_called);
777+}
778+
779+TEST(NoTLSFuture, and_then_calls_back_with_correct_value_when_promise_is_already_fulfilled)
780+{
781+ constexpr int expected{0xfeed};
782+
783+ mcl::NoTLSPromise<int> promise;
784+ promise.set_value(expected);
785+
786+ auto future = promise.get_future();
787+ future.and_then(
788+ [](int result)
789+ {
790+ using namespace testing;
791+ EXPECT_THAT(result, Eq(expected));
792+ });
793+}
794+
795+TEST(NoTLSFuture, and_then_is_not_called_until_promise_is_fulfilled_by_copy)
796+{
797+ constexpr char const* expected{"And then nothing turned itself inside out"};
798+
799+ mcl::NoTLSPromise<std::string> promise;
800+ auto future = promise.get_future();
801+
802+ bool called{false};
803+ future.and_then(
804+ [&called](std::string&& value)
805+ {
806+ using namespace testing;
807+ called = true;
808+ EXPECT_THAT(value, StrEq(expected));
809+ });
810+
811+ EXPECT_FALSE(called);
812+
813+ std::string expected_copy{expected};
814+
815+ promise.set_value(expected_copy);
816+ EXPECT_TRUE(called);
817+}
818+
819+TEST(NoTLSFuture, and_then_is_not_called_until_promise_is_fulfilled_by_moving)
820+{
821+ constexpr char const* expected{"And then nothing turned itself inside out"};
822+
823+ mcl::NoTLSPromise<std::string> promise;
824+ auto future = promise.get_future();
825+
826+ bool called{false};
827+ future.and_then(
828+ [&called](std::string&& value)
829+ {
830+ using namespace testing;
831+ called = true;
832+ EXPECT_THAT(value, StrEq(expected));
833+ });
834+
835+ EXPECT_FALSE(called);
836+
837+ promise.set_value(std::move(std::string{expected}));
838+ EXPECT_TRUE(called);
839+}
840+
841+TEST(NoTLSFuture, or_else_is_called_when_promise_is_broken)
842+{
843+ auto promise = std::make_unique<mcl::NoTLSPromise<int>>();
844+ auto future = promise->get_future();
845+
846+ bool called{false};
847+ future.or_else([&called](auto) { called = true; });
848+
849+ EXPECT_FALSE(called);
850+ promise.reset();
851+ EXPECT_TRUE(called);
852+}

Subscribers

People subscribed via source and target branches