File Coverage

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


line stmt bran cond sub pod time code
1 5     5   350702 use strict;
  5         57  
  5         143  
2 5     5   24 use warnings;
  5         9  
  5         310  
3              
4             package Try::ALRM;
5              
6             our $VERSION = q{0.81};
7              
8 5     5   30 use Exporter qw/import/;
  5         9  
  5         4090  
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 1005 my $timeout = shift;
18 51 100       245 if ( defined $timeout ) {
19 5         21 _assert_timeout($timeout);
20 5         8 $TIMEOUT = $timeout;
21             }
22 51         607 return $TIMEOUT;
23             }
24              
25             # setter/getter for $Try::ALRM::TRIES
26             sub tries (;$) {
27 43     43 1 183 my $tries = shift;
28 43 100       179 if ( defined $tries ) {
29 1         3 _assert_tries($tries);
30 1         2 $TRIES = $tries;
31             }
32 43         871 return $TRIES;
33             }
34              
35             #NOTE: C a case of C, where C<< tries => 1 >>.
36             sub try_once (&;@) {
37 7     7 1 30 &retry( @_, tries => 1 ); #&retry, bypasses prototype
38             }
39              
40             sub retry(&;@) {
41 11     11 1 45 unshift @_, q{retry}; # adding marker, will be key for this &
42 11         66 my %TODO = @_;
43 11         35 my $TODO = \%TODO;
44              
45 11   50 0   58 my $RETRY = $TODO->{retry} // sub { }; # defaults to no-op
46 11   100     72 my $ALRM = $TODO->{ALRM} // $SIG{ALRM}; # local ALRM defaults to global $SIG{ALRM}
47 11   66     70 my $timeout = $TODO->{timeout} // $TIMEOUT;
48 11   33     50 my $tries = $TODO->{tries} // $TRIES;
49 11   100 6   86 my $FINALLY = $TODO->{finally} // sub { };
50              
51 11         35 local $TIMEOUT = $timeout; # make available to timeout(;$)
52 11         39 local $TRIES = $tries; # make available to tries(;$)
53              
54 11         57 my ( $attempts, $succeeded );
55              
56             TIMED_ATTEMPTS:
57 11         59 for my $attempt ( 1 .. $TRIES ) {
58 15         57 $attempts = $attempt;
59 15         39 my $retry = 0;
60              
61             # NOTE: handler always becomes a local wrapper
62             local $SIG{ALRM} = sub {
63 15     15   106 ++$retry;
64 15 100       510 if ( ref($ALRM) =~ m/^CODE$|::/ ) {
65 11         109 $ALRM->($attempt);
66             }
67 15         521 };
68              
69             # actual alarm code
70 15         213 alarm($timeout);
71 15         167 $RETRY->($attempt);
72 15         5990017 alarm 0;
73 15 50       12648 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         106 $FINALLY->( $attempts, $succeeded );
81             }
82              
83             sub ALRM (&;@) {
84 7     7 1 44 unshift @_, q{ALRM};
85 7         48 return @_;
86             }
87              
88             sub finally (&;@) {
89 5     5 1 32 unshift @_, q{finally}; # create marker, will be key for &
90 5         50 return @_;
91             }
92              
93             # internal method, validation
94             sub _assert_timeout {
95 5     5   9 my $timeout = shift;
96 5 50       28 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   2 my $timeout = shift;
104 1 50       5 if ( int $timeout <= 0 ) {
105 0           die qq{timeout must be an integer >= 1!\n};
106             }
107             }
108              
109             __PACKAGE__
110              
111             __END__