+ for (my $i = 0; $i < @$inflight; $i += 3) {
+ $buf .= "$inflight->[$i]\n";
+ }
+ $self->{out}->blocking(1); # brand new pipe, should never block
+ print { $self->{out} } $buf or $self->fail("write error: $!");
+ $self->{out}->blocking(0);
+ my $req = shift @$inflight;
+ unshift(@$inflight, \$req); # \$ref to indicate retried
+
+ cat_async_step($self, $inflight); # take one step
+}
+
+# returns true if prefetch is successful
+sub async_prefetch {
+ my ($self, $oid, $cb, $arg) = @_;
+ my $inflight = $self->{inflight} or return;
+ return if @$inflight;
+ substr($oid, 0, 0) = 'contents ' if $self->{-bc};
+ write_all($self, $self->{out}, "$oid\n", \&cat_async_step, $inflight);
+ push(@$inflight, $oid, $cb, $arg);
+}
+
+sub cat_async_step ($$) {
+ my ($self, $inflight) = @_;
+ die 'BUG: inflight empty or odd' if scalar(@$inflight) < 3;
+ my ($req, $cb, $arg) = @$inflight[0, 1, 2];
+ my $rbuf = delete($self->{rbuf}) // \(my $new = '');
+ my ($bref, $oid, $type, $size);
+ my $head = my_readline($self->{in}, $rbuf);
+ my $cmd = ref($req) ? $$req : $req;
+ # ->fail may be called via Gcf2Client.pm
+ my $bc = $self->{-bc};
+ if ($head =~ /^([0-9a-f]{40,}) (\S+) ([0-9]+)$/) {
+ ($oid, $type, $size) = ($1, $2, $3 + 0);
+ unless ($bc && $cmd =~ /\Ainfo /) { # --batch-command
+ $bref = my_read($self->{in}, $rbuf, $size + 1) or
+ $self->fail(defined($bref) ?
+ 'read EOF' : "read: $!");
+ chop($$bref) eq "\n" or
+ $self->fail('LF missing after blob');
+ }
+ } elsif ($bc && $cmd =~ /\Ainfo / &&
+ $head =~ / (missing|ambiguous)\n/) {
+ $type = $1;
+ $oid = substr($cmd, 5);
+ } elsif ($head =~ s/ missing\n//s) {
+ $oid = $head;
+ # ref($req) indicates it's already been retried
+ # -gcf2 retries internally, so it never hits this path:
+ if (!ref($req) && !$in_cleanup && $self->alternates_changed) {
+ return cat_async_retry($self, $inflight);
+ }
+ $type = 'missing';
+ if ($oid eq '') {
+ $oid = $cmd;
+ $oid =~ s/\A(?:contents|info) // if $bc;
+ }
+ } else {
+ my $err = $! ? " ($!)" : '';
+ $self->fail("bad result from async cat-file: $head$err");