Mir

Merge lp:~alan-griffiths/mir/mir_lifecycle_connection_lost into lp:mir

Proposed by Alan Griffiths
Status: Merged
Approved by: Alan Griffiths
Approved revision: no longer in the source branch.
Merged at revision: 1111
Proposed branch: lp:~alan-griffiths/mir/mir_lifecycle_connection_lost
Merge into: lp:mir
Diff against target: 294 lines (+129/-20)
9 files modified
include/shared/mir_toolkit/common.h (+2/-1)
src/client/lifecycle_control.cpp (+0/-2)
src/client/mir_client_library.cpp (+5/-0)
src/client/mir_connection.cpp (+14/-0)
src/client/rpc/mir_socket_rpc_channel.cpp (+11/-13)
src/client/rpc/mir_socket_rpc_channel.h (+1/-0)
tests/acceptance-tests/CMakeLists.txt (+1/-0)
tests/acceptance-tests/test_server_disconnect.cpp (+93/-0)
tests/unit-tests/frontend/test_published_socket_connector.cpp (+2/-4)
To merge this branch: bzr merge lp:~alan-griffiths/mir/mir_lifecycle_connection_lost
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Alexandros Frantzis (community) Approve
Kevin DuBois (community) Approve
Alan Griffiths Pending
Review via email: mp+189375@code.launchpad.net

This proposal supersedes a proposal from 2013-10-03.

Commit message

client & tests: Provide a lifecycle callback when server connection is lost.

Description of the change

client & tests: Provide a lifecycle callback when server connection is lost.

The client can register a handler using mir_connection_set_lifecycle_event_callback() this will then receive a new event "mir_lifecycle_connection_lost" when a server connection dies. After this the connection (and associated surfaces and buffers) is useless and should be closed down.

Typically, a client app might want to save state & exit and/or try to build a new connection.

If the client hasn't registered a callback handler, then use we a default handler that raises SIGTERM - as that leads to sensible behavior in the examples.

To post a comment you must log in.
Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

I've found an intermittent failure in this test - will fix once I've diagnosed it.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) : Posted in a previous version of this proposal
review: Abstain
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote : Posted in a previous version of this proposal

Looks good.

review: Approve
Revision history for this message
Kevin DuBois (kdub) wrote : Posted in a previous version of this proposal

looks good to me too

review: Approve
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

Resubmitted with correct target

