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
=== modified file 'src/CMakeLists.txt'
--- src/CMakeLists.txt 2012-06-14 22:29:47 +0000
+++ src/CMakeLists.txt 2012-07-06 21:51:19 +0000
@@ -16,6 +16,7 @@
16 VALA_C16 VALA_C
17 Cerbere.vala17 Cerbere.vala
18 SettingsManager.vala18 SettingsManager.vala
19 SessionManager.vala
19 Watchdog.vala20 Watchdog.vala
20 ProcessInfo.vala21 ProcessInfo.vala
21 PACKAGES22 PACKAGES
@@ -26,6 +27,7 @@
26 --target-glib=2.32 # Remember to keep this updated.27 --target-glib=2.32 # Remember to keep this updated.
27)28)
2829
29add_executable(cerbere ${VALA_C})30set(EXEC_NAME ${CMAKE_PROJECT_NAME})
30target_link_libraries(cerbere ${DEPS_LIBRARIES})31add_executable(${EXEC_NAME} ${VALA_C})
31install(TARGETS cerbere RUNTIME DESTINATION bin)32target_link_libraries(${EXEC_NAME} ${DEPS_LIBRARIES})
33install(TARGETS ${EXEC_NAME} RUNTIME DESTINATION bin)
3234
=== modified file 'src/Cerbere.vala'
--- src/Cerbere.vala 2012-06-14 22:28:44 +0000
+++ src/Cerbere.vala 2012-07-06 21:51:19 +0000
@@ -28,17 +28,28 @@
28public class Cerbere : Application {28public class Cerbere : Application {
2929
30 public static SettingsManager settings { get; private set; default = null; }30 public static SettingsManager settings { get; private set; default = null; }
31 private Watchdog watchdog;31 private Watchdog? watchdog = null;
32 private SessionManager.ClientService? sm_client = null;
3233
33 construct {34 construct {
34 application_id = "org.pantheon.cerbere";35 application_id = "org.pantheon.cerbere";
35 flags = ApplicationFlags.IS_SERVICE;36 flags = ApplicationFlags.IS_SERVICE;
37 Log.set_handler (null, LogLevelFlags.LEVEL_MASK, log_handler);
38 }
39
40 private static void log_handler (string? domain, LogLevelFlags level, string message) {
41 if (level >= LogLevelFlags.LEVEL_INFO)
42 level = LogLevelFlags.LEVEL_MESSAGE;
43 Log.default_handler (domain, level, message);
36 }44 }
3745
38 protected override void startup () {46 protected override void startup () {
47 // Try to register Cerbere with the session manager.
48 // This is a non-blocking action.
49 register_session_client_async ();
50
39 this.settings = new SettingsManager ();51 this.settings = new SettingsManager ();
4052 start_processes (this.settings.process_list);
41 this.start_processes (this.settings.process_list);
4253
43 // Monitor changes to the process list54 // Monitor changes to the process list
44 this.settings.process_list_changed.connect (this.start_processes);55 this.settings.process_list_changed.connect (this.start_processes);
@@ -47,6 +58,27 @@
47 main_loop.run ();58 main_loop.run ();
48 }59 }
4960
61 private async void register_session_client_async () {
62 if (this.sm_client != null)
63 return;
64
65 this.sm_client = new SessionManager.ClientService (this.application_id);
66
67 try {
68 this.sm_client.register ();
69 } catch (SessionManager.ConnectionError e) {
70 critical (e.message);
71 }
72
73 if (this.sm_client != null) {
74 // The session manager may ask us to quit the service, and so we do.
75 this.sm_client.stop_service.connect ( () => {
76 message ("Exiting...");
77 this.quit_mainloop ();
78 });
79 }
80 }
81
50 private void start_processes (string[] process_list) {82 private void start_processes (string[] process_list) {
51 if (this.watchdog == null) {83 if (this.watchdog == null) {
52 this.watchdog = new Watchdog ();84 this.watchdog = new Watchdog ();
5385
=== modified file 'src/ProcessInfo.vala'
--- src/ProcessInfo.vala 2012-06-17 16:22:40 +0000
+++ src/ProcessInfo.vala 2012-07-06 21:51:19 +0000
@@ -43,7 +43,7 @@
4343
44 public void reset_crash_count () {44 public void reset_crash_count () {
45 this.crash_count = 0;45 this.crash_count = 0;
46 debug ("Crash count of '%s' has been reset", this.command);46 message ("Crash count of '%s' has been reset", this.command);
47 }47 }
4848
49 public async void run_async () {49 public async void run_async () {
@@ -51,10 +51,10 @@
51 }51 }
5252
53 private void run () {53 private void run () {
54 message ("STARTING process: %s", command);54 debug ("STARTING process: %s", command);
5555
56 if (this.status == Status.RUNNING) {56 if (this.status == Status.RUNNING) {
57 message ("Process %s is already running", command);57 warning ("Process %s is already running. Not starting it again...", command);
58 return;58 return;
59 }59 }
6060
@@ -102,7 +102,7 @@
102 if (pid != this.pid)102 if (pid != this.pid)
103 return;103 return;
104104
105 message ("Process '%s' exited", command);105 message ("Process '%s' watch exit", command);
106106
107 // Check exit status107 // Check exit status
108 if (Process.if_exited (status) || Process.if_signaled (status) || Process.core_dump (status)) {108 if (Process.if_exited (status) || Process.if_signaled (status) || Process.core_dump (status)) {
@@ -126,13 +126,12 @@
126 double elapsed_secs = this.timer.elapsed ();126 double elapsed_secs = this.timer.elapsed ();
127 double crash_time_interval_secs = (double)Cerbere.settings.crash_time_interval / 1000.0;127 double crash_time_interval_secs = (double)Cerbere.settings.crash_time_interval / 1000.0;
128128
129 debug ("Elapsed time = %f secs", elapsed_secs);129 message ("ET = %f secs\tMin allowed time = %f", elapsed_secs, crash_time_interval_secs);
130 debug ("Min allowed time = %f secs", crash_time_interval_secs);
131130
132 if (elapsed_secs <= crash_time_interval_secs) { // process crashed131 if (elapsed_secs <= crash_time_interval_secs) { // process crashed
133 this.crash_count ++;132 this.crash_count ++;
134 normal_exit = false;133 normal_exit = false;
135 message ("PROCESS '%s' CRASHED (#%u)", this.command, this.crash_count);134 warning ("PROCESS '%s' CRASHED (#%u)", this.command, this.crash_count);
136 }135 }
137136
138 // Remove the current timer137 // Remove the current timer
139138
=== added file 'src/SessionManager.vala'
--- src/SessionManager.vala 1970-01-01 00:00:00 +0000
+++ src/SessionManager.vala 2012-07-06 21:51:19 +0000
@@ -0,0 +1,205 @@
1/* -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- */
2/*
3 * Copyright (C) 2012 Victor Eduardo <victoreduardm@gmail.com>
4 *
5 * Cerbere is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * Cerbere is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with Cerbere; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301 USA
19 *
20 * Authors: Victor Eduardo <victoreduardm@gmail.com>
21 */
22
23namespace SessionManager {
24
25 public errordomain ConnectionError {
26 CONNECTION_FAILED,
27 CLIENT_REGISTRATION_FAILED
28 }
29
30
31 /**
32 * GNOME Session Manager DBus API
33 *
34 * API Reference: [[http://www.gnome.org/~mccann/gnome-session/docs/gnome-session.html]]
35 * (Consulted on July 4, 2012.)
36 */
37
38 private const string DBUS_NAME = "org.gnome.SessionManager";
39 private const string DBUS_PATH = "/org/gnome/SessionManager";
40
41 [DBus (name = "org.gnome.SessionManager")]
42 private interface SessionManager : Object {
43 // Many API methods have been left out. Feel free to add them when required.
44 public abstract void RegisterClient (string app_id, string client_startup_id,
45 out string client_id) throws IOError;
46 public abstract void UnregisterClient (ObjectPath client_id) throws IOError;
47 }
48
49 [DBus (name = "org.gnome.SessionManager.ClientPrivate")]
50 private interface ClientPrivate : Object {
51 public abstract void EndSessionResponse (bool is_ok, string reason) throws IOError;
52 public signal void QueryEndSession (uint flags);
53 public signal void EndSession (uint flags);
54 public signal void CancelEndSession ();
55 public signal void Stop ();
56 }
57
58
59 /**
60 * CLIENT
61 *
62 * This class handles both the registration of the service,
63 * and action requests coming from the session-manager side.
64 */
65
66 public class ClientService : Object {
67 public signal void stop_service ();
68
69 private SessionManager? session_manager = null;
70 private ClientPrivate? client = null;
71 private string? client_id = null;
72 private string? app_id = null;
73
74 public ClientService (string app_id) {
75 this.app_id = app_id;
76 }
77
78 public void register () throws ConnectionError {
79 bool connected = true;
80
81 if (session_manager == null) {
82 connected = connect_session ();
83
84 if (!connected) {
85 throw new ConnectionError.CONNECTION_FAILED ("Could not connect to session manager");
86 }
87 }
88
89 connected = register_client ();
90
91 if (!connected) {
92 // Disconnect from SM
93 session_manager = null;
94 throw new ConnectionError.CLIENT_REGISTRATION_FAILED ("Unable to register client with session manager");
95 }
96
97 }
98
99 public void unregister () {
100 return_if_fail (session_manager != null && client_id != null);
101
102 debug ("Unregistering client: %s", client_id);
103
104 try {
105 session_manager.UnregisterClient (new ObjectPath (client_id));
106 } catch (IOError e) {
107 critical (e.message);
108 }
109 }
110
111 private bool connect_session () {
112 if (session_manager == null) {
113 try {
114 session_manager = Bus.get_proxy_sync (BusType.SESSION, DBUS_NAME, DBUS_PATH);
115 } catch (IOError e) {
116 critical (e.message);
117 }
118 }
119
120 return session_manager != null;
121 }
122
123 private bool register_client () {
124 return_val_if_fail (session_manager != null && app_id != null, false);
125
126 string? startup_id = Environment.get_variable ("DESKTOP_AUTOSTART_ID");
127
128 if (startup_id == null) {
129 critical ("Could not get value of environment variable DESKTOP_AUTOSTART_ID");
130 return false;
131 }
132
133 // Register client
134 if (client == null) {
135 try {
136 session_manager.RegisterClient (app_id, startup_id, out client_id);
137 } catch (IOError e) {
138 critical ("Could not register client: %s", e.message);
139 }
140
141 return_val_if_fail (client_id != null, false);
142
143 debug ("Registered session manager client: %s", client_id);
144
145 // Get client
146 try {
147 client = Bus.get_proxy_sync (BusType.SESSION, DBUS_NAME, client_id);
148 } catch (IOError e) {
149 critical ("Could not get client: %s", e.message);
150 return_val_if_reached (false);
151 }
152
153 debug ("Obtained gnome-session client proxy");
154
155 // Connect signals
156 client.QueryEndSession.connect (on_client_query_end_session);
157 client.EndSession.connect (on_client_end_session);
158 client.CancelEndSession.connect (on_client_cancel_end_session);
159 client.Stop.connect (on_client_stop);
160 }
161
162 return client != null;
163 }
164
165
166 /** ClientPrivate Signal handlers **/
167
168 private void on_client_query_end_session (uint flags) {
169 debug ("Client query end session");
170 return_if_fail (client != null);
171
172 send_end_session_response (true);
173 }
174
175 private void on_client_end_session (uint flags) {
176 debug ("Client end session");
177 return_if_fail (client != null);
178
179 send_end_session_response (true);
180 on_client_stop ();
181 }
182
183 private void on_client_cancel_end_session () {
184 debug ("Client: Received EndSessionCanceled signal");
185 }
186
187 private void on_client_stop () {
188 debug ("Client: Received Stop signal");
189 this.unregister ();
190 this.stop_service ();
191 }
192
193 private inline void send_end_session_response (bool is_okay, string reason = "") {
194 return_if_fail (client != null);
195
196 // Tell the session manager whether it's okay to logout, shut down, etc.
197 try {
198 debug ("Sending is_okay = %s to session manager", is_okay.to_string ());
199 client.EndSessionResponse (true, "");
200 } catch (IOError e) {
201 warning ("Couldn't reply to session manager: %s", e.message);
202 }
203 }
204 }
205}
0206
=== modified file 'src/SettingsManager.vala'
--- src/SettingsManager.vala 2012-06-14 22:28:44 +0000
+++ src/SettingsManager.vala 2012-07-06 21:51:19 +0000
@@ -30,7 +30,7 @@
30 static const string CRASH_TIME_INTERVAL_KEY = "crash-time-interval";30 static const string CRASH_TIME_INTERVAL_KEY = "crash-time-interval";
31 static const string MONITORED_PROCESSES_KEY = "monitored-processes";31 static const string MONITORED_PROCESSES_KEY = "monitored-processes";
3232
33 public string[] process_list { get; set; }33 public string[] process_list { get; set; }
34 public uint max_crashes { get; set; default = 0; }34 public uint max_crashes { get; set; default = 0; }
35 public uint crash_time_interval { get; set; default = 0; }35 public uint crash_time_interval { get; set; default = 0; }
3636
@@ -47,6 +47,6 @@
47 }47 }
4848
49 private void on_process_list_changed () {49 private void on_process_list_changed () {
50 this.process_list_changed (this.settings.get_strv (MONITORED_PROCESSES_KEY));50 this.process_list_changed (this.process_list);
51 }51 }
52}52}
5353
=== modified file 'src/Watchdog.vala'
--- src/Watchdog.vala 2012-06-17 04:31:37 +0000
+++ src/Watchdog.vala 2012-07-06 21:51:19 +0000
@@ -25,10 +25,10 @@
25 * Victor Eduardo <victoreduardm@gmail.com>25 * Victor Eduardo <victoreduardm@gmail.com>
26 */26 */
2727
28public class Watchdog {28public class Watchdog : Object {
29
29 // Contains ALL the processes that are being monitored30 // Contains ALL the processes that are being monitored
30 private Gee.HashMap<string, ProcessInfo> processes;31 private Gee.HashMap<string, ProcessInfo> processes;
31 private Mutex data_lock;
3232
33 public Watchdog () {33 public Watchdog () {
34 this.processes = new Gee.HashMap<string, ProcessInfo> ();34 this.processes = new Gee.HashMap<string, ProcessInfo> ();
@@ -43,16 +43,17 @@
43 return;43 return;
4444
45 // Check if a process for this command has already been created45 // Check if a process for this command has already been created
46 if (this.processes.has_key (command))46 if (this.processes.has_key (command)) {
47 return;47 return;
48 }
4849
49 // Create new process50 // Create new process
50 var process = new ProcessInfo (command);51 var process = new ProcessInfo (command);
5152
52 // Add it to the table53 // Add it to the table
53 this.data_lock.lock ();54 lock (this.processes) {
54 this.processes[command] = process;55 this.processes[command] = process;
55 this.data_lock.unlock ();56 }
5657
57 // Exit handler. Respawning occurs here58 // Exit handler. Respawning occurs here
58 process.exited.connect ( (normal_exit) => {59 process.exited.connect ( (normal_exit) => {
@@ -62,6 +63,8 @@
62 process.reset_crash_count ();63 process.reset_crash_count ();
63 }64 }
6465
66 bool remove_process = false;
67
65 // if still in the list, relaunch if possible68 // if still in the list, relaunch if possible
66 if (command in Cerbere.settings.process_list) {69 if (command in Cerbere.settings.process_list) {
67 // Check if the process is still present in the table since it could have been removed.70 // Check if the process is still present in the table since it could have been removed.
@@ -73,25 +76,28 @@
73 process.run_async (); // Reload right away76 process.run_async (); // Reload right away
74 }77 }
75 else {78 else {
76 message ("'%s' exceeded the maximum number of crashes allowed (%s). It won't be launched again",79 warning ("'%s' exceeded the maximum number of crashes allowed (%s). It won't be launched again", command, max_crashes.to_string ());
77 command, max_crashes.to_string ());80 remove_process = true;
78 }81 }
79 }82 }
80 else {83 else {
81 // If a process is not in the table, it means it wasn't re-launched after it exited, so84 // If a process is not in the table, it means it wasn't re-launched
82 // in theory this code is never reached.85 // after it exited, so in theory this code is never reached.
83 warning ("You should NEVER get this message. If you're getting it, file a bug!");86 critical ("Please file a bug at http://launchpad.net/cerbere and attach your ~/.xsession-errors file.");
84 }87 }
85 }88 }
86 else {89 else {
87 message ("'%s' is no longer on settings. It will not be monitored anymore", command);90 warning ("'%s' is no longer in settings (not monitored))", command);
88
89 process.reset_crash_count (); // reset91 process.reset_crash_count (); // reset
92 remove_process = true;
93 }
9094
91 // Remove from the list. At this point the reference count should drop to 0 and free the process.95 // Remove from the table. At this point the reference count should
92 this.data_lock.lock ();96 // drop to 0 and free the process info.
93 this.processes.unset (command);97 if (remove_process) {
94 this.data_lock.unlock ();98 lock (this.processes) {
99 this.processes.unset (command);
100 }
95 }101 }
96 });102 });
97103

Subscribers

People subscribed via source and target branches

to all changes: