X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=lib%2FPublicInbox%2FGit.pm;h=181026c7bc1b726ff562e1b488f3256a0bf98c87;hb=b80dccff433cc529040e3c11604d76f669570511;hp=776e483291b61fc639aa6b4aac5a4b66ce8c350a;hpb=10ee3548084c125f20fe2c830faea2a43413be92;p=public-inbox.git diff --git a/lib/PublicInbox/Git.pm b/lib/PublicInbox/Git.pm index 776e4832..181026c7 100644 --- a/lib/PublicInbox/Git.pm +++ b/lib/PublicInbox/Git.pm @@ -8,15 +8,17 @@ # There are also API changes to simplify our usage and data set. package PublicInbox::Git; use strict; -use warnings; +use v5.10.1; +use parent qw(Exporter); use POSIX (); use IO::Handle; # ->autoflush +use Errno qw(EINTR); use File::Glob qw(bsd_glob GLOB_NOSORT); +use Time::HiRes qw(stat); use PublicInbox::Spawn qw(popen_rd); use PublicInbox::Tmpfile; -use base qw(Exporter); +use Carp qw(croak); our @EXPORT_OK = qw(git_unquote git_quote); -use Errno qw(EINTR); our $PIPE_BUFSIZ = 65536; # Linux default our $in_cleanup; @@ -230,26 +232,71 @@ sub cat_file { $result->[0]; } -sub check { - my ($self, $obj) = @_; - _bidi_pipe($self, qw(--batch-check in_c out_c pid_c err_c)); - print { $self->{out_c} } $obj, "\n" or fail($self, "write error: $!"); - my $rbuf = ''; # TODO: async + {chk_rbuf} - chomp(my $line = my_readline($self->{in_c}, \$rbuf)); - my ($hex, $type, $size) = split(' ', $line); +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->{rbuf_c}) // \(my $new = ''); + chomp(my $line = my_readline($self->{in_c}, $rbuf)); + my ($hex, $type, $size) = split(/ /, $line); - # Future versions of git.git may show 'ambiguous', but for now, + # Future versions of git.git may have type=ambiguous, but for now, # we must handle 'dangling' below (and maybe some other oddball # stuff): # https://public-inbox.org/git/20190118033845.s2vlrb3wd3m2jfzu@dcvr/T/ - return if $type eq 'missing' || $type eq 'ambiguous'; - if ($hex eq 'dangling' || $hex eq 'notdir' || $hex eq 'loop') { - my $ret = my_read($self->{in_c}, \$rbuf, $type + 1); + my $ret = my_read($self->{in_c}, $rbuf, $type + 1); fail($self, defined($ret) ? 'read EOF' : "read: $!") if !$ret; - return; } + eval { $cb->($hex, $type, $size, $arg, $self) }; + warn "E: check($req) $@\n" if $@; + $self->{rbuf_c} = $rbuf if $$rbuf ne ''; +} +sub check_async_wait ($) { + my ($self) = @_; + my $inflight_c = delete $self->{inflight_c} or return; + while (scalar(@$inflight_c)) { + check_async_step($self, $inflight_c); + } +} + +sub check_async_begin ($) { + my ($self) = @_; + cleanup($self) if alternates_changed($self); + _bidi_pipe($self, qw(--batch-check in_c out_c pid_c err_c)); + die 'BUG: already in async check' if $self->{inflight_c}; + $self->{inflight_c} = []; +} + +sub check_async ($$$$) { + my ($self, $oid, $cb, $arg) = @_; + my $inflight_c = $self->{inflight_c} // check_async_begin($self); + if (scalar(@$inflight_c) >= MAX_INFLIGHT) { + check_async_step($self, $inflight_c); + } + print { $self->{out_c} } $oid, "\n" or fail($self, "write error: $!"); + push(@$inflight_c, $oid, $cb, $arg); +} + +sub _check_cb { # check_async callback + my ($hex, $type, $size, $result) = @_; + @$result = ($hex, $type, $size); +} + +sub check { + my ($self, $oid) = @_; + my $result = []; + check_async($self, $oid, \&_check_cb, $result); + check_async_wait($self); + my ($hex, $type, $size) = @$result; + + # Future versions of git.git may show 'ambiguous', but for now, + # we must handle 'dangling' below (and maybe some other oddball + # stuff): + # https://public-inbox.org/git/20190118033845.s2vlrb3wd3m2jfzu@dcvr/T/ + return if $type eq 'missing' || $type eq 'ambiguous'; + return if $hex eq 'dangling' || $hex eq 'notdir' || $hex eq 'loop'; ($hex, $type, $size); } @@ -273,7 +320,7 @@ sub cat_async_abort ($) { sub fail { my ($self, $msg) = @_; $self->{inflight} ? cat_async_abort($self) : cleanup($self); - die $msg; + croak("git $self->{git_dir}: $msg"); } sub popen { @@ -296,6 +343,7 @@ sub cleanup { my ($self) = @_; local $in_cleanup = 1; delete $self->{async_cat}; + check_async_wait($self); cat_async_wait($self); _destroy($self, qw(cat_rbuf in out pid)); _destroy($self, qw(chk_rbuf in_c out_c pid_c err_c)); @@ -363,6 +411,22 @@ sub cat_async ($$$;$) { push(@$inflight, $oid, $cb, $arg); } +# this is safe to call inside $cb, but not guaranteed to enqueue +# returns true if successful, undef if not. +sub async_prefetch { + my ($self, $oid, $cb, $arg) = @_; + if (defined($self->{async_cat}) && (my $inflight = $self->{inflight})) { + # we could use MAX_INFLIGHT here w/o the halving, + # but lets not allow one client to monopolize a git process + if (scalar(@$inflight) < int(MAX_INFLIGHT/2)) { + print { $self->{out} } $oid, "\n" or + fail($self, "write error: $!"); + return push(@$inflight, $oid, $cb, $arg); + } + } + undef; +} + sub extract_cmt_time { my ($bref, undef, undef, undef, $modified) = @_;