]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/Git.pm
git: async_abort includes --batch-check requests
[public-inbox.git] / lib / PublicInbox / Git.pm
index 3c577ab317b29339271f468f3538345ed14690ca..f15ace1a3d625b5d78242eb64238e4cdc2bc72ed 100644 (file)
@@ -62,6 +62,8 @@ sub git_quote ($) {
 
 sub new {
        my ($class, $git_dir) = @_;
+       $git_dir =~ tr!/!/!s;
+       $git_dir =~ s!/*\z!!s;
        # may contain {-tmp} field for File::Temp::Dir
        bless { git_dir => $git_dir, alt_st => '', -git_path => {} }, $class
 }
@@ -110,18 +112,22 @@ sub _bidi_pipe {
                }
                return;
        }
-       my ($out_r, $out_w);
-       pipe($out_r, $out_w) or $self->fail("pipe failed: $!");
-       my @cmd = (qw(git), "--git-dir=$self->{git_dir}",
+       pipe(my ($out_r, $out_w)) or $self->fail("pipe failed: $!");
+       my $rdr = { 0 => $out_r };
+       my $gd = $self->{git_dir};
+       if ($gd =~ s!/([^/]+/[^/]+)\z!/!) {
+               $rdr->{-C} = $gd;
+               $gd = $1;
+       }
+       my @cmd = (qw(git), "--git-dir=$gd",
                        qw(-c core.abbrev=40 cat-file), $batch);
-       my $redir = { 0 => $out_r };
        if ($err) {
                my $id = "git.$self->{git_dir}$batch.err";
                my $fh = tmpfile($id) or $self->fail("tmpfile($id): $!");
                $self->{$err} = $fh;
-               $redir->{2} = $fh;
+               $rdr->{2} = $fh;
        }
-       my ($in_r, $p) = popen_rd(\@cmd, undef, $redir);
+       my ($in_r, $p) = popen_rd(\@cmd, undef, $rdr);
        $self->{$pid} = $p;
        $self->{"$pid.owner"} = $$;
        $out_w->autoflush(1);
@@ -196,7 +202,7 @@ sub cat_async_step ($$) {
        my ($self, $inflight) = @_;
        die 'BUG: inflight empty or odd' if scalar(@$inflight) < 3;
        my ($req, $cb, $arg) = splice(@$inflight, 0, 3);
-       my $rbuf = delete($self->{cat_rbuf}) // \(my $new = '');
+       my $rbuf = delete($self->{rbuf}) // \(my $new = '');
        my ($bref, $oid, $type, $size);
        my $head = my_readline($self->{in}, $rbuf);
        # ->fail may be called via Gcf2Client.pm
@@ -219,7 +225,7 @@ sub cat_async_step ($$) {
                my $err = $! ? " ($!)" : '';
                $self->fail("bad result from async cat-file: $head$err");
        }
-       $self->{cat_rbuf} = $rbuf if $$rbuf ne '';
+       $self->{rbuf} = $rbuf if $$rbuf ne '';
        eval { $cb->($bref, $oid, $type, $size, $arg) };
        warn "E: $oid: $@\n" if $@;
 }
@@ -253,7 +259,7 @@ sub check_async_step ($$) {
        my ($self, $inflight_c) = @_;
        die 'BUG: inflight empty or odd' if scalar(@$inflight_c) < 3;
        my ($req, $cb, $arg) = splice(@$inflight_c, 0, 3);
-       my $rbuf = delete($self->{chk_rbuf}) // \(my $new = '');
+       my $rbuf = delete($self->{rbuf_c}) // \(my $new = '');
        chomp(my $line = my_readline($self->{in_c}, $rbuf));
        my ($hex, $type, $size) = split(/ /, $line);
 
@@ -265,7 +271,7 @@ sub check_async_step ($$) {
                my $ret = my_read($self->{in_c}, $rbuf, $type + 1);
                $self->fail(defined($ret) ? 'read EOF' : "read: $!") if !$ret;
        }
-       $self->{chk_rbuf} = $rbuf if $$rbuf ne '';
+       $self->{rbuf_c} = $rbuf if $$rbuf ne '';
        eval { $cb->($hex, $type, $size, $arg, $self) };
        warn "E: check($req) $@\n" if $@;
 }
@@ -327,24 +333,28 @@ sub _destroy {
        dwaitpid($p) if $$ == $self->{"$pid.owner"};
 }
 
-sub cat_async_abort ($) {
+sub async_abort ($) {
        my ($self) = @_;
-       if (my $inflight = $self->{inflight}) {
-               while (@$inflight) {
-                       my ($req, $cb, $arg) = splice(@$inflight, 0, 3);
-                       $req =~ s/ .*//; # drop git_dir for Gcf2Client
-                       eval { $cb->(undef, $req, undef, undef, $arg) };
-                       warn "E: $req: $@ (in abort)\n" if $@;
+       while (scalar(@{$self->{inflight_c} // []}) ||
+                       scalar(@{$self->{inflight} // []})) {
+               for my $c ('', '_c') {
+                       my $q = $self->{"inflight$c"};
+                       while (@$q) {
+                               my ($req, $cb, $arg) = splice(@$q, 0, 3);
+                               $req =~ s/ .*//; # drop git_dir for Gcf2Client
+                               eval { $cb->(undef, $req, undef, undef, $arg) };
+                               warn "E: $req: $@ (in abort)\n" if $@;
+                       }
+                       delete $self->{"inflight$c"};
+                       delete $self->{"rbuf$c"};
                }
-               delete $self->{cat_rbuf};
-               delete $self->{inflight};
        }
        cleanup($self);
 }
 
 sub fail { # may be augmented in subclasses
        my ($self, $msg) = @_;
-       cat_async_abort($self);
+       async_abort($self);
        croak(ref($self) . ' ' . ($self->{git_dir} // '') . ": $msg");
 }
 
@@ -385,25 +395,26 @@ sub async_wait_all ($) {
        my ($self) = @_;
        while (scalar(@{$self->{inflight_c} // []}) ||
                        scalar(@{$self->{inflight} // []})) {
-               $self->check_async_wait;
-               $self->cat_async_wait;
+               check_async_wait($self);
+               cat_async_wait($self);
        }
 }
 
 # returns true if there are pending "git cat-file" processes
 sub cleanup {
-       my ($self) = @_;
+       my ($self, $lazy) = @_;
+       return 1 if $lazy && (scalar(@{$self->{inflight_c} // []}) ||
+                               scalar(@{$self->{inflight} // []}));
        local $in_cleanup = 1;
        delete $self->{async_cat};
        async_wait_all($self);
        delete $self->{inflight};
        delete $self->{inflight_c};
-       _destroy($self, qw(cat_rbuf in out pid));
-       _destroy($self, qw(chk_rbuf in_c out_c pid_c err_c));
-       defined($self->{pid}) || defined($self->{pid_c});
+       _destroy($self, qw(rbuf in out pid));
+       _destroy($self, qw(rbuf_c in_c out_c pid_c err_c));
+       undef;
 }
 
-
 # assuming a well-maintained repo, this should be a somewhat
 # accurate estimation of its size
 # TODO: show this in the WWW UI as a hint to potential cloners
@@ -526,18 +537,19 @@ sub manifest_entry {
 # returns true if there are pending cat-file processes
 sub cleanup_if_unlinked {
        my ($self) = @_;
-       return cleanup($self) if $^O ne 'linux';
+       return cleanup($self, 1) if $^O ne 'linux';
        # Linux-specific /proc/$PID/maps access
        # TODO: support this inside git.git
        my $ret = 0;
        for my $fld (qw(pid pid_c)) {
                my $pid = $self->{$fld} // next;
-               open my $fh, '<', "/proc/$pid/maps" or return cleanup($self);
+               open my $fh, '<', "/proc/$pid/maps" or return cleanup($self, 1);
                while (<$fh>) {
                        # n.b. we do not restart for unlinked multi-pack-index
                        # since it's not too huge, and the startup cost may
                        # be higher.
-                       return cleanup($self) if /\.(?:idx|pack) \(deleted\)$/;
+                       /\.(?:idx|pack) \(deleted\)$/ and
+                               return cleanup($self, 1);
                }
                ++$ret;
        }