Skip Menu |

This queue is for tickets about the IO-Async CPAN distribution.

Report information
The Basics
Id: 84557
Status: resolved
Priority: 0/
Queue: IO-Async

People
Owner: Nobody in particular
Requestors: TEAM [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: 0.56
Fixed in: 0.57



Subject: ->stop doesn't when called from on_tick callback
The following "run N times" idiom doesn't quite work as expected, since ->start is called immediately after the on_tick callback: #!/usr/bin/env perl use strict; use warnings; use IO::Async::Loop; use IO::Async::Timer::Periodic; use Future; my $loop = IO::Async::Loop->new; my $f = Future->new; my $count = 0; my $t = IO::Async::Timer::Periodic->new( interval => 0.1, on_tick => sub { $f->done if ++$count > 5 && !$f->is_ready; warn "exceeded 5 iterations" if $count > 6; } ); $loop->add($t); $t->start; $f->on_done(sub { warn "Stopping timer"; $t->stop }); $loop->run; __END__ Stopping timer at timer.pl line 20. exceeded 5 iterations at timer.pl line 15. exceeded 5 iterations at timer.pl line 15. exceeded 5 iterations at timer.pl line 15. exceeded 5 iterations at timer.pl line 15. ... One workaround would be to set a flag when this happens (patch attached), only affects ::Periodic as far as I can see. cheers, Tom
Subject: periodic-timer-stop.diff
=== modified file 'lib/IO/Async/Timer.pm' --- lib/IO/Async/Timer.pm 2013-04-05 18:24:12 +0000 +++ lib/IO/Async/Timer.pm 2013-04-10 17:49:16 +0000 @@ -167,6 +167,7 @@ return; } + $self->{please_stop} = 1; return if !$self->is_running; my $loop = $self->loop or croak "Cannot stop a Timer that is not in a Loop"; === modified file 'lib/IO/Async/Timer/Periodic.pm' --- lib/IO/Async/Timer/Periodic.pm 2013-04-05 18:24:12 +0000 +++ lib/IO/Async/Timer/Periodic.pm 2013-04-10 17:49:36 +0000 @@ -229,7 +229,7 @@ $self->invoke_event( on_tick => ); - $self->start; + $self->start unless delete $self->{please_stop}; } ); }
An updated version of the patch which passes tests. Not 100% sure whether it does the right thing though - presumably there's a reason for clearing the current ID before the on_tick callback?
Subject: periodic-timer-stop2.diff
=== modified file 'lib/IO/Async/Timer.pm' --- lib/IO/Async/Timer.pm 2013-04-05 18:24:12 +0000 +++ lib/IO/Async/Timer.pm 2013-04-10 18:00:26 +0000 @@ -168,6 +168,7 @@ } return if !$self->is_running; + $self->{please_stop} = 1; my $loop = $self->loop or croak "Cannot stop a Timer that is not in a Loop"; === modified file 'lib/IO/Async/Timer/Periodic.pm' --- lib/IO/Async/Timer/Periodic.pm 2013-04-05 18:24:12 +0000 +++ lib/IO/Async/Timer/Periodic.pm 2013-04-10 18:02:03 +0000 @@ -223,13 +223,13 @@ return $self->_capture_weakself( sub { my $self = shift; + $self->invoke_event( on_tick => ); + undef $self->{first_interval}; undef $self->{id}; - $self->invoke_event( on_tick => ); - - $self->start; + $self->start unless delete $self->{please_stop}; } ); }
On Wed Apr 10 13:55:40 2013, TEAM wrote: Show quoted text
> The following "run N times" idiom doesn't quite work as expected, > since ->start is called immediately after the on_tick callback: > > #!/usr/bin/env perl > use strict; > use warnings; > use IO::Async::Loop; > use IO::Async::Timer::Periodic; > use Future; > > my $loop = IO::Async::Loop->new; > my $f = Future->new; > my $count = 0; > my $t = IO::Async::Timer::Periodic->new( > interval => 0.1, > on_tick => sub { > $f->done if ++$count > 5 && !$f->is_ready; > warn "exceeded 5 iterations" if $count > 6; > } > ); > $loop->add($t); > $t->start; > $f->on_done(sub { warn "Stopping timer"; $t->stop }); > $loop->run; > > __END__ > > Stopping timer at timer.pl line 20. > exceeded 5 iterations at timer.pl line 15. > exceeded 5 iterations at timer.pl line 15. > exceeded 5 iterations at timer.pl line 15. > exceeded 5 iterations at timer.pl line 15. > ...
Ah yes, oops. Show quoted text
> One workaround would be to set a flag when this happens (patch > attached), only affects ::Periodic as far as I can see.
I think I've found a neater one, to rely on the side-effect that T:Periodic ->stop clears the next_time flag. Find attached a patch to implement that and add a permanent unit-test. -- Paul Evans
Subject: rt84557.patch
=== modified file 'lib/IO/Async/Timer/Periodic.pm' --- lib/IO/Async/Timer/Periodic.pm 2013-04-05 18:24:12 +0000 +++ lib/IO/Async/Timer/Periodic.pm 2013-04-12 21:59:55 +0000 @@ -229,7 +229,8 @@ $self->invoke_event( on_tick => ); - $self->start; + # detect ->stop + $self->start if defined $self->{next_time}; } ); } === modified file 't/22timer-periodic.t' --- t/22timer-periodic.t 2013-04-05 17:40:31 +0000 +++ t/22timer-periodic.t 2013-04-12 21:59:55 +0000 @@ -148,6 +148,29 @@ $loop->remove( $timer ); } +# Self-stopping +{ + my $count = 0; + my $timer = IO::Async::Timer::Periodic->new( + interval => 0.1 * AUT, + + on_tick => sub { $count++; shift->stop if $count >= 5 }, + ); + + $loop->add( $timer ); + $timer->start; + + my $timedout; + my $id = $loop->watch_time( after => 1 * AUT, code => sub { $timedout++ } ); + + wait_for { $timedout }; + + is( $count, 5, 'Self-stopping timer can stop itself' ); + + $loop->remove( $timer ); + $loop->unwatch_time( $id ); +} + ## Subclass my $sub_tick = 0;
Was fixed in 0.57 -- Paul Evans