+# return true if complete, false if incomplete (or failure)
+sub shutdn_tls_step ($) {
+ my ($self) = @_;
+ my $sock = $self->{sock} or return;
+ return $self->close if $sock->stop_SSL(SSL_fast_shutdown => 1);
+ return $self->close if $! != EAGAIN;
+ epwait($sock, PublicInbox::TLS::epollbit() | EPOLLONESHOT);
+ unshift(@{$self->{wbuf}}, \&shutdn_tls_step); # autovivifies
+ 0;
+}
+
+# don't bother with shutdown($sock, 2), we don't fork+exec w/o CLOEXEC
+# or fork w/o exec, so no inadvertant socket sharing
+sub shutdn ($) {
+ my ($self) = @_;
+ my $sock = $self->{sock} or return;
+ if (ref($sock) eq 'IO::Socket::SSL') {
+ shutdn_tls_step($self);
+ } else {
+ $self->close;
+ }
+}
+
+# must be called with eval, PublicInbox::DS may not be loaded (see t/qspawn.t)
+sub dwaitpid ($$$) {
+ die "Not in EventLoop\n" unless $in_loop;
+ push @$wait_pids, [ @_ ]; # [ $pid, $cb, $arg ]
+
+ # We could've just missed our SIGCHLD, cover it, here:
+ requeue(\&reap_pids);
+}
+
+sub _run_later () {
+ my $run = $later_queue or return;
+ $later_timer = $later_queue = undef;
+ $_->() for @$run;
+}