+sub check_async_wait ($) {
+ my ($self) = @_;
+ my $inflight_c = $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);
+ while (scalar(@$inflight_c) >= MAX_INFLIGHT) {
+ check_async_step($self, $inflight_c);
+ }
+ print { $self->{out_c} } $oid, "\n" or $self->fail("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';