Revision history for this message
Kevin DuBois (kdub) :
review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looks good.

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/shared/mir_toolkit/common.h'
2--- include/shared/mir_toolkit/common.h 2013-09-12 21:36:55 +0000
3+++ include/shared/mir_toolkit/common.h 2013-10-04 16:55:04 +0000
4@@ -76,7 +76,8 @@
5 typedef enum MirLifecycleState
6 {
7 mir_lifecycle_state_will_suspend,
8- mir_lifecycle_state_resumed
9+ mir_lifecycle_state_resumed,
10+ mir_lifecycle_connection_lost
11 } MirLifecycleState;
12
13 typedef enum MirPowerMode
14
15=== modified file 'src/client/lifecycle_control.cpp'
16--- src/client/lifecycle_control.cpp 2013-08-23 12:23:47 +0000
17+++ src/client/lifecycle_control.cpp 2013-10-04 16:55:04 +0000
18@@ -18,8 +18,6 @@
19
20 #include "lifecycle_control.h"
21
22-#include <memory>
23-
24 namespace mcl = mir::client;
25
26 mcl::LifecycleControl::LifecycleControl()
27
28=== modified file 'src/client/mir_client_library.cpp'
29--- src/client/mir_client_library.cpp 2013-10-03 03:44:08 +0000
30+++ src/client/mir_client_library.cpp 2013-10-04 16:55:04 +0000
31@@ -345,9 +345,14 @@
32 }
33
34 MirWaitHandle* mir_surface_swap_buffers(MirSurface *surface, mir_surface_callback callback, void * context)
35+try
36 {
37 return surface->next_buffer(callback, context);
38 }
39+catch (std::exception const&)
40+{
41+ return 0;
42+}
43
44 void mir_surface_swap_buffers_sync(MirSurface *surface)
45 {
46
47=== modified file 'src/client/mir_connection.cpp'
48--- src/client/mir_connection.cpp 2013-10-03 03:44:08 +0000
49+++ src/client/mir_connection.cpp 2013-10-04 16:55:04 +0000
50@@ -31,6 +31,7 @@
51 #include <algorithm>
52 #include <cstddef>
53 #include <unistd.h>
54+#include <signal.h>
55
56 namespace mcl = mir::client;
57 namespace mircv = mir::input::receiver;
58@@ -201,6 +202,18 @@
59 return new_wait_handle;
60 }
61
62+namespace
63+{
64+void default_lifecycle_event_handler(MirLifecycleState transition)
65+{
66+ if (transition == mir_lifecycle_connection_lost)
67+ {
68+ raise(SIGTERM);
69+ }
70+}
71+}
72+
73+
74 void MirConnection::connected(mir_connected_callback callback, void * context)
75 {
76 bool safe_to_callback = true;
77@@ -225,6 +238,7 @@
78 platform = client_platform_factory->create_client_platform(this);
79 native_display = platform->create_egl_native_display();
80 display_configuration->set_configuration(connect_result.display_configuration());
81+ lifecycle_control->set_lifecycle_event_handler(default_lifecycle_event_handler);
82 }
83
84 if (safe_to_callback) callback(this, context);
85
86=== modified file 'src/client/rpc/mir_socket_rpc_channel.cpp'
87--- src/client/rpc/mir_socket_rpc_channel.cpp 2013-10-03 03:44:08 +0000
88+++ src/client/rpc/mir_socket_rpc_channel.cpp 2013-10-04 16:55:04 +0000
89@@ -30,19 +30,12 @@
90
91 #include <boost/exception/errinfo_errno.hpp>
92 #include <boost/throw_exception.hpp>
93-
94 #include <boost/bind.hpp>
95-#include <boost/throw_exception.hpp>
96
97-#include <cstring>
98-#include <sstream>
99 #include <stdexcept>
100
101-#include <pthread.h>
102 #include <signal.h>
103
104-#include <stdio.h>
105-
106 namespace mcl = mir::client;
107 namespace mclr = mir::client::rpc;
108
109@@ -58,7 +51,8 @@
110 socket(io_service),
111 surface_map(surface_map),
112 display_configuration(disp_config),
113- lifecycle_control(lifecycle_control)
114+ lifecycle_control(lifecycle_control),
115+ disconnected(false)
116 {
117 socket.connect(endpoint);
118 init();
119@@ -76,7 +70,8 @@
120 socket(io_service),
121 surface_map(surface_map),
122 display_configuration(disp_config),
123- lifecycle_control(lifecycle_control)
124+ lifecycle_control(lifecycle_control),
125+ disconnected(false)
126 {
127 socket.assign(boost::asio::local::stream_protocol(), native_socket);
128 init();
129@@ -84,10 +79,12 @@
130
131 void mclr::MirSocketRpcChannel::notify_disconnected()
132 {
133- // TODO enable configuring the kill mechanism
134- io_service.stop();
135- raise (SIGTERM);
136- pending_calls.force_completion();
137+ if (!disconnected.exchange(true))
138+ {
139+ io_service.stop();
140+ lifecycle_control->call_lifecycle_event_handler(mir_lifecycle_connection_lost);
141+ pending_calls.force_completion();
142+ }
143 }
144
145 void mclr::MirSocketRpcChannel::init()
146@@ -288,6 +285,7 @@
147 if (error)
148 {
149 rpc_report->invocation_failed(invocation, error);
150+ notify_disconnected();
151 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to send message to server: " + error.message()));
152 }
153 else
154
155=== modified file 'src/client/rpc/mir_socket_rpc_channel.h'
156--- src/client/rpc/mir_socket_rpc_channel.h 2013-10-03 03:44:08 +0000
157+++ src/client/rpc/mir_socket_rpc_channel.h 2013-10-04 16:55:04 +0000
158@@ -98,6 +98,7 @@
159 std::shared_ptr<SurfaceMap> surface_map;
160 std::shared_ptr<DisplayConfiguration> display_configuration;
161 std::shared_ptr<LifecycleControl> lifecycle_control;
162+ std::atomic<bool> disconnected;
163 };
164
165 }
166
167=== modified file 'tests/acceptance-tests/CMakeLists.txt'
168--- tests/acceptance-tests/CMakeLists.txt 2013-09-05 17:44:44 +0000
169+++ tests/acceptance-tests/CMakeLists.txt 2013-10-04 16:55:04 +0000
170@@ -16,6 +16,7 @@
171 test_nested_mir.cpp
172 test_display_configuration.cpp
173 test_surfaces_with_output_id.cpp
174+ test_server_disconnect.cpp
175 )
176
177 list(APPEND SOURCES
178
179=== added file 'tests/acceptance-tests/test_server_disconnect.cpp'
180--- tests/acceptance-tests/test_server_disconnect.cpp 1970-01-01 00:00:00 +0000
181+++ tests/acceptance-tests/test_server_disconnect.cpp 2013-10-04 16:55:04 +0000
182@@ -0,0 +1,93 @@
183+/*
184+ * Copyright © 2013 Canonical Ltd.
185+ *
186+ * This program is free software: you can redistribute it and/or modify it
187+ * under the terms of the GNU General Public License version 3,
188+ * as published by the Free Software Foundation.
189+ *
190+ * This program is distributed in the hope that it will be useful,
191+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
192+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
193+ * GNU General Public License for more details.
194+ *
195+ * You should have received a copy of the GNU General Public License
196+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
197+ *
198+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
199+ */
200+
201+#include "mir_toolkit/mir_client_library.h"
202+
203+#include "mir_test_framework/display_server_test_fixture.h"
204+#include "mir_test_framework/cross_process_sync.h"
205+
206+#include <gtest/gtest.h>
207+#include <gmock/gmock.h>
208+
209+#include <atomic>
210+
211+namespace mtf = mir_test_framework;
212+
213+using ServerDisconnect = mtf::BespokeDisplayServerTestFixture;
214+
215+namespace
216+{
217+struct MockEventHandler
218+{
219+ MOCK_METHOD1(handle, void(MirLifecycleState transition));
220+
221+ static void handle(MirConnection*, MirLifecycleState transition, void* event_handler)
222+ {
223+ static_cast<MockEventHandler*>(event_handler)->handle(transition);
224+ }
225+};
226+
227+struct MyTestingClientConfiguration : mtf::TestingClientConfiguration
228+{
229+ void exec() override
230+ {
231+ static MirSurfaceParameters const params =
232+ { __PRETTY_FUNCTION__, 33, 45, mir_pixel_format_abgr_8888,
233+ mir_buffer_usage_hardware, mir_display_output_id_invalid };
234+
235+ MockEventHandler mock_event_handler;
236+
237+ auto connection = mir_connect_sync(mtf::test_socket_file().c_str() , __PRETTY_FUNCTION__);
238+ mir_connection_set_lifecycle_event_callback(connection, &MockEventHandler::handle, &mock_event_handler);
239+ auto surface = mir_connection_create_surface_sync(connection, &params);
240+
241+ std::atomic<bool> signalled(false);
242+
243+ EXPECT_CALL(mock_event_handler, handle(mir_lifecycle_connection_lost)).Times(1).
244+ WillOnce(testing::InvokeWithoutArgs([&] { signalled.store(true); }));
245+
246+ sync.signal_ready();
247+
248+ using clock = std::chrono::high_resolution_clock;
249+
250+ auto time_limit = clock::now() + std::chrono::seconds(2);
251+
252+ while (!signalled.load() && clock::now() < time_limit)
253+ {
254+ mir_surface_swap_buffers_sync(surface);
255+ }
256+ }
257+
258+ mtf::CrossProcessSync sync;
259+};
260+}
261+
262+TEST_F(ServerDisconnect, client_detects_server_shutdown)
263+{
264+ TestingServerConfiguration server_config;
265+ launch_server_process(server_config);
266+
267+ MyTestingClientConfiguration client_config;
268+ launch_client_process(client_config);
269+
270+ run_in_test_process([this, &client_config]
271+ {
272+ client_config.sync.wait_for_signal_ready_for();
273+ shutdown_server_process();
274+ });
275+}
276
277=== modified file 'tests/unit-tests/frontend/test_published_socket_connector.cpp'
278--- tests/unit-tests/frontend/test_published_socket_connector.cpp 2013-09-27 16:06:10 +0000
279+++ tests/unit-tests/frontend/test_published_socket_connector.cpp 2013-10-04 16:55:04 +0000
280@@ -221,12 +221,10 @@
281 Mock::VerifyAndClearExpectations(client.get());
282
283 EXPECT_CALL(*client->rpc_report, invocation_failed(InvocationMethodEq("disconnect"),_));
284- EXPECT_CALL(*client, disconnect_done()).Times(0);
285+ EXPECT_CALL(*client, disconnect_done()).Times(1);
286
287 EXPECT_THROW({
288- // We don't know if this will be called, so it can't auto destruct
289- std::unique_ptr<google::protobuf::Closure> new_callback(google::protobuf::NewPermanentCallback(client.get(), &mt::TestProtobufClient::disconnect_done));
290- client->display_server.disconnect(0, &client->ignored, &client->ignored, new_callback.get());
291+ client->display_server.disconnect(0, &client->ignored, &client->ignored, google::protobuf::NewCallback(client.get(), &mt::TestProtobufClient::disconnect_done));
292 client->wait_for_disconnect_done();
293 }, std::runtime_error);
294 }

Subscribers

People subscribed via source and target branches