File Coverage

blib/lib/Try/ALRM.pm
Criterion Covered Total %
statement 51 55 92.7
branch 9 12 75.0
condition 9 15 60.0
subroutine 13 14 92.8
pod 6 6 100.0
total 88 102 86.2


line stmt bran cond sub pod time code
1 5     5   537653 use strict;
  5         8  
  5         168  
2 5     5   43 use warnings;
  5         7  
  5         480  
3              
4             package Try::ALRM;
5              
6             our $VERSION = q{1.00};
7              
8 5     5   45 use Exporter qw/import/;
  5         19  
  5         3413  
9             our @EXPORT = qw(try_once retry ALRM finally timeout tries);
10             our @EXPORT_OK = qw(try_once retry ALRM finally timeout tries);
11              
12             our $TIMEOUT = 60;
13             our $TRIES = 3;
14              
15             # setter/getter for $Try::ALRM::TIMEOUT
16             sub timeout (;$) {
17 51     51 1 916850 my $timeout = shift;
18 51 100       217 if ( defined $timeout ) {
19 5         56 _assert_timeout($timeout);
20 5         10 $TIMEOUT = $timeout;
21             }
22 51         4515 return $TIMEOUT;
23             }
24              
25             # setter/getter for $Try::ALRM::TRIES
26             sub tries (;$) {
27 43     43 1 105 my $tries = shift;
28 43 100       125 if ( defined $tries ) {
29 1         4 _assert_tries($tries);
30 1         2 $TRIES = $tries;
31             }
32 43         2015 return $TRIES;
33             }
34              
35             #NOTE: C a case of C, where C<< tries => 1 >>.
36             sub try_once (&;@) {
37 7     7 1 32 &retry( @_, tries => 1 ); #&retry, bypasses prototype
38             }
39              
40             sub retry(&;@) {
41 11     11 1 34 unshift @_, q{retry}; # adding marker, will be key for this &
42 11         61 my %TODO = @_;
43 11         25 my $TODO = \%TODO;
44              
45 11   33 0   50 my $RETRY = $TODO->{retry} // sub { }; # defaults to no-op
46 11   100     54 my $ALRM = $TODO->{ALRM} // $SIG{ALRM}; # local ALRM defaults to global $SIG{ALRM}
47 11   66     46 my $timeout = $TODO->{timeout} // $TIMEOUT;
48 11   33     54 my $tries = $TODO->{tries} // $TRIES;
49 11   66 6   55 my $FINALLY = $TODO->{finally} // sub { };
50              
51 11         24 local $TIMEOUT = $timeout; # make available to timeout(;$)
52 11         22 local $TRIES = $tries; # make available to tries(;$)
53              
54 11         23 my ( $attempts, $succeeded );
55              
56             TIMED_ATTEMPTS:
57 11         36 for my $attempt ( 1 .. $TRIES ) {
58 15         33 $attempts = $attempt;
59 15         25 my $retry = 0;
60              
61             # NOTE: handler always becomes a local wrapper
62             local $SIG{ALRM} = sub {
63 15     15   68 ++$retry;
64 15 100       312 if ( ref($ALRM) =~ m/^CODE$|::/ ) {
65 11         94 $ALRM->($attempt);
66             }
67 15         273 };
68              
69             # actual alarm code
70 15         105 alarm($timeout);
71 15         108 $RETRY->($attempt);
72 15         5992791 alarm 0;
73 15 50       16561 unless ( $retry == 1 ) {
74 0         0 ++$succeeded;
75 0         0 last;
76             }
77             }
78              
79             # "finally" (defaults to no-op 'sub {}' if block is not defined)
80 11         82 $FINALLY->( $attempts, $succeeded );
81             }
82              
83             sub ALRM (&;@) {
84 7     7 1 25 unshift @_, q{ALRM};
85 7         40 return @_;
86             }
87              
88             sub finally (&;@) {
89 5     5 1 22 unshift @_, q{finally}; # create marker, will be key for &
90 5         29 return @_;
91             }
92              
93             # internal method, validation
94             sub _assert_timeout {
95 5     5   11 my $timeout = shift;
96 5 50       35 if ( int $timeout <= 0 ) {
97 0         0 die qq{timeout must be an integer >= 1!\n};
98             }
99             }
100              
101             # internal method, validation
102             sub _assert_tries {
103 1     1   1 my $timeout = shift;
104 1 50       3 if ( int $timeout <= 0 ) {
105 0           die qq{timeout must be an integer >= 1!\n};
106             }
107             }
108              
109             __PACKAGE__
110              
111             __END__