tag 646649 patch upstream
thanks

On Wed, Oct 26, 2011 at 02:10:18AM +0200, Mònica Ramírez Arceda wrote:
> Source: libio-async-loop-epoll-perl
> Version: 0.11-1
> Severity: serious
> Tags: wheezy sid
> User: debian...@lists.debian.org
> Usertags: qa-ftbfs-20111022 qa-ftbfs
> Justification: FTBFS on amd64

> > #   Failed test 'Other timers still fire after self-cancelling one'
> > #   at /usr/share/perl5/IO/Async/LoopTests.pm line 458.
> > #          got: undef
> > #     expected: '1'
> > # Looks like you failed 1 test of 15.
> > t/02loop-timer.t .... 
> > Dubious, test returned 1 (wstat 256, 0x100)
> > Failed 1/15 subtests 

This is easily reproducible for me.

The bug seems to be in timeouts rounding down to the nearest full
millisecond. See the commit messages of the attached proposed patches,
which I've also sent upstream via the CPAN ticket.

 https://rt.cpan.org/Public/Bug/Display.html?id=73498
-- 
Niko Tyni   nt...@debian.org
>From 9a6a6e60897777a4c220890ba709eb5ea3572418 Mon Sep 17 00:00:00 2001
From: Niko Tyni <nt...@debian.org>
Date: Sun, 1 Apr 2012 14:10:00 +0300
Subject: [PATCH 1/2] TODO tests to detect loop_once() returning too soon

The intermittent t/02loop-timer.t failures seen in [rt.cpan.org #73498]
happen because epoll_pwait() uses an integer millisecond value for its
timeout parameter, so fractional values will get rounded down. The
loop_once() method of IO::Async::Loop::Epoll doesn't take this into
account, so the next scheduled callback may therefore well not get
triggered on just one loop_once() call.

If control comes back to the calling code before the remaining millisecond
is spent, the next loop_once() call will end up calling epoll_pwait()
with a timeout of zero, which makes it return immediately. This may result
in a busy loop of several loop_once() calls until the millisecond tick
actually happens.

Add (hopefully) more deterministic TODO tests to detect such
situations. As IO::Async::Loop will never sleep more than one second
($MAX_SIGWAIT_TIME), we test with a range of timeouts between one and
700 milliseconds that loop_once() will only return when a callback has
actually been called.

The busy loop seems to be longer on the small timeouts - perhaps
epoll_pwait(2) uses a finer resolution for shorter timeouts than the
longer ones?
---
 MANIFEST        |    1 +
 t/07loop-once.t |   27 +++++++++++++++++++++++++++
 2 files changed, 28 insertions(+), 0 deletions(-)
 create mode 100644 t/07loop-once.t

diff --git a/MANIFEST b/MANIFEST
index 0010c9e..fc55176 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -14,4 +14,5 @@ t/03loop-signal.t
 t/04loop-idle.t
 t/05loop-child.t
 t/06loop-control.t
+t/07loop-once.t
 t/99pod.t
diff --git a/t/07loop-once.t b/t/07loop-once.t
new file mode 100644
index 0000000..2cad2e3
--- /dev/null
+++ b/t/07loop-once.t
@@ -0,0 +1,27 @@
+#!/usr/bin/perl -w
+my @delays;
+
+BEGIN { @delays = (.001, .005, .007, .01, .05, .07, .1, .5, .7) }
+
+use strict;
+use Test::More tests => 2 * @delays;
+use Time::HiRes q/time/;
+
+use IO::Async::Loop::Epoll;
+my $loop = IO::Async::Loop::Epoll->new;
+
+for my $delay (@delays) {
+    my $done;
+    my $i = 1;
+    my $starttime = time();
+
+    $loop->enqueue_timer( delay => $delay, code => sub { $done++ } );
+    $i++ while
+        !$loop->loop_once( 1 )         # callback not triggered yet
+        && (time() - $starttime < 1);  # guard against infinite loops
+    TODO: { local $::TODO = "[rt.cpan.org #73498]";
+    is ($i, 1, "Just one loop run is enough to wait $delay seconds");
+    }
+    is ($done, 1, "Timer actually got triggered within one second");
+}
+ 
-- 
1.7.9.1

>From eab695afa987bde81db22f0dc7f689677cef6794 Mon Sep 17 00:00:00 2001
From: Niko Tyni <nt...@debian.org>
Date: Sun, 1 Apr 2012 15:16:04 +0300
Subject: [PATCH 2/2] Fix loop_once() returning too soon

epoll_pwait() uses an integer millisecond value for its timeout parameter,
so round up any fractional values. This avoids early returns during the
very last millisecond before the next scheduled event.

As detailed in the previous commit message, this should fix the
intermittent t/02loop-timer.t failures seen in [rt.cpan.org #73498].
---
 lib/IO/Async/Loop/Epoll.pm |    4 ++--
 t/07loop-once.t            |    2 --
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/lib/IO/Async/Loop/Epoll.pm b/lib/IO/Async/Loop/Epoll.pm
index 9f577c3..abb818d 100644
--- a/lib/IO/Async/Loop/Epoll.pm
+++ b/lib/IO/Async/Loop/Epoll.pm
@@ -22,7 +22,7 @@ use IO::Epoll qw(
    EPOLLIN EPOLLOUT EPOLLHUP EPOLLERR
 );
 
-use POSIX qw( EINTR EPERM SIG_BLOCK SIG_UNBLOCK sigprocmask sigaction );
+use POSIX qw( EINTR EPERM SIG_BLOCK SIG_UNBLOCK sigprocmask sigaction ceil );
 
 =head1 NAME
 
@@ -143,7 +143,7 @@ sub loop_once
 
    $self->_adjust_timeout( \$timeout );
 
-   my $msec = defined $timeout ? $timeout * 1000 : -1;
+   my $msec = defined $timeout ? ceil($timeout * 1000) : -1;
 
    my $ret = epoll_pwait( $self->{epoll}, $self->{maxevents}, $msec, $self->{sigmask} );
 
diff --git a/t/07loop-once.t b/t/07loop-once.t
index 2cad2e3..5b46109 100644
--- a/t/07loop-once.t
+++ b/t/07loop-once.t
@@ -19,9 +19,7 @@ for my $delay (@delays) {
     $i++ while
         !$loop->loop_once( 1 )         # callback not triggered yet
         && (time() - $starttime < 1);  # guard against infinite loops
-    TODO: { local $::TODO = "[rt.cpan.org #73498]";
     is ($i, 1, "Just one loop run is enough to wait $delay seconds");
-    }
     is ($done, 1, "Timer actually got triggered within one second");
 }
  
-- 
1.7.9.1

Reply via email to