From 61b1834c5366c78f0e4d3b21a142d7dd6d6322c1 Mon Sep 17 00:00:00 2001 From: Fabian Vogt Date: Tue, 13 Apr 2021 15:41:00 +0200 Subject: [PATCH] signalblocker: Also block SIGCHLD The perl-unaware threads created by OpenCV also crash when they encounter a SIGCHLD signal, which happens by using perl's "system" function for instance. --- signalblocker.pm | 22 +++++++++++----------- t/28-signalblocker.t | 14 ++++++++++++-- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/signalblocker.pm b/signalblocker.pm index 1ff3652c..dea56e12 100644 --- a/signalblocker.pm +++ b/signalblocker.pm @@ -1,4 +1,4 @@ -# Copyright © 2020 SUSE LLC +# Copyright © 2021 SUSE LLC # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -19,23 +19,23 @@ use Mojo::Base -base; use bmwqemu; use POSIX ':signal_h'; -# OpenCV forks a lot of threads and the TERM signal we may get from the -# parent process would be delivered to an undefined thread. But as those -# threads do not have a perl interpreter, the perl signal handler (we set -# later) would crash. So we need to block the TERM signal in the forked -# processes before we set the signal handler of our choice. +# OpenCV forks a lot of threads and the signals we may get (TERM from the +# parent, CHLD from children) would be delivered to an undefined thread. +# But as those threads do not have a perl interpreter, the perl signal +# handler would crash. We need to block those signals in those threads, so +# that they get delivered only to those threads which can handle it. sub new { my ($class, @args) = @_; # block signals - bmwqemu::diag('Blocking SIGTERM'); + bmwqemu::diag('Blocking SIGCHLD and SIGCHLD'); my %old_sig = %SIG; $SIG{TERM} = 'IGNORE'; $SIG{INT} = 'IGNORE'; $SIG{HUP} = 'IGNORE'; - my $sigset = POSIX::SigSet->new(SIGTERM); - die "Could not block SIGTERM\n" unless defined sigprocmask(SIG_BLOCK, $sigset, undef); + my $sigset = POSIX::SigSet->new(SIGCHLD, SIGTERM); + die "Could not block SIGCHLD and SIGTERM\n" unless defined sigprocmask(SIG_BLOCK, $sigset, undef); # create the actual object holding the information to restore the previous state my $self = $class->SUPER::new(@args); @@ -48,8 +48,8 @@ sub DESTROY { my ($self) = @_; # set back signal handling to default to be able to terminate properly - bmwqemu::diag('Unblocking SIGTERM'); - die "Could not unblock SIGTERM\n" unless defined sigprocmask(SIG_UNBLOCK, $self->{_sigset}, undef); + bmwqemu::diag('Unblocking SIGCHLD and SIGTERM'); + die "Could not unblock SIGCHLD and SIGTERM\n" unless defined sigprocmask(SIG_UNBLOCK, $self->{_sigset}, undef); %SIG = %{$self->{_old_sig}}; } diff --git a/t/28-signalblocker.t b/t/28-signalblocker.t index 31ddb6f6..9e1a99a5 100755 --- a/t/28-signalblocker.t +++ b/t/28-signalblocker.t @@ -1,6 +1,6 @@ #!/usr/bin/perl # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2021 SUSE LLC # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -44,9 +44,13 @@ my $no_signal_blocker = $ENV{OS_AUTOINST_TEST_NO_SIGNAL_BLOCKER}; sub thread_count { scalar split("\n", `ps huH p $$`) } is(my $last_thread_count = thread_count, 1, 'initially one thread'); -# count SIGTERMs we receive; this handler should work after creating/destroying the signal blocker +# count SIGTERMs we receive; those handlers should work after creating/destroying the signal blocker +# Note that without these handlers, there won't be any crash in Perl's signal handler as it's never +# registered for those signals. my $received_sigterm = 0; $SIG{TERM} = sub { $received_sigterm += 1; note "received SIGTERM $received_sigterm"; }; +my $received_sigchld = 0; +$SIG{CHLD} = sub { $received_sigchld += 1; note "received SIGCHLD $received_sigchld"; }; # initialize OpenCV via signalblocker and create_threads { @@ -75,6 +79,12 @@ waitpid $fork, 0; note 'waiting for at least one signal to be handled' and sleep .2 until $received_sigterm >= 1 || ($timeout -= .2) < 0; note "handled $received_sigterm TERM signals"; ok($received_sigterm > 0, "received SIGTERM $received_sigterm times; no crashes after at least 200 ms idling time"); + +$received_sigchld = 0; +# 0 here means WIFEXITED and WEXITSTATUS == 0 +cmp_ok(system("true"), '==', 0, 'system returns exit status'); +is($received_sigchld, 1, 'got SIGCHLD after system'); + cmp_ok(thread_count, '<=', $last_thread_count, 'still no new threads after sending signals'); done_testing; -- 2.31.1