Merge lp:~alan-griffiths/miral/fix-1668651 into lp:miral

Proposed by Alan Griffiths
Status: Merged
Approved by: Gerry Boland
Approved revision: 536
Merged at revision: 525
Proposed branch: lp:~alan-griffiths/miral/fix-1668651
Merge into: lp:miral
Diff against target: 430 lines (+120/-41)
10 files modified
miral-shell/decoration_provider.cpp (+8/-8)
miral-shell/decoration_provider.h (+0/-2)
miral-shell/shell_main.cpp (+6/-2)
miral-shell/titlebar_window_manager.cpp (+3/-9)
miral-shell/titlebar_window_manager.h (+5/-1)
miral/CMakeLists.txt (+2/-1)
miral/internal_client.cpp (+61/-9)
miral/join_client_threads.h (+24/-0)
miral/runner.cpp (+11/-2)
miral/symbols.map (+0/-7)
To merge this branch: bzr merge lp:~alan-griffiths/miral/fix-1668651
Reviewer Review Type Date Requested Status
Gerry Boland (community) Approve
Review via email: mp+318616@code.launchpad.net

Commit message

miral-shell shutdown was a mess. Fixed.

Description of the change

miral-shell shutdown was a mess.

After fixing the segfault, the server closed down first leading to SIGHUP causing the server to shutdown with EXIT_FAILURE.

So changed the lifecycle callback.

Then operations on the decorations "client" were failing (because the connection had closed). So made it possible for the shell main() to join the client threads.

The server now closes cleanly.

Not sure this is the cleanest code, so any suggestions are welcome.

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

Note that on versions of Mir prior to 0.24 closing the server with a signal (e.g. SIGTERM) still leads to a segfault. That's because we lack mir::server::add_stop_callback() - or any way to hook into that mechanism for closing the server.

That affects 16.04LTS (Mir 0.21) but is "only" a crash on exit. I don't think we need to block. (But I'll think about possible solutions.)

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

