AnyEvent-Retry
view release on metacpan or search on metacpan
lib/AnyEvent/Retry.pm view on Meta::CPAN
isa => PositiveNum,
default => 0,
);
has 'autostart' => (
is => 'ro',
isa => 'Bool',
default => 0,
);
has '_timer' => (
init_arg => undef,
writer => '_set_timer',
clearer => 'kill_timer',
predicate => 'has_timer',
);
sub BUILD {
my $self = shift;
$self->start if $self->autostart;
}
sub DEMOLISH {
my $self = shift;
$self->kill_timer;
if(!$self->_sent_result){
$self->_sent_result(1);
$self->on_failure->(demolish => 'DEMOLISH');
}
}
# set a timer to call handle_tick in the future
sub set_timer {
my ($self, $time, $i) = @_;
return $self->handle_tick($i) if $time <= 0;
weaken $self;
$self->_set_timer(
AnyEvent->timer( after => $time, cb => sub {
$self->kill_timer;
$self->handle_tick($i);
}),
);
return;
}
# called when the timer ticks; start the user's code running
sub handle_tick {
my ($self, $this_i) = @_;
$self->run_code;
}
# called when the user's code signals success or error
sub handle_result {
my ($self, $success, $status, $msg) = @_;
# if we hit these two cases, we are done forever
lib/AnyEvent/Retry.pm view on Meta::CPAN
# no error, but not success (try again later)
my ($next_time, $next_i) = $self->interval->next;
if($self->max_tries > 0 && $next_i > $self->max_tries){
# done forever
$self->_sent_result(1);
$self->on_failure->( max_tries => $self->max_tries );
return;
}
# we didn't get the result this time, and we haven't exceeded
# $max_tries, so set the timer and do the whole thing again
$self->set_timer( $next_time, $next_i );
return;
}
# start the user's code running, with a continuation-passing-style
# callback to call when the result is ready
sub run_code {
my ($self) = @_;
# we weaken $self here so that if the user does "undef $retry", we
# DEMOLISH the object and silently discard the results of the
lib/AnyEvent/Retry.pm view on Meta::CPAN
return unless defined $self;
$self->handle_result(0, 'run error', $msg);
return;
};
try { $self->try->($success, $error) }
catch { $self->handle_result(0, 'startup error', $_) };
return;
}
# if the timer is running, stop it until resume is called
sub pause {
my $self = shift;
$self->kill_timer;
}
# fake a timer tick; run the user code, and set the timer to retry if
# necessary
sub resume {
my $self = shift;
$self->kill_timer; # just in case
$self->handle_tick(0);
}
# start the process. if the timer is running, die. if the timer is
# not running, start completely over.
sub start {
my $self = shift;
confess 'the job is already running' if $self->has_timer;
$self->interval->reset;
$self->_sent_result(0);
$self->set_timer( $self->after, 0 );
return;
}
__PACKAGE__->meta->make_immutable;
=pod
=head1 NAME
lib/AnyEvent/Retry.pm view on Meta::CPAN
See the INITARGS section below for information on what params to pass.
=head2 start
Start the job. Dies if the job is already running.
(You can call this again when the job is done to run the job again.)
=head2 pause
Stop the timer, pausing the job until C<resume> is called.
=head2 resume
Resume the task as though the last-running timer just expired.
=head1 INITARGS
=head2 try
Required. This is the coderef to run repeatedly. It is passed two
coderefs as args, C<success_cb> and C<error_cb>. Your coderef must
call one of those; success with a true value if the process is
complete and should not run again, success with a false value if the
process should run again, or error with an error message if the
t/retry-basic.t view on Meta::CPAN
my $times = 0;
my $r = AnyEvent::Retry->new(
on_failure => sub { $cv->croak($_[1]) },
on_success => sub { $cv->send($_[0]) },
max_tries => 50,
interval => { Fibonacci => { scale => 1/1000 } },
try => sub {
my ($success, $error) = @_;
$times++;
my $t; $t = AnyEvent->timer( after => 0.01, cb => sub {
undef $t;
$success->(AnyEvent->now - $start > 1 ? AnyEvent->now : 0);
});
},
);
$r->start;
my $end;
lives_ok {
t/retry-undef.t view on Meta::CPAN
my $cv = AnyEvent->condvar;
my $r = AnyEvent::Retry->new(
on_failure => sub { $cv->croak($_[1]) },
on_success => sub { $cv->send($_[0]) },
max_tries => 50,
interval => { Fibonacci => { scale => 1/1000 } },
try => sub {}, # do nothing
);
$r->start;
my $t = AnyEvent->timer( after => 0.1, cb => sub {
ok !$r->has_timer, 'no timer running';
undef $r;
} );
throws_ok {
$cv->recv;
} qr/DEMOLISH/,
'we destroyed $r and the failure callback was called with DEMOLISH';
}
{
my $cv = AnyEvent->condvar;
my $r = AnyEvent::Retry->new(
on_failure => sub { $cv->croak($_[1]) },
on_success => sub { $cv->send($_[0]) },
max_tries => 50,
interval => { Constant => { interval => 9999 } },
try => sub { $_[0]->(0) }, # always unsuccessful
);
$r->start;
my $t = AnyEvent->timer( after => 0.1, cb => sub {
ok $r->has_timer, 'timer is running';
undef $r;
} );
throws_ok {
$cv->recv;
} qr/DEMOLISH/,
'we get the DEMOLISH message even when the timer is running';
}
done_testing;
( run in 1.078 second using v1.01-cache-2.11-cpan-49f99fa48dc )