File Coverage

blib/lib/Time/Out.pm
Criterion Covered Total %
statement 46 50 92.0
branch 18 22 81.8
condition 7 9 77.7
subroutine 6 8 75.0
pod 0 2 0.0
total 77 91 84.6


line stmt bran cond sub pod time code
1             package Time::Out ;
2             @ISA = qw(Exporter) ;
3             @EXPORT_OK = qw(timeout) ;
4              
5 3     3   31030 use strict ;
  3         8  
  3         125  
6 3     3   15 use Exporter ;
  3         5  
  3         135  
7 3     3   19 use Carp ;
  3         9  
  3         355  
8              
9              
10             BEGIN {
11 3 50   3   45 if (Time::HiRes->can('alarm')){
12 0         0 Time::HiRes->import('alarm') ;
13             }
14 3 50       1470 if (Time::HiRes->can('time')){
15 0         0 Time::HiRes->import('time') ;
16             }
17             }
18              
19              
20             $Time::Out::VERSION = '0.11' ;
21              
22              
23             sub timeout($@){
24 17     17 0 14143 my $secs = shift ;
25 17 100       382 carp("Timeout value evaluates to 0: no timeout will be set") if ! $secs ;
26 17         237 my $code = pop ;
27 17 50 33     123 usage() unless ((defined($code))&&(UNIVERSAL::isa($code, 'CODE'))) ;
28 17         33 my @other_args = @_ ;
29              
30             # Disable any pending alarms.
31 17         91 my $prev_alarm = alarm(0) ;
32 17         47 my $prev_time = time() ;
33 17         27 my $dollar_at = undef ;
34 17         22 my @ret = () ;
35             {
36             # Disable alarm to prevent possible race condition between end of eval and execution of alarm(0) after eval.
37 17     0   22 local $SIG{ALRM} = sub {} ;
  17         287  
  0         0  
38 17         32 @ret = eval {
39 17     9   202 local $SIG{ALRM} = sub { die $code } ;
  9         3000454  
40 17 100 100     73 if (($prev_alarm)&&($prev_alarm < $secs)){
41             # A shorter alarm was pending, let's use it instead.
42 1         7 alarm($prev_alarm) ;
43             }
44             else {
45 16         82 alarm($secs) ;
46             }
47 17         118 my @ret = $code->(@other_args) ;
48 11         14001697 alarm(0) ;
49 5         80 @ret ;
50             } ;
51 17         174 alarm(0) ;
52 17         202 $dollar_at = $@ ;
53             }
54              
55 17         43 my $new_time = time() ;
56 17         39 my $new_alarm = $prev_alarm - ($new_time - $prev_time) ;
57 17 100       99 if ($new_alarm > 0){
    100          
58             # Rearm old alarm with remaining time.
59 2         13 alarm($new_alarm) ;
60             }
61             elsif ($prev_alarm){
62             # Old alarm has already expired.
63 1         55 kill 'ALRM', $$ ;
64             }
65              
66 16 100       54 if ($dollar_at){
67 11 100 100     101 if ((ref($dollar_at))&&($dollar_at eq $code)){
68 8         27 $@ = "timeout" ;
69             }
70             else {
71 3 100       5 if (! ref($dollar_at)){
72 2         5 chomp($dollar_at) ;
73 2         19 die("$dollar_at\n") ;
74             }
75             else {
76 1         76 croak $dollar_at ;
77             }
78             }
79             }
80              
81 13 50       87 return wantarray ? @ret : $ret[0] ;
82             }
83              
84              
85             sub usage {
86 0     0 0   croak("Usage: timeout \$nb_secs => sub {\n #code\n} ;\n") ;
87             }
88              
89              
90              
91             1 ;