File: timeout.pm

package info (click to toggle)
libcatmandu-perl 1.2024-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,552 kB
  • sloc: perl: 17,037; makefile: 24; sh: 1
file content (146 lines) | stat: -rw-r--r-- 2,934 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package Catmandu::Fix::Bind::timeout;

use Catmandu::Sane;

our $VERSION = '1.2024';

use Moo;
use Clone ();
use Time::HiRes;
use namespace::clean;

with 'Catmandu::Fix::Bind';

has time  => (is => 'ro');
has units => (is => 'ro', default => sub {'SECONDS'});
has sleep => (is => 'rw');

sub unit {
    my ($self, $data) = @_;

    my $sleep = $self->time;
    my $units = $self->units // 'SECONDS';

    if ($units =~ /^MICROSECOND(S)?$/i) {
        $sleep /= 1000000;
    }
    elsif ($units =~ /^MILLISECOND(S)$/i) {
        $sleep /= 1000;
    }
    elsif ($units =~ /^SECOND(S)?$/i) {

        # ok
    }
    elsif ($units =~ /^MINUTE(S)?$/i) {
        $sleep *= 60;
    }
    elsif ($units =~ /^HOUR(S)?$/i) {
        $sleep *= 3600;
    }
    else {
        # ok - use seconds
    }

    $self->sleep($sleep);

    [$data, Clone::clone($data)];
}

sub bind {
    my ($self, $mvar, $func) = @_;

    my $sleep = $self->sleep();

    if ($sleep >= 0) {
        my $start = [Time::HiRes::gettimeofday];

        $mvar->[0] = $func->($mvar->[0]);

        $sleep -= Time::HiRes::tv_interval($start);

        $self->sleep($sleep);
    }

    $mvar;
}

sub result {
    my ($self, $mvar) = @_;

    if ($self->sleep < 0) {
        $self->log->warn("timeout after > "
                . $self->time . " "
                . $self->units . " : "
                . (-1 * $self->sleep)
                . " extra time");
        inline_replace($mvar->[0], $mvar->[1]);
    }

    $self->sleep < 0 ? $mvar->[1] : $mvar->[0];
}

sub inline_replace {
    my ($old, $new) = @_;

    for my $key (keys %$old) {
        delete $old->{$key};
    }

    for my $key (keys %$new) {
        $old->{$key} = $new->{$key};
    }
}

1;

__END__

=pod

=head1 NAME

Catmandu::Fix::Bind::timeout - run fixes that should run within a time limit

=head1 SYNOPSIS

    # The following bind will run fix1(), fix2(), ... fixN() only if the
    # action can be done in 5 seconds
    do timeout(time:5, units:seconds)
       fix1()
       fix2()
       fix3()
       .
       .
       .
       fixN()
    end

    next_fix()

=head1 DESCRIPTION

The timeout binder will run the supplied block only when all the fixes can be
run within a time limit. All fixes (except side-effects) are ignored when the
block can't be executed within the time limit.

=head1 CONFIGURATION

=head2 timeout(time => VALUE , units => MICROSECOND|MILLISECONDS|SECONDS|MINUTES|HOURS)

Set a timeout to VALUE. This timeout doesn't prevent a fix script to run longer than the
specified value, but it does prevent fixes to have any effect when the timeout has been reached.

    # This script will run 10 seconds
    do timeout(time:5, units:seconds)
       add_field(foo,ok) # This will be ignored
       sleep(10,seconds)
       set_field(foo,error) # This will be ignored
    end

At timeout a log message of level WARN will be generated.

=head1 SEE ALSO

L<Catmandu::Fix::Bind>

=cut