Mir

Merge lp:~afrantzis/mir/fix-resources-release-on-shutdown-lp-1170643 into lp:~mir-team/mir/trunk

Proposed by Alexandros Frantzis
Status: Merged
Approved by: Robert Carr
Approved revision: no longer in the source branch.
Merged at revision: 685
Proposed branch: lp:~afrantzis/mir/fix-resources-release-on-shutdown-lp-1170643
Merge into: lp:~mir-team/mir/trunk
Diff against target: 364 lines (+224/-38)
3 files modified
src/server/shell/session_manager.cpp (+16/-0)
tests/acceptance-tests/test_server_shutdown.cpp (+194/-38)
tests/integration-tests/shell/test_session_manager.cpp (+14/-0)
To merge this branch: bzr merge lp:~afrantzis/mir/fix-resources-release-on-shutdown-lp-1170643
Reviewer Review Type Date Requested Status
Robert Carr (community) Approve
Alan Griffiths Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+163562@code.launchpad.net

Commit message

shell: Close all open sessions when destroying the SessionManager

We need to do this manually to break the cyclic dependency between msh::Session
and msh::InputTargetListener, since our implementations of these interfaces
keep strong references to each other. This change fixes LP:#1170643.

Description of the change

shell: Close all open sessions when destroying the SessionManager

We need to do this manually to break the cyclic dependency between msh::Session
and msh::InputTargetListener, since our implementations of these interfaces
keep strong references to each other. This change fixes LP:#1170643.

The test code is complicated by the need to synchronize and relay results across processes. The Flag class could be made into a more general utility and used by other tests, but I left this for another MP.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

LGTM

review: Approve
Revision history for this message
Robert Carr (robertcarr) wrote :

