File Coverage

blib/lib/Catmandu/Fix/Bind/timeout.pm
Criterion Covered Total %
statement 31 35 88.5
branch 7 14 50.0
condition 1 2 50.0
subroutine 8 8 100.0
pod 0 3 0.0
total 47 62 75.8


line stmt bran cond sub pod time code
1             package Catmandu::Fix::Bind::timeout;
2              
3 1     1   1226 use Catmandu::Sane;
  1         3  
  1         8  
4              
5             our $VERSION = '1.2020';
6              
7 1     1   8 use Moo;
  1         2  
  1         7  
8 1     1   391 use Clone ();
  1         3  
  1         29  
9 1     1   6 use Time::HiRes;
  1         2  
  1         8  
10 1     1   73 use namespace::clean;
  1         3  
  1         43  
11              
12             with 'Catmandu::Fix::Bind';
13              
14             has time => (is => 'ro');
15             has units => (is => 'ro', default => sub {'SECONDS'});
16             has sleep => (is => 'rw');
17              
18             sub unit {
19 17     17 0 46 my ($self, $data) = @_;
20              
21 17         54 my $sleep = $self->time;
22 17   50     65 my $units = $self->units // 'SECONDS';
23              
24 17 50       134 if ($units =~ /^MICROSECOND(S)?$/i) {
    50          
    50          
    0          
    0          
25 0         0 $sleep /= 1000000;
26             }
27             elsif ($units =~ /^MILLISECOND(S)$/i) {
28 0         0 $sleep /= 1000;
29             }
30             elsif ($units =~ /^SECOND(S)?$/i) {
31              
32             # ok
33             }
34             elsif ($units =~ /^MINUTE(S)?$/i) {
35 0         0 $sleep *= 60;
36             }
37             elsif ($units =~ /^HOUR(S)?$/i) {
38 0         0 $sleep *= 3600;
39             }
40             else {
41             # ok - use seconds
42             }
43              
44 17         62 $self->sleep($sleep);
45              
46 17         445 [$data, Clone::clone($data)];
47             }
48              
49             sub bind {
50             my ($self, $mvar, $func) = @_;
51              
52             my $sleep = $self->sleep();
53              
54             if ($sleep >= 0) {
55             my $start = [Time::HiRes::gettimeofday];
56              
57             $mvar->[0] = $func->($mvar->[0]);
58              
59             $sleep -= Time::HiRes::tv_interval($start);
60              
61             $self->sleep($sleep);
62             }
63              
64             $mvar;
65             }
66              
67             sub result {
68 15     15 0 48 my ($self, $mvar) = @_;
69              
70 15 100       66 if ($self->sleep < 0) {
71 3         78 $self->log->warn("timeout after > "
72             . $self->time . " "
73             . $self->units . " : "
74             . (-1 * $self->sleep)
75             . " extra time");
76 3         288 inline_replace($mvar->[0], $mvar->[1]);
77             }
78              
79 15 100       321 $self->sleep < 0 ? $mvar->[1] : $mvar->[0];
80             }
81              
82             sub inline_replace {
83 3     3 0 9 my ($old, $new) = @_;
84              
85 3         19 for my $key (keys %$old) {
86 4         19 delete $old->{$key};
87             }
88              
89 3         15 for my $key (keys %$new) {
90 3         15 $old->{$key} = $new->{$key};
91             }
92             }
93              
94             1;
95              
96             __END__
97              
98             =pod
99              
100             =head1 NAME
101              
102             Catmandu::Fix::Bind::timeout - run fixes that should run within a time limit
103              
104             =head1 SYNOPSIS
105              
106             # The following bind will run fix1(), fix2(), ... fixN() only if the
107             # action can be done in 5 seconds
108             do timeout(time:5, units:seconds)
109             fix1()
110             fix2()
111             fix3()
112             .
113             .
114             .
115             fixN()
116             end
117              
118             next_fix()
119              
120             =head1 DESCRIPTION
121              
122             The timeout binder will run the supplied block only when all the fixes can be
123             run within a time limit. All fixes (except side-effects) are ignored when the
124             block can't be executed within the time limit.
125              
126             =head1 CONFIGURATION
127              
128             =head2 timeout(time => VALUE , units => MICROSECOND|MILLISECONDS|SECONDS|MINUTES|HOURS)
129              
130             Set a timeout to VALUE. This timeout doesn't prevent a fix script to run longer than the
131             specified value, but it does prevent fixes to have any effect when the timeout has been reached.
132              
133             # This script will run 10 seconds
134             do timeout(time:5, units:seconds)
135             add_field(foo,ok) # This will be ignored
136             sleep(10,seconds)
137             set_field(foo,error) # This will be ignored
138             end
139              
140             At timeout a log message of level WARN will be generated.
141              
142             =head1 SEE ALSO
143              
144             L<Catmandu::Fix::Bind>
145              
146             =cut