Merge lp:~ted/ubuntu-app-launch/suspend-signal into lp:ubuntu-app-launch/15.04

Proposed by Ted Gould
Status: Merged
Approved by: dobey
Approved revision: 198
Merged at revision: 187
Proposed branch: lp:~ted/ubuntu-app-launch/suspend-signal
Merge into: lp:ubuntu-app-launch/15.04
Diff against target: 513 lines (+309/-8)
6 files modified
data/com.canonical.UbuntuAppLaunch.xml (+8/-0)
debian/libubuntu-app-launch2.symbols (+4/-0)
libubuntu-app-launch/ubuntu-app-launch.c (+181/-3)
libubuntu-app-launch/ubuntu-app-launch.h (+63/-0)
tests/libual-test.cc (+39/-0)
tools/ubuntu-app-watch.c (+14/-5)
To merge this branch: bzr merge lp:~ted/ubuntu-app-launch/suspend-signal
Reviewer Review Type Date Requested Status
dobey (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+251510@code.launchpad.net

Commit message

Emit a signal when applications are paused or resumed

Description of the change

This way services can respond appropriately whether they're getting the message.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
196. By Ted Gould

Add in symbols

197. By Ted Gould

Attaching bug

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)
198. By Ted Gould

Adding to the informational dbus xml file

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
dobey (dobey) wrote :

