Merge lp:~sforshee/powerd/register-ack-api into lp:powerd
- register-ack-api
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Matt Fischer |
Approved revision: | 89 |
Merged at revision: | 75 |
Proposed branch: | lp:~sforshee/powerd/register-ack-api |
Merge into: | lp:powerd |
Prerequisite: | lp:~sforshee/powerd/hash-iterator |
Diff against target: |
877 lines (+643/-21) 11 files modified
CMakeLists.txt (+2/-1) cli/CMakeLists.txt (+1/-0) cli/client-test.c (+310/-0) cli/powerd-cli.c (+10/-15) cli/powerd-cli.h (+42/-0) data/com.canonical.powerd.xml (+12/-0) src/power-request.c (+36/-5) src/powerd-client.c (+162/-0) src/powerd-internal.h (+10/-0) src/powerd-object.c (+56/-0) src/powerd.cpp (+2/-0) |
To merge this branch: | bzr merge lp:~sforshee/powerd/register-ack-api |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Approve | |
Matt Fischer (community) | Approve | ||
Review via email: mp+176516@code.launchpad.net |
Commit message
Add support for registering clients to acknowledge system state changes
Description of the change
Add support for registering clients to acknowledge system state changes
PS Jenkins bot (ps-jenkins) wrote : | # |
- 85. By Seth Forshee
-
powerd-object: Remove unneeded argument from ack dbus handler
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:85
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 86. By Seth Forshee
-
Add state argument to ackStateChange dbus API
Without any arguments to the ackStateChange API we have a
possibility for races. If two state changes happen back-to-back,
a client who sends the ack late for the first state change might
inadvertantly acknowledge the second state change. Explicilty
specifying which state change is being acknowledge will help
avoid such issues.Also add a test to verify that acking with the wrong state fails
as expected.
Matt Fischer (mfisch) wrote : | # |
Comments so far:
488 + powerd_
Should we list the offenders here?
powerd-client.c is a confusing name since it's still part of the service, but I'm okay keeping it if you like the name.
583 + powerd_
Either the text here or the variable you are printing does not make sense. Are you trying to print the state?
Should we drop a client from the ack list if they don't send acks? What if they don't respond to 10 in a row for example?
Should we drop the name argument (this is pending some investigation by me)
Seth Forshee (sforshee) wrote : | # |
On Wed, Jul 24, 2013 at 05:39:29PM -0000, Matthew Fischer wrote:
> Comments so far:
>
> 488 + powerd_
>
> Should we list the offenders here?
powerd_
> powerd-client.c is a confusing name since it's still part of the service, but I'm okay keeping it if you like the name.
I'm open to suggestions ;-)
> 583 + powerd_
>
> Either the text here or the variable you are printing does not make sense. Are you trying to print the state?
The text is the problem. I'll change it.
> Should we drop a client from the ack list if they don't send acks? What if they don't respond to 10 in a row for example?
This might be a good idea. Could we go ahead and get it in without this
feature and make a work item for it?
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:86
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 87. By Seth Forshee
-
powerd-cli: Print results for more client tests
powerd-cli still can't print results for every test, but it
should print resulsts whenever possible. - 88. By Seth Forshee
-
powerd-cli: Remove some debug prints in client-test
- 89. By Seth Forshee
-
powerd-client: Reword confusing error message
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:89
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2013-07-10 21:39:32 +0000 |
3 | +++ CMakeLists.txt 2013-07-24 20:12:24 +0000 |
4 | @@ -41,9 +41,10 @@ |
5 | src/display.c |
6 | src/display-request.c |
7 | src/log.c |
8 | - src/powerd-object.c |
9 | src/power-request.c |
10 | src/power-source.c |
11 | + src/powerd-client.c |
12 | + src/powerd-object.c |
13 | src/powerd-sensors.cpp |
14 | src/util.c |
15 | src/${GDBUS_NAME}.c |
16 | |
17 | === modified file 'cli/CMakeLists.txt' |
18 | --- cli/CMakeLists.txt 2013-06-06 21:43:31 +0000 |
19 | +++ cli/CMakeLists.txt 2013-07-24 20:12:24 +0000 |
20 | @@ -2,6 +2,7 @@ |
21 | SRCS |
22 | |
23 | powerd-cli.c |
24 | + client-test.c |
25 | ) |
26 | |
27 | link_directories( |
28 | |
29 | === added file 'cli/client-test.c' |
30 | --- cli/client-test.c 1970-01-01 00:00:00 +0000 |
31 | +++ cli/client-test.c 2013-07-24 20:12:24 +0000 |
32 | @@ -0,0 +1,310 @@ |
33 | +/* |
34 | + * Copyright 2013 Canonical Ltd. |
35 | + * |
36 | + * This file is part of powerd. |
37 | + * |
38 | + * powerd is free software; you can redistribute it and/or modify |
39 | + * it under the terms of the GNU General Public License as published by |
40 | + * the Free Software Foundation; version 3. |
41 | + * |
42 | + * powerd is distributed in the hope that it will be useful, |
43 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
44 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
45 | + * GNU General Public License for more details. |
46 | + * |
47 | + * You should have received a copy of the GNU General Public License |
48 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
49 | + */ |
50 | + |
51 | +#include <stdio.h> |
52 | +#include <string.h> |
53 | +#include <glib.h> |
54 | +#include <glib-object.h> |
55 | +#include <gio/gio.h> |
56 | + |
57 | +#include "powerd-cli.h" |
58 | + |
59 | +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) |
60 | + |
61 | +static struct client_test_data { |
62 | + GDBusProxy *proxy; |
63 | + int state; |
64 | + int ack_delay; |
65 | + guint ack_timeout_id; |
66 | + powerd_cookie_t cookie; |
67 | +} test_data; |
68 | + |
69 | +enum ack_status { |
70 | + NOT_ACKED, |
71 | + ACK_SUCCESS, |
72 | + ACK_FAILURE, |
73 | + ACK_TIMEOUT, |
74 | +} ack_status;; |
75 | + |
76 | +static GMainLoop *main_loop; |
77 | + |
78 | +static gboolean run_next_test_handler(gpointer unused); |
79 | + |
80 | +static void reset_test_state(void) |
81 | +{ |
82 | + ack_status = NOT_ACKED; |
83 | +} |
84 | + |
85 | +static void run_next_test(void) |
86 | +{ |
87 | + g_timeout_add(0, run_next_test_handler, NULL); |
88 | +} |
89 | + |
90 | +static gboolean register_client(GDBusProxy *proxy) |
91 | +{ |
92 | + GVariant *ret; |
93 | + GError *error = NULL; |
94 | + |
95 | + ret = g_dbus_proxy_call_sync(proxy, "registerClient", |
96 | + g_variant_new("(s)", "powerd-cli"), |
97 | + G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); |
98 | + if (!ret) { |
99 | + cli_warn("registerClient failed: %s", error->message); |
100 | + g_error_free(error); |
101 | + return FALSE; |
102 | + } |
103 | + |
104 | + g_variant_unref(ret); |
105 | + return TRUE; |
106 | +} |
107 | + |
108 | +static gboolean unregister_client(GDBusProxy *proxy) |
109 | +{ |
110 | + GVariant *ret; |
111 | + GError *error = NULL; |
112 | + |
113 | + ret = g_dbus_proxy_call_sync(proxy, "unregisterClient", |
114 | + g_variant_new("(s)", "powerd-cli"), |
115 | + G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); |
116 | + if (!ret) { |
117 | + cli_warn("unregisterClient failed: %s", error->message); |
118 | + g_error_free(error); |
119 | + return FALSE; |
120 | + } |
121 | + |
122 | + g_variant_unref(ret); |
123 | + return TRUE; |
124 | +} |
125 | + |
126 | +static gboolean ack_state_change(GDBusProxy *proxy, int state) |
127 | +{ |
128 | + GVariant *ret; |
129 | + GError *error = NULL; |
130 | + |
131 | + ret = g_dbus_proxy_call_sync(proxy, "ackStateChange", |
132 | + g_variant_new("(i)", state), |
133 | + G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); |
134 | + if (!ret) { |
135 | + cli_warn("ackStateChange failed: %s", error->message); |
136 | + g_error_free(error); |
137 | + ack_status = ACK_FAILURE; |
138 | + return FALSE; |
139 | + } |
140 | + |
141 | + g_variant_unref(ret); |
142 | + ack_status = ACK_SUCCESS; |
143 | + return TRUE; |
144 | +} |
145 | + |
146 | +static gboolean send_ack_handler(gpointer unused) |
147 | +{ |
148 | + if (ack_state_change(test_data.proxy, test_data.state)) |
149 | + run_next_test(); |
150 | + return FALSE; |
151 | +} |
152 | + |
153 | +static gboolean ack_timeout_handler(gpointer unused) |
154 | +{ |
155 | + test_data.ack_timeout_id = 0; |
156 | + ack_status = ACK_TIMEOUT; |
157 | + run_next_test(); |
158 | + return FALSE; |
159 | +} |
160 | + |
161 | +static void powerd_signal_handler(GDBusProxy *proxy, gchar *sender, |
162 | + gchar *signal, GVariant *params, |
163 | + gpointer unused) |
164 | +{ |
165 | + cli_debug("Received signal %s\n", signal); |
166 | + if (strcmp(signal, "SysPowerStateChange")) |
167 | + return; |
168 | + |
169 | + /* Check if we're trying to time out */ |
170 | + if (test_data.ack_delay < 0) |
171 | + return; |
172 | + |
173 | + if (test_data.ack_timeout_id > 0) { |
174 | + g_source_remove(test_data.ack_timeout_id); |
175 | + test_data.ack_timeout_id = 0; |
176 | + } |
177 | + |
178 | + if (test_data.ack_delay == 0) { |
179 | + /* immediate ack */ |
180 | + ack_state_change(proxy, test_data.state); |
181 | + run_next_test(); |
182 | + } else { |
183 | + /* delayed ack */ |
184 | + g_timeout_add(test_data.ack_delay, send_ack_handler, NULL); |
185 | + } |
186 | +} |
187 | + |
188 | +static gboolean register_test(gpointer unused) |
189 | +{ |
190 | + gboolean result; |
191 | + |
192 | + result = do_test(register_client(test_data.proxy) == TRUE); |
193 | + if (!result) |
194 | + g_main_loop_quit(main_loop); |
195 | + else { |
196 | + run_next_test(); |
197 | + } |
198 | + return FALSE; |
199 | +} |
200 | + |
201 | +static gboolean ack_test_part1(gpointer unused) |
202 | +{ |
203 | + printf("Testing ack on active state transition\n"); |
204 | + |
205 | + test_data.state = POWERD_SYS_STATE_ACTIVE; |
206 | + test_data.ack_delay = 0; |
207 | + reset_test_state(); |
208 | + test_data.ack_timeout_id = g_timeout_add(5000, ack_timeout_handler, NULL); |
209 | + requestSysState(test_data.state, &test_data.cookie); |
210 | + return FALSE; |
211 | +} |
212 | + |
213 | +static gboolean ack_test_part2(gpointer unused) |
214 | +{ |
215 | + /* Check result of part 1 */ |
216 | + do_test(ack_status == ACK_SUCCESS); |
217 | + |
218 | + reset_test_state(); |
219 | + printf("Testing ack on suspend state transition\n"); |
220 | + test_data.state = POWERD_SYS_STATE_SUSPEND; |
221 | + test_data.ack_delay = 0; |
222 | + test_data.ack_timeout_id = g_timeout_add(5000, ack_timeout_handler, NULL); |
223 | + clearSysState(test_data.cookie); |
224 | + return FALSE; |
225 | +} |
226 | + |
227 | +static gboolean ack_test_check_result(gpointer unused) |
228 | +{ |
229 | + do_test(ack_status == ACK_SUCCESS); |
230 | + run_next_test(); |
231 | + return FALSE; |
232 | +} |
233 | + |
234 | +static gboolean no_ack_test_part1(gpointer unused) |
235 | +{ |
236 | + printf("Testing active state transition with no ack\n"); |
237 | + |
238 | + reset_test_state(); |
239 | + test_data.state = POWERD_SYS_STATE_ACTIVE; |
240 | + test_data.ack_delay = -1; |
241 | + test_data.ack_timeout_id = g_timeout_add(5000, ack_timeout_handler, NULL); |
242 | + requestSysState(test_data.state, &test_data.cookie); |
243 | + return FALSE; |
244 | +} |
245 | + |
246 | +static gboolean no_ack_test_part2(gpointer unused) |
247 | +{ |
248 | + printf("Testing suspend state transition with no ack\n"); |
249 | + |
250 | + reset_test_state(); |
251 | + test_data.state = POWERD_SYS_STATE_SUSPEND; |
252 | + test_data.ack_delay = -1; |
253 | + test_data.ack_timeout_id = g_timeout_add(5000, ack_timeout_handler, NULL); |
254 | + clearSysState(test_data.cookie); |
255 | + return FALSE; |
256 | +} |
257 | + |
258 | +static gboolean bad_ack_test(gpointer unused) |
259 | +{ |
260 | + printf("Testing acknowledge of wrong state\n"); |
261 | + |
262 | + reset_test_state(); |
263 | + test_data.state = POWERD_SYS_STATE_SUSPEND; |
264 | + test_data.ack_delay = 0; |
265 | + test_data.ack_timeout_id = g_timeout_add(5000, ack_timeout_handler, NULL); |
266 | + requestSysState(POWERD_SYS_STATE_ACTIVE, &test_data.cookie); |
267 | + return FALSE; |
268 | +} |
269 | + |
270 | +static gboolean bad_ack_test_result(gpointer unused) |
271 | +{ |
272 | + do_test(ack_status == ACK_FAILURE); |
273 | + run_next_test(); |
274 | + return FALSE; |
275 | +} |
276 | + |
277 | +static gboolean unregister_test(gpointer unused) |
278 | +{ |
279 | + gboolean result; |
280 | + |
281 | + result = do_test(unregister_client(test_data.proxy) == TRUE); |
282 | + if (!result) |
283 | + g_main_loop_quit(main_loop); |
284 | + else |
285 | + run_next_test(); |
286 | + return FALSE; |
287 | +} |
288 | + |
289 | +static gboolean no_unregister_test(gpointer unused) |
290 | +{ |
291 | + printf("Testing exit without unregistering\n"); |
292 | + run_next_test(); |
293 | + return FALSE; |
294 | +} |
295 | + |
296 | +static GSourceFunc client_tests[] = { |
297 | + register_test, |
298 | + ack_test_part1, |
299 | + ack_test_part2, |
300 | + ack_test_check_result, |
301 | + no_ack_test_part1, |
302 | + no_ack_test_part2, |
303 | + bad_ack_test, |
304 | + bad_ack_test_result, |
305 | + unregister_test, |
306 | + register_test, |
307 | + no_unregister_test, |
308 | +}; |
309 | + |
310 | +static gboolean run_next_test_handler(gpointer unused) |
311 | +{ |
312 | + static int test_num = 0; |
313 | + |
314 | + if (test_data.ack_timeout_id > 0) { |
315 | + g_source_remove(test_data.ack_timeout_id); |
316 | + test_data.ack_timeout_id = 0; |
317 | + } |
318 | + |
319 | + if (test_num >= ARRAY_SIZE(client_tests)) { |
320 | + test_num = 0; |
321 | + g_main_loop_quit(main_loop); |
322 | + return FALSE; |
323 | + } |
324 | + |
325 | + g_timeout_add(0, client_tests[test_num], NULL); |
326 | + test_num++; |
327 | + return FALSE; |
328 | +} |
329 | + |
330 | +void powerd_cli_client_test(GDBusProxy *proxy) |
331 | +{ |
332 | + printf("Running client tests, cannot verify all results\n"); |
333 | + |
334 | + silent_errors = TRUE; |
335 | + test_data.proxy = proxy; |
336 | + main_loop = g_main_loop_new(NULL, FALSE); |
337 | + g_signal_connect(proxy, "g-signal", G_CALLBACK(powerd_signal_handler), NULL); |
338 | + g_timeout_add(0, run_next_test_handler, NULL); |
339 | + g_main_loop_run(main_loop); |
340 | + g_main_loop_unref(main_loop); |
341 | + silent_errors = FALSE; |
342 | +} |
343 | |
344 | === modified file 'cli/powerd-cli.c' |
345 | --- cli/powerd-cli.c 2013-07-19 20:01:31 +0000 |
346 | +++ cli/powerd-cli.c 2013-07-24 20:12:24 +0000 |
347 | @@ -29,23 +29,13 @@ |
348 | #include <uuid/uuid.h> |
349 | #include <powerd.h> |
350 | |
351 | -#define do_test(test) \ |
352 | - ({ \ |
353 | - gboolean result; \ |
354 | - printf("Test: " #test "\n"); \ |
355 | - result = (test); \ |
356 | - printf(" result: %s\n", result ? "PASSED" : "FAILED"); \ |
357 | - result; \ |
358 | - }) |
359 | +#include "powerd-cli.h" |
360 | |
361 | #define TEST_NUM_SYS_REQUESTS 5 |
362 | #define TEST_NUM_DISP_REQUESTS (POWERD_NUM_DISPLAY_STATES * 2) |
363 | |
364 | /* Set to TRUE during tests to silence expected errors */ |
365 | -static gboolean silent_errors = FALSE; |
366 | - |
367 | -#define cli_warn(f, a...) do { if (!silent_errors) g_warning(f, ##a); } while (0) |
368 | -#define cli_debug(f, a...) g_debug(f, ##a) |
369 | +gboolean silent_errors = FALSE; |
370 | |
371 | GDBusProxy *powerd_proxy = NULL; |
372 | char test_dbusname[128] = ""; |
373 | @@ -102,7 +92,7 @@ |
374 | } |
375 | } |
376 | |
377 | -static gboolean |
378 | +gboolean |
379 | requestSysState(int state, |
380 | powerd_cookie_t *cookie) |
381 | { |
382 | @@ -312,7 +302,7 @@ |
383 | } |
384 | } |
385 | |
386 | -static gboolean |
387 | +gboolean |
388 | clearSysState(powerd_cookie_t cookie) |
389 | { |
390 | GVariant *ret = NULL; |
391 | @@ -717,6 +707,7 @@ |
392 | "exit, causing the request to be dropped.\n"); |
393 | printf("clear-sys <cookie> - clear a System state request given a cookie\n"); |
394 | printf("clear-disp <cookie> - clear a Display state request given a cookie\n"); |
395 | + printf("client-test - test powerd registration / ack API\n"); |
396 | printf("display <on|dc> [bright] [proximity] [disableab]\n"\ |
397 | "\tMake a display state request with the input parameters.\n"\ |
398 | "\tThe first argument represents the state of the display:\n"\ |
399 | @@ -763,7 +754,8 @@ |
400 | (strcmp(argv[1],"display")) && |
401 | (strcmp(argv[1],"help")) && |
402 | (strcmp(argv[1],"clear-disp")) && |
403 | - (strcmp(argv[1],"clear-sys"))) { |
404 | + (strcmp(argv[1],"clear-sys")) && |
405 | + (strcmp(argv[1],"client-test"))) { |
406 | fprintf(stderr,"Invalid option %s\n",argv[1]); |
407 | usage(argv[0]); |
408 | return -1; |
409 | @@ -905,5 +897,8 @@ |
410 | // Exit here without cleanup |
411 | return 0; |
412 | } |
413 | + else if (!strcmp(argv[1], "client-test")) { |
414 | + powerd_cli_client_test(powerd_proxy); |
415 | + } |
416 | return 0; |
417 | } |
418 | |
419 | === added file 'cli/powerd-cli.h' |
420 | --- cli/powerd-cli.h 1970-01-01 00:00:00 +0000 |
421 | +++ cli/powerd-cli.h 2013-07-24 20:12:24 +0000 |
422 | @@ -0,0 +1,42 @@ |
423 | +/* |
424 | + * Copyright 2013 Canonical Ltd. |
425 | + * |
426 | + * This file is part of powerd. |
427 | + * |
428 | + * powerd is free software; you can redistribute it and/or modify |
429 | + * it under the terms of the GNU General Public License as published by |
430 | + * the Free Software Foundation; version 3. |
431 | + * |
432 | + * powerd is distributed in the hope that it will be useful, |
433 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
434 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
435 | + * GNU General Public License for more details. |
436 | + * |
437 | + * You should have received a copy of the GNU General Public License |
438 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
439 | + */ |
440 | + |
441 | +#ifndef __POWERD_CLI_H__ |
442 | +#define __POWERD_CLI_H__ |
443 | + |
444 | +#include <powerd.h> |
445 | + |
446 | +#define do_test(test) \ |
447 | + ({ \ |
448 | + gboolean result; \ |
449 | + printf("Test: " #test "\n"); \ |
450 | + result = (test); \ |
451 | + printf(" result: %s\n", result ? "PASSED" : "FAILED"); \ |
452 | + result; \ |
453 | + }) |
454 | + |
455 | +extern gboolean silent_errors; |
456 | + |
457 | +#define cli_warn(f, a...) do { if (!silent_errors) g_warning(f, ##a); } while (0) |
458 | +#define cli_debug(f, a...) g_debug(f, ##a) |
459 | + |
460 | +gboolean requestSysState(int state, powerd_cookie_t *cookie); |
461 | +gboolean clearSysState(powerd_cookie_t cookie); |
462 | +void powerd_cli_client_test(GDBusProxy *proxy); |
463 | + |
464 | +#endif /* __POWERD_CLI_H__ */ |
465 | |
466 | === modified file 'data/com.canonical.powerd.xml' |
467 | --- data/com.canonical.powerd.xml 2013-07-17 22:39:08 +0000 |
468 | +++ data/com.canonical.powerd.xml 2013-07-24 20:12:24 +0000 |
469 | @@ -25,6 +25,18 @@ |
470 | <arg type="u" name="flags" direction="in" /> |
471 | </method> |
472 | |
473 | + <method name="registerClient"> |
474 | + <arg type="s" name="name" direction="in" /> |
475 | + </method> |
476 | + |
477 | + <method name="unregisterClient"> |
478 | + <arg type="s" name="name" direction="in" /> |
479 | + </method> |
480 | + |
481 | + <method name="ackStateChange"> |
482 | + <arg type="i" name="state" direction="in" /> |
483 | + </method> |
484 | + |
485 | <method name="clearDisplayState"> |
486 | <arg type="s" name="cookie" direction="in" /> |
487 | </method> |
488 | |
489 | === modified file 'src/power-request.c' |
490 | --- src/power-request.c 2013-07-19 22:55:17 +0000 |
491 | +++ src/power-request.c 2013-07-24 20:12:24 +0000 |
492 | @@ -36,7 +36,7 @@ |
493 | |
494 | #include "libsuspend.h" |
495 | |
496 | -#define NOTIFICATION_TIMEOUT_MSECS 100 |
497 | +#define NOTIFICATION_TIMEOUT_MSECS 2000 |
498 | |
499 | struct SysStateRequest { |
500 | gchar *owner; |
501 | @@ -81,7 +81,9 @@ |
502 | #define INVALID_STATE -1 |
503 | static GQueue queued_state_changes; |
504 | static enum SysPowerStates pending_system_state = INVALID_STATE; |
505 | +static guint state_machine_source_id; |
506 | static gint update_pending; |
507 | +static gboolean ack_pending; |
508 | |
509 | static const char power_request_wakelock_name[] = "powerd_power_request"; |
510 | gint suspend_block_count; |
511 | @@ -410,14 +412,21 @@ |
512 | if (ret) |
513 | powerd_warn("Failed to prepare for suspend: %d", ret); |
514 | } |
515 | + ack_pending = powerd_client_transition_start(pending_system_state); |
516 | powerd_sys_state_signal_emit(pending_system_state); |
517 | |
518 | - *delay = NOTIFICATION_TIMEOUT_MSECS; |
519 | + *delay = ack_pending ? NOTIFICATION_TIMEOUT_MSECS : 0; |
520 | return STATE_TRANSITION_NOTIFY; |
521 | } |
522 | |
523 | static enum state_transition_state process_state_notify(guint *delay) |
524 | { |
525 | + if (ack_pending) { |
526 | + powerd_warn("Timeout waiting for acks on state transition"); |
527 | + ack_pending = FALSE; |
528 | + } |
529 | + |
530 | + powerd_client_transition_finish(pending_system_state); |
531 | *delay = 0; |
532 | return STATE_TRANSITION_COMPLETE; |
533 | } |
534 | @@ -463,6 +472,7 @@ |
535 | { |
536 | guint delay; |
537 | |
538 | + state_machine_source_id = 0; |
539 | do { |
540 | switch (cur_state_transition_state) { |
541 | case STATE_TRANSITION_DEQUEUE: |
542 | @@ -480,9 +490,30 @@ |
543 | } while (delay == 0 && |
544 | cur_state_transition_state != STATE_TRANSITION_IDLE); |
545 | |
546 | - if (delay != 0 && cur_state_transition_state != STATE_TRANSITION_IDLE) |
547 | - g_timeout_add(delay, delayed_state_transition_process, NULL); |
548 | - |
549 | + if (delay != 0 && cur_state_transition_state != STATE_TRANSITION_IDLE) { |
550 | + state_machine_source_id = g_timeout_add(delay, |
551 | + delayed_state_transition_process, |
552 | + NULL); |
553 | + } |
554 | + |
555 | +} |
556 | + |
557 | +/* Must be called from main loop */ |
558 | +void power_request_transition_acked(void) |
559 | +{ |
560 | + if (cur_state_transition_state != STATE_TRANSITION_NOTIFY) { |
561 | + powerd_error("%s called while not waiting for acks", __func__); |
562 | + return; |
563 | + } |
564 | + |
565 | + powerd_debug("All acks received for state transition"); |
566 | + ack_pending = FALSE; |
567 | + if (state_machine_source_id) { |
568 | + g_source_remove(state_machine_source_id); |
569 | + state_machine_source_id = g_timeout_add(0, |
570 | + delayed_state_transition_process, |
571 | + NULL); |
572 | + } |
573 | } |
574 | |
575 | static void check_queued_state_changes(void) |
576 | |
577 | === added file 'src/powerd-client.c' |
578 | --- src/powerd-client.c 1970-01-01 00:00:00 +0000 |
579 | +++ src/powerd-client.c 2013-07-24 20:12:24 +0000 |
580 | @@ -0,0 +1,162 @@ |
581 | +/* |
582 | + * Copyright 2013 Canonical Ltd. |
583 | + * |
584 | + * This file is part of powerd. |
585 | + * |
586 | + * powerd is free software; you can redistribute it and/or modify |
587 | + * it under the terms of the GNU General Public License as published by |
588 | + * the Free Software Foundation; version 3. |
589 | + * |
590 | + * powerd is distributed in the hope that it will be useful, |
591 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
592 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
593 | + * GNU General Public License for more details. |
594 | + * |
595 | + * You should have received a copy of the GNU General Public License |
596 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
597 | + */ |
598 | + |
599 | +#include <errno.h> |
600 | +#include <glib.h> |
601 | +#include "powerd-internal.h" |
602 | +#include "log.h" |
603 | + |
604 | +struct powerd_client { |
605 | + const char *dbus_name; |
606 | + const char *name; |
607 | + gboolean ack_pending; |
608 | +}; |
609 | + |
610 | +static GHashTable *client_hash; |
611 | +static int acks_needed; |
612 | +static int ack_state; |
613 | + |
614 | +static void client_ack(struct powerd_client *client) |
615 | +{ |
616 | + if (!client->ack_pending) |
617 | + return; |
618 | + client->ack_pending = FALSE; |
619 | + |
620 | + if (acks_needed <= 0) { |
621 | + powerd_error("Client needs ack while total acks needed is %d", |
622 | + acks_needed); |
623 | + return; |
624 | + } |
625 | + if (--acks_needed == 0) |
626 | + power_request_transition_acked(); |
627 | +} |
628 | + |
629 | +/* Must be called from main loop */ |
630 | +int powerd_client_register(const char *dbus_name, const char *name) |
631 | +{ |
632 | + struct powerd_client *client; |
633 | + |
634 | + if (!dbus_name || !name) |
635 | + return -EINVAL; |
636 | + |
637 | + client = g_new0(struct powerd_client, 1); |
638 | + if (!client) |
639 | + return -ENOMEM; |
640 | + |
641 | + client->dbus_name = g_strdup(dbus_name); |
642 | + client->name = g_strdup(name); |
643 | + g_hash_table_insert(client_hash, (gpointer)client->dbus_name, client); |
644 | + powerd_dbus_name_watch_add(dbus_name); |
645 | + return 0; |
646 | +} |
647 | + |
648 | +/* Must be called from main loop */ |
649 | +void powerd_client_unregister(const char *dbus_name) |
650 | +{ |
651 | + struct powerd_client *client; |
652 | + |
653 | + client = g_hash_table_lookup(client_hash, dbus_name); |
654 | + if (!client) |
655 | + return; |
656 | + |
657 | + client_ack(client); |
658 | + powerd_dbus_name_watch_remove(client->dbus_name); |
659 | + g_hash_table_remove(client_hash, client->dbus_name); |
660 | +} |
661 | + |
662 | +/* |
663 | + * Must be called from main loop. Returns TRUE if caller should |
664 | + * wait for acks. |
665 | + */ |
666 | +gboolean powerd_client_transition_start(int state) |
667 | +{ |
668 | + GHashTableIter iter; |
669 | + gpointer key, value; |
670 | + |
671 | + acks_needed = 0; |
672 | + ack_state = state; |
673 | + |
674 | + g_hash_table_iter_init(&iter, client_hash); |
675 | + while (g_hash_table_iter_next(&iter, &key, &value)) { |
676 | + struct powerd_client *client = value; |
677 | + client->ack_pending = TRUE; |
678 | + acks_needed++; |
679 | + } |
680 | + return acks_needed != 0; |
681 | +} |
682 | + |
683 | +/* Must be called from main loop */ |
684 | +void powerd_client_transition_finish(int state) |
685 | +{ |
686 | + GHashTableIter iter; |
687 | + gpointer key, value; |
688 | + |
689 | + if (state != ack_state) { |
690 | + powerd_warn("Attempt to finish client transition with wrong state"); |
691 | + return; |
692 | + } |
693 | + |
694 | + if (acks_needed) |
695 | + return; |
696 | + acks_needed = 0; |
697 | + |
698 | + g_hash_table_iter_init(&iter, client_hash); |
699 | + while (g_hash_table_iter_next(&iter, &key, &value)) { |
700 | + struct powerd_client *client = value; |
701 | + if (client->ack_pending) { |
702 | + powerd_warn("Client %s (%s) failed to acknowledge state change", |
703 | + client->name, client->dbus_name); |
704 | + client->ack_pending = FALSE; |
705 | + } |
706 | + } |
707 | +} |
708 | + |
709 | +/* Must be called from main loop */ |
710 | +int powerd_client_ack(const char *dbus_name, int state) |
711 | +{ |
712 | + struct powerd_client *client; |
713 | + |
714 | + if (state != ack_state) |
715 | + return -EINVAL; |
716 | + |
717 | + client = g_hash_table_lookup(client_hash, dbus_name); |
718 | + if (!client) |
719 | + return -ENODEV; |
720 | + client_ack(client); |
721 | + return 0; |
722 | +} |
723 | + |
724 | +static void client_hash_destroy(gpointer data) |
725 | +{ |
726 | + struct powerd_client *client = data; |
727 | + g_free((gpointer)client->dbus_name); |
728 | + g_free((gpointer)client->name); |
729 | + g_free(client); |
730 | +} |
731 | + |
732 | +int powerd_client_init(void) |
733 | +{ |
734 | + client_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, |
735 | + client_hash_destroy); |
736 | + return 0; |
737 | +} |
738 | + |
739 | +void powerd_client_deinit(void) |
740 | +{ |
741 | + g_hash_table_destroy(client_hash); |
742 | +} |
743 | |
744 | === modified file 'src/powerd-internal.h' |
745 | --- src/powerd-internal.h 2013-07-19 23:09:47 +0000 |
746 | +++ src/powerd-internal.h 2013-07-24 20:12:24 +0000 |
747 | @@ -104,6 +104,7 @@ |
748 | gboolean request_sys_state_internal(int state, uuid_t *cookie, |
749 | const gchar *owner); |
750 | gboolean clear_sys_state_internal(uuid_t cookie); |
751 | +void power_request_transition_acked(void); |
752 | void update_system_state(void); |
753 | void power_request_init(void); |
754 | void power_request_deinit(void); |
755 | @@ -149,6 +150,15 @@ |
756 | int powerd_ps_init(void); |
757 | void powerd_ps_deinit(void); |
758 | |
759 | +/* Client functions */ |
760 | +int powerd_client_register(const char *dbus_name, const char *name); |
761 | +void powerd_client_unregister(const char *dbus_name); |
762 | +gboolean powerd_client_transition_start(int state); |
763 | +void powerd_client_transition_finish(int state); |
764 | +int powerd_client_ack(const char *dbus_name, int state); |
765 | +int powerd_client_init(void); |
766 | +void powerd_client_deinit(void); |
767 | + |
768 | /* Utility functions */ |
769 | int powerd_run_mainloop_sync(int (*func)(gpointer), gpointer data); |
770 | guint powerd_uuid_hash(gconstpointer key); |
771 | |
772 | === modified file 'src/powerd-object.c' |
773 | --- src/powerd-object.c 2013-07-17 22:39:08 +0000 |
774 | +++ src/powerd-object.c 2013-07-24 20:12:24 +0000 |
775 | @@ -21,6 +21,7 @@ |
776 | #include <gio/gio.h> |
777 | #include <string.h> |
778 | #include <stdio.h> |
779 | +#include <errno.h> |
780 | #include "powerd-internal.h" |
781 | #include "powerd-dbus.h" |
782 | #include "log.h" |
783 | @@ -86,6 +87,54 @@ |
784 | return; |
785 | } |
786 | |
787 | +static gboolean handle_register_client(PowerdSource *obj, |
788 | + GDBusMethodInvocation *invocation, |
789 | + const gchar *name) |
790 | +{ |
791 | + const char *owner; |
792 | + int ret; |
793 | + |
794 | + owner = g_dbus_method_invocation_get_sender(invocation); |
795 | + ret = powerd_client_register(owner, name); |
796 | + if (ret) |
797 | + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, |
798 | + G_DBUS_ERROR_FAILED, |
799 | + "Could not register client"); |
800 | + else |
801 | + g_dbus_method_invocation_return_value(invocation, NULL); |
802 | + return TRUE; |
803 | +} |
804 | + |
805 | +static gboolean handle_unregister_client(PowerdSource *obj, |
806 | + GDBusMethodInvocation *invocation, |
807 | + const gchar *name) |
808 | +{ |
809 | + const char *owner = g_dbus_method_invocation_get_sender(invocation); |
810 | + powerd_client_unregister(owner); |
811 | + g_dbus_method_invocation_return_value(invocation, NULL); |
812 | + return TRUE; |
813 | +} |
814 | + |
815 | +static gboolean handle_ack_state_change(PowerdSource *obj, |
816 | + GDBusMethodInvocation *invocation, |
817 | + int state) |
818 | +{ |
819 | + const char *owner; |
820 | + int ret; |
821 | + |
822 | + owner = g_dbus_method_invocation_get_sender(invocation); |
823 | + ret = powerd_client_ack(owner, state); |
824 | + if (ret) { |
825 | + const char *msg = (ret == -EINVAL) ? "Invalid state" : |
826 | + "Client not registered"; |
827 | + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, |
828 | + G_DBUS_ERROR_INVALID_ARGS, msg); |
829 | + } else { |
830 | + g_dbus_method_invocation_return_value(invocation, NULL); |
831 | + } |
832 | + return TRUE; |
833 | +} |
834 | + |
835 | void |
836 | powerd_bus_acquired_cb (GDBusConnection *connection, |
837 | const gchar *name, |
838 | @@ -120,6 +169,12 @@ |
839 | G_CALLBACK(handle_update_display_request), powerd_source); |
840 | g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-clear-display-state", |
841 | G_CALLBACK(handle_clear_display_request), powerd_source); |
842 | + g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-register-client", |
843 | + G_CALLBACK(handle_register_client), powerd_source); |
844 | + g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-unregister-client", |
845 | + G_CALLBACK(handle_unregister_client), powerd_source); |
846 | + g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-ack-state-change", |
847 | + G_CALLBACK(handle_ack_state_change), powerd_source); |
848 | g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-list-display-requests", |
849 | G_CALLBACK(handle_list_display_requests), powerd_source); |
850 | |
851 | @@ -237,6 +292,7 @@ |
852 | powerd_warn("%s vanished from dbus, clearing associated requests", name); |
853 | clear_sys_state_by_owner(name); |
854 | clear_disp_state_by_owner(name); |
855 | + powerd_client_unregister(name); |
856 | // This can return false if the hash entry was removed because the |
857 | // last request was dropped, in other words, this call will fail |
858 | // if the callers cleaned-up properly, so ignore any FALSE return |
859 | |
860 | === modified file 'src/powerd.cpp' |
861 | --- src/powerd.cpp 2013-07-18 17:31:38 +0000 |
862 | +++ src/powerd.cpp 2013-07-24 20:12:24 +0000 |
863 | @@ -471,6 +471,7 @@ |
864 | powerd_debug("Auto-dim Timeout is %d seconds\n", dim_timeout); |
865 | |
866 | libsuspend_init(0); |
867 | + powerd_client_init(); |
868 | power_request_init(); |
869 | display_request_init(); |
870 | dbus_name_watch_init(); |
871 | @@ -509,5 +510,6 @@ |
872 | powerd_backlight_deinit(); |
873 | display_request_deinit(); |
874 | power_request_deinit(); |
875 | + powerd_client_deinit(); |
876 | return g_exit_code; |
877 | } |
PASSED: Continuous integration, rev:84 jenkins. qa.ubuntu. com/job/ powerd- ci/95/ jenkins. qa.ubuntu. com/job/ powerd- saucy-armhf- ci/49 jenkins. qa.ubuntu. com/job/ powerd- saucy-armhf- ci/49/artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: s-jenkins: 8080/job/ powerd- ci/95/rebuild
http://