+sub dwaitpid ($;$$) {
+ my ($pid, $cb, $arg) = @_;
+ if ($in_loop) {
+ push @$wait_pids, [ $pid, $cb, $arg ];
+ # We could've just missed our SIGCHLD, cover it, here:
+ enqueue_reap();
+ } else {
+ my $ret = waitpid($pid, 0);
+ if ($ret == $pid) {
+ if ($cb) {
+ eval { $cb->($arg, $pid) };
+ carp "E: dwaitpid($pid) !in_loop: $@" if $@;
+ }
+ } else {
+ carp "waitpid($pid, 0) = $ret, \$!=$!, \$?=$?";
+ }
+ }
+}
+
+sub _run_later () {
+ my $q = $later_q or return;
+ $later_timer = $later_q = undef;
+ $Stack{later_q} = $q;
+ $_->() for @$q;
+ delete $Stack{later_q};
+}
+
+sub later ($) {
+ push @$later_q, $_[0]; # autovivifies @$later_q
+ $later_timer //= add_timer(60, \&_run_later);
+}
+
+sub expire_old () {
+ my $now = now();
+ my $exp = $EXPTIME;
+ my $old = $now - $exp;
+ my %new;
+ while (my ($fd, $idle_at) = each %$EXPMAP) {
+ if ($idle_at < $old) {
+ my $ds_obj = $DescriptorMap{$fd};
+ $new{$fd} = $idle_at if !$ds_obj->shutdn;
+ } else {
+ $new{$fd} = $idle_at;
+ }
+ }
+ $EXPMAP = \%new;
+ $exp_timer = scalar(keys %new) ? later(\&expire_old) : undef;
+}
+
+sub update_idle_time {
+ my ($self) = @_;
+ my $sock = $self->{sock} or return;
+ $EXPMAP->{fileno($sock)} = now();
+ $exp_timer //= later(\&expire_old);
+}
+
+sub not_idle_long {
+ my ($self, $now) = @_;
+ my $sock = $self->{sock} or return;
+ my $idle_at = $EXPMAP->{fileno($sock)} or return;
+ ($idle_at + $EXPTIME) > $now;