-# Copyright (C) 2016-2020 all contributors <meta@public-inbox.org>
+# Copyright (C) 2016-2021 all contributors <meta@public-inbox.org>
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
# Like most Perl modules in public-inbox, this is internal and
# operate in. This can be useful to ensure smaller inboxes can
# be cloned while cloning of large inboxes is maxed out.
#
-# This does not depend on PublicInbox::DS or any other external
-# scheduling mechanism, you just need to call start() and finish()
-# appropriately. However, public-inbox-httpd (which uses PublicInbox::DS)
-# will be able to schedule this based on readability of stdout from
-# the spawned process. See GitHTTPBackend.pm and SolverGit.pm for
-# usage examples. It does not depend on any form of threading.
+# This does not depend on the PublicInbox::DS::event_loop or any
+# other external scheduling mechanism, you just need to call
+# start() and finish() appropriately. However, public-inbox-httpd
+# (which uses PublicInbox::DS) will be able to schedule this
+# based on readability of stdout from the spawned process.
+# See GitHTTPBackend.pm and SolverGit.pm for usage examples.
+# It does not depend on any form of threading.
#
# This is useful for scheduling CGI execution of both long-lived
# git-http-backend(1) process (for "git clone") as well as short-lived
$self->{cmd} = $o{quiet} ? undef : $cmd;
eval {
# popen_rd may die on EMFILE, ENFILE
- ($self->{rpipe}, $self->{pid}) = popen_rd($cmd, $cmd_env, \%o);
+ $self->{rpipe} = popen_rd($cmd, $cmd_env, \%o);
- die "E: $!" unless defined($self->{pid});
+ die "E: $!" unless defined($self->{rpipe});
$limiter->{running}++;
$start_cb->($self); # EPOLL_CTL_ADD may ENOSPC/ENOMEM
$msg;
}
-sub log_err ($$) {
- my ($env, $msg) = @_;
- $env->{'psgi.errors'}->print($msg, "\n");
-}
-
sub finalize ($$) {
my ($self, $err) = @_;
$self->{err} = $err;
}
if ($env && $self->{cmd}) {
- log_err($env, join(' ', @{$self->{cmd}}) . ": $err");
+ warn join(' ', @{$self->{cmd}}) . ": $err";
}
}
if ($qx_cb) {
}
}
-# callback for dwaitpid
-sub waitpid_err ($$) {
- my ($self, $pid) = @_;
- my $xpid = delete $self->{pid};
- my $err;
- if (defined $pid) {
- if ($pid > 0) { # success!
- $err = child_err($?);
- } elsif ($pid < 0) { # ??? does this happen in our case?
- $err = "W: waitpid($xpid, 0) => $pid: $!";
- } # else should not be called with pid == 0
- }
- finalize($self, $err);
-}
-
-sub do_waitpid ($) {
- my ($self) = @_;
- my $pid = $self->{pid};
- # PublicInbox::DS may not be loaded
- eval { PublicInbox::DS::dwaitpid($pid, \&waitpid_err, $self) };
- # done if we're running in PublicInbox::DS::EventLoop
- if ($@) {
- # non public-inbox-{httpd,nntpd} callers may block:
- my $ret = waitpid($pid, 0);
- waitpid_err($self, $ret);
- }
-}
+# callback for dwaitpid or ProcessPipe
+sub waitpid_err { finalize($_[0], child_err($?)) }
sub finish ($;$) {
my ($self, $err) = @_;
- if (delete $self->{rpipe}) {
- do_waitpid($self);
- } else {
- finalize($self, $err);
- }
+ my $tied_pp = delete($self->{rpipe}) or return finalize($self, $err);
+ my PublicInbox::ProcessPipe $pp = tied *$tied_pp;
+ @$pp{qw(cb arg)} = (\&waitpid_err, $self); # for ->DESTROY
}
sub start ($$$) {
# PSGI servers.
sub event_step {
my ($self, $err) = @_; # $err: $!
- log_err($self->{psgi_env}, "psgi_{return,qx} $err") if defined($err);
+ warn "psgi_{return,qx} $err" if defined($err);
finish($self);
my ($fh, $qx_fh) = delete(@$self{qw(fh qx_fh)});
$fh->close if $fh; # async-only (psgi_return)
sub rd_hdr ($) {
my ($self) = @_;
# typically used for reading CGI headers
- # we must loop until EAGAIN for EPOLLET in HTTPD/Async.pm
# We also need to check EINTR for generic PSGI servers.
my $ret;
my $total_rd = 0;
$total_rd += $r;
eval { $ret = $ph_cb->($total_rd, $hdr_buf, $ph_arg) };
if ($@) {
- log_err($self->{psgi_env}, "parse_hdr: $@");
+ warn "parse_hdr: $@";
$ret = [ 500, [], [ "Internal error\n" ] ];
}
} else {
# caller should notify us when it's ready:
return if $! == EAGAIN;
next if $! == EINTR; # immediate retry
- log_err($self->{psgi_env}, "error reading header: $!");
+ warn "error reading header: $!";
$ret = [ 500, [], [ "Internal error\n" ] ];
}
} until (defined $ret);
}
sub setup_rlimit {
- my ($self, $name, $config) = @_;
+ my ($self, $name, $cfg) = @_;
foreach my $rlim (@PublicInbox::Spawn::RLIMITS) {
my $k = lc($rlim);
$k =~ tr/_//d;
$k = "publicinboxlimiter.$name.$k";
- defined(my $v = $config->{$k}) or next;
+ defined(my $v = $cfg->{$k}) or next;
my @rlimit = split(/\s*,\s*/, $v);
if (scalar(@rlimit) == 1) {
push @rlimit, $rlimit[0];