about summary refs log tree commit homepage
path: root/t/spawn.t
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2023-08-29 17:20:16 +0000
committerEric Wong <e@80x24.org>2023-08-29 21:57:13 +0000
commit1ea097fe25d3717ad0b8e232268a6d55d8ed7225 (patch)
tree2c8fd60178fe124e050ba880e412fb3bc6a8674e /t/spawn.t
parentbd0dc6ad0391f811f5248d83538a2eef8f74de95 (diff)
downloadpublic-inbox-1ea097fe25d3717ad0b8e232268a6d55d8ed7225.tar.gz
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
Diffstat (limited to 't/spawn.t')
-rw-r--r--t/spawn.t34
1 files 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: {