I don't like the @replaceme as it means CI train ends up modifying the change set when it lands in trunk, but otherwise looks OK.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/com.canonical.UbuntuAppLaunch.xml'
2--- data/com.canonical.UbuntuAppLaunch.xml 2014-04-30 15:45:23 +0000
3+++ data/com.canonical.UbuntuAppLaunch.xml 2015-03-04 14:26:50 +0000
4@@ -20,5 +20,13 @@
5 <arg type="s" name="appid" />
6 <arg type="s" name="stage" />
7 </signal>
8+ <signal name="ApplicationPaused">
9+ <arg type="s" name="appid" />
10+ <arg type="at" name="pids" />
11+ </signal>
12+ <signal name="ApplicationResumed">
13+ <arg type="s" name="appid" />
14+ <arg type="at" name="pids" />
15+ </signal>
16 </interface>
17 </node>
18
19=== modified file 'debian/libubuntu-app-launch2.symbols'
20--- debian/libubuntu-app-launch2.symbols 2014-09-15 19:40:14 +0000
21+++ debian/libubuntu-app-launch2.symbols 2015-03-04 14:26:50 +0000
22@@ -7,7 +7,9 @@
23 ubuntu_app_launch_list_running_apps@Base 0.4
24 ubuntu_app_launch_observer_add_app_failed@Base 0.4
25 ubuntu_app_launch_observer_add_app_focus@Base 0.4
26+ ubuntu_app_launch_observer_add_app_paused@Base 0replaceme
27 ubuntu_app_launch_observer_add_app_resume@Base 0.4
28+ ubuntu_app_launch_observer_add_app_resumed@Base 0replaceme
29 ubuntu_app_launch_observer_add_app_started@Base 0.4
30 ubuntu_app_launch_observer_add_app_starting@Base 0.4
31 ubuntu_app_launch_observer_add_app_stop@Base 0.4
32@@ -15,7 +17,9 @@
33 ubuntu_app_launch_observer_add_helper_stop@Base 0.4
34 ubuntu_app_launch_observer_delete_app_failed@Base 0.4
35 ubuntu_app_launch_observer_delete_app_focus@Base 0.4
36+ ubuntu_app_launch_observer_delete_app_paused@Base 0replaceme
37 ubuntu_app_launch_observer_delete_app_resume@Base 0.4
38+ ubuntu_app_launch_observer_delete_app_resumed@Base 0replaceme
39 ubuntu_app_launch_observer_delete_app_started@Base 0.4
40 ubuntu_app_launch_observer_delete_app_starting@Base 0.4
41 ubuntu_app_launch_observer_delete_app_stop@Base 0.4
42
43=== modified file 'libubuntu-app-launch/ubuntu-app-launch.c'
44--- libubuntu-app-launch/ubuntu-app-launch.c 2014-11-20 20:33:59 +0000
45+++ libubuntu-app-launch/ubuntu-app-launch.c 2015-03-04 14:26:50 +0000
46@@ -499,10 +499,61 @@
47 return FALSE;
48 }
49
50+/* Throw out a DBus signal that we've signalled all of these processes. This
51+ is the fun GVariant building part. */
52+static void
53+notify_signalling (GList * pids, const gchar * appid, const gchar * signal_name)
54+{
55+ GDBusConnection * conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
56+ if (conn == NULL) {
57+ return;
58+ }
59+
60+ /* Pull together a PID array */
61+ GVariant *pidarray = NULL;
62+ if (pids == NULL) {
63+ pidarray = g_variant_new_array(G_VARIANT_TYPE_UINT64, NULL, 0);
64+ } else {
65+ GList * i;
66+ GVariantBuilder builder;
67+ g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
68+
69+ for (i = pids; i != NULL; i = g_list_next(i))
70+ g_variant_builder_add_value(&builder, g_variant_new_uint64(GPOINTER_TO_INT(i->data)));
71+
72+ pidarray = g_variant_builder_end(&builder);
73+ }
74+
75+ /* Combine into the wrapping tuple */
76+ GVariantBuilder btuple;
77+ g_variant_builder_init(&btuple, G_VARIANT_TYPE_TUPLE);
78+ g_variant_builder_add_value(&btuple, g_variant_new_string(appid));
79+ g_variant_builder_add_value(&btuple, pidarray);
80+
81+ /* Emit !!! */
82+ GError * error = NULL;
83+ g_dbus_connection_emit_signal(conn,
84+ NULL, /* destination */
85+ "/", /* path */
86+ "com.canonical.UbuntuAppLaunch", /* interface */
87+ signal_name, /* signal */
88+ g_variant_builder_end(&btuple), /* params, the same */
89+ &error);
90+
91+ if (error != NULL) {
92+ g_warning("Unable to emit signal '%s' for appid '%s': %s", signal_name, appid, error->message);
93+ g_error_free(error);
94+ } else {
95+ g_debug("Emmitted '%s' to DBus", signal_name);
96+ }
97+
98+ g_object_unref(conn);
99+}
100+
101 /* Gets all the pids for an appid and sends a signal to all of them. This also
102 loops to ensure no new pids are added while we're signaling */
103 static gboolean
104-signal_to_cgroup (const gchar * appid, int signal, const gchar * oomscore)
105+signal_to_cgroup (const gchar * appid, int signal, const gchar * oomscore, const gchar * signal_name)
106 {
107 GHashTable * pidssignaled = g_hash_table_new(g_direct_hash, g_direct_equal);
108 guint hash_table_size = 0;
109@@ -543,6 +594,7 @@
110 /* If it grew, then try again */
111 } while (hash_table_size != g_hash_table_size(pidssignaled));
112
113+ notify_signalling(g_hash_table_get_keys(pidssignaled), appid, signal_name);
114 g_hash_table_destroy(pidssignaled);
115
116 return retval;
117@@ -612,14 +664,14 @@
118 ubuntu_app_launch_pause_application (const gchar * appid)
119 {
120 report_zg_event(appid, ZEITGEIST_ZG_LEAVE_EVENT);
121- return signal_to_cgroup(appid, SIGSTOP, "900");
122+ return signal_to_cgroup(appid, SIGSTOP, "900", "ApplicationPaused");
123 }
124
125 gboolean
126 ubuntu_app_launch_resume_application (const gchar * appid)
127 {
128 report_zg_event(appid, ZEITGEIST_ZG_ACCESS_EVENT);
129- return signal_to_cgroup(appid, SIGCONT, "100");
130+ return signal_to_cgroup(appid, SIGCONT, "100", "ApplicationResumed");
131 }
132
133 gchar *
134@@ -709,6 +761,16 @@
135 gpointer user_data;
136 };
137
138+/* The data we keep for each failed observer */
139+typedef struct _paused_resumed_observer_t paused_resumed_observer_t;
140+struct _paused_resumed_observer_t {
141+ GDBusConnection * conn;
142+ guint sighandle;
143+ UbuntuAppLaunchAppPausedResumedObserver func;
144+ gpointer user_data;
145+ const gchar * lttng_signal;
146+};
147+
148 /* The lists of Observers */
149 static GList * starting_array = NULL;
150 static GList * started_array = NULL;
151@@ -716,6 +778,8 @@
152 static GList * focus_array = NULL;
153 static GList * resume_array = NULL;
154 static GList * failed_array = NULL;
155+static GList * paused_array = NULL;
156+static GList * resumed_array = NULL;
157
158 static void
159 observer_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
160@@ -994,6 +1058,81 @@
161 return TRUE;
162 }
163
164+/* Handle the paused signal when it occurs, call the observer */
165+static void
166+paused_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
167+{
168+ paused_resumed_observer_t * observer = (paused_resumed_observer_t *)user_data;
169+
170+ ual_tracepoint(observer_start, observer->lttng_signal);
171+
172+ if (observer->func != NULL) {
173+ GArray * pidarray = g_array_new(TRUE, TRUE, sizeof(GPid));
174+ GVariant * appid = g_variant_get_child_value(params, 0);
175+ GVariant * pids = g_variant_get_child_value(params, 1);
176+ guint64 pid;
177+ GVariantIter thispid;
178+ g_variant_iter_init(&thispid, pids);
179+
180+ while (g_variant_iter_loop(&thispid, "t", &pid)) {
181+ GPid gpid = (GPid)pid; /* Should be a no-op for most architectures, but just in case */
182+ g_array_append_val(pidarray, gpid);
183+ }
184+
185+ observer->func(g_variant_get_string(appid, NULL), (GPid *)pidarray->data, observer->user_data);
186+
187+ g_array_free(pidarray, TRUE);
188+ g_variant_unref(appid);
189+ g_variant_unref(pids);
190+ }
191+
192+ ual_tracepoint(observer_finish, observer->lttng_signal);
193+}
194+
195+static gboolean
196+paused_resumed_generic (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data, GList ** queue, const gchar * signal_name, const gchar * lttng_signal)
197+{
198+ GDBusConnection * conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
199+
200+ if (conn == NULL) {
201+ return FALSE;
202+ }
203+
204+ paused_resumed_observer_t * observert = g_new0(paused_resumed_observer_t, 1);
205+
206+ observert->conn = conn;
207+ observert->func = observer;
208+ observert->user_data = user_data;
209+ observert->lttng_signal = lttng_signal;
210+
211+ *queue = g_list_prepend(*queue, observert);
212+
213+ observert->sighandle = g_dbus_connection_signal_subscribe(conn,
214+ NULL, /* sender */
215+ "com.canonical.UbuntuAppLaunch", /* interface */
216+ signal_name, /* signal */
217+ "/", /* path */
218+ NULL, /* arg0 */
219+ G_DBUS_SIGNAL_FLAGS_NONE,
220+ paused_signal_cb,
221+ observert,
222+ NULL); /* user data destroy */
223+
224+ return TRUE;
225+}
226+
227+gboolean
228+ubuntu_app_launch_observer_add_app_paused (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data)
229+{
230+ return paused_resumed_generic(observer, user_data, &paused_array, "ApplicationPaused", "paused");
231+}
232+
233+gboolean
234+ubuntu_app_launch_observer_add_app_resumed (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data)
235+{
236+ return paused_resumed_generic(observer, user_data, &resumed_array, "ApplicationResumed", "resumed");
237+}
238+
239 static gboolean
240 delete_app_generic (UbuntuAppLaunchAppObserver observer, gpointer user_data, GList ** list)
241 {
242@@ -1078,6 +1217,45 @@
243 return TRUE;
244 }
245
246+static gboolean
247+paused_resumed_delete (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data, GList ** list)
248+{
249+ paused_resumed_observer_t * observert = NULL;
250+ GList * look;
251+
252+ for (look = *list; look != NULL; look = g_list_next(look)) {
253+ observert = (paused_resumed_observer_t *)look->data;
254+
255+ if (observert->func == observer && observert->user_data == user_data) {
256+ break;
257+ }
258+ }
259+
260+ if (look == NULL) {
261+ return FALSE;
262+ }
263+
264+ g_dbus_connection_signal_unsubscribe(observert->conn, observert->sighandle);
265+ g_object_unref(observert->conn);
266+
267+ g_free(observert);
268+ *list = g_list_delete_link(*list, look);
269+
270+ return TRUE;
271+}
272+
273+gboolean
274+ubuntu_app_launch_observer_delete_app_paused (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data)
275+{
276+ return paused_resumed_delete(observer, user_data, &paused_array);
277+}
278+
279+gboolean
280+ubuntu_app_launch_observer_delete_app_resumed (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data)
281+{
282+ return paused_resumed_delete(observer, user_data, &resumed_array);
283+}
284+
285 typedef void (*per_instance_func_t) (GDBusConnection * con, GVariant * prop_dict, gpointer user_data);
286
287 static void
288
289=== modified file 'libubuntu-app-launch/ubuntu-app-launch.h'
290--- libubuntu-app-launch/ubuntu-app-launch.h 2014-08-13 15:11:45 +0000
291+++ libubuntu-app-launch/ubuntu-app-launch.h 2015-03-04 14:26:50 +0000
292@@ -53,6 +53,15 @@
293 typedef void (*UbuntuAppLaunchAppFailedObserver) (const gchar * appid, UbuntuAppLaunchAppFailed failure_type, gpointer user_data);
294
295 /**
296+ * UbuntuAppLaunchAppPausedResumedObserver:
297+ * @appid: App ID of the application being paused
298+ * @pids: Zero terminated array of PIDs
299+ *
300+ * Function prototype for application paused and resumed observers.
301+ */
302+typedef void (*UbuntuAppLaunchAppPausedResumedObserver) (const gchar * appid, GPid * pids, gpointer user_data);
303+
304+/**
305 * UbuntuAppLaunchHelperObserver:
306 *
307 * Function to watch for helpers that are starting and stopping
308@@ -210,6 +219,32 @@
309 gpointer user_data);
310
311 /**
312+ * ubuntu_app_launch_observer_add_app_paused:
313+ * @observer: (scope notified): Callback when an application is paused
314+ * @user_data: (allow-none) (closure): Data to pass to the observer
315+ *
316+ * Sets up a callback to get called each time an application
317+ * is paused.
318+ *
319+ * Return value: Whether adding the observer was successful.
320+ */
321+gboolean ubuntu_app_launch_observer_add_app_paused (UbuntuAppLaunchAppPausedResumedObserver observer,
322+ gpointer user_data);
323+
324+/**
325+ * ubuntu_app_launch_observer_add_app_resumed:
326+ * @observer: (scope notified): Callback when an application is resumed
327+ * @user_data: (allow-none) (closure): Data to pass to the observer
328+ *
329+ * Sets up a callback to get called each time an application
330+ * is resumed. Which is after the SIGCONT has been sent to the pids.
331+ *
332+ * Return value: Whether adding the observer was successful.
333+ */
334+gboolean ubuntu_app_launch_observer_add_app_resumed (UbuntuAppLaunchAppPausedResumedObserver observer,
335+ gpointer user_data);
336+
337+/**
338 * ubuntu_app_launch_observer_delete_app_starting:
339 * @observer: (scope notified): Callback to remove
340 * @user_data: (closure) (allow-none): Data that was passed to the observer
341@@ -221,6 +256,7 @@
342 */
343 gboolean ubuntu_app_launch_observer_delete_app_starting (UbuntuAppLaunchAppObserver observer,
344 gpointer user_data);
345+
346 /**
347 * ubuntu_app_launch_observer_delete_app_started:
348 * @observer: (scope notified): Callback to remove
349@@ -284,6 +320,33 @@
350 */
351 gboolean ubuntu_app_launch_observer_delete_app_failed (UbuntuAppLaunchAppFailedObserver observer,
352 gpointer user_data);
353+
354+/**
355+ * ubuntu_app_launch_observer_delete_app_paused:
356+ * @observer: (scope notified): Callback to remove
357+ * @user_data: (closure) (allow-none): Data to pass to the observer
358+ *
359+ * Removes a previously registered callback to ensure it no longer
360+ * gets signaled.
361+ *
362+ * Return value: Whether deleting the observer was successful.
363+ */
364+gboolean ubuntu_app_launch_observer_delete_app_paused (UbuntuAppLaunchAppPausedResumedObserver observer,
365+ gpointer user_data);
366+
367+/**
368+ * ubuntu_app_launch_observer_delete_app_resumed:
369+ * @observer: (scope notified): Callback to remove
370+ * @user_data: (closure) (allow-none): Data to pass to the observer
371+ *
372+ * Removes a previously registered callback to ensure it no longer
373+ * gets signaled.
374+ *
375+ * Return value: Whether deleting the observer was successful.
376+ */
377+gboolean ubuntu_app_launch_observer_delete_app_resumed (UbuntuAppLaunchAppPausedResumedObserver observer,
378+ gpointer user_data);
379+
380 /**
381 * ubuntu_app_launch_list_running_apps:
382 *
383
384=== modified file 'tests/libual-test.cc'
385--- tests/libual-test.cc 2014-10-28 12:43:11 +0000
386+++ tests/libual-test.cc 2015-03-04 14:26:50 +0000
387@@ -1272,6 +1272,14 @@
388 return TRUE;
389 }
390
391+static void
392+signal_increment (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
393+{
394+ guint * count = (guint *)user_data;
395+ g_debug("Count incremented to: %d", *count + 1);
396+ *count = *count + 1;
397+}
398+
399 TEST_F(LibUAL, PauseResume)
400 {
401 g_setenv("UBUNTU_APP_LAUNCH_OOM_PROC_PATH", CMAKE_BINARY_DIR "/libual-proc" , 1);
402@@ -1342,8 +1350,33 @@
403 } while (dbus_test_task_get_state(DBUS_TEST_TASK(cgmock2)) != DBUS_TEST_TASK_STATE_RUNNING &&
404 dbus_test_task_get_state(DBUS_TEST_TASK(zgmock)) != DBUS_TEST_TASK_STATE_RUNNING);
405
406+ /* Setup signal handling */
407+ guint paused_count = 0;
408+ guint resumed_count = 0;
409+ guint paused_signal = g_dbus_connection_signal_subscribe(bus,
410+ nullptr,
411+ "com.canonical.UbuntuAppLaunch",
412+ "ApplicationPaused",
413+ "/",
414+ nullptr,
415+ G_DBUS_SIGNAL_FLAGS_NONE,
416+ signal_increment,
417+ &paused_count,
418+ nullptr);
419+ guint resumed_signal = g_dbus_connection_signal_subscribe(bus,
420+ nullptr,
421+ "com.canonical.UbuntuAppLaunch",
422+ "ApplicationResumed",
423+ "/",
424+ nullptr,
425+ G_DBUS_SIGNAL_FLAGS_NONE,
426+ signal_increment,
427+ &resumed_count,
428+ nullptr);
429+
430 /* Test it */
431 EXPECT_NE(0, datacnt);
432+ paused_count = 0;
433
434 /* Pause the app */
435 EXPECT_TRUE(ubuntu_app_launch_pause_application("com.test.good_application_1.2.3"));
436@@ -1354,6 +1387,7 @@
437 pause(200);
438
439 /* Check data coming out */
440+ EXPECT_EQ(1, paused_count);
441 EXPECT_EQ(0, datacnt);
442
443 /* Check to make sure we sent the event to ZG */
444@@ -1370,6 +1404,7 @@
445 ASSERT_TRUE(g_file_get_contents(oomadjfile, &pauseoomscore, NULL, NULL));
446 EXPECT_STREQ("900", pauseoomscore);
447 g_free(pauseoomscore);
448+ resumed_count = 0;
449
450 /* Now Resume the App */
451 EXPECT_TRUE(ubuntu_app_launch_resume_application("com.test.good_application_1.2.3"));
452@@ -1377,6 +1412,7 @@
453 pause(200);
454
455 EXPECT_NE(0, datacnt);
456+ EXPECT_EQ(1, resumed_count);
457
458 /* Check to make sure we sent the event to ZG */
459 numcalls = 0;
460@@ -1400,6 +1436,9 @@
461
462 g_spawn_command_line_sync("rm -rf " CMAKE_BINARY_DIR "/libual-proc", NULL, NULL, NULL, NULL);
463
464+ g_dbus_connection_signal_unsubscribe(bus, paused_signal);
465+ g_dbus_connection_signal_unsubscribe(bus, resumed_signal);
466+
467 /* Kill ZG default instance :-( */
468 ZeitgeistLog * log = zeitgeist_log_get_default();
469 g_object_unref(log);
470
471=== modified file 'tools/ubuntu-app-watch.c'
472--- tools/ubuntu-app-watch.c 2014-04-30 16:34:06 +0000
473+++ tools/ubuntu-app-watch.c 2015-03-04 14:26:50 +0000
474@@ -41,9 +41,16 @@
475 }
476
477 void
478-resume (const gchar * appid, gpointer user_data)
479-{
480- g_print("Resume %s\n", appid);
481+resumed (const gchar * appid, GPid * pids, gpointer user_data)
482+{
483+ g_print("Resumed %s\n", appid);
484+ return;
485+}
486+
487+void
488+paused (const gchar * appid, GPid * pids, gpointer user_data)
489+{
490+ g_print("Paused %s\n", appid);
491 return;
492 }
493
494@@ -79,7 +86,8 @@
495 ubuntu_app_launch_observer_add_app_started(started, NULL);
496 ubuntu_app_launch_observer_add_app_stop(stopped, NULL);
497 ubuntu_app_launch_observer_add_app_focus(focus, NULL);
498- ubuntu_app_launch_observer_add_app_resume(resume, NULL);
499+ ubuntu_app_launch_observer_add_app_resumed(resumed, NULL);
500+ ubuntu_app_launch_observer_add_app_paused(paused, NULL);
501 ubuntu_app_launch_observer_add_app_failed(fail, NULL);
502
503 GMainLoop * mainloop = g_main_loop_new(NULL, FALSE);
504@@ -89,7 +97,8 @@
505 ubuntu_app_launch_observer_delete_app_started(started, NULL);
506 ubuntu_app_launch_observer_delete_app_stop(stopped, NULL);
507 ubuntu_app_launch_observer_delete_app_focus(focus, NULL);
508- ubuntu_app_launch_observer_delete_app_resume(resume, NULL);
509+ ubuntu_app_launch_observer_delete_app_resumed(resumed, NULL);
510+ ubuntu_app_launch_observer_delete_app_paused(paused, NULL);
511 ubuntu_app_launch_observer_delete_app_failed(fail, NULL);
512
513 g_main_loop_unref(mainloop);

Subscribers

People subscribed via source and target branches