From 89932c1500f927165c243015d70318f5cf3082da Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 13 Feb 2019 11:35:46 +0100 Subject: lib: Don't leak children in igt_waitchildren_timeout Instead of cleaning up the mess in igt_exit make sure we don't even let it out of the container. See also commit 754876378d6c9b2775e8c07b4d16f9878c55949f Author: Chris Wilson Date: Fri Feb 26 22:11:10 2016 +0000 igt/gem_sync: Enforce a timeout of 20s which added this helper. To make sure that everyone follows the rules, add an assert. We're keeping the cleanup code as a failsafe, and because it speeds up the testcase I'm following up with. v2: Chris pointed out that my original patch did nothing. Which I didn't catch because my testcase was also broken. Unfortunately this means we need to open code part of the waiting. v3: The 2nd __igt_waitchildren() isn't necessary, __igt_waitchildren recovers from EINTR already and keeps waiting (Chris Wilson). v4: Change the timeout signal vs waitchildren logic to be race-free (Chris). This changes the exit code for a timeout from IGT_EXIT_FAILURE to SIGKILL + 128. v5: Clarify the docs (Chris). Reviewed-by: Chris Wilson Cc: Chris Wilson Signed-off-by: Daniel Vetter --- lib/igt_core.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) (limited to 'lib/igt_core.c') diff --git a/lib/igt_core.c b/lib/igt_core.c index cbbe79f8..607b34cd 100644 --- a/lib/igt_core.c +++ b/lib/igt_core.c @@ -1525,6 +1525,7 @@ void igt_exit(void) for (int c = 0; c < num_test_children; c++) kill(test_children[c], SIGKILL); + assert(!num_test_children); if (!test_with_subtests) { struct timespec now; @@ -1832,20 +1833,40 @@ void igt_waitchildren(void) igt_fail(err); } +static void igt_alarm_killchildren(int signal) +{ + igt_info("Timed out waiting for children\n"); + + for (int c = 0; c < num_test_children; c++) + kill(test_children[c], SIGKILL); +} + /** * igt_waitchildren_timeout: * @seconds: timeout in seconds to wait * @reason: debug string explaining what timedout * - * Wait for all children forked with igt_fork, for a maximum of @seconds. - * - * Wraps igt_waitchildren() and igt_set_timeout() + * Wait for all children forked with igt_fork, for a maximum of @seconds. If the + * timeout expires, kills all children, cleans them up, and then fails by + * calling igt_fail(). */ void igt_waitchildren_timeout(int seconds, const char *reason) { - igt_set_timeout(seconds, reason); - igt_waitchildren(); + struct sigaction sa; + int ret; + + sa.sa_handler = igt_alarm_killchildren; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + + sigaction(SIGALRM, &sa, NULL); + + alarm(seconds); + + ret = __igt_waitchildren(); igt_reset_timeout(); + if (ret) + igt_fail(ret); } /* exit handler code */ -- cgit v1.2.3