From 53eafcd90a3179200192263807cf3df7c869b500 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 30 Jan 2024 07:22:21 +0000 Subject: spawn: support some rlimit uses via Inline::C BSD::Resource isn't packaged for Alpine (as of 3.19), but we also have optional Inline::C support and already rely on calling setrlimit(2) directly from the Inline::C version of pi_fork_exec. --- lib/PublicInbox/ExtSearchIdx.pm | 1 + lib/PublicInbox/Limiter.pm | 15 +++++++++------ lib/PublicInbox/Spawn.pm | 31 ++++++++++++++++++++++++------- t/spawn.t | 21 +++++++++++++-------- 4 files changed, 47 insertions(+), 21 deletions(-) diff --git a/lib/PublicInbox/ExtSearchIdx.pm b/lib/PublicInbox/ExtSearchIdx.pm index 53078124..ebbffffc 100644 --- a/lib/PublicInbox/ExtSearchIdx.pm +++ b/lib/PublicInbox/ExtSearchIdx.pm @@ -22,6 +22,7 @@ use Scalar::Util qw(blessed); use Sys::Hostname qw(hostname); use File::Glob qw(bsd_glob GLOB_NOSORT); use PublicInbox::MultiGit; +use PublicInbox::Spawn (); use PublicInbox::Search; use PublicInbox::SearchIdx qw(prepare_stack is_ancestor is_bad_blob); use PublicInbox::OverIdx; diff --git a/lib/PublicInbox/Limiter.pm b/lib/PublicInbox/Limiter.pm index 48a2b6a3..a8d08fc3 100644 --- a/lib/PublicInbox/Limiter.pm +++ b/lib/PublicInbox/Limiter.pm @@ -31,14 +31,17 @@ sub setup_rlimit { } elsif (scalar(@rlimit) != 2) { warn "could not parse $k: $v\n"; } - eval { require BSD::Resource }; - if ($@) { - warn "BSD::Resource missing for $rlim"; - next; - } + my $inf = $v =~ /\binfinity\b/i ? + $PublicInbox::Spawn::RLIMITS{RLIM_INFINITY} // eval { + require BSD::Resource; + BSD::Resource::RLIM_INFINITY(); + } // do { + warn "BSD::Resource missing for $rlim"; + next; + } : undef; for my $i (0..$#rlimit) { next if $rlimit[$i] ne 'INFINITY'; - $rlimit[$i] = BSD::Resource::RLIM_INFINITY(); + $rlimit[$i] = $inf; } $self->{$rlim} = \@rlimit; } diff --git a/lib/PublicInbox/Spawn.pm b/lib/PublicInbox/Spawn.pm index e6b12994..e36659ce 100644 --- a/lib/PublicInbox/Spawn.pm +++ b/lib/PublicInbox/Spawn.pm @@ -21,10 +21,11 @@ use IO::Handle (); use Carp qw(croak); use PublicInbox::IO; our @EXPORT_OK = qw(which spawn popen_rd popen_wr run_die run_wait run_qx); -our @RLIMITS = qw(RLIMIT_CPU RLIMIT_CORE RLIMIT_DATA); +our (@RLIMITS, %RLIMITS); use autodie qw(close open pipe seek sysseek truncate); BEGIN { + @RLIMITS = qw(RLIMIT_CPU RLIMIT_CORE RLIMIT_DATA); my $all_libc = <<'ALL_LIBC'; # all *nix systems we support #include #include @@ -283,14 +284,28 @@ void recv_cmd4(PerlIO *s, SV *buf, STRLEN n) Inline_Stack_Done; } #endif /* defined(CMSG_SPACE) && defined(CMSG_LEN) */ -ALL_LIBC +void rlimit_map() +{ + Inline_Stack_Vars; + Inline_Stack_Reset; +ALL_LIBC my $inline_dir = $ENV{PERL_INLINE_DIRECTORY} // ( $ENV{XDG_CACHE_HOME} // ( ($ENV{HOME} // '/nonexistent').'/.cache' ) ).'/public-inbox/inline-c'; undef $all_libc unless -d $inline_dir; if (defined $all_libc) { + for (@RLIMITS, 'RLIM_INFINITY') { + $all_libc .= <new($inline_dir. @@ -316,6 +331,7 @@ ALL_LIBC } if (defined $all_libc) { # set for Gcf2 $ENV{PERL_INLINE_DIRECTORY} = $inline_dir; + %RLIMITS = rlimit_map(); } else { require PublicInbox::SpawnPP; *pi_fork_exec = \&PublicInbox::SpawnPP::pi_fork_exec @@ -361,11 +377,12 @@ sub spawn ($;$$) { my $rlim = []; foreach my $l (@RLIMITS) { my $v = $opt->{$l} // next; - my $r = eval "require BSD::Resource; BSD::Resource::$l();"; - unless (defined $r) { - warn "$l undefined by BSD::Resource: $@\n"; - next; - } + my $r = $RLIMITS{$l} // + eval "require BSD::Resource; BSD::Resource::$l();" // + do { + warn "$l undefined by BSD::Resource: $@\n"; + next; + }; push @$rlim, $r, @$v; } my $cd = $opt->{'-C'} // ''; # undef => NULL mapping doesn't work? diff --git a/t/spawn.t b/t/spawn.t index 48f541b8..5b17ed38 100644 --- a/t/spawn.t +++ b/t/spawn.t @@ -6,7 +6,7 @@ use Test::More; use PublicInbox::Spawn qw(which spawn popen_rd run_qx); require PublicInbox::Sigfd; require PublicInbox::DS; - +my $rlimit_map = PublicInbox::Spawn->can('rlimit_map'); { my $true = which('true'); ok($true, "'true' command found with which()"); @@ -192,14 +192,19 @@ EOF } SKIP: { - eval { - require BSD::Resource; - defined(BSD::Resource::RLIMIT_CPU()) - } or skip 'BSD::Resource::RLIMIT_CPU missing', 3; + if ($rlimit_map) { # Inline::C installed + my %rlim = $rlimit_map->(); + ok defined($rlim{RLIMIT_CPU}), 'RLIMIT_CPU defined'; + } else { + eval { + require BSD::Resource; + defined(BSD::Resource::RLIMIT_CPU()) + } or skip 'BSD::Resource::RLIMIT_CPU missing', 3; + } my $cmd = [ $^X, qw(-w -e), <<'EOM' ]; use POSIX qw(:signal_h); -use BSD::Resource qw(times); use Time::HiRes qw(time); # gettimeofday +my $have_bsd_resource = eval { require BSD::Resource }; my $set = POSIX::SigSet->new; $set->emptyset; # spawn() defaults to blocking all signals sigprocmask(SIG_SETMASK, $set) or die "SIG_SETMASK: $!"; @@ -211,10 +216,10 @@ while (1) { # 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; + my @t = $have_bsd_resource ? BSD::Resource::times() : (0, 0); $tot = $t[0] + $t[1]; if (time > $next) { - warn "# T: @t (utime, ctime, cutime, cstime)\n"; + warn "# T: @t (utime, ctime, cutime, cstime)\n" if @t; $next = time + 1.1; } } -- cgit v1.2.3-24-ge0c7