From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-Status: No, score=-4.0 required=3.0 tests=ALL_TRUSTED,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 05D201F55B for ; Sat, 30 May 2020 08:51:11 +0000 (UTC) From: Eric Wong To: meta@public-inbox.org Subject: [PATCH] testcommon: speed up wait_for_tail() on GNU/Linux Date: Sat, 30 May 2020 08:51:10 +0000 Message-Id: <20200530085110.4695-1-e@yhbt.net> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: Somewhat recent versions of GNU tail(1) use inotify(7) on Linux; so don't penalize hackers using TAIL='tail -F' to run their tests with extra delays. Ironically, we still need to busy loop on /proc/$TAIL_PID/{fd,fdinfo} since inotify doesn't seem to support procfs. --- lib/PublicInbox/TestCommon.pm | 43 +++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/lib/PublicInbox/TestCommon.pm b/lib/PublicInbox/TestCommon.pm index 79e597f5..246047b1 100644 --- a/lib/PublicInbox/TestCommon.pm +++ b/lib/PublicInbox/TestCommon.pm @@ -5,6 +5,7 @@ package PublicInbox::TestCommon; use strict; use parent qw(Exporter); +use v5.10.1; use Fcntl qw(FD_CLOEXEC F_SETFD F_GETFD :seek); use POSIX qw(dup2); use IO::Socket::INET; @@ -249,7 +250,39 @@ sub run_script ($;$$) { $? == 0; } -sub wait_for_tail () { sleep(2) } +sub tick (;$) { + my $tick = shift // 0.1; + select undef, undef, undef, $tick; + 1; +} + +sub wait_for_tail ($;$) { + my ($tail_pid, $stop) = @_; + my $wait = 2; + if ($^O eq 'linux') { # GNU tail may use inotify + state $tail_has_inotify; + return tick if $stop && $tail_has_inotify; + my $end = time + $wait; + my @ino; + do { + @ino = grep { + readlink($_) =~ /\binotify\b/ + } glob("/proc/$tail_pid/fd/*"); + } while (!@ino && time <= $end and tick); + return if !@ino; + $tail_has_inotify = 1; + $ino[0] =~ s!/fd/!/fdinfo/!; + my @info; + do { + if (open my $fh, '<', $ino[0]) { + local $/ = "\n"; + @info = grep(/^inotify wd:/, <$fh>); + } + } while (scalar(@info) < 2 && time <= $end and tick); + } else { + sleep($wait); + } +} # like system() built-in, but uses spawn() for env/rdr + vfork sub xsys { @@ -294,7 +327,7 @@ sub start_script { exec(split(' ', $tail_cmd), @paths); die "$tail_cmd failed: $!"; } - wait_for_tail(); + wait_for_tail($tail_pid); } } defined(my $pid = fork) or die "fork: $!\n"; @@ -359,9 +392,9 @@ sub join { sub DESTROY { my ($self) = @_; return if $self->{owner} != $$; - if (my $tail = delete $self->{tail_pid}) { - PublicInbox::TestCommon::wait_for_tail(); - CORE::kill('TERM', $tail); + if (my $tail_pid = delete $self->{tail_pid}) { + PublicInbox::TestCommon::wait_for_tail($tail_pid, 1); + CORE::kill('TERM', $tail_pid); } $self->join('TERM'); }