Merge lp:~andreas-pokorny/mir/use-lifecycleevent-in-mirserver into lp:mir
- use-lifecycleevent-in-mirserver
- Merge into development-branch
Status: | Rejected |
---|---|
Rejected by: | Andreas Pokorny |
Proposed branch: | lp:~andreas-pokorny/mir/use-lifecycleevent-in-mirserver |
Merge into: | lp:mir |
Diff against target: |
580 lines (+289/-32) 11 files modified
src/include/server/mir/default_server_status_listener.h (+12/-12) src/server/CMakeLists.txt (+1/-0) src/server/default_server_configuration.cpp (+2/-2) src/server/default_server_status_listener.cpp (+50/-0) src/server/graphics/nested/display.cpp (+70/-7) src/server/graphics/nested/display.h (+15/-0) src/server/graphics/nested/host_connection.h (+1/-0) src/server/graphics/nested/mir_client_host_connection.cpp (+27/-8) src/server/graphics/nested/mir_client_host_connection.h (+3/-0) tests/include/mir/test/doubles/stub_host_connection.h (+4/-0) tests/unit-tests/graphics/nested/test_nested_display.cpp (+104/-3) |
To merge this branch: | bzr merge lp:~andreas-pokorny/mir/use-lifecycleevent-in-mirserver |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mir CI Bot | continuous-integration | Approve | |
Alan Griffiths | Approve | ||
Chris Halse Rogers | Needs Fixing | ||
Alberto Aguirre (community) | Needs Information | ||
Brandon Schaefer (community) | Approve | ||
Cemil Azizoglu (community) | Approve | ||
Review via email: mp+292375@code.launchpad.net |
Commit message
Use the builtin lifecycle event as an indication of lifecycle changes of the server
With this change all sessions will receive lifecycle events when the server pauses or resumes. The nested display will use this event to feed the registered pause/resume handlers. Thus a nested server will pause when the hosting server pauses due to a vt switch.
This is a preparation step to use resume events and focus changes as a trigger for informing clients about missed key state changes.
Description of the change
This is a first step towards the sticky-alt-key problem that may happen after vt switches.
With this change the nested session is aware that the system compositor is paused, and gets paused to.
I also spiked using focus events as an indication, but that leads to several problematic scenarios during display configuration changes.
The proposed server status handling in DefaultServerSt
Mir CI Bot (mir-ci-bot) wrote : | # |
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:3468
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Alan Griffiths (alan-griffiths) wrote : | # |
-ctest -S "$TEST_
+ctest -S "$TEST_
Doesn't belong in this MP.
Alan Griffiths (alan-griffiths) wrote : | # |
"With this change the active session will receive lifecycle events when the server pauses or resumes. The nested display will use this event to feed the registered pause/resume handlers. Thus a nested server will pause when the hosting server pauses due to a vt switch."
Don't all sessions need the lifecycle event?
Andreas Pokorny (andreas-pokorny) wrote : | # |
> "With this change the active session will receive lifecycle events when the
> server pauses or resumes. The nested display will use this event to feed the
> registered pause/resume handlers. Thus a nested server will pause when the
> hosting server pauses due to a vt switch."
>
> Don't all sessions need the lifecycle event?
Good point..
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:3469
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Alan Griffiths (alan-griffiths) wrote : | # |
Nit:
+ enum DisplayState
+ {
+ running,
+ paused,
+ };
Our guidelines say to prefer scoped enums (i.e. "enum class")
Brandon Schaefer (brandontschaefer) wrote : | # |
lgtm
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Daniel van Vugt (vanvugt) wrote : | # |
20:55:57 /��BUILDDIR�
20:55:57 write(lifecycle
20:55:57 ^~~~~ ~~~~~~~
Chris Halse Rogers (raof) wrote : | # |
174 + uint64_t dummy = 1;
175 + write(lifecycle
This can be slightly simplified as eventfd_
But you should probably be checking for error and throwing if so; otherwise we'll get unexpected strange behaviour later on in the extremely unlikely event that eventfd_write fails.
Needs-fixing based on that.
Now that I think of it, I'm somewhat concerned about us overloading the meaning of mir_lifecycle_
I know that using focus events for this ran into the problem of spurious focus changes during display configuration changes; would fixing those spurious focus changes be easy enough, and allow us to just use update-
Andreas Pokorny (andreas-pokorny) wrote : | # |
> Now that I think of it, I'm somewhat concerned about us overloading the
> meaning of mir_lifecycle_
> For applications these mean “serialise your state to disc, because I'm about
> to kill you” and “restore your state from disc, because I've started you up
> again after killing you” respectively.
>
> I know that using focus events for this ran into the problem of spurious focus
> changes during display configuration changes; would fixing those spurious
> focus changes be easy enough, and allow us to just use update-keystate-on-
> focus-change everywhere?
I will look into the second part. On the lifecycle event I thought unity8 also does that, and might not kill the application between the two events, but I might be wrong here. I had a similar concern of overloading semantics when using focus lost as an indication that the server should pause.
Chris Halse Rogers (raof) wrote : | # |
Yeah, it'd be really nice if we actually had a separate
system-compositor protocol. unity-system-
server, and Unity8 isn't a normal Mir client, and it's been a continual
source of friction.
Chris Halse Rogers (raof) wrote : | # |
Actually, back up. Why are we pausing the nested server on focus-lost?
Alberto Aguirre (albaguirre) wrote : | # |
Why overload the lifecycle semantics? Would it be too redudant to add: "mir_lifecycle_
Chris Halse Rogers (raof) wrote : | # |
> Why overload the lifecycle semantics? Would it be too redudant to add:
> "mir_lifecycle_
What do these mean to non-system-
- 3470. By Andreas Pokorny
-
merge current lp:mir
- 3471. By Andreas Pokorny
-
throw on write
Alan Griffiths (alan-griffiths) wrote : | # |
> Why overload the lifecycle semantics? Would it be too redudant to add:
> "mir_lifecycle_
They mean the server is pausing/resuming the client.
I don't think they should be used here.
Andreas Pokorny (andreas-pokorny) wrote : | # |
> > Why overload the lifecycle semantics? Would it be too redudant to add:
> > "mir_lifecycle_
>
> What do these mean to non-system-
The same actually.. the change makes the server state transitive to the nested server. And the default nested server would decide that when paused clients are paused too. When resumed all clients are resumed to their previous state too.
I think that part of the behavior would be a shell decision. I.e. a nested servers could still also decide to send lifecycle stop to clients when paused.. and use the resume flank to restart the clients.
Andreas Pokorny (andreas-pokorny) wrote : | # |
> > Why overload the lifecycle semantics? Would it be too redudant to add:
> > "mir_lifecycle_
>
> They mean the server is pausing/resuming the client.
>
> I don't think they should be used here.
Whats the need fixing?
Andreas Pokorny (andreas-pokorny) wrote : | # |
> Actually, back up. Why are we pausing the nested server on focus-lost?
Because we can, but we do not have to. This has the advantage that it would stop repeat handling and application not-responding detection in a system state in which we would be happy if applications are not responding.
We could install a specific handler for either surface attribute focus, or life cycle events. And then not pause the server.
I just believe it makes sense to use that information and pause processing.
Not only for power saving reasons but also to make the nested server less different from a system compositor.
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:3471
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 3472. By Andreas Pokorny
-
enum class instead of enum
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:3472
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Chris Halse Rogers (raof) wrote : | # |
> > > Why overload the lifecycle semantics? Would it be too redudant to add:
> > > "mir_lifecycle_
> >
> > What do these mean to non-system-
>
> The same actually.. the change makes the server state transitive to the nested
> server. And the default nested server would decide that when paused clients
> are paused too. When resumed all clients are resumed to their previous state
> too.
>
> I think that part of the behavior would be a shell decision. I.e. a nested
> servers could still also decide to send lifecycle stop to clients when
> paused.. and use the resume flank to restart the clients.
But we already have pause/resume events for clients, but with different semantics. Do we now have two different levels of “paused” - paused-
It seems like forwarding on the paused lifecycle events is shell-specific, and should not be in the DefaultServerLi
I'm not too adverse to USC sending _will_suspend and _resumed events and nested acting on them, but I don't think the former bit should be here?
Alan Griffiths (alan-griffiths) wrote : | # |
+ if (lifecycle_
+ lifecycle_
It would be better to initialize lifecycle_callback with a null functor than rely on checking at the call site.
Alan Griffiths (alan-griffiths) wrote : | # |
*Needs discussion*
OK, I've thought about it and read the spec.
I think the confusion comes from a mismatch between MirLifecycleState and the design.
"Event: application_
"This event is triggered when the application is about to be suspended. Application authors can rely on this event to account for the situation that the UI will not be visible to the user anymore and that they will be granted at most minor CPU timeslices and no GPU timeslices. The event is accompanied by an archive that application authors can rely on to store application-
It does makes sense to automatically propagate this to clients. But is that what mir_lifecycle_
"Event: application_
"This event is triggered when the application is about to be terminated, i.e., the process containing the application instance will be terminated. The event is accompanied by an archive that application authors can rely on to store application-
Alan Griffiths (alan-griffiths) wrote : | # |
> *Needs discussion*
>
> OK, I've thought about it and read the spec.
>
> I think the confusion comes from a mismatch between MirLifecycleState and the
> design.
After hearing more, things are not clear and this MP doesn't add any new confusion.
- 3473. By Andreas Pokorny
-
initialize lifecycle event callback
- 3474. By Andreas Pokorny
-
merge lp:mir and resolve if(functor) functor() complaint..
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:3474
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Andreas Pokorny (andreas-pokorny) wrote : | # |
After a short discussion we realized that mir needs to have a different behavior on vt switch:
https:/
and in general we should not have to use mir lifecycle for that..
so rejecting this MP & branch
Unmerged revisions
- 3474. By Andreas Pokorny
-
merge lp:mir and resolve if(functor) functor() complaint..
- 3473. By Andreas Pokorny
-
initialize lifecycle event callback
- 3472. By Andreas Pokorny
-
enum class instead of enum
- 3471. By Andreas Pokorny
-
throw on write
- 3470. By Andreas Pokorny
-
merge current lp:mir
- 3469. By Andreas Pokorny
-
Emit life cycle event to all sessions and revert ctest changes
- 3468. By Andreas Pokorny
-
update lp:mir
- 3467. By Andreas Pokorny
-
Use the builtin lifecycle event as an indication of lifecycle changes of the server
With this change the active session will receive lifecycle events when the server pauses or resumes. The nested display will use this event to feed the registered pause/resume handlers. Thus a nested server will pause when the hosting server pauses due to a vt switch.
This is a preparation step to use resume events and focus events as a trigger for informing clients about missed key state changes.
Preview Diff
1 | === modified file 'src/include/server/mir/default_server_status_listener.h' |
2 | --- src/include/server/mir/default_server_status_listener.h 2015-02-22 07:46:25 +0000 |
3 | +++ src/include/server/mir/default_server_status_listener.h 2016-06-10 18:31:23 +0000 |
4 | @@ -1,5 +1,5 @@ |
5 | /* |
6 | - * Copyright © 2013 Canonical Ltd. |
7 | + * Copyright © 2013,2016 Canonical Ltd. |
8 | * |
9 | * This program is free software: you can redistribute it and/or modify it |
10 | * under the terms of the GNU General Public License version 3, |
11 | @@ -20,23 +20,23 @@ |
12 | #define MIR_DEFAULT_SERVER_STATUS_LISTENER_H_ |
13 | |
14 | #include "mir/server_status_listener.h" |
15 | +#include "mir/scene/session.h" |
16 | |
17 | namespace mir |
18 | { |
19 | +namespace scene |
20 | +{ |
21 | +class SessionContainer; |
22 | +} |
23 | class DefaultServerStatusListener : public virtual ServerStatusListener |
24 | { |
25 | public: |
26 | - virtual void paused() |
27 | - { |
28 | - } |
29 | - |
30 | - virtual void resumed() |
31 | - { |
32 | - } |
33 | - |
34 | - virtual void started() |
35 | - { |
36 | - } |
37 | + DefaultServerStatusListener(std::shared_ptr<scene::SessionContainer> const& session_container); |
38 | + void paused() override; |
39 | + void resumed() override; |
40 | + void started() override; |
41 | +private: |
42 | + std::shared_ptr<scene::SessionContainer> const session_container; |
43 | }; |
44 | } |
45 | |
46 | |
47 | === modified file 'src/server/CMakeLists.txt' |
48 | --- src/server/CMakeLists.txt 2016-06-02 05:33:50 +0000 |
49 | +++ src/server/CMakeLists.txt 2016-06-10 18:31:23 +0000 |
50 | @@ -52,6 +52,7 @@ |
51 | terminate_with_current_exception.cpp |
52 | display_server.cpp |
53 | default_server_configuration.cpp |
54 | + default_server_status_listener.cpp |
55 | glib_main_loop.cpp |
56 | glib_main_loop_sources.cpp |
57 | default_emergency_cleanup.cpp |
58 | |
59 | === modified file 'src/server/default_server_configuration.cpp' |
60 | --- src/server/default_server_configuration.cpp 2016-06-02 05:33:50 +0000 |
61 | +++ src/server/default_server_configuration.cpp 2016-06-10 18:31:23 +0000 |
62 | @@ -166,9 +166,9 @@ |
63 | std::shared_ptr<mir::ServerStatusListener> mir::DefaultServerConfiguration::the_server_status_listener() |
64 | { |
65 | return server_status_listener( |
66 | - []() |
67 | + [this]() |
68 | { |
69 | - return std::make_shared<mir::DefaultServerStatusListener>(); |
70 | + return std::make_shared<mir::DefaultServerStatusListener>(the_session_container()); |
71 | }); |
72 | } |
73 | |
74 | |
75 | === added file 'src/server/default_server_status_listener.cpp' |
76 | --- src/server/default_server_status_listener.cpp 1970-01-01 00:00:00 +0000 |
77 | +++ src/server/default_server_status_listener.cpp 2016-06-10 18:31:23 +0000 |
78 | @@ -0,0 +1,50 @@ |
79 | +/* |
80 | + * Copyright © 2016 Canonical Ltd. |
81 | + * |
82 | + * This program is free software: you can redistribute it and/or modify it |
83 | + * under the terms of the GNU General Public License version 3, |
84 | + * as published by the Free Software Foundation. |
85 | + * |
86 | + * This program is distributed in the hope that it will be useful, |
87 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
88 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
89 | + * GNU General Public License for more details. |
90 | + * |
91 | + * You should have received a copy of the GNU General Public License |
92 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
93 | + * |
94 | + * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com> |
95 | + */ |
96 | + |
97 | +#include "mir/default_server_status_listener.h" |
98 | +#include "scene/session_container.h" |
99 | + |
100 | +#include "mir/scene/session.h" |
101 | + |
102 | +mir::DefaultServerStatusListener::DefaultServerStatusListener( |
103 | + std::shared_ptr<scene::SessionContainer> const& session_container) |
104 | + : session_container{session_container} |
105 | +{ |
106 | +} |
107 | + |
108 | +void mir::DefaultServerStatusListener::paused() |
109 | +{ |
110 | + session_container->for_each( |
111 | + [](std::shared_ptr<scene::Session> const& session) |
112 | + { |
113 | + session->set_lifecycle_state(mir_lifecycle_state_will_suspend); |
114 | + }); |
115 | +} |
116 | + |
117 | +void mir::DefaultServerStatusListener::resumed() |
118 | +{ |
119 | + session_container->for_each( |
120 | + [](std::shared_ptr<scene::Session> const& session) |
121 | + { |
122 | + session->set_lifecycle_state(mir_lifecycle_state_resumed); |
123 | + }); |
124 | +} |
125 | + |
126 | +void mir::DefaultServerStatusListener::started() |
127 | +{ |
128 | +} |
129 | |
130 | === modified file 'src/server/graphics/nested/display.cpp' |
131 | --- src/server/graphics/nested/display.cpp 2016-05-20 13:08:56 +0000 |
132 | +++ src/server/graphics/nested/display.cpp 2016-06-10 18:31:23 +0000 |
133 | @@ -26,6 +26,7 @@ |
134 | #include "mir/graphics/gl_context.h" |
135 | #include "mir/graphics/surfaceless_egl_context.h" |
136 | #include "mir/graphics/display_configuration_policy.h" |
137 | +#include "mir/graphics/event_handler_register.h" |
138 | #include "mir/graphics/overlapping_output_grouping.h" |
139 | #include "mir/graphics/gl_config.h" |
140 | #include "mir/graphics/egl_error.h" |
141 | @@ -33,6 +34,7 @@ |
142 | #include "mir_toolkit/mir_connection.h" |
143 | #include "mir/raii.h" |
144 | |
145 | +#include <sys/eventfd.h> |
146 | #include <boost/throw_exception.hpp> |
147 | #include <stdexcept> |
148 | #include <sstream> |
149 | @@ -186,8 +188,14 @@ |
150 | display_report{display_report}, |
151 | egl_display{connection->egl_native_display(), gl_config}, |
152 | outputs{}, |
153 | - current_configuration(std::make_unique<NestedDisplayConfiguration>(connection->create_display_config())) |
154 | + current_configuration(std::make_unique<NestedDisplayConfiguration>(connection->create_display_config())), |
155 | + lifecycle_trigger{eventfd(0, EFD_CLOEXEC|EFD_NONBLOCK|EFD_SEMAPHORE)} |
156 | { |
157 | + if (lifecycle_trigger < 0) |
158 | + BOOST_THROW_EXCEPTION((std::system_error{errno, |
159 | + std::system_category(), |
160 | + "Failed to create event fd for nested display"})); |
161 | + |
162 | decltype(current_configuration) conf{dynamic_cast<NestedDisplayConfiguration*>(current_configuration->clone().release())}; |
163 | |
164 | initial_conf_policy->apply_to(*conf); |
165 | @@ -198,11 +206,27 @@ |
166 | swap(current_configuration, conf); |
167 | } |
168 | |
169 | + connection->set_lifecycle_event_callback( |
170 | + [this](MirLifecycleState state) |
171 | + { |
172 | + std::lock_guard<std::mutex> lock_display_state(pause_resume_status_mutex); |
173 | + lifecycle_state = state; |
174 | + uint64_t dummy = 1; |
175 | + if (write(lifecycle_trigger, &dummy, sizeof dummy) != sizeof dummy) |
176 | + BOOST_THROW_EXCEPTION((std::system_error{errno, |
177 | + std::system_category(), |
178 | + "Failed to notify lifecycle event fd notification"})); |
179 | + |
180 | + }); |
181 | + |
182 | create_surfaces(*current_configuration); |
183 | } |
184 | |
185 | mgn::Display::~Display() noexcept |
186 | { |
187 | + connection->set_lifecycle_event_callback([](MirLifecycleState){}); |
188 | + if (registered) |
189 | + registered->unregister_fd_handler(this); |
190 | eglBindAPI(MIR_SERVER_EGL_OPENGL_API); |
191 | if (eglGetCurrentContext() == egl_display.egl_context()) |
192 | eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
193 | @@ -343,21 +367,33 @@ |
194 | } |
195 | |
196 | void mgn::Display::register_pause_resume_handlers( |
197 | - EventHandlerRegister& /*handlers*/, |
198 | - DisplayPauseHandler const& /*pause_handler*/, |
199 | - DisplayResumeHandler const& /*resume_handler*/) |
200 | + EventHandlerRegister& handlers, |
201 | + DisplayPauseHandler const& pause_handler, |
202 | + DisplayResumeHandler const& resume_handler) |
203 | { |
204 | - // No need to do anything |
205 | + registered = &handlers; |
206 | + handlers.register_fd_handler( |
207 | + {int(lifecycle_trigger)}, |
208 | + this, |
209 | + [this](int) |
210 | + { |
211 | + update_pause_state(); |
212 | + }); |
213 | + std::lock_guard<std::mutex> lock_display_state(pause_resume_status_mutex); |
214 | + this->pause_handler = pause_handler; |
215 | + this->resume_handler = resume_handler; |
216 | } |
217 | |
218 | void mgn::Display::pause() |
219 | { |
220 | - // No need to do anything |
221 | + std::lock_guard<std::mutex> lock_display_state(pause_resume_status_mutex); |
222 | + display_state = DisplayState::paused; |
223 | } |
224 | |
225 | void mgn::Display::resume() |
226 | { |
227 | - // No need to do anything |
228 | + std::lock_guard<std::mutex> lock_display_state(pause_resume_status_mutex); |
229 | + display_state = DisplayState::running; |
230 | } |
231 | |
232 | auto mgn::Display::create_hardware_cursor(std::shared_ptr<mg::CursorImage> const& /*initial_image*/) -> std::shared_ptr<mg::Cursor> |
233 | @@ -375,3 +411,30 @@ |
234 | { |
235 | return nullptr; |
236 | } |
237 | + |
238 | +void mgn::Display::update_pause_state() |
239 | +{ |
240 | + uint64_t dummy; |
241 | + if (read(lifecycle_trigger, &dummy, sizeof dummy) != sizeof dummy) |
242 | + { |
243 | + if (errno == EAGAIN) |
244 | + { |
245 | + return; |
246 | + } |
247 | + BOOST_THROW_EXCEPTION((std::system_error{errno, |
248 | + std::system_category(), |
249 | + "Failed to consume nested fd notification"})); |
250 | + } |
251 | + |
252 | + std::unique_lock<std::mutex> lock_display_state(pause_resume_status_mutex); |
253 | + if (display_state != DisplayState::running && lifecycle_state == mir_lifecycle_state_resumed && resume_handler) |
254 | + { |
255 | + lock_display_state.unlock(); |
256 | + resume_handler(); |
257 | + } |
258 | + else if (display_state != DisplayState::paused && lifecycle_state == mir_lifecycle_state_will_suspend && pause_handler) |
259 | + { |
260 | + lock_display_state.unlock(); |
261 | + pause_handler(); |
262 | + } |
263 | +} |
264 | |
265 | === modified file 'src/server/graphics/nested/display.h' |
266 | --- src/server/graphics/nested/display.h 2016-05-20 13:08:56 +0000 |
267 | +++ src/server/graphics/nested/display.h 2016-06-10 18:31:23 +0000 |
268 | @@ -23,6 +23,7 @@ |
269 | #include "mir/graphics/display_buffer.h" |
270 | #include "mir/graphics/display_configuration.h" |
271 | #include "mir/graphics/egl_resources.h" |
272 | +#include "mir/fd.h" |
273 | |
274 | #include "mir_toolkit/client_types.h" |
275 | |
276 | @@ -154,8 +155,22 @@ |
277 | std::mutex mutable configuration_mutex; |
278 | std::unique_ptr<NestedDisplayConfiguration> current_configuration; |
279 | |
280 | + std::mutex pause_resume_status_mutex; |
281 | + mir::Fd lifecycle_trigger; |
282 | + EventHandlerRegister* registered{nullptr}; |
283 | + enum class DisplayState |
284 | + { |
285 | + running, |
286 | + paused, |
287 | + }; |
288 | + DisplayState display_state{DisplayState::running}; |
289 | + MirLifecycleState lifecycle_state{mir_lifecycle_connection_lost}; |
290 | + DisplayPauseHandler pause_handler; |
291 | + DisplayResumeHandler resume_handler; |
292 | + |
293 | void create_surfaces(mir::graphics::DisplayConfiguration const& configuration); |
294 | void complete_display_initialization(MirPixelFormat format); |
295 | + void update_pause_state(); |
296 | }; |
297 | |
298 | } |
299 | |
300 | === modified file 'src/server/graphics/nested/host_connection.h' |
301 | --- src/server/graphics/nested/host_connection.h 2016-06-07 16:20:53 +0000 |
302 | +++ src/server/graphics/nested/host_connection.h 2016-06-10 18:31:23 +0000 |
303 | @@ -56,6 +56,7 @@ |
304 | virtual void set_cursor_image(CursorImage const& image) = 0; |
305 | virtual void hide_cursor() = 0; |
306 | virtual auto graphics_platform_library() -> std::string = 0; |
307 | + virtual void set_lifecycle_event_callback(std::function<void(MirLifecycleState)> const& cb) = 0; |
308 | |
309 | virtual UniqueInputConfig create_input_device_config() = 0; |
310 | virtual void set_input_device_change_callback(std::function<void(UniqueInputConfig)> const& cb) = 0; |
311 | |
312 | === modified file 'src/server/graphics/nested/mir_client_host_connection.cpp' |
313 | --- src/server/graphics/nested/mir_client_host_connection.cpp 2016-06-09 13:15:34 +0000 |
314 | +++ src/server/graphics/nested/mir_client_host_connection.cpp 2016-06-10 18:31:23 +0000 |
315 | @@ -29,6 +29,7 @@ |
316 | #include "mir/input/input_device_observer.h" |
317 | #include "mir/frontend/event_sink.h" |
318 | #include "mir/server_action_queue.h" |
319 | +#include "mir/report_exception.h" |
320 | |
321 | #include <boost/throw_exception.hpp> |
322 | #include <boost/exception/errinfo_errno.hpp> |
323 | @@ -62,12 +63,6 @@ |
324 | *reply_ptr = reply; |
325 | } |
326 | |
327 | -static void nested_lifecycle_event_callback_thunk(MirConnection* /*connection*/, MirLifecycleState state, void *context) |
328 | -{ |
329 | - msh::HostLifecycleEventListener* listener = static_cast<msh::HostLifecycleEventListener*>(context); |
330 | - listener->lifecycle_event_occurred(state); |
331 | -} |
332 | - |
333 | MirPixelFormat const cursor_pixel_format = mir_pixel_format_argb_8888; |
334 | |
335 | void copy_image(MirGraphicsRegion const& g, mg::CursorImage const& image) |
336 | @@ -227,6 +222,7 @@ |
337 | std::shared_ptr<msh::HostLifecycleEventListener> const& host_lifecycle_event_listener) |
338 | : mir_connection{mir_connect_sync(host_socket.c_str(), name.c_str())}, |
339 | conf_change_callback{[]{}}, |
340 | + lifecycle_callback{[](MirLifecycleState){}}, |
341 | host_lifecycle_event_listener{host_lifecycle_event_listener}, |
342 | event_callback{[](MirEvent const&, mir::geometry::Rectangle const&){}} |
343 | { |
344 | @@ -241,8 +237,20 @@ |
345 | |
346 | mir_connection_set_lifecycle_event_callback( |
347 | mir_connection, |
348 | - nested_lifecycle_event_callback_thunk, |
349 | - std::static_pointer_cast<void>(host_lifecycle_event_listener).get()); |
350 | + [](MirConnection* /*connection*/, MirLifecycleState state, void *context) |
351 | + { |
352 | + mgn::MirClientHostConnection* obj = static_cast<mgn::MirClientHostConnection*>(context); |
353 | + try |
354 | + { |
355 | + obj->handle_lifecycle_event(state); |
356 | + } |
357 | + catch(...) |
358 | + { |
359 | + report_exception(); |
360 | + abort(); |
361 | + } |
362 | + }, |
363 | + static_cast<void*>(this)); |
364 | |
365 | mir_connection_set_input_config_change_callback( |
366 | mir_connection, |
367 | @@ -412,3 +420,14 @@ |
368 | { |
369 | event_callback(event, source_frame); |
370 | } |
371 | + |
372 | +void mgn::MirClientHostConnection::set_lifecycle_event_callback(std::function<void(MirLifecycleState)> const& cb) |
373 | +{ |
374 | + lifecycle_callback = cb; |
375 | +} |
376 | + |
377 | +void mgn::MirClientHostConnection::handle_lifecycle_event(MirLifecycleState state) |
378 | +{ |
379 | + host_lifecycle_event_listener->lifecycle_event_occurred(state); |
380 | + lifecycle_callback(state); |
381 | +} |
382 | |
383 | === modified file 'src/server/graphics/nested/mir_client_host_connection.h' |
384 | --- src/server/graphics/nested/mir_client_host_connection.h 2016-06-07 16:20:53 +0000 |
385 | +++ src/server/graphics/nested/mir_client_host_connection.h 2016-06-10 18:31:23 +0000 |
386 | @@ -66,6 +66,7 @@ |
387 | int width, int height, MirPixelFormat pf, char const* name, |
388 | MirBufferUsage usage, uint32_t output_id) override; |
389 | void set_display_config_change_callback(std::function<void()> const& cb) override; |
390 | + void set_lifecycle_event_callback(std::function<void(MirLifecycleState)> const& cb) override; |
391 | void apply_display_config(MirDisplayConfiguration&) override; |
392 | |
393 | void set_cursor_image(CursorImage const& image) override; |
394 | @@ -80,12 +81,14 @@ |
395 | void set_input_event_callback(std::function<void(MirEvent const&, mir::geometry::Rectangle const&)> const& cb) override; |
396 | void emit_input_event(MirEvent const& cb, mir::geometry::Rectangle const& source_frame) override; |
397 | |
398 | + void handle_lifecycle_event(MirLifecycleState state); |
399 | private: |
400 | void update_input_config(UniqueInputConfig input_config); |
401 | std::mutex surfaces_mutex; |
402 | |
403 | MirConnection* const mir_connection; |
404 | std::function<void()> conf_change_callback; |
405 | + std::function<void(MirLifecycleState)> lifecycle_callback; |
406 | std::shared_ptr<msh::HostLifecycleEventListener> const host_lifecycle_event_listener; |
407 | |
408 | std::vector<HostSurface*> surfaces; |
409 | |
410 | === modified file 'tests/include/mir/test/doubles/stub_host_connection.h' |
411 | --- tests/include/mir/test/doubles/stub_host_connection.h 2016-06-09 13:15:34 +0000 |
412 | +++ tests/include/mir/test/doubles/stub_host_connection.h 2016-06-10 18:31:23 +0000 |
413 | @@ -50,6 +50,10 @@ |
414 | { |
415 | } |
416 | |
417 | + void set_lifecycle_event_callback(std::function<void(MirLifecycleState)> const&) override |
418 | + { |
419 | + } |
420 | + |
421 | void apply_display_config(MirDisplayConfiguration&) override {} |
422 | |
423 | std::shared_ptr<graphics::nested::HostSurface> |
424 | |
425 | === modified file 'tests/unit-tests/graphics/nested/test_nested_display.cpp' |
426 | --- tests/unit-tests/graphics/nested/test_nested_display.cpp 2016-05-04 20:53:03 +0000 |
427 | +++ tests/unit-tests/graphics/nested/test_nested_display.cpp 2016-06-10 18:31:23 +0000 |
428 | @@ -21,8 +21,9 @@ |
429 | #include "src/server/graphics/nested/host_surface.h" |
430 | #include "src/server/report/null/display_report.h" |
431 | #include "mir/graphics/default_display_configuration_policy.h" |
432 | -#include "src/server/input/null_input_dispatcher.h" |
433 | +#include "mir/graphics/event_handler_register.h" |
434 | #include "mir_display_configuration_builder.h" |
435 | +#include "mir/events/event_builders.h" |
436 | |
437 | #include "mir/test/doubles/mock_egl.h" |
438 | #include "mir/test/doubles/mock_gl_config.h" |
439 | @@ -44,6 +45,32 @@ |
440 | namespace |
441 | { |
442 | |
443 | +struct StubEventHandlerRegister : mg::EventHandlerRegister |
444 | +{ |
445 | +public: |
446 | + void register_signal_handler(std::initializer_list<int>, std::function<void(int)> const&) override |
447 | + { |
448 | + } |
449 | + |
450 | + void register_signal_handler(std::initializer_list<int>, mir::UniqueModulePtr<std::function<void(int)>>) override |
451 | + { |
452 | + } |
453 | + |
454 | + void register_fd_handler(std::initializer_list<int> fds, void const*, std::function<void(int)> const& callback) override |
455 | + { |
456 | + int fd = *begin(fds); |
457 | + event_callback = [fd, callback]{callback(fd);}; |
458 | + } |
459 | + |
460 | + void register_fd_handler(std::initializer_list<int>, |
461 | + void const*, |
462 | + mir::UniqueModulePtr<std::function<void(int)>>) override{}; |
463 | + |
464 | + void unregister_fd_handler(void const*) override{}; |
465 | + |
466 | + std::function<void()> event_callback; |
467 | +}; |
468 | + |
469 | class SingleDisplayHostConnection : public mtd::StubHostConnection |
470 | { |
471 | public: |
472 | @@ -51,6 +78,23 @@ |
473 | { |
474 | return mt::build_trivial_configuration(); |
475 | } |
476 | + |
477 | + void set_lifecycle_event_callback(std::function<void(MirLifecycleState)> const& cb) override |
478 | + { |
479 | + lifecycle_callback = cb; |
480 | + } |
481 | + |
482 | + void simulate_suspend() |
483 | + { |
484 | + lifecycle_callback(mir_lifecycle_state_will_suspend); |
485 | + } |
486 | + |
487 | + void simulate_resume() |
488 | + { |
489 | + lifecycle_callback(mir_lifecycle_state_resumed); |
490 | + } |
491 | + |
492 | + std::function<void(MirLifecycleState)> lifecycle_callback; |
493 | }; |
494 | |
495 | class MockApplyDisplayConfigHostConnection : public SingleDisplayHostConnection |
496 | @@ -67,7 +111,7 @@ |
497 | { |
498 | auto nested_display_raw = new mgn::Display{ |
499 | platform, |
500 | - std::make_shared<SingleDisplayHostConnection>(), |
501 | + connection, |
502 | mt::fake_shared(null_display_report), |
503 | mt::fake_shared(default_conf_policy), |
504 | gl_config}; |
505 | @@ -75,13 +119,33 @@ |
506 | return std::unique_ptr<mgn::Display>{nested_display_raw}; |
507 | } |
508 | |
509 | + std::shared_ptr<SingleDisplayHostConnection> const connection = std::make_shared<SingleDisplayHostConnection>(); |
510 | testing::NiceMock<mtd::MockEGL> mock_egl; |
511 | - mi::NullInputDispatcher null_input_dispatcher; |
512 | mir::report::null::DisplayReport null_display_report; |
513 | mg::CloneDisplayConfigurationPolicy default_conf_policy; |
514 | mtd::StubGLConfig stub_gl_config; |
515 | std::shared_ptr<mtd::NullPlatform> null_platform{ |
516 | std::make_shared<mtd::NullPlatform>()}; |
517 | + StubEventHandlerRegister stub_event_handler_register; |
518 | + |
519 | + MOCK_METHOD0(pause, void()); |
520 | + MOCK_METHOD0(resume, void()); |
521 | + |
522 | + void install_pause_resume_handlers(mgn::Display& display) |
523 | + { |
524 | + display.register_pause_resume_handlers( |
525 | + stub_event_handler_register, |
526 | + [this]() |
527 | + { |
528 | + pause(); |
529 | + return true; |
530 | + }, |
531 | + [this]() |
532 | + { |
533 | + resume(); |
534 | + return true; |
535 | + }); |
536 | + } |
537 | }; |
538 | |
539 | } |
540 | @@ -243,3 +307,40 @@ |
541 | |
542 | auto dummy_context = nested_display->create_gl_context(); |
543 | } |
544 | + |
545 | +TEST_F(NestedDisplay, pauses_server_on_suspend) |
546 | +{ |
547 | + using namespace testing; |
548 | + |
549 | + auto const nested_display = create_nested_display( |
550 | + null_platform, |
551 | + mt::fake_shared(stub_gl_config)); |
552 | + |
553 | + install_pause_resume_handlers(*nested_display); |
554 | + |
555 | + EXPECT_CALL(*this, pause()); |
556 | + connection->simulate_suspend(); |
557 | + stub_event_handler_register.event_callback(); |
558 | +} |
559 | + |
560 | +TEST_F(NestedDisplay, resumes_server_on_lifecycle_resume) |
561 | +{ |
562 | + using namespace testing; |
563 | + |
564 | + auto const nested_display = create_nested_display( |
565 | + null_platform, |
566 | + mt::fake_shared(stub_gl_config)); |
567 | + |
568 | + install_pause_resume_handlers(*nested_display); |
569 | + |
570 | + EXPECT_CALL(*this, pause()); |
571 | + EXPECT_CALL(*this, resume()); |
572 | + |
573 | + connection->simulate_suspend(); |
574 | + stub_event_handler_register.event_callback(); |
575 | + |
576 | + nested_display->pause(); |
577 | + |
578 | + connection->simulate_resume(); |
579 | + stub_event_handler_register.event_callback(); |
580 | +} |
PASSED: Continuous integration, rev:3467 /mir-jenkins. ubuntu. com/job/ mir-ci/ 871/ /mir-jenkins. ubuntu. com/job/ build-mir/ 892 /mir-jenkins. ubuntu. com/job/ build-0- fetch/929 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= vivid+overlay/ 920 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= xenial/ 920 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= clang,platform= mesa,release= vivid+overlay/ 902 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= clang,platform= mesa,release= vivid+overlay/ 902/artifact/ output/ *zip*/output. zip /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= xenial/ 902 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= xenial/ 902/artifact/ output/ *zip*/output. zip /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= android, release= vivid+overlay/ 902 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= android, release= vivid+overlay/ 902/artifact/ output/ *zip*/output. zip /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= android, release= vivid+overlay/ 902 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= android, release= vivid+overlay/ 902/artifact/ output/ *zip*/output. zip /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= mesa,release= xenial/ 902 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= mesa,release= xenial/ 902/artifact/ output/ *zip*/output. zip
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild: /mir-jenkins. ubuntu. com/job/ mir-ci/ 871/rebuild
https:/