next_step($self); # onto do_git_apply
}
+sub qsp_qx ($$$) {
+ my ($self, $qsp, $cb) = @_;
+ $qsp->{qsp_err} = \($self->{-qsp_err} = '');
+ $qsp->psgi_qx($self->{psgi_env}, $self->{limiter}, $cb, $self);
+}
+
sub prepare_index ($) {
my ($self) = @_;
my $patches = $self->{patches};
my $cmd = [ qw(git update-index -z --index-info) ];
my $qsp = PublicInbox::Qspawn->new($cmd, $self->{git_env}, $rdr);
$path_a = git_quote($path_a);
- $qsp->{qsp_err} = \($self->{-qsp_err} = '');
$self->{-msg} = "index prepared:\n$mode_a $oid_full\t$path_a";
- $qsp->psgi_qx($self->{psgi_env}, undef, \&update_index_result, $self);
+ qsp_qx $self, $qsp, \&update_index_result;
}
# pure Perl "git init"
my @cmd = qw(git ls-files -s -z);
my $qsp = PublicInbox::Qspawn->new(\@cmd, $self->{git_env});
$self->{-cur_di} = $di;
- $qsp->{qsp_err} = \($self->{-qsp_err} = '');
- $qsp->psgi_qx($self->{psgi_env}, undef, \&ls_files_result, $self);
+ qsp_qx $self, $qsp, \&ls_files_result;
}
sub do_git_apply ($) {
my $opt = { 2 => 1, -C => _tmp($self)->dirname, quiet => 1 };
my $qsp = PublicInbox::Qspawn->new(\@cmd, $self->{git_env}, $opt);
$self->{-cur_di} = $di;
- $qsp->{qsp_err} = \($self->{-qsp_err} = '');
- $qsp->psgi_qx($self->{psgi_env}, undef, \&apply_result, $self);
+ qsp_qx $self, $qsp, \&apply_result;
}
sub di_url ($$) {
'160000' => 'g', # commit (gitlink)
);
+# TODO: not fork safe, but we don't fork w/o exec in PublicInbox::WWW
+my (@solver_q, $solver_lim);
+my $solver_nr = 0;
+
sub html_page ($$;@) {
my ($ctx, $code) = @_[0, 1];
my $wcb = delete $ctx->{-wcb};
'</code></pre></td></tr></table>'.dbg_log($ctx), @def);
}
-# GET /$INBOX/$GIT_OBJECT_ID/s/
-# GET /$INBOX/$GIT_OBJECT_ID/s/$FILENAME
-sub show ($$;$) {
- my ($ctx, $oid_b, $fn) = @_;
- my $hints = $ctx->{hints} = {};
+sub start_solver ($) {
+ my ($ctx) = @_;
while (my ($from, $to) = each %QP_MAP) {
my $v = $ctx->{qp}->{$from} // next;
- $hints->{$to} = $v if $v ne '';
+ $ctx->{hints}->{$to} = $v if $v ne '';
}
- $ctx->{fn} = $fn;
- $ctx->{-tmp} = File::Temp->newdir("solver.$oid_b-XXXX", TMPDIR => 1);
+ $ctx->{-next_solver} = PublicInbox::OnDestroy->new($$, \&next_solver);
+ ++$solver_nr;
+ $ctx->{-tmp} = File::Temp->newdir("solver.$ctx->{oid_b}-XXXX",
+ TMPDIR => 1);
$ctx->{lh} or open $ctx->{lh}, '+>>', "$ctx->{-tmp}/solve.log";
my $solver = PublicInbox::SolverGit->new($ctx->{ibx},
\&solve_result, $ctx);
+ $solver->{limiter} = $solver_lim;
$solver->{gits} //= [ $ctx->{git} ];
$solver->{tmp} = $ctx->{-tmp}; # share tmpdir
# PSGI server will call this immediately and give us a callback (-wcb)
+ $solver->solve(@$ctx{qw(env lh oid_b hints)});
+}
+
+# run the next solver job when done and DESTROY-ed
+sub next_solver {
+ --$solver_nr;
+ # XXX FIXME: client may've disconnected if it waited a long while
+ start_solver(shift(@solver_q) // return);
+}
+
+sub may_start_solver ($) {
+ my ($ctx) = @_;
+ $solver_lim //= $ctx->{www}->{pi_cfg}->limiter('codeblob');
+ if ($solver_nr >= $solver_lim->{max}) {
+ @solver_q > 128 ? html_page($ctx, 503, 'too busy')
+ : push(@solver_q, $ctx);
+ } else {
+ start_solver($ctx);
+ }
+}
+
+# GET /$INBOX/$GIT_OBJECT_ID/s/
+# GET /$INBOX/$GIT_OBJECT_ID/s/$FILENAME
+sub show ($$;$) {
+ my ($ctx, $oid_b, $fn) = @_;
+ @$ctx{qw(oid_b fn)} = ($oid_b, $fn);
sub {
$ctx->{-wcb} = $_[0]; # HTTP write callback
- $solver->solve($ctx->{env}, $ctx->{lh}, $oid_b, $hints);
+ may_start_solver $ctx;
};
}