+ my $ret = my_read($self->{in_c}, $rbuf, $type + 1);
+ $self->fail(defined($ret) ? 'read EOF' : "read: $!") if !$ret;
+ }
+ $self->{rbuf_c} = $rbuf if $$rbuf ne '';
+ splice(@$inflight_c, 0, 3); # don't retry $cb on ->fail
+ eval { $cb->($hex, $type, $size, $arg, $self) };
+ async_err($self, $req, $hex, $@, 'check') if $@;
+}
+
+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);