LGTM!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/server/shell/session_manager.cpp'
2--- src/server/shell/session_manager.cpp 2013-05-02 00:11:18 +0000
3+++ src/server/shell/session_manager.cpp 2013-05-14 09:55:30 +0000
4@@ -57,6 +57,22 @@
5
6 msh::SessionManager::~SessionManager()
7 {
8+ /*
9+ * Close all open sessions. We need to do this manually here
10+ * to break the cyclic dependency between msh::Session
11+ * and msh::InputTargetListener, since our implementations
12+ * of these interfaces keep strong references to each other.
13+ * TODO: Investigate other solutions (e.g. weak_ptr)
14+ */
15+ std::vector<std::shared_ptr<msh::Session>> sessions;
16+
17+ app_container->for_each([&](std::shared_ptr<Session> const& session)
18+ {
19+ sessions.push_back(session);
20+ });
21+
22+ for (auto& session : sessions)
23+ close_session(session);
24 }
25
26 std::shared_ptr<mf::Session> msh::SessionManager::open_session(
27
28=== modified file 'tests/acceptance-tests/test_server_shutdown.cpp'
29--- tests/acceptance-tests/test_server_shutdown.cpp 2013-04-25 21:17:37 +0000
30+++ tests/acceptance-tests/test_server_shutdown.cpp 2013-05-14 09:55:30 +0000
31@@ -20,6 +20,7 @@
32 #include "mir/graphics/renderer.h"
33
34 #include "mir_test_framework/display_server_test_fixture.h"
35+#include "mir_test/fake_event_hub_input_configuration.h"
36
37 #include <gtest/gtest.h>
38
39@@ -28,7 +29,10 @@
40 #include <fcntl.h>
41
42 namespace mg = mir::graphics;
43+namespace mi = mir::input;
44+namespace mia = mir::input::android;
45 namespace mtf = mir_test_framework;
46+namespace mtd = mir::test::doubles;
47
48 namespace
49 {
50@@ -56,19 +60,49 @@
51 {
52 }
53
54+class Flag
55+{
56+public:
57+ explicit Flag(std::string const& flag_file)
58+ : flag_file{flag_file}
59+ {
60+ std::remove(flag_file.c_str());
61+ }
62+
63+ void set()
64+ {
65+ close(open(flag_file.c_str(), O_CREAT, S_IWUSR | S_IRUSR));
66+ }
67+
68+ bool is_set()
69+ {
70+ int fd = -1;
71+ if ((fd = open(flag_file.c_str(), O_RDONLY, S_IWUSR | S_IRUSR)) != -1)
72+ {
73+ close(fd);
74+ return true;
75+ }
76+ return false;
77+ }
78+
79+ void wait()
80+ {
81+ while (!is_set())
82+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
83+ }
84+
85+private:
86+ std::string const flag_file;
87+};
88+
89 }
90
91 TEST_F(BespokeDisplayServerTestFixture, server_can_shut_down_when_clients_are_blocked)
92 {
93- static std::string const next_buffer_done1{"next_buffer_done1_c5d49978.tmp"};
94- static std::string const next_buffer_done2{"next_buffer_done2_c5d49978.tmp"};
95- static std::string const next_buffer_done3{"next_buffer_done3_c5d49978.tmp"};
96- static std::string const server_done{"server_done_c5d49978.tmp"};
97-
98- std::remove(next_buffer_done1.c_str());
99- std::remove(next_buffer_done2.c_str());
100- std::remove(next_buffer_done3.c_str());
101- std::remove(server_done.c_str());
102+ Flag next_buffer_done1{"next_buffer_done1_c5d49978.tmp"};
103+ Flag next_buffer_done2{"next_buffer_done2_c5d49978.tmp"};
104+ Flag next_buffer_done3{"next_buffer_done3_c5d49978.tmp"};
105+ Flag server_done{"server_done_c5d49978.tmp"};
106
107 struct ServerConfig : TestingServerConfiguration
108 {
109@@ -82,10 +116,10 @@
110
111 struct ClientConfig : TestingClientConfiguration
112 {
113- ClientConfig(std::string const& next_buffer_done,
114- std::string const& server_done)
115- : next_buffer_done{next_buffer_done},
116- server_done{server_done}
117+ ClientConfig(Flag& next_buffer_done,
118+ Flag& server_done)
119+ : next_buffer_done(next_buffer_done),
120+ server_done(server_done)
121 {
122 }
123
124@@ -110,8 +144,8 @@
125 /* Ask for the first second buffer (should block) */
126 mir_surface_next_buffer(surf, null_surface_callback, nullptr);
127
128- set_flag(next_buffer_done);
129- wait_for(server_done);
130+ next_buffer_done.set();
131+ server_done.wait();
132
133 /*
134 * TODO: Releasing the connection to a shut down server blocks
135@@ -122,25 +156,10 @@
136 */
137 }
138
139- void set_flag(std::string const& flag_file)
140- {
141- close(open(flag_file.c_str(), O_CREAT, S_IWUSR | S_IRUSR));
142- }
143-
144- void wait_for(std::string const& flag_file)
145- {
146- int fd = -1;
147- while ((fd = open(flag_file.c_str(), O_RDONLY, S_IWUSR | S_IRUSR)) == -1)
148- {
149- std::this_thread::sleep_for(std::chrono::milliseconds(1));
150- }
151- close(fd);
152- }
153-
154- std::string const next_buffer_done;
155- std::string const server_done;
156+ Flag& next_buffer_done;
157+ Flag& server_done;
158 };
159-
160+
161 ClientConfig client_config1{next_buffer_done1, server_done};
162 ClientConfig client_config2{next_buffer_done2, server_done};
163 ClientConfig client_config3{next_buffer_done3, server_done};
164@@ -152,14 +171,151 @@
165 run_in_test_process([&]
166 {
167 /* Wait until the clients are blocked on getting the second buffer */
168- client_config1.wait_for(next_buffer_done1);
169- client_config2.wait_for(next_buffer_done2);
170- client_config3.wait_for(next_buffer_done3);
171+ next_buffer_done1.wait();
172+ next_buffer_done2.wait();
173+ next_buffer_done3.wait();
174
175 /* Shutting down the server should not block */
176 shutdown_server_process();
177
178 /* Notify the clients that we are done (we only need to set the flag once) */
179- client_config1.set_flag(server_done);
180- });
181+ server_done.set();
182+ });
183+}
184+
185+TEST_F(BespokeDisplayServerTestFixture, server_releases_resources_on_shutdown_with_connected_clients)
186+{
187+ Flag surface_created1{"surface_created1_7e9c69fc.tmp"};
188+ Flag surface_created2{"surface_created2_7e9c69fc.tmp"};
189+ Flag surface_created3{"surface_created3_7e9c69fc.tmp"};
190+ Flag server_done{"server_done_7e9c69fc.tmp"};
191+ Flag resources_freed_success{"resources_free_success_7e9c69fc.tmp"};
192+ Flag resources_freed_failure{"resources_free_failure_7e9c69fc.tmp"};
193+
194+ /* Use the real input manager, but with a fake event hub */
195+ struct ServerConfig : TestingServerConfiguration
196+ {
197+ std::shared_ptr<mia::InputConfiguration> the_input_configuration() override
198+ {
199+ if (!input_configuration)
200+ {
201+ input_configuration =
202+ std::make_shared<mtd::FakeEventHubInputConfiguration>(
203+ std::initializer_list<std::shared_ptr<mi::EventFilter> const>{},
204+ the_viewable_area(),
205+ std::shared_ptr<mi::CursorListener>());
206+ }
207+
208+ return input_configuration;
209+ }
210+
211+ std::shared_ptr<mi::InputManager> the_input_manager() override
212+ {
213+ return DefaultServerConfiguration::the_input_manager();
214+ }
215+
216+ std::shared_ptr<mir::shell::InputTargetListener> the_input_target_listener() override
217+ {
218+ return DefaultServerConfiguration::the_input_target_listener();
219+ }
220+
221+ std::shared_ptr<mia::InputConfiguration> input_configuration;
222+ };
223+
224+ auto server_config = std::make_shared<ServerConfig>();
225+ launch_server_process(*server_config);
226+
227+ struct ClientConfig : TestingClientConfiguration
228+ {
229+ ClientConfig(Flag& surface_created,
230+ Flag& server_done)
231+ : surface_created(surface_created),
232+ server_done(server_done)
233+ {
234+ }
235+
236+ void exec()
237+ {
238+ MirConnection* connection = mir_connect_sync(mir_test_socket, __PRETTY_FUNCTION__);
239+
240+ ASSERT_TRUE(connection != NULL);
241+
242+ MirSurfaceParameters const request_params =
243+ {
244+ __PRETTY_FUNCTION__,
245+ 640, 480,
246+ mir_pixel_format_abgr_8888,
247+ mir_buffer_usage_hardware
248+ };
249+
250+ mir_connection_create_surface_sync(connection, &request_params);
251+
252+ surface_created.set();
253+ server_done.wait();
254+ }
255+
256+ Flag& surface_created;
257+ Flag& server_done;
258+ };
259+
260+ ClientConfig client_config1{surface_created1, server_done};
261+ ClientConfig client_config2{surface_created2, server_done};
262+ ClientConfig client_config3{surface_created3, server_done};
263+
264+ launch_client_process(client_config1);
265+ launch_client_process(client_config2);
266+ launch_client_process(client_config3);
267+
268+ run_in_test_process([&]
269+ {
270+ /* Wait until the clients have created a surface */
271+ surface_created1.wait();
272+ surface_created2.wait();
273+ surface_created3.wait();
274+
275+ /* Shut down the server */
276+ shutdown_server_process();
277+
278+ /* Wait until we have checked the resources in the server process */
279+ while (!resources_freed_failure.is_set() && !resources_freed_success.is_set())
280+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
281+
282+ /* Fail if the resources have not been freed */
283+ if (resources_freed_failure.is_set())
284+ ADD_FAILURE();
285+
286+ /* Notify the clients that we are done (we only need to set the flag once) */
287+ server_done.set();
288+ });
289+
290+ /*
291+ * Check that all resources are freed after destroying the server config.
292+ * Note that these checks are run multiple times: in the server process,
293+ * in each of the client processes and in the test process. We only care about
294+ * the results in the server process (in the other cases the checks will
295+ * succeed anyway, since we are not using the config object).
296+ */
297+ std::weak_ptr<mir::graphics::Display> display = server_config->the_display();
298+ std::weak_ptr<mir::compositor::Compositor> compositor = server_config->the_compositor();
299+ std::weak_ptr<mir::frontend::Communicator> communicator = server_config->the_communicator();
300+ std::weak_ptr<mir::input::InputManager> input_manager = server_config->the_input_manager();
301+
302+ server_config.reset();
303+
304+ EXPECT_EQ(0, display.use_count());
305+ EXPECT_EQ(0, compositor.use_count());
306+ EXPECT_EQ(0, communicator.use_count());
307+ EXPECT_EQ(0, input_manager.use_count());
308+
309+ if (display.use_count() != 0 ||
310+ compositor.use_count() != 0 ||
311+ communicator.use_count() != 0 ||
312+ input_manager.use_count() != 0)
313+ {
314+ resources_freed_failure.set();
315+ }
316+ else
317+ {
318+ resources_freed_success.set();
319+ }
320 }
321
322=== modified file 'tests/integration-tests/shell/test_session_manager.cpp'
323--- tests/integration-tests/shell/test_session_manager.cpp 2013-05-02 00:11:18 +0000
324+++ tests/integration-tests/shell/test_session_manager.cpp 2013-05-14 09:55:30 +0000
325@@ -70,6 +70,8 @@
326 auto session2 = session_manager.open_session("Microsoft Access", std::shared_ptr<me::EventSink>());
327 auto session3 = session_manager.open_session("WordPerfect", std::shared_ptr<me::EventSink>());
328
329+ Mock::VerifyAndClearExpectations(&focus_setter);
330+
331 {
332 InSequence seq;
333 EXPECT_CALL(focus_setter, set_focus_to(Eq(session1))).Times(1);
334@@ -80,6 +82,11 @@
335 session_manager.focus_next();
336 session_manager.focus_next();
337 session_manager.focus_next();
338+
339+ Mock::VerifyAndClearExpectations(&focus_setter);
340+
341+ // Possible change of focus while sessions are closed on shutdown
342+ EXPECT_CALL(focus_setter, set_focus_to(_)).Times(AtLeast(0));
343 }
344
345 TEST(TestSessionManagerAndFocusSelectionStrategy, closing_applications_transfers_focus)
346@@ -108,6 +115,8 @@
347 auto session2 = session_manager.open_session("Microsoft Access", std::shared_ptr<me::EventSink>());
348 auto session3 = session_manager.open_session("WordPerfect", std::shared_ptr<me::EventSink>());
349
350+ Mock::VerifyAndClearExpectations(&focus_setter);
351+
352 {
353 InSequence seq;
354 EXPECT_CALL(focus_setter, set_focus_to(Eq(session2))).Times(1);
355@@ -116,4 +125,9 @@
356
357 session_manager.close_session(session3);
358 session_manager.close_session(session2);
359+
360+ Mock::VerifyAndClearExpectations(&focus_setter);
361+
362+ // Possible change of focus while sessions are closed on shutdown
363+ EXPECT_CALL(focus_setter, set_focus_to(_)).Times(AtLeast(0));
364 }

Subscribers

People subscribed via source and target branches