> That affects 16.04LTS (Mir 0.21) but is "only" a crash on exit. I don't think
> we need to block. (But I'll think about possible solutions.)

Found a solution

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

I've considered maintaining an internal list of InternalClientRunners and calling join_client_thread() on all of them immediately *after* executing any client provided stop callbacks.

That would simplify the main() functions (miral-kiosk needs nothing, miral-shell just needs the hook to close the decorations provider).

It would also be possible to introduce a mechanism to notify internal clients of a close request by supplying overloads that provide a callback for this purpose.

But all that is providing convenience for simple usecases when the mechanisms provided here are needed to address the general case.

Revision history for this message
Gerry Boland (gerboland) wrote :

All looks ok!

review: Approve
Revision history for this message
Gerry Boland (gerboland) wrote :

Ok, this makes sense.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'miral-shell/decoration_provider.cpp'
2--- miral-shell/decoration_provider.cpp 2017-02-24 13:00:28 +0000
3+++ miral-shell/decoration_provider.cpp 2017-03-02 13:47:39 +0000
4@@ -258,7 +258,6 @@
5
6 DecorationProvider::~DecorationProvider()
7 {
8- stop();
9 }
10
11 void DecorationProvider::stop()
12@@ -271,16 +270,18 @@
13
14 enqueue_work([this]
15 {
16- {
17+ if (connection)
18+ {
19 #if MIR_CLIENT_API_VERSION < MIR_VERSION_NUMBER(0, 26, 2)
20 auto const Workaround_lp_1667645 =
21- WindowSpec::for_normal_window(connection, 100, 100, mir_pixel_format_xrgb_8888).create_window();
22+ WindowSpec::for_normal_window(connection, 100, 100, mir_pixel_format_xrgb_8888)
23+ .set_name(wallpaper_name).create_window();
24 #endif
25 wallpaper.erase(begin(wallpaper), end(wallpaper));
26- }
27+ }
28 connection.reset();
29- stop_work();
30 });
31+ stop_work();
32 }
33
34 namespace
35@@ -423,7 +424,7 @@
36 void DecorationProvider::place_new_decoration(miral::WindowSpecification& window_spec)
37 {
38 auto const name = window_spec.name().value();
39- if (name == "wallpaper") return;
40+ if (name == wallpaper_name) return;
41
42 std::lock_guard<decltype(mutex)> lock{mutex};
43
44@@ -532,7 +533,6 @@
45
46 Worker::~Worker()
47 {
48- if (worker.joinable()) worker.join();
49 }
50
51 void Worker::do_work()
52@@ -560,7 +560,7 @@
53
54 void Worker::start_work()
55 {
56- worker = std::thread{[this] { do_work(); }};
57+ do_work();
58 }
59
60 void Worker::stop_work()
61
62=== modified file 'miral-shell/decoration_provider.h'
63--- miral-shell/decoration_provider.h 2017-02-21 11:54:06 +0000
64+++ miral-shell/decoration_provider.h 2017-03-02 13:47:39 +0000
65@@ -31,7 +31,6 @@
66 #include <atomic>
67 #include <map>
68 #include <mutex>
69-#include <thread>
70 #include <condition_variable>
71 #include <queue>
72
73@@ -51,7 +50,6 @@
74 std::condition_variable work_cv;
75 WorkQueue work_queue;
76 bool work_done = false;
77- std::thread worker;
78
79 void do_work();
80 };
81
82=== modified file 'miral-shell/shell_main.cpp'
83--- miral-shell/shell_main.cpp 2016-12-01 11:48:56 +0000
84+++ miral-shell/shell_main.cpp 2017-03-02 13:47:39 +0000
85@@ -1,5 +1,5 @@
86 /*
87- * Copyright © 2016 Canonical Ltd.
88+ * Copyright © 2016-2017 Canonical Ltd.
89 *
90 * This program is free software: you can redistribute it and/or modify
91 * it under the terms of the GNU General Public License version 3 as
92@@ -37,17 +37,21 @@
93 {
94 using namespace miral;
95
96+ std::function<void()> shutdown_hook{[]{}};
97+
98 SpinnerSplash spinner;
99 InternalClientLauncher launcher;
100 ActiveOutputsMonitor outputs_monitor;
101 WindowManagerOptions window_managers
102 {
103- add_window_manager_policy<TitlebarWindowManagerPolicy>("titlebar", spinner, launcher),
104+ add_window_manager_policy<TitlebarWindowManagerPolicy>("titlebar", spinner, launcher, shutdown_hook),
105 add_window_manager_policy<TilingWindowManagerPolicy>("tiling", spinner, launcher, outputs_monitor),
106 };
107
108 MirRunner runner{argc, argv};
109
110+ runner.add_stop_callback([&] { shutdown_hook(); });
111+
112 auto const quit_on_ctrl_alt_bksp = [&](MirEvent const* event)
113 {
114 if (mir_event_get_type(event) != mir_event_type_input)
115
116=== modified file 'miral-shell/titlebar_window_manager.cpp'
117--- miral-shell/titlebar_window_manager.cpp 2017-02-21 11:54:06 +0000
118+++ miral-shell/titlebar_window_manager.cpp 2017-03-02 13:47:39 +0000
119@@ -49,12 +49,14 @@
120 TitlebarWindowManagerPolicy::TitlebarWindowManagerPolicy(
121 WindowManagerTools const& tools,
122 SpinnerSplash const& spinner,
123- miral::InternalClientLauncher const& launcher) :
124+ miral::InternalClientLauncher const& launcher,
125+ std::function<void()>& shutdown_hook) :
126 CanonicalWindowManagerPolicy(tools),
127 spinner{spinner},
128 decoration_provider{std::make_unique<DecorationProvider>(tools)}
129 {
130 launcher.launch("decorations", *decoration_provider);
131+ shutdown_hook = [this] { decoration_provider->stop(); };
132
133 for (auto key : {KEY_F1, KEY_F2, KEY_F3, KEY_F4})
134 key_to_workspace[key] = this->tools.create_workspace();
135@@ -497,14 +499,6 @@
136 }
137 }
138
139- // TODO this is a workaround for the lack of a way to detect server exit (Mir bug lp:1593655)
140- // We need to exit the titlebar_provider "client" thread before the server exits
141- if (action == mir_keyboard_action_down && scan_code == KEY_BACKSPACE &&
142- (modifiers == (mir_input_event_modifier_alt | mir_input_event_modifier_ctrl)))
143- {
144- decoration_provider->stop();
145- }
146-
147 return false;
148 }
149
150
151=== modified file 'miral-shell/titlebar_window_manager.h'
152--- miral-shell/titlebar_window_manager.h 2017-02-21 11:54:06 +0000
153+++ miral-shell/titlebar_window_manager.h 2017-03-02 13:47:39 +0000
154@@ -36,7 +36,11 @@
155 class TitlebarWindowManagerPolicy : public miral::CanonicalWindowManagerPolicy, miral::WorkspacePolicy
156 {
157 public:
158- TitlebarWindowManagerPolicy(miral::WindowManagerTools const& tools, SpinnerSplash const& spinner, miral::InternalClientLauncher const& launcher);
159+ TitlebarWindowManagerPolicy(
160+ miral::WindowManagerTools const& tools,
161+ SpinnerSplash const& spinner,
162+ miral::InternalClientLauncher const& launcher,
163+ std::function<void()>& shutdown_hook);
164 ~TitlebarWindowManagerPolicy();
165
166 virtual miral::WindowSpecification place_new_window(
167
168=== modified file 'miral/CMakeLists.txt'
169--- miral/CMakeLists.txt 2017-02-20 17:31:11 +0000
170+++ miral/CMakeLists.txt 2017-03-02 13:47:39 +0000
171@@ -21,7 +21,8 @@
172 window_management_trace.cpp window_management_trace.h
173 xcursor_loader.cpp xcursor_loader.h
174 xcursor.c xcursor.h
175- both_versions.h
176+ both_versions.h
177+ join_client_threads.h
178 )
179
180 set_source_files_properties(xcursor.c PROPERTIES COMPILE_DEFINITIONS _GNU_SOURCE)
181
182=== modified file 'miral/internal_client.cpp'
183--- miral/internal_client.cpp 2017-02-15 12:17:12 +0000
184+++ miral/internal_client.cpp 2017-03-02 13:47:39 +0000
185@@ -1,5 +1,5 @@
186 /*
187- * Copyright © 2016 Canonical Ltd.
188+ * Copyright © 2016-2017 Canonical Ltd.
189 *
190 * This program is free software: you can redistribute it and/or modify it
191 * under the terms of the GNU General Public License version 3,
192@@ -17,6 +17,7 @@
193 */
194
195 #include "miral/internal_client.h"
196+#include "join_client_threads.h"
197 #include "both_versions.h"
198
199 #include <mir/fd.h>
200@@ -24,10 +25,14 @@
201 #include <mir/scene/session.h>
202 #include <mir/main_loop.h>
203
204+#define MIR_LOG_COMPONENT "miral::Internal Client"
205+#include <mir/log.h>
206+
207 #include <atomic>
208 #include <condition_variable>
209 #include <mutex>
210 #include <thread>
211+#include <map>
212
213 namespace
214 {
215@@ -39,6 +44,8 @@
216 std::function<void(std::weak_ptr<mir::scene::Session> const session)> connect_notification);
217
218 void run(mir::Server& server);
219+ void join_client_thread();
220+
221 ~InternalClientRunner();
222
223 private:
224@@ -52,13 +59,34 @@
225 std::function<void(mir::client::Connection connection)> const client_code;
226 std::function<void(std::weak_ptr<mir::scene::Session> const session)> connect_notification;
227 };
228-}
229-
230-class miral::StartupInternalClient::Self : InternalClientRunner
231-{
232-public:
233+
234+std::mutex client_runners_mutex;
235+std::multimap<mir::Server*, std::weak_ptr<InternalClientRunner>> client_runners;
236+
237+void register_runner(mir::Server* server, std::weak_ptr<InternalClientRunner> internal_client)
238+{
239+ std::lock_guard<decltype(client_runners_mutex)> lock{client_runners_mutex};
240+ client_runners.emplace(server, std::move(internal_client));
241+}
242+
243+void join_runners_for(mir::Server* server)
244+{
245+ std::lock_guard<decltype(client_runners_mutex)> lock{client_runners_mutex};
246+ auto range = client_runners.equal_range(server);
247+
248+ for (auto i = range.first; i != range.second; ++i)
249+ {
250+ if (auto runner = i->second.lock())
251+ runner->join_client_thread();
252+ }
253+
254+ client_runners.erase(range.first, range.second);
255+}
256+}
257+
258+class miral::StartupInternalClient::Self : public InternalClientRunner
259+{
260 using InternalClientRunner::InternalClientRunner;
261- using InternalClientRunner::run;
262 };
263
264 InternalClientRunner::InternalClientRunner(
265@@ -86,6 +114,16 @@
266
267 connection = mir::client::Connection{mir_connect_sync(connect_string, name.c_str())};
268
269+ mir_connection_set_lifecycle_event_callback(
270+ connection,
271+ [](MirConnection*, MirLifecycleState transition, void*)
272+ {
273+ // The default handler kills the process with SIGHUP - which is unhelpful for internal clients
274+ if (transition == mir_lifecycle_connection_lost)
275+ mir::log_warning("server disconnected before connection released by client");
276+ },
277+ this);
278+
279 std::unique_lock<decltype(mutex)> lock{mutex};
280 cv.wait(lock, [&] { return !!session.lock(); });
281
282@@ -98,6 +136,12 @@
283
284 InternalClientRunner::~InternalClientRunner()
285 {
286+ join_client_thread();
287+}
288+
289+
290+void InternalClientRunner::join_client_thread()
291+{
292 if (thread.joinable())
293 {
294 thread.join();
295@@ -124,6 +168,8 @@
296
297 void miral::StartupInternalClient::operator()(mir::Server& server)
298 {
299+ register_runner(&server, internal_client);
300+
301 server.add_init_callback([this, &server]
302 {
303 server.the_main_loop()->enqueue(this, [this, &server]
304@@ -138,7 +184,7 @@
305 struct miral::InternalClientLauncher::Self
306 {
307 mir::Server* server = nullptr;
308- std::unique_ptr<InternalClientRunner> runner;
309+ std::shared_ptr<InternalClientRunner> runner;
310 };
311
312 void miral::InternalClientLauncher::operator()(mir::Server& server)
313@@ -155,9 +201,15 @@
314 std::function<void(mir::client::Connection connection)> const& client_code,
315 std::function<void(std::weak_ptr<mir::scene::Session> const session)> const& connect_notification) const
316 {
317- self->runner = std::make_unique<InternalClientRunner>(name, client_code, connect_notification);
318+ self->runner = std::make_shared<InternalClientRunner>(name, client_code, connect_notification);
319 self->server->the_main_loop()->enqueue(this, [this] { self->runner->run(*self->server); });
320+ register_runner(self->server, self->runner);
321 }
322
323 miral::InternalClientLauncher::InternalClientLauncher() : self{std::make_shared<Self>()} {}
324 miral::InternalClientLauncher::~InternalClientLauncher() = default;
325+
326+void join_client_threads(mir::Server* server)
327+{
328+ join_runners_for(server);
329+}
330
331=== added file 'miral/join_client_threads.h'
332--- miral/join_client_threads.h 1970-01-01 00:00:00 +0000
333+++ miral/join_client_threads.h 2017-03-02 13:47:39 +0000
334@@ -0,0 +1,24 @@
335+/*
336+ * Copyright © 2017 Canonical Ltd.
337+ *
338+ * This program is free software: you can redistribute it and/or modify it
339+ * under the terms of the GNU General Public License version 3,
340+ * as published by the Free Software Foundation.
341+ *
342+ * This program is distributed in the hope that it will be useful,
343+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
344+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
345+ * GNU General Public License for more details.
346+ *
347+ * You should have received a copy of the GNU General Public License
348+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
349+ *
350+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
351+ */
352+
353+#ifndef MIRAL_JOIN_CLIENT_THREADS_H
354+#define MIRAL_JOIN_CLIENT_THREADS_H
355+
356+void join_client_threads(mir::Server* server);
357+
358+#endif //MIRAL_JOIN_CLIENT_THREADS_H
359
360=== modified file 'miral/runner.cpp'
361--- miral/runner.cpp 2016-12-01 11:48:56 +0000
362+++ miral/runner.cpp 2017-03-02 13:47:39 +0000
363@@ -1,5 +1,5 @@
364 /*
365- * Copyright © 2016 Canonical Ltd.
366+ * Copyright © 2016-2017 Canonical Ltd.
367 *
368 * This program is free software: you can redistribute it and/or modify it
369 * under the terms of the GNU General Public License version 3,
370@@ -17,6 +17,7 @@
371 */
372
373 #include "miral/runner.h"
374+#include "join_client_threads.h"
375
376 #include <mir/server.h>
377 #include <mir/main_loop.h>
378@@ -29,6 +30,10 @@
379 #include <mutex>
380 #include <thread>
381
382+#if MIR_SERVER_VERSION < MIR_VERSION_NUMBER(0, 24, 0)
383+#include <csignal>
384+#endif
385+
386 namespace
387 {
388 inline auto filename(std::string path) -> std::string
389@@ -50,7 +55,7 @@
390
391 std::mutex mutex;
392 std::function<void()> start_callback{[]{}};
393- std::function<void()> stop_callback{[]{}};
394+ std::function<void()> stop_callback{[this]{ join_client_threads(weak_server.lock().get()); }};
395 std::function<void()> exception_handler{static_cast<void(*)()>(mir::report_exception)};
396 std::weak_ptr<mir::Server> weak_server;
397 };
398@@ -215,6 +220,10 @@
399 auto const main_loop = server->the_main_loop();
400 main_loop->enqueue(this, start_callback);
401
402+#if MIR_SERVER_VERSION < MIR_VERSION_NUMBER(0, 24, 0)
403+ main_loop->register_signal_handler({SIGINT, SIGTERM}, [this](int) {stop_callback();});
404+#endif
405+
406 server->run();
407 }
408
409
410=== modified file 'miral/symbols.map'
411--- miral/symbols.map 2017-02-15 17:32:34 +0000
412+++ miral/symbols.map 2017-03-02 13:47:39 +0000
413@@ -381,17 +381,10 @@
414 miral::WorkspacePolicy::advise_adding_to_workspace*;
415 miral::WorkspacePolicy::advise_removing_from_workspace*;
416 miral::WorkspacePolicy::operator*;
417- miral::toolkit::Window::Window*;
418 non-virtual?thunk?to?miral::WorkspacePolicy::?WorkspacePolicy*;
419 non-virtual?thunk?to?miral::WorkspacePolicy::advise_adding_to_workspace*;
420 non-virtual?thunk?to?miral::WorkspacePolicy::advise_removing_from_workspace*;
421 typeinfo?for?miral::WorkspacePolicy;
422- typeinfo?for?miral::toolkit::Window;
423- typeinfo?for?miral::toolkit::WindowId;
424- typeinfo?for?miral::toolkit::WindowSpec;
425 vtable?for?miral::WorkspacePolicy;
426- vtable?for?miral::toolkit::Window;
427- vtable?for?miral::toolkit::WindowId;
428- vtable?for?miral::toolkit::WindowSpec;
429 };
430 } MIRAL_1.2;

Subscribers

People subscribed via source and target branches