From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-4.2 required=3.0 tests=ALL_TRUSTED,BAYES_00, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 437CD1F406 for ; Tue, 29 Aug 2023 17:21:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=80x24.org; s=selector1; t=1693329710; bh=/K1QBe/lNeIwhc0jS0ktzICx/M4vMJijO69Eu7MWXJ0=; h=Date:From:To:Subject:In-Reply-To:From; b=H3YggxL4TrJV3sqRqEpLwp/pvcKtEqQ3pzESDhogMcaAi2rB50KAezDLBHZW1q7uU DE2WuJdMvRJ1uhcB/4er8fcsC3s+M70nKilJLxJzM+v87FtjgunLgWxblUTodaccii 6Nki3UGkbPcO7avMvWD6vKbaSMLlALyftE6WuUJg= Date: Tue, 29 Aug 2023 17:20:16 +0000 From: Eric Wong To: meta@public-inbox.org Subject: [PATCH v2] t/spawn.t: workaround OpenBSD RLIMIT_CPU delays Message-ID: <20230829172016.M648190@dcvr> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline In-Reply-To: <20230829165521.41372-1-e@80x24.org> List-Id: RLIMIT_CPU on OpenBSD doesn't work reliably with few syscalls or on mostly idle systems. Even at its most accurate, it takes an extra second to fire compared to FreeBSD or Linux due to internal accounting differences, but worst case even the SIGKILL can be 50s delayed. So rewrite the CPU burner script in Perl where we can unblock SIGXCPU and reliably use more syscalls. Link: https://marc.info/?i=20230829010110.M269767@dcvr --- v2: - only enable Perl warnings in subprocess if enabled in parent - gettimeofday(2) instead of time(2) for resolution and accuracy - increase warn delay to quiet non-OpenBSD systems - avoid $tot accumulation since times(2) is already cumulative - update comment based on feedback from OpenBSD bug report - stricter check exit status check - quiet readline result on success Range-diff: 1: 61dd1ad5 ! 1: d3d732dc t/spawn.t: workaround OpenBSD RLIMIT_CPU delays @@ t/spawn.t: SKIP: { - my ($r, $w); - pipe($r, $w) or die "pipe: $!"; - my $cmd = ['sh', '-c', 'while true; do :; done']; -+ my $cmd = [$^X, '-w', '-e', <<'EOM' ]; ++ my $cmd = [ $^X, ($^W ? ('-w') : ()), '-e', <<'EOM' ]; +use POSIX qw(:signal_h); +use BSD::Resource qw(times); ++use Time::HiRes qw(time); # gettimeofday +my $set = POSIX::SigSet->new; +$set->emptyset; # spawn() defaults to blocking all signals +sigprocmask(SIG_SETMASK, $set) or die "SIG_SETMASK: $!"; +my $tot = 0; +$SIG{XCPU} = sub { print "SIGXCPU $tot\n"; exit(1) }; -+my $next = time + 1; ++my $next = time + 1.1; +while (1) { -+ # OpenBSD needs both `times' and `write' (via Perl warn) syscalls -+ # here are both required to trigger RLIMIT_CPU; not sure why. -+ # Even the hard limit seems ignored unless we make those syscalls. -+ # Staying entirely in userspace had no effect, and neither did -+ # some other syscalls tried. Neither fstat, nor sigprocmask were -+ # able to cause either SIGXCPU or SIGKILL. -+ # to fire on respective soft and hard rlimits being exceeded. ++ # OpenBSD needs some syscalls (e.g. `times', `gettimeofday' ++ # and `write' (via Perl warn)) on otherwise idle systems to ++ # hit RLIMIT_CPU and fire signals: ++ # https://marc.info/?i=02A4BB8D-313C-464D-845A-845EB6136B35@gmail.com + my @t = times; -+ $tot += $_ for ($t[0], $t[1]); ++ $tot = $t[0] + $t[1]; + if (time > $next) { + warn "# T: @t (utime, ctime, cutime, cstime)\n"; -+ $next = time + 1; ++ $next = time + 1.1; + } +} +EOM @@ t/spawn.t: SKIP: { vec($rset, fileno($r), 1) = 1; ok(select($rset, undef, undef, 5), 'child died before timeout'); is(waitpid($pid, 0), $pid, 'XCPU child process reaped'); -+ like(my $line = readline($r), qr/SIGXCPU/, 'SIGXCPU handled'); -+ diag "line=$line"; - isnt($?, 0, 'non-zero exit status'); +- isnt($?, 0, 'non-zero exit status'); ++ my $line; ++ like($line = readline($r), qr/SIGXCPU/, 'SIGXCPU handled') or ++ diag explain($line); ++ is($? >> 8, 1, 'non-zero exit status'); } + SKIP: { t/spawn.t | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/t/spawn.t b/t/spawn.t index ff95ae8e..9ed3be36 100644 --- a/t/spawn.t +++ b/t/spawn.t @@ -185,18 +185,42 @@ SKIP: { require BSD::Resource; defined(BSD::Resource::RLIMIT_CPU()) } or skip 'BSD::Resource::RLIMIT_CPU missing', 3; - my ($r, $w); - pipe($r, $w) or die "pipe: $!"; - my $cmd = ['sh', '-c', 'while true; do :; done']; + my $cmd = [ $^X, ($^W ? ('-w') : ()), '-e', <<'EOM' ]; +use POSIX qw(:signal_h); +use BSD::Resource qw(times); +use Time::HiRes qw(time); # gettimeofday +my $set = POSIX::SigSet->new; +$set->emptyset; # spawn() defaults to blocking all signals +sigprocmask(SIG_SETMASK, $set) or die "SIG_SETMASK: $!"; +my $tot = 0; +$SIG{XCPU} = sub { print "SIGXCPU $tot\n"; exit(1) }; +my $next = time + 1.1; +while (1) { + # OpenBSD needs some syscalls (e.g. `times', `gettimeofday' + # and `write' (via Perl warn)) on otherwise idle systems to + # hit RLIMIT_CPU and fire signals: + # https://marc.info/?i=02A4BB8D-313C-464D-845A-845EB6136B35@gmail.com + my @t = times; + $tot = $t[0] + $t[1]; + if (time > $next) { + warn "# T: @t (utime, ctime, cutime, cstime)\n"; + $next = time + 1.1; + } +} +EOM + pipe(my($r, $w)) or die "pipe: $!"; my $fd = fileno($w); - my $opt = { RLIMIT_CPU => [ 1, 1 ], RLIMIT_CORE => [ 0, 0 ], 1 => $fd }; + my $opt = { RLIMIT_CPU => [ 1, 9 ], RLIMIT_CORE => [ 0, 0 ], 1 => $fd }; my $pid = spawn($cmd, undef, $opt); close $w or die "close(w): $!"; my $rset = ''; vec($rset, fileno($r), 1) = 1; ok(select($rset, undef, undef, 5), 'child died before timeout'); is(waitpid($pid, 0), $pid, 'XCPU child process reaped'); - isnt($?, 0, 'non-zero exit status'); + my $line; + like($line = readline($r), qr/SIGXCPU/, 'SIGXCPU handled') or + diag explain($line); + is($? >> 8, 1, 'non-zero exit status'); } SKIP: {