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 )