Merge lp:~vanvugt/mir/wait-result into lp:mir
- wait-result
- Merge into development-branch
Status: | Work in progress |
---|---|
Proposed branch: | lp:~vanvugt/mir/wait-result |
Merge into: | lp:mir |
Diff against target: |
575 lines (+142/-54) 13 files modified
include/client/mir_toolkit/mir_wait.h (+8/-0) src/client/buffer_stream.cpp (+2/-2) src/client/mir_connection.cpp (+8/-6) src/client/mir_prompt_session.cpp (+3/-3) src/client/mir_screencast.cpp (+4/-3) src/client/mir_surface.cpp (+14/-7) src/client/mir_surface.h (+1/-1) src/client/mir_wait_api.cpp (+13/-0) src/client/mir_wait_handle.cpp (+16/-12) src/client/mir_wait_handle.h (+18/-3) src/client/symbols.map (+6/-1) tests/acceptance-tests/test_client_library.cpp (+22/-9) tests/unit-tests/client/test_wait_handle.cpp (+27/-7) |
To merge this branch: | bzr merge lp:~vanvugt/mir/wait-result |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alan Griffiths | Disapprove | ||
Chris Halse Rogers | Disapprove | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Mir development team | Pending | ||
Review via email: mp+252403@code.launchpad.net |
Commit message
Add a new client function: mir_wait_
This is needed for use with client functions that change server state but can't read back results (or simply don't care for anything more than a bool). The need for this is limited right now, but it's coming in the near future, I promise.
Description of the change
PS Jenkins bot (ps-jenkins) wrote : | # |
Chris Halse Rogers (raof) wrote : | # |
On first inspection this looks unnecessary and ugly to me; maybe once I see the use you intend to put it to it'll appear more reasonable?
Daniel van Vugt (vanvugt) wrote : | # |
It's necessary for the reasons outlined in the description. And it avoids the need for adding new callback types, which would be very ugly.
Chris Halse Rogers (raof) wrote : | # |
Yes, but could you give an example of such a function?
The closest I can think of is along the lines of mir_surface_
Daniel van Vugt (vanvugt) wrote : | # |
Oh good point. You can find the answer buried deep in the diff but I will explain:
mir_
mir_
mir_
The last case is the curious one. So I'm saying if result == true then the shell has successfully set exactly what you asked for.
If result == false, then the shell hasn't done what you asked for, but it might have done something different (e.g. chosen a similar state it supports instead of what you asked for).
So maybe the attribute/state result needs a rethink. But mir_surface_
mir_
only returns true if ALL attributes of new_spec could be applied and supported together. Else result==false and nothing is changed. Totally clear and unambiguous in that case. And the behaviour of such functions should be documented clearly in the client headers.
Chris Halse Rogers (raof) wrote : | # |
Ah, right.
My feeling is that this use in particular could be better done on the callback-side by providing an aggregate surface event with all the relevant new state at once. Morph then becomes a set_state-like void call.
This also cleans up server-driven changes somewhat - for example, we can send the client can an event containing both state_fullscreen and the new surface size.
Presumably the server will be sending the updated state anyway? The client can't actually rely on the values matching new_spec even after mir_wait_
My feeling is that any client code that responds to attributes that isn't being directly driven from a surface event callback is fundamentally incorrect, and we shouldn't make it appear as though you can safely do anything else.
Chris Halse Rogers (raof) wrote : | # |
Ah, right.
My feeling is that this use in particular could be better done on the callback-side by providing an aggregate surface event with all the relevant new state at once. Morph then becomes a set_state-like void call.
This also cleans up server-driven changes somewhat - for example, we can send the client can an event containing both state_fullscreen and the new surface size.
Presumably the server will be sending the updated state anyway? The client can't actually rely on the values matching new_spec even after mir_wait_
My feeling is that any client code that responds to attributes that isn't being directly driven from a surface event callback is fundamentally incorrect, and we shouldn't make it appear as though you can safely do anything else.
Chris Halse Rogers (raof) wrote : | # |
Ah, right.
My feeling is that this use in particular could be better done on the callback-side by providing an aggregate surface event with all the relevant new state at once. Morph then becomes a set_state-like void call.
This also cleans up server-driven changes somewhat - for example, we can send the client can an event containing both state_fullscreen and the new surface size.
Presumably the server will be sending the updated state anyway? The client can't actually rely on the values matching new_spec even after mir_wait_
My feeling is that any client code that responds to attributes that isn't being directly driven from a surface event callback is fundamentally incorrect, and we shouldn't make it appear as though you can safely do anything else.
Alan Griffiths (alan-griffiths) wrote : | # |
> Ah, right.
>
> My feeling is that this use in particular could be better done on the
> callback-side by providing an aggregate surface event with all the relevant
> new state at once. Morph then becomes a set_state-like void call.
>
> This also cleans up server-driven changes somewhat - for example, we can send
> the client can an event containing both state_fullscreen and the new surface
> size.
>
> Presumably the server will be sending the updated state anyway? The client
> can't actually rely on the values matching new_spec even after
> mir_wait_
> because the server may have already sent - and the client event thread already
> processed - new attributes.
>
> My feeling is that any client code that responds to attributes that isn't
> being directly driven from a surface event callback is fundamentally
> incorrect, and we shouldn't make it appear as though you can safely do
> anything else.
+1
Daniel van Vugt (vanvugt) wrote : | # |
Well, I don't need this right now.
But in the near future when you can't tell if client functions succeeded or not, we'll need something. Preferably not callbacks, because they're so awkward to use. Maybe events, but still they're much more awkward for simple clients to use than what's presented here.
I really like this approach. In theory it allows us to completely abolish callbacks in future as you could do something like:
wait = mir_create_
...
... other things ...
...
surf = mir_wait_
Chris Halse Rogers (raof) wrote : | # |
You can't completely abolish callbacks without making asynchronously waiting on a result painful, but, yes, I do like the continuation/
I don't see how this is a step in that direction, though, and I think the specific uses in this MP make the API harder to use - in the case of waiting for a construction it's a duplication of foo_is_valid() without the ability to query the error condition, and in the case of waiting for set_foo() it's promoting an incorrect interaction with the client API.
I'm fully supportive of reworking our MirWaitHandle API to be less cumbersome, but that probably merits a ML discussion.
¹: Via ctx = FooBegin/result = FooEnd(ctx) pairs
²: Via GAsyncResult
Daniel van Vugt (vanvugt) wrote : | # |
1. The assertion at the start of your comment is incorrect. As demonstrated by this branch. A single function call is obviously less "painful" than writing callbacks. Admittedly this counts as opinion, but I think you'd probably find 99% of developers agree that a single function call that avoids the need for callbacks is preferable.
2. "in the case of waiting for a construction it's a duplication of foo_is_valid()". Yep foo_is_valid would become unnecessary, which is a good thing. That's one less function that clients need to bother calling.
3. "without the ability to query the error condition". Incorrect. I already documented future plans for that:
376 + // Suggested future uses:
377 + // const char* error;
For example (bug 1431190):
int err = mir_wait_
fprintf(stderr, "Thingy failed due to: %s\n", strerror(err));
or (without fixing bug 1431190):
const char *err = mir_wait_
fprintf(stderr, "Thingy failed due to: %s\n", err);
Another value in the approach here is that it solves the thread safety problem of querying errors we have today (mir_X_
Chris Halse Rogers (raof) wrote : | # |
I'm confused. How does your branch allow asynchronously waiting on a result? mir_wait_for_result blocks until the result is available; doing something with that is either synchronous or requires spinning up a thread to block.
Look, I'm all for replacing MirWaitHandle with MirAsyncResult and all the relevant things. I think that warrants a mailing list discussion, though, and I don't think that this branch gets us closer to MirAsyncResult.
Daniel van Vugt (vanvugt) wrote : | # |
I don't understand what's unclear. It allows asynchronously waiting as the client can choose to do other things before it gets around to calling any mir_wait_for* function. Just as the existing mir_wait_* functions allow clients to be asynchronous.
I totally agree some WaitForMultiple
MirWaitHandle handles[5];
mir_
mir_
So there's no reason why such future functionality should invalidate the work proposed here.
Alan Griffiths (alan-griffiths) wrote : | # |
I don't see anything in the recent discussion that relates to the pointlessness of being able to wait for a boolean result that tells the client nothing useful. In fact, I see little point in being able to wait on these requests to the server.
> The last case is the curious one. So I'm saying if result == true then the shell has successfully
> set exactly what you asked for.
But even if we were to make a guarantee that the corresponding state change event has been processed by the client before the wait completes with true there can be no guarantee that the state hasn't changed again.
So whether the result is true or false the boolean tells nothing about the current state.
Chris Halse Rogers (raof) wrote : | # |
On Mon, Mar 16, 2015 at 6:34 PM, Daniel van Vugt
<email address hidden> wrote:
> I don't understand what's unclear. It allows asynchronously waiting
> as the client can choose to do other things before it gets around to
> calling any mir_wait_for* function. Just as the existing mir_wait_*
> functions allow clients to be asynchronous.
Aaah! This is where my misunderstanding is; we have different
definitions of “asynchronously waiting”.
When I say “asynchronously waiting” I mean some mechanism by which
the client can be *notified* when the thing has happened, and can do
arbitrary processing in the meantime. This basically requires
callbacks, or equivalent language features (async/await for C# & Vala,
for example).
I don't think that “the client can choose to do other things before
blocking its thread” is valuable. It's insufficient for toolkits and
not much better than *_sync for the few simple clients using the client
API directly.
Additionally, I'm with Alan - having mir_surface_set_* return a wait
handle is a mistake that we should correct by removing the wait handle
(and possibly providing a “the server is not going to honour that
request, sorry” mechanism), not by providing a boolean return value
that is impossible to use correctly.
Daniel van Vugt (vanvugt) wrote : | # |
The value of this branch is more clear in the surface morphing prototype:
https:/
It's not "pointless". It is /probably/ required that the client be told if their multi-attribute modification has happened or not. In the latter "not" case there is no event to wait for. So you need this wait-for-result, or something similar like wait-for-
In the mean time, it hurts nobody to not land anything in this area. Just that we'll soon have a situation where some features of the client API can't return errors to the client. Which might turn out to be acceptable...
Chris Halse Rogers (raof) wrote : | # |
On Tue, Mar 17, 2015 at 3:07 PM, Daniel van Vugt
<email address hidden> wrote:
> The value of this branch is more clear in the surface morphing
> prototype:
> https:/
>
> It's not "pointless". It is /probably/ required that the client be
> told if their multi-attribute modification has happened or not. In
> the latter "not" case there is no event to wait for. So you need this
> wait-for-result, or something similar like
> wait-for-
I agree that we need something; I think that the something has to be an
event, because it has to be useful to toolkits for which blocking their
thread is unacceptable.
GTK isn't going to use wait_for_
provide a guarantee that it doesn't block, so we'll either need an
event or to turn our wait-handles into something more continuationy.
Daniel van Vugt (vanvugt) wrote : | # |
Come to think of it, needs more opinions still.
I get what you're saying but you're making the assumption that some generic "event" (in win32 known as a HANDLE) can't be implemented under the name "MirWaitHandle". I think it could be, transparently. I know this because I've done the cross-platform wait handle API exercise before. In fact it's quite easy with the internal structure of MirWaitHandle right now.
On that theme, it would be easy to implement the non-blocking "peek" functionality you're talking about too.
Given our client types are all opaque, it sounds like you're just blocking because you don't like the name "MirWaitHandle". And I kind of agree, but that's a pre-existing issue and it's not a major penalty to live with that name.
Chris Halse Rogers (raof) wrote : | # |
Oh, indeed. MirWaitHandle *could* grow a mir_wait_
that would set the callback to be triggered when the wait handle
becomes ready.
I think that if we were to do that we'd want to significantly rework
the rest of the client API, though - we'd have two separate ways to do
essentially the same thing. Not that I'm opposed to that, mind.
What I'm specifically blocking on here is mir_surface_set_* returning a
wait handle that returns a value. You could resolve this by making all
the setters return a null wait handle, which we already handle as
immediately available. And later, when we feel like breaking client
API, we remove the wait handle return from them completely.
That would make me abstain, pending a ML discussion of what we actually
want WaitHandles to be.
Daniel van Vugt (vanvugt) wrote : | # |
"What I'm specifically blocking on here is mir_surface_set_* returning a wait handle that returns a value."
You'll notice I haven't directly modified any of those functions here. It would be inconsistent to treat those MirWaitHandles differently to any other MirWaitHandle. It should also be remembered that this proposal solves the thread safety problem without resorting to callbacks. Nobody has proposed any other solution that achieves this.
"You could resolve this by making all the setters return a null wait handle"
You're asking me to break existing behaviour that has been in place for two years already. Admittedly the wait handle on setters is low value and will be ignored by most users, but we get it for free. And any corresponding _get call needs to be synchronized with the wait handle from the _set, because all our _get functions are non-blocking (they just return the local cache contents). So we presently have non-blocking _get/_set functions with the ability to synchronize them. Sounds like a good thing to me. You're asking for some of that functionality to be removed and I'm still not sure why. It also feels off topic for this MP.
Chris Halse Rogers (raof) wrote : | # |
On Wed, Mar 18, 2015 at 2:29 PM, Daniel van Vugt
<email address hidden> wrote:
> "What I'm specifically blocking on here is mir_surface_set_*
> returning a wait handle that returns a value."
>
> You'll notice I haven't directly modified any of those functions
> here. It would be inconsistent to treat those MirWaitHandles
> differently to any other MirWaitHandle. It should also be remembered
> that this proposal solves the thread safety problem without resorting
> to callbacks. Nobody has proposed any other solution that achieves
> this.
I don't dispute that nobody has proposed a solution that doesn't have
callbacks. I equally don't think that this *is a solution*, and I think
that the reason nobody has proposed a solution that doesn't have
callbacks is because there *is* no solution that doesn't have callbacks
or language features equivalent to implicit callbacks.
This is a solution to a slightly different problem, but I don't think
it's a problem that is useful to solve.
>
> "You could resolve this by making all the setters return a null wait
> handle"
>
> You're asking me to break existing behaviour that has been in place
> for two years already.
The behaviour changes slightly, but it doesn't change correctness;
there is currently no correct way to use mir_wait_for on a wait handle
returned by one of the *_set functions.
> Admittedly the wait handle on setters is low value and will be
> ignored by most users, but we get it for free. And any corresponding
> _get call needs to be synchronized with the wait handle from the
> _set, because all our _get functions are non-blocking (they just
> return the local cache contents). So we presently have non-blocking
> _get/_set functions with the ability to synchronize them.
But we *don't* have an ability to synchronise them - if you call
mir_surface_
then it is *not* guaranteed that an immediate call to
mir_surface_
> Sounds like a good thing to me. You're asking for some of that
> functionality to be removed and I'm still not sure why. It also feels
> off topic for this MP.
Because the functionality doesn't actually exist, but looks like it
does. This part of the API rates a -10 on Rusty's API scale - it's an
API that is *not possible* to use correctly.
I think it's not off-topic for this MP because this MP extends the
functionality that doesn't exist in a way that might tempt people to
use it and write incorrect programs.
Daniel van Vugt (vanvugt) wrote : | # |
These statements are either false, or describe a bug I'm not yet aware of:
"The behaviour changes slightly, but it doesn't change correctness;
there is currently no correct way to use mir_wait_for on a wait handle
returned by one of the *_set functions.
But we *don't* have an ability to synchronise them - if you call
mir_surface_
then it is *not* guaranteed that an immediate call to
mir_surface_
When I implemented the attribute system in 2013, it did work. Correct usage looks like:
w = mir_surface_
mir_wait_for(w);
y = mir_surface_
assert(y == Y);
If this doesn't work any more then we have a bug. But I suspect it does still work because I revisited the test cases for it as part of this proposal.
Daniel van Vugt (vanvugt) wrote : | # |
Ah, what you might be talking about is multiple threads doing the same thing. Indeed in that case calling _get will be racy. There's no guarantee that w represents the request for Y in that case.
So yeah, we need something better that correctly matches result instance to request instance.
Chris Halse Rogers (raof) wrote : | # |
On Thu, Mar 19, 2015 at 1:37 PM, Daniel van Vugt
<email address hidden> wrote:
> Ah, what you might be talking about is multiple threads doing the
> same thing. Indeed in that case calling _get will be racy. There's no
> guarantee that w represents the request for Y in that case.
Not even multiple threads doing the same thing; the server can change
the surface state without the client requesting it (obvious example is
drag-title-
the thread executing mir_wait_for continues execution.
Surface states are event driven; attempts to make them appear otherwise
make the client API harder to use.
Unmerged revisions
- 2388. By Daniel van Vugt
-
Fix style.
- 2387. By Daniel van Vugt
-
More acceptance tests
- 2386. By Daniel van Vugt
-
And some acceptance-testing for the new client function.
- 2385. By Daniel van Vugt
-
Use more consistent and less ambiguous mir_wait_for_ naming.
- 2384. By Daniel van Vugt
-
Add unit test for MirResult
- 2383. By Daniel van Vugt
-
Treat attribute settings with values different to what was requested
as a failure (even if the attribute successfully changed value to something
else). - 2382. By Daniel van Vugt
-
Merge latest trunk
- 2381. By Daniel van Vugt
-
Wire up result_received
- 2380. By Daniel van Vugt
-
Prototype client API.
- 2379. By Daniel van Vugt
-
Remove fields not yet in use
Preview Diff
1 | === modified file 'include/client/mir_toolkit/mir_wait.h' |
2 | --- include/client/mir_toolkit/mir_wait.h 2014-03-31 14:36:08 +0000 |
3 | +++ include/client/mir_toolkit/mir_wait.h 2015-03-10 08:02:21 +0000 |
4 | @@ -46,6 +46,14 @@ |
5 | */ |
6 | void mir_wait_for_one(MirWaitHandle *wait_handle); |
7 | |
8 | +/** |
9 | + * Wait on the supplied handle until one instance of the associated request |
10 | + * has completed. This is the same as mir_wait_for_one with the extra |
11 | + * feature of returning a result. |
12 | + * \param [in] wait_handle Handle returned by an asynchronous request |
13 | + * \returns Whether the request succeeded. |
14 | + */ |
15 | +bool mir_wait_for_result(MirWaitHandle* wait_handle); |
16 | |
17 | #ifdef __cplusplus |
18 | } |
19 | |
20 | === modified file 'src/client/buffer_stream.cpp' |
21 | --- src/client/buffer_stream.cpp 2015-02-18 05:27:28 +0000 |
22 | +++ src/client/buffer_stream.cpp 2015-03-10 08:02:21 +0000 |
23 | @@ -226,7 +226,7 @@ |
24 | |
25 | done(); |
26 | |
27 | - next_buffer_wait_handle.result_received(); |
28 | + next_buffer_wait_handle.result_received(true); |
29 | } |
30 | |
31 | /* mcl::EGLNativeSurface interface for EGLNativeWindow integration */ |
32 | @@ -250,7 +250,7 @@ |
33 | void mcl::BufferStream::on_configured() |
34 | { |
35 | std::unique_lock<decltype(mutex)> lock(mutex); |
36 | - configure_wait_handle.result_received(); |
37 | + configure_wait_handle.result_received(true); |
38 | } |
39 | |
40 | // TODO: It's a little strange that we use SurfaceAttrib here for the swap interval |
41 | |
42 | === modified file 'src/client/mir_connection.cpp' |
43 | --- src/client/mir_connection.cpp 2015-02-18 05:27:28 +0000 |
44 | +++ src/client/mir_connection.cpp 2015-03-10 08:02:21 +0000 |
45 | @@ -191,7 +191,7 @@ |
46 | data.surface->handle_event(*unfocus); |
47 | } |
48 | data.callback(data.surface, data.context); |
49 | - data.handle->result_received(); |
50 | + data.handle->result_received(true); |
51 | delete data.surface; |
52 | } |
53 | |
54 | @@ -282,7 +282,7 @@ |
55 | } |
56 | |
57 | if (safe_to_callback) callback(this, context); |
58 | - connect_wait_handle.result_received(); |
59 | + connect_wait_handle.result_received(!connect_result.has_error()); |
60 | } |
61 | |
62 | MirWaitHandle* MirConnection::connect( |
63 | @@ -318,7 +318,7 @@ |
64 | |
65 | // Ensure no racy lifecycle notifications can happen after disconnect completes |
66 | lifecycle_control->set_lifecycle_event_handler([](MirLifecycleState){}); |
67 | - disconnect_wait_handle.result_received(); |
68 | + disconnect_wait_handle.result_received(true); |
69 | } |
70 | |
71 | MirWaitHandle* MirConnection::disconnect() |
72 | @@ -353,7 +353,8 @@ |
73 | |
74 | callback(this, reply, context); |
75 | |
76 | - platform_operation_wait_handle.result_received(); |
77 | + platform_operation_wait_handle.result_received( |
78 | + !platform_operation_reply.has_error()); |
79 | } |
80 | |
81 | MirWaitHandle* MirConnection::platform_operation( |
82 | @@ -529,10 +530,11 @@ |
83 | |
84 | set_error_message(display_configuration_response.error()); |
85 | |
86 | - if (!display_configuration_response.has_error()) |
87 | + bool ok = !display_configuration_response.has_error(); |
88 | + if (ok) |
89 | display_configuration->set_configuration(display_configuration_response); |
90 | |
91 | - return configure_display_wait_handle.result_received(); |
92 | + return configure_display_wait_handle.result_received(ok); |
93 | } |
94 | |
95 | MirWaitHandle* MirConnection::configure_display(MirDisplayConfiguration* config) |
96 | |
97 | === modified file 'src/client/mir_prompt_session.cpp' |
98 | --- src/client/mir_prompt_session.cpp 2015-02-18 05:27:28 +0000 |
99 | +++ src/client/mir_prompt_session.cpp 2015-03-10 08:02:21 +0000 |
100 | @@ -114,7 +114,7 @@ |
101 | } |
102 | |
103 | callback(this, context); |
104 | - start_wait_handle.result_received(); |
105 | + start_wait_handle.result_received(!session.has_error()); |
106 | } |
107 | |
108 | void MirPromptSession::done_stop(mir_prompt_session_callback callback, void* context) |
109 | @@ -122,7 +122,7 @@ |
110 | set_state(mir_prompt_session_state_stopped); |
111 | |
112 | callback(this, context); |
113 | - stop_wait_handle.result_received(); |
114 | + stop_wait_handle.result_received(true); |
115 | } |
116 | |
117 | char const* MirPromptSession::get_error_message() |
118 | @@ -168,5 +168,5 @@ |
119 | fds.push_back(socket_fd_response.fd(i)); |
120 | |
121 | callback(this, size, fds.data(), context); |
122 | - fds_for_prompt_providers_wait_handle.result_received(); |
123 | + fds_for_prompt_providers_wait_handle.result_received(true); |
124 | } |
125 | |
126 | === modified file 'src/client/mir_screencast.cpp' |
127 | --- src/client/mir_screencast.cpp 2015-02-18 05:27:28 +0000 |
128 | +++ src/client/mir_screencast.cpp 2015-03-10 08:02:21 +0000 |
129 | @@ -104,21 +104,22 @@ |
130 | void MirScreencast::screencast_created( |
131 | mir_screencast_callback callback, void* context) |
132 | { |
133 | - if (!protobuf_screencast.has_error()) |
134 | + bool ok = !protobuf_screencast.has_error(); |
135 | + if (ok) |
136 | { |
137 | buffer_stream = buffer_stream_factory->make_consumer_stream(server, |
138 | protobuf_screencast.buffer_stream(), "MirScreencast"); |
139 | } |
140 | |
141 | callback(this, context); |
142 | - create_screencast_wait_handle.result_received(); |
143 | + create_screencast_wait_handle.result_received(ok); |
144 | } |
145 | |
146 | void MirScreencast::released( |
147 | mir_screencast_callback callback, void* context) |
148 | { |
149 | callback(this, context); |
150 | - release_wait_handle.result_received(); |
151 | + release_wait_handle.result_received(true); |
152 | } |
153 | |
154 | mir::client::ClientBufferStream* MirScreencast::get_buffer_stream() |
155 | |
156 | === modified file 'src/client/mir_surface.cpp' |
157 | --- src/client/mir_surface.cpp 2015-03-05 05:47:28 +0000 |
158 | +++ src/client/mir_surface.cpp 2015-03-10 08:02:21 +0000 |
159 | @@ -206,6 +206,7 @@ |
160 | |
161 | void MirSurface::created(mir_surface_callback callback, void * context) |
162 | { |
163 | + bool ok = true; |
164 | { |
165 | std::lock_guard<decltype(mutex)> lock(mutex); |
166 | if (!surface.has_id()) |
167 | @@ -214,7 +215,7 @@ |
168 | surface.set_error("Error processing surface create response, no ID (disconnected?)"); |
169 | |
170 | callback(this, context); |
171 | - create_wait_handle.result_received(); |
172 | + create_wait_handle.result_received(false); |
173 | return; |
174 | } |
175 | } |
176 | @@ -237,12 +238,13 @@ |
177 | } |
178 | catch (std::exception const& error) |
179 | { |
180 | + ok = false; |
181 | surface.set_error(std::string{"Error processing Surface creating response:"} + |
182 | boost::diagnostic_information(error)); |
183 | } |
184 | |
185 | callback(this, context); |
186 | - create_wait_handle.result_received(); |
187 | + create_wait_handle.result_received(ok); |
188 | } |
189 | |
190 | MirWaitHandle* MirSurface::release_surface( |
191 | @@ -313,7 +315,9 @@ |
192 | |
193 | configure_wait_handle.expect_result(); |
194 | server->configure_surface(0, &setting, &configure_result, |
195 | - google::protobuf::NewCallback(this, &MirSurface::on_configured)); |
196 | + google::protobuf::NewCallback(this, |
197 | + &MirSurface::on_configured, |
198 | + value)); |
199 | |
200 | return &configure_wait_handle; |
201 | } |
202 | @@ -322,7 +326,7 @@ |
203 | { |
204 | void signal_response_received(MirWaitHandle* handle) |
205 | { |
206 | - handle->result_received(); |
207 | + handle->result_received(true); |
208 | } |
209 | } |
210 | |
211 | @@ -361,7 +365,7 @@ |
212 | return !response.has_error(); |
213 | } |
214 | |
215 | -void MirSurface::on_configured() |
216 | +void MirSurface::on_configured(int requested_value) |
217 | { |
218 | std::lock_guard<decltype(mutex)> lock(mutex); |
219 | |
220 | @@ -370,6 +374,8 @@ |
221 | configure_result.has_attrib()) |
222 | { |
223 | int a = configure_result.attrib(); |
224 | + bool ok = !configure_result.has_error() && |
225 | + configure_result.ivalue() == requested_value; |
226 | |
227 | switch (a) |
228 | { |
229 | @@ -384,17 +390,18 @@ |
230 | assert(configure_result.has_error()); |
231 | break; |
232 | default: |
233 | + ok = false; |
234 | assert(false); |
235 | break; |
236 | } |
237 | |
238 | - configure_wait_handle.result_received(); |
239 | + configure_wait_handle.result_received(ok); |
240 | } |
241 | } |
242 | |
243 | void MirSurface::on_cursor_configured() |
244 | { |
245 | - configure_cursor_wait_handle.result_received(); |
246 | + configure_cursor_wait_handle.result_received(true); |
247 | } |
248 | |
249 | |
250 | |
251 | === modified file 'src/client/mir_surface.h' |
252 | --- src/client/mir_surface.h 2015-03-05 05:47:28 +0000 |
253 | +++ src/client/mir_surface.h 2015-03-10 08:02:21 +0000 |
254 | @@ -141,7 +141,7 @@ |
255 | private: |
256 | mutable std::mutex mutex; // Protects all members of *this |
257 | |
258 | - void on_configured(); |
259 | + void on_configured(int requested_value); |
260 | void on_cursor_configured(); |
261 | void created(mir_surface_callback callback, void* context); |
262 | MirPixelFormat convert_ipc_pf_to_geometry(google::protobuf::int32 pf) const; |
263 | |
264 | === modified file 'src/client/mir_wait_api.cpp' |
265 | --- src/client/mir_wait_api.cpp 2014-03-31 14:36:08 +0000 |
266 | +++ src/client/mir_wait_api.cpp 2015-03-10 08:02:21 +0000 |
267 | @@ -30,3 +30,16 @@ |
268 | if (wait_handle) |
269 | wait_handle->wait_for_one(); |
270 | } |
271 | + |
272 | +bool mir_wait_for_result(MirWaitHandle* wait_handle) |
273 | +{ |
274 | + if (!wait_handle) |
275 | + return false; |
276 | + |
277 | + /* |
278 | + * As a MirWaitHandle may get recycled immediately, the structure cannot |
279 | + * store a result beyond the lifetime of the mir_wait_for_ call. So any |
280 | + * result needs to be copied out immediately. Even if it's ignored. |
281 | + */ |
282 | + return wait_handle->wait_for_one().success; |
283 | +} |
284 | |
285 | === modified file 'src/client/mir_wait_handle.cpp' |
286 | --- src/client/mir_wait_handle.cpp 2015-01-21 07:34:50 +0000 |
287 | +++ src/client/mir_wait_handle.cpp 2015-03-10 08:02:21 +0000 |
288 | @@ -19,11 +19,14 @@ |
289 | |
290 | #include "mir_wait_handle.h" |
291 | |
292 | +MirResult::MirResult(bool ok) : success{ok} |
293 | +{ |
294 | +} |
295 | + |
296 | MirWaitHandle::MirWaitHandle() : |
297 | guard(), |
298 | wait_condition(), |
299 | - expecting(0), |
300 | - received(0) |
301 | + expecting(0) |
302 | { |
303 | } |
304 | |
305 | @@ -38,11 +41,11 @@ |
306 | expecting++; |
307 | } |
308 | |
309 | -void MirWaitHandle::result_received() |
310 | +void MirWaitHandle::result_received(MirResult const& r) |
311 | { |
312 | std::lock_guard<std::mutex> lock(guard); |
313 | |
314 | - received++; |
315 | + received.push_back(r); |
316 | wait_condition.notify_all(); |
317 | } |
318 | |
319 | @@ -50,9 +53,9 @@ |
320 | { |
321 | std::unique_lock<std::mutex> lock(guard); |
322 | |
323 | - wait_condition.wait(lock, [&]{ return received == expecting; }); |
324 | + wait_condition.wait(lock, [&]{ return received.size() == expecting; }); |
325 | |
326 | - received = 0; |
327 | + received.clear(); |
328 | expecting = 0; |
329 | } |
330 | |
331 | @@ -60,17 +63,18 @@ |
332 | { |
333 | std::unique_lock<std::mutex> lock(guard); |
334 | |
335 | - wait_condition.wait_for(lock, limit, [&]{ return received == expecting; }); |
336 | + wait_condition.wait_for(lock, limit, [&]{ return received.size() == expecting; }); |
337 | } |
338 | |
339 | - |
340 | -void MirWaitHandle::wait_for_one() // wait for any single result |
341 | +MirResult MirWaitHandle::wait_for_one() // wait for any single result |
342 | { |
343 | std::unique_lock<std::mutex> lock(guard); |
344 | |
345 | - wait_condition.wait(lock, [&]{ return received != 0; }); |
346 | + wait_condition.wait(lock, [&]{ return !received.empty(); }); |
347 | |
348 | - --received; |
349 | + auto head = received.front(); |
350 | + received.pop_front(); |
351 | --expecting; |
352 | + |
353 | + return head; |
354 | } |
355 | - |
356 | |
357 | === modified file 'src/client/mir_wait_handle.h' |
358 | --- src/client/mir_wait_handle.h 2013-10-03 03:44:08 +0000 |
359 | +++ src/client/mir_wait_handle.h 2015-03-10 08:02:21 +0000 |
360 | @@ -23,11 +23,24 @@ |
361 | #include <chrono> |
362 | #include <condition_variable> |
363 | #include <mutex> |
364 | +#include <deque> |
365 | |
366 | /** |
367 | * \addtogroup mir_toolkit |
368 | * @{ |
369 | */ |
370 | + |
371 | +struct MirResult |
372 | +{ |
373 | + MirResult(bool ok); |
374 | + |
375 | + bool success; |
376 | + // Suggested future uses: |
377 | + // const char* error; |
378 | + // struct MirSurface* surface; |
379 | + // struct MirConnection* connection; |
380 | +}; |
381 | + |
382 | struct MirWaitHandle |
383 | { |
384 | public: |
385 | @@ -36,16 +49,18 @@ |
386 | |
387 | void expect_result(); |
388 | void result_received(); |
389 | + void result_received(MirResult const&); |
390 | void wait_for_all(); |
391 | - void wait_for_one(); |
392 | + MirResult wait_for_one(); |
393 | void wait_for_pending(std::chrono::milliseconds limit); |
394 | |
395 | private: |
396 | std::mutex guard; |
397 | std::condition_variable wait_condition; |
398 | |
399 | - int expecting; |
400 | - int received; |
401 | + typedef std::deque<MirResult> Queue; |
402 | + Queue::size_type expecting; |
403 | + Queue received; |
404 | }; |
405 | /**@}*/ |
406 | |
407 | |
408 | === modified file 'src/client/symbols.map' |
409 | --- src/client/symbols.map 2015-02-18 05:27:28 +0000 |
410 | +++ src/client/symbols.map 2015-03-10 08:02:21 +0000 |
411 | @@ -37,10 +37,15 @@ |
412 | mir_surface_spec_set_preferred_orientation; |
413 | } MIR_CLIENT_8.1; |
414 | |
415 | -MIR_CLIENT_8.3 { |
416 | +MIR_CLIENT_8.3 { # Mir 0.12 |
417 | global: |
418 | mir_connection_create_spec_for_menu; |
419 | mir_connection_create_spec_for_tooltip; |
420 | mir_connection_create_spec_for_dialog; |
421 | mir_connection_platform_operation; |
422 | } MIR_CLIENT_8.2; |
423 | + |
424 | +MIR_CLIENT_8.4 { # Mir 0.13 |
425 | + global: |
426 | + mir_wait_for_result; |
427 | +} MIR_CLIENT_8.3; |
428 | |
429 | === modified file 'tests/acceptance-tests/test_client_library.cpp' |
430 | --- tests/acceptance-tests/test_client_library.cpp 2015-02-18 05:27:28 +0000 |
431 | +++ tests/acceptance-tests/test_client_library.cpp 2015-03-10 08:02:21 +0000 |
432 | @@ -152,7 +152,7 @@ |
433 | { |
434 | MirWaitHandle* wh = mir_connect(new_connection().c_str(), __PRETTY_FUNCTION__, connection_callback, this); |
435 | EXPECT_THAT(wh, NotNull()); |
436 | - mir_wait_for(wh); |
437 | + EXPECT_TRUE(mir_wait_for_result(wh)); |
438 | |
439 | ASSERT_THAT(connection, NotNull()); |
440 | EXPECT_TRUE(mir_connection_is_valid(connection)); |
441 | @@ -161,6 +161,19 @@ |
442 | mir_connection_release(connection); |
443 | } |
444 | |
445 | +TEST_F(ClientLibrary, client_library_cant_connect) |
446 | +{ |
447 | + MirWaitHandle* wh = mir_connect("/dev/null", __PRETTY_FUNCTION__, connection_callback, this); |
448 | + EXPECT_THAT(wh, IsNull()); |
449 | + EXPECT_FALSE(mir_wait_for_result(wh)); |
450 | + |
451 | + ASSERT_THAT(connection, NotNull()); |
452 | + EXPECT_FALSE(mir_connection_is_valid(connection)); |
453 | + EXPECT_THAT(mir_connection_get_error_message(connection), StrNe("")); |
454 | + |
455 | + mir_connection_release(connection); |
456 | +} |
457 | + |
458 | TEST_F(ClientLibrary, synchronous_connection) |
459 | { |
460 | connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); |
461 | @@ -267,16 +280,16 @@ |
462 | |
463 | EXPECT_THAT(mir_surface_get_state(surface), Eq(mir_surface_state_restored)); |
464 | |
465 | - mir_wait_for(mir_surface_set_state(surface, mir_surface_state_fullscreen)); |
466 | - EXPECT_THAT(mir_surface_get_state(surface), Eq(mir_surface_state_fullscreen)); |
467 | - |
468 | - mir_wait_for(mir_surface_set_state(surface, static_cast<MirSurfaceState>(999))); |
469 | - EXPECT_THAT(mir_surface_get_state(surface), Eq(mir_surface_state_fullscreen)); |
470 | - |
471 | - mir_wait_for(mir_surface_set_state(surface, mir_surface_state_minimized)); |
472 | + EXPECT_TRUE(mir_wait_for_result(mir_surface_set_state(surface, mir_surface_state_fullscreen))); |
473 | + EXPECT_THAT(mir_surface_get_state(surface), Eq(mir_surface_state_fullscreen)); |
474 | + |
475 | + EXPECT_FALSE(mir_wait_for_result(mir_surface_set_state(surface, static_cast<MirSurfaceState>(999)))); |
476 | + EXPECT_THAT(mir_surface_get_state(surface), Eq(mir_surface_state_fullscreen)); |
477 | + |
478 | + EXPECT_TRUE(mir_wait_for_result(mir_surface_set_state(surface, mir_surface_state_minimized))); |
479 | EXPECT_THAT(mir_surface_get_state(surface), Eq(mir_surface_state_minimized)); |
480 | |
481 | - mir_wait_for(mir_surface_set_state(surface, static_cast<MirSurfaceState>(888))); |
482 | + EXPECT_FALSE(mir_wait_for_result(mir_surface_set_state(surface, static_cast<MirSurfaceState>(888)))); |
483 | EXPECT_THAT(mir_surface_get_state(surface), Eq(mir_surface_state_minimized)); |
484 | |
485 | // Stress-test synchronization logic with some flooding |
486 | |
487 | === modified file 'tests/unit-tests/client/test_wait_handle.cpp' |
488 | --- tests/unit-tests/client/test_wait_handle.cpp 2015-01-21 07:34:50 +0000 |
489 | +++ tests/unit-tests/client/test_wait_handle.cpp 2015-03-10 08:02:21 +0000 |
490 | @@ -33,7 +33,7 @@ |
491 | |
492 | for (int i = 0; i < arbitrary_number_of_events; i++) |
493 | { |
494 | - w.result_received(); |
495 | + w.result_received(true); |
496 | } |
497 | |
498 | w.wait_for_all(); |
499 | @@ -51,7 +51,7 @@ |
500 | w.expect_result(); |
501 | |
502 | for (int j = 1; j <= i; j++) |
503 | - w.result_received(); |
504 | + w.result_received(true); |
505 | |
506 | w.wait_for_all(); |
507 | } |
508 | @@ -70,7 +70,7 @@ |
509 | in->expect_result(); |
510 | in->wait_for_all(); |
511 | symmetric_result = i; |
512 | - out->result_received(); |
513 | + out->result_received(true); |
514 | } |
515 | } |
516 | } |
517 | @@ -84,7 +84,7 @@ |
518 | for (int i = 1; i <= max; i++) |
519 | { |
520 | out.expect_result(); |
521 | - in.result_received(); |
522 | + in.result_received(true); |
523 | out.wait_for_all(); |
524 | ASSERT_EQ(symmetric_result, i); |
525 | } |
526 | @@ -103,7 +103,7 @@ |
527 | in->wait_for_all(); |
528 | asymmetric_result = i; |
529 | for (int j = 1; j <= i; j++) |
530 | - out->result_received(); |
531 | + out->result_received(true); |
532 | } |
533 | } |
534 | } |
535 | @@ -116,7 +116,7 @@ |
536 | |
537 | for (int i = 1; i <= max; i++) |
538 | { |
539 | - in.result_received(); |
540 | + in.result_received(true); |
541 | for (int j = 1; j <= i; j++) |
542 | out.expect_result(); |
543 | out.wait_for_all(); |
544 | @@ -153,10 +153,30 @@ |
545 | std::thread c(crowded_thread, &w, max); |
546 | |
547 | for (int n = 0; n < max * 3; n++) |
548 | - w.result_received(); |
549 | + w.result_received(true); |
550 | |
551 | a.join(); |
552 | b.join(); |
553 | c.join(); |
554 | } |
555 | |
556 | +TEST(WaitHandle, returns_result_value) |
557 | +{ |
558 | + MirWaitHandle w; |
559 | + |
560 | + w.result_received(true); |
561 | + EXPECT_TRUE(w.wait_for_one().success); |
562 | + |
563 | + w.result_received(false); |
564 | + EXPECT_FALSE(w.wait_for_one().success); |
565 | + |
566 | + for (int i = 0; i < 10; ++i) |
567 | + w.result_received(true); |
568 | + for (int i = 0; i < 10; ++i) |
569 | + w.result_received(false); |
570 | + |
571 | + for (int i = 0; i < 10; ++i) |
572 | + EXPECT_TRUE(w.wait_for_one().success); |
573 | + for (int i = 0; i < 10; ++i) |
574 | + EXPECT_FALSE(w.wait_for_one().success); |
575 | +} |
PASSED: Continuous integration, rev:2388 jenkins. qa.ubuntu. com/job/ mir-ci/ 3176/ jenkins. qa.ubuntu. com/job/ mir-android- vivid-i386- build/1579 jenkins. qa.ubuntu. com/job/ mir-clang- vivid-amd64- build/1578 jenkins. qa.ubuntu. com/job/ mir-mediumtests -vivid- touch/1533 jenkins. qa.ubuntu. com/job/ mir-vivid- amd64-ci/ 1173 jenkins. qa.ubuntu. com/job/ mir-vivid- amd64-ci/ 1173/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mir-mediumtests -builder- vivid-armhf/ 1533 jenkins. qa.ubuntu. com/job/ mir-mediumtests -builder- vivid-armhf/ 1533/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mir-mediumtests -runner- mako/4549 s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 18691
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/mir- ci/3176/ rebuild
http://