Merge lp:~victored/cerbere/session-manager-client into lp:~elementary-pantheon/cerbere/cerbere

Proposed by Victor Martinez
Status: Merged
Merged at revision: 30
Proposed branch: lp:~victored/cerbere/session-manager-client
Merge into: lp:~elementary-pantheon/cerbere/cerbere
Diff against target: 455 lines (+276/-32)
6 files modified
src/CMakeLists.txt (+5/-3)
src/Cerbere.vala (+35/-3)
src/ProcessInfo.vala (+6/-7)
src/SessionManager.vala (+205/-0)
src/SettingsManager.vala (+2/-2)
src/Watchdog.vala (+23/-17)
To merge this branch: bzr merge lp:~victored/cerbere/session-manager-client
Reviewer Review Type Date Requested Status
Cody Garver (community) Approve
Review via email: mp+113806@code.launchpad.net

Description of the change

Say goodbye to the long desktop startup times ...

To post a comment you must log in.
Revision history for this message
Cody Garver (codygarver) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/CMakeLists.txt'
2--- src/CMakeLists.txt 2012-06-14 22:29:47 +0000
3+++ src/CMakeLists.txt 2012-07-06 21:51:19 +0000
4@@ -16,6 +16,7 @@
5 VALA_C
6 Cerbere.vala
7 SettingsManager.vala
8+ SessionManager.vala
9 Watchdog.vala
10 ProcessInfo.vala
11 PACKAGES
12@@ -26,6 +27,7 @@
13 --target-glib=2.32 # Remember to keep this updated.
14 )
15
16-add_executable(cerbere ${VALA_C})
17-target_link_libraries(cerbere ${DEPS_LIBRARIES})
18-install(TARGETS cerbere RUNTIME DESTINATION bin)
19+set(EXEC_NAME ${CMAKE_PROJECT_NAME})
20+add_executable(${EXEC_NAME} ${VALA_C})
21+target_link_libraries(${EXEC_NAME} ${DEPS_LIBRARIES})
22+install(TARGETS ${EXEC_NAME} RUNTIME DESTINATION bin)
23
24=== modified file 'src/Cerbere.vala'
25--- src/Cerbere.vala 2012-06-14 22:28:44 +0000
26+++ src/Cerbere.vala 2012-07-06 21:51:19 +0000
27@@ -28,17 +28,28 @@
28 public class Cerbere : Application {
29
30 public static SettingsManager settings { get; private set; default = null; }
31- private Watchdog watchdog;
32+ private Watchdog? watchdog = null;
33+ private SessionManager.ClientService? sm_client = null;
34
35 construct {
36 application_id = "org.pantheon.cerbere";
37 flags = ApplicationFlags.IS_SERVICE;
38+ Log.set_handler (null, LogLevelFlags.LEVEL_MASK, log_handler);
39+ }
40+
41+ private static void log_handler (string? domain, LogLevelFlags level, string message) {
42+ if (level >= LogLevelFlags.LEVEL_INFO)
43+ level = LogLevelFlags.LEVEL_MESSAGE;
44+ Log.default_handler (domain, level, message);
45 }
46
47 protected override void startup () {
48+ // Try to register Cerbere with the session manager.
49+ // This is a non-blocking action.
50+ register_session_client_async ();
51+
52 this.settings = new SettingsManager ();
53-
54- this.start_processes (this.settings.process_list);
55+ start_processes (this.settings.process_list);
56
57 // Monitor changes to the process list
58 this.settings.process_list_changed.connect (this.start_processes);
59@@ -47,6 +58,27 @@
60 main_loop.run ();
61 }
62
63+ private async void register_session_client_async () {
64+ if (this.sm_client != null)
65+ return;
66+
67+ this.sm_client = new SessionManager.ClientService (this.application_id);
68+
69+ try {
70+ this.sm_client.register ();
71+ } catch (SessionManager.ConnectionError e) {
72+ critical (e.message);
73+ }
74+
75+ if (this.sm_client != null) {
76+ // The session manager may ask us to quit the service, and so we do.
77+ this.sm_client.stop_service.connect ( () => {
78+ message ("Exiting...");
79+ this.quit_mainloop ();
80+ });
81+ }
82+ }
83+
84 private void start_processes (string[] process_list) {
85 if (this.watchdog == null) {
86 this.watchdog = new Watchdog ();
87
88=== modified file 'src/ProcessInfo.vala'
89--- src/ProcessInfo.vala 2012-06-17 16:22:40 +0000
90+++ src/ProcessInfo.vala 2012-07-06 21:51:19 +0000
91@@ -43,7 +43,7 @@
92
93 public void reset_crash_count () {
94 this.crash_count = 0;
95- debug ("Crash count of '%s' has been reset", this.command);
96+ message ("Crash count of '%s' has been reset", this.command);
97 }
98
99 public async void run_async () {
100@@ -51,10 +51,10 @@
101 }
102
103 private void run () {
104- message ("STARTING process: %s", command);
105+ debug ("STARTING process: %s", command);
106
107 if (this.status == Status.RUNNING) {
108- message ("Process %s is already running", command);
109+ warning ("Process %s is already running. Not starting it again...", command);
110 return;
111 }
112
113@@ -102,7 +102,7 @@
114 if (pid != this.pid)
115 return;
116
117- message ("Process '%s' exited", command);
118+ message ("Process '%s' watch exit", command);
119
120 // Check exit status
121 if (Process.if_exited (status) || Process.if_signaled (status) || Process.core_dump (status)) {
122@@ -126,13 +126,12 @@
123 double elapsed_secs = this.timer.elapsed ();
124 double crash_time_interval_secs = (double)Cerbere.settings.crash_time_interval / 1000.0;
125
126- debug ("Elapsed time = %f secs", elapsed_secs);
127- debug ("Min allowed time = %f secs", crash_time_interval_secs);
128+ message ("ET = %f secs\tMin allowed time = %f", elapsed_secs, crash_time_interval_secs);
129
130 if (elapsed_secs <= crash_time_interval_secs) { // process crashed
131 this.crash_count ++;
132 normal_exit = false;
133- message ("PROCESS '%s' CRASHED (#%u)", this.command, this.crash_count);
134+ warning ("PROCESS '%s' CRASHED (#%u)", this.command, this.crash_count);
135 }
136
137 // Remove the current timer
138
139=== added file 'src/SessionManager.vala'
140--- src/SessionManager.vala 1970-01-01 00:00:00 +0000
141+++ src/SessionManager.vala 2012-07-06 21:51:19 +0000
142@@ -0,0 +1,205 @@
143+/* -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- */
144+/*
145+ * Copyright (C) 2012 Victor Eduardo <victoreduardm@gmail.com>
146+ *
147+ * Cerbere is free software; you can redistribute it and/or modify
148+ * it under the terms of the GNU General Public License as published by
149+ * the Free Software Foundation; either version 2 of the License, or
150+ * (at your option) any later version.
151+ *
152+ * Cerbere is distributed in the hope that it will be useful,
153+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
154+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
155+ * GNU General Public License for more details.
156+ *
157+ * You should have received a copy of the GNU General Public License
158+ * along with Cerbere; if not, write to the Free Software
159+ * Foundation, Inc., 51 Franklin St, Fifth Floor,
160+ * Boston, MA 02110-1301 USA
161+ *
162+ * Authors: Victor Eduardo <victoreduardm@gmail.com>
163+ */
164+
165+namespace SessionManager {
166+
167+ public errordomain ConnectionError {
168+ CONNECTION_FAILED,
169+ CLIENT_REGISTRATION_FAILED
170+ }
171+
172+
173+ /**
174+ * GNOME Session Manager DBus API
175+ *
176+ * API Reference: [[http://www.gnome.org/~mccann/gnome-session/docs/gnome-session.html]]
177+ * (Consulted on July 4, 2012.)
178+ */
179+
180+ private const string DBUS_NAME = "org.gnome.SessionManager";
181+ private const string DBUS_PATH = "/org/gnome/SessionManager";
182+
183+ [DBus (name = "org.gnome.SessionManager")]
184+ private interface SessionManager : Object {
185+ // Many API methods have been left out. Feel free to add them when required.
186+ public abstract void RegisterClient (string app_id, string client_startup_id,
187+ out string client_id) throws IOError;
188+ public abstract void UnregisterClient (ObjectPath client_id) throws IOError;
189+ }
190+
191+ [DBus (name = "org.gnome.SessionManager.ClientPrivate")]
192+ private interface ClientPrivate : Object {
193+ public abstract void EndSessionResponse (bool is_ok, string reason) throws IOError;
194+ public signal void QueryEndSession (uint flags);
195+ public signal void EndSession (uint flags);
196+ public signal void CancelEndSession ();
197+ public signal void Stop ();
198+ }
199+
200+
201+ /**
202+ * CLIENT
203+ *
204+ * This class handles both the registration of the service,
205+ * and action requests coming from the session-manager side.
206+ */
207+
208+ public class ClientService : Object {
209+ public signal void stop_service ();
210+
211+ private SessionManager? session_manager = null;
212+ private ClientPrivate? client = null;
213+ private string? client_id = null;
214+ private string? app_id = null;
215+
216+ public ClientService (string app_id) {
217+ this.app_id = app_id;
218+ }
219+
220+ public void register () throws ConnectionError {
221+ bool connected = true;
222+
223+ if (session_manager == null) {
224+ connected = connect_session ();
225+
226+ if (!connected) {
227+ throw new ConnectionError.CONNECTION_FAILED ("Could not connect to session manager");
228+ }
229+ }
230+
231+ connected = register_client ();
232+
233+ if (!connected) {
234+ // Disconnect from SM
235+ session_manager = null;
236+ throw new ConnectionError.CLIENT_REGISTRATION_FAILED ("Unable to register client with session manager");
237+ }
238+
239+ }
240+
241+ public void unregister () {
242+ return_if_fail (session_manager != null && client_id != null);
243+
244+ debug ("Unregistering client: %s", client_id);
245+
246+ try {
247+ session_manager.UnregisterClient (new ObjectPath (client_id));
248+ } catch (IOError e) {
249+ critical (e.message);
250+ }
251+ }
252+
253+ private bool connect_session () {
254+ if (session_manager == null) {
255+ try {
256+ session_manager = Bus.get_proxy_sync (BusType.SESSION, DBUS_NAME, DBUS_PATH);
257+ } catch (IOError e) {
258+ critical (e.message);
259+ }
260+ }
261+
262+ return session_manager != null;
263+ }
264+
265+ private bool register_client () {
266+ return_val_if_fail (session_manager != null && app_id != null, false);
267+
268+ string? startup_id = Environment.get_variable ("DESKTOP_AUTOSTART_ID");
269+
270+ if (startup_id == null) {
271+ critical ("Could not get value of environment variable DESKTOP_AUTOSTART_ID");
272+ return false;
273+ }
274+
275+ // Register client
276+ if (client == null) {
277+ try {
278+ session_manager.RegisterClient (app_id, startup_id, out client_id);
279+ } catch (IOError e) {
280+ critical ("Could not register client: %s", e.message);
281+ }
282+
283+ return_val_if_fail (client_id != null, false);
284+
285+ debug ("Registered session manager client: %s", client_id);
286+
287+ // Get client
288+ try {
289+ client = Bus.get_proxy_sync (BusType.SESSION, DBUS_NAME, client_id);
290+ } catch (IOError e) {
291+ critical ("Could not get client: %s", e.message);
292+ return_val_if_reached (false);
293+ }
294+
295+ debug ("Obtained gnome-session client proxy");
296+
297+ // Connect signals
298+ client.QueryEndSession.connect (on_client_query_end_session);
299+ client.EndSession.connect (on_client_end_session);
300+ client.CancelEndSession.connect (on_client_cancel_end_session);
301+ client.Stop.connect (on_client_stop);
302+ }
303+
304+ return client != null;
305+ }
306+
307+
308+ /** ClientPrivate Signal handlers **/
309+
310+ private void on_client_query_end_session (uint flags) {
311+ debug ("Client query end session");
312+ return_if_fail (client != null);
313+
314+ send_end_session_response (true);
315+ }
316+
317+ private void on_client_end_session (uint flags) {
318+ debug ("Client end session");
319+ return_if_fail (client != null);
320+
321+ send_end_session_response (true);
322+ on_client_stop ();
323+ }
324+
325+ private void on_client_cancel_end_session () {
326+ debug ("Client: Received EndSessionCanceled signal");
327+ }
328+
329+ private void on_client_stop () {
330+ debug ("Client: Received Stop signal");
331+ this.unregister ();
332+ this.stop_service ();
333+ }
334+
335+ private inline void send_end_session_response (bool is_okay, string reason = "") {
336+ return_if_fail (client != null);
337+
338+ // Tell the session manager whether it's okay to logout, shut down, etc.
339+ try {
340+ debug ("Sending is_okay = %s to session manager", is_okay.to_string ());
341+ client.EndSessionResponse (true, "");
342+ } catch (IOError e) {
343+ warning ("Couldn't reply to session manager: %s", e.message);
344+ }
345+ }
346+ }
347+}
348
349=== modified file 'src/SettingsManager.vala'
350--- src/SettingsManager.vala 2012-06-14 22:28:44 +0000
351+++ src/SettingsManager.vala 2012-07-06 21:51:19 +0000
352@@ -30,7 +30,7 @@
353 static const string CRASH_TIME_INTERVAL_KEY = "crash-time-interval";
354 static const string MONITORED_PROCESSES_KEY = "monitored-processes";
355
356- public string[] process_list { get; set; }
357+ public string[] process_list { get; set; }
358 public uint max_crashes { get; set; default = 0; }
359 public uint crash_time_interval { get; set; default = 0; }
360
361@@ -47,6 +47,6 @@
362 }
363
364 private void on_process_list_changed () {
365- this.process_list_changed (this.settings.get_strv (MONITORED_PROCESSES_KEY));
366+ this.process_list_changed (this.process_list);
367 }
368 }
369
370=== modified file 'src/Watchdog.vala'
371--- src/Watchdog.vala 2012-06-17 04:31:37 +0000
372+++ src/Watchdog.vala 2012-07-06 21:51:19 +0000
373@@ -25,10 +25,10 @@
374 * Victor Eduardo <victoreduardm@gmail.com>
375 */
376
377-public class Watchdog {
378+public class Watchdog : Object {
379+
380 // Contains ALL the processes that are being monitored
381 private Gee.HashMap<string, ProcessInfo> processes;
382- private Mutex data_lock;
383
384 public Watchdog () {
385 this.processes = new Gee.HashMap<string, ProcessInfo> ();
386@@ -43,16 +43,17 @@
387 return;
388
389 // Check if a process for this command has already been created
390- if (this.processes.has_key (command))
391+ if (this.processes.has_key (command)) {
392 return;
393+ }
394
395 // Create new process
396 var process = new ProcessInfo (command);
397
398 // Add it to the table
399- this.data_lock.lock ();
400- this.processes[command] = process;
401- this.data_lock.unlock ();
402+ lock (this.processes) {
403+ this.processes[command] = process;
404+ }
405
406 // Exit handler. Respawning occurs here
407 process.exited.connect ( (normal_exit) => {
408@@ -62,6 +63,8 @@
409 process.reset_crash_count ();
410 }
411
412+ bool remove_process = false;
413+
414 // if still in the list, relaunch if possible
415 if (command in Cerbere.settings.process_list) {
416 // Check if the process is still present in the table since it could have been removed.
417@@ -73,25 +76,28 @@
418 process.run_async (); // Reload right away
419 }
420 else {
421- message ("'%s' exceeded the maximum number of crashes allowed (%s). It won't be launched again",
422- command, max_crashes.to_string ());
423+ warning ("'%s' exceeded the maximum number of crashes allowed (%s). It won't be launched again", command, max_crashes.to_string ());
424+ remove_process = true;
425 }
426 }
427 else {
428- // If a process is not in the table, it means it wasn't re-launched after it exited, so
429- // in theory this code is never reached.
430- warning ("You should NEVER get this message. If you're getting it, file a bug!");
431+ // If a process is not in the table, it means it wasn't re-launched
432+ // after it exited, so in theory this code is never reached.
433+ critical ("Please file a bug at http://launchpad.net/cerbere and attach your ~/.xsession-errors file.");
434 }
435 }
436 else {
437- message ("'%s' is no longer on settings. It will not be monitored anymore", command);
438-
439+ warning ("'%s' is no longer in settings (not monitored))", command);
440 process.reset_crash_count (); // reset
441+ remove_process = true;
442+ }
443
444- // Remove from the list. At this point the reference count should drop to 0 and free the process.
445- this.data_lock.lock ();
446- this.processes.unset (command);
447- this.data_lock.unlock ();
448+ // Remove from the table. At this point the reference count should
449+ // drop to 0 and free the process info.
450+ if (remove_process) {
451+ lock (this.processes) {
452+ this.processes.unset (command);
453+ }
454 }
455 });
456

Subscribers

People subscribed via source and target branches

to all changes: