line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# <@LICENSE> |
2
|
|
|
|
|
|
|
# Licensed to the Apache Software Foundation (ASF) under one or more |
3
|
|
|
|
|
|
|
# contributor license agreements. See the NOTICE file distributed with |
4
|
|
|
|
|
|
|
# this work for additional information regarding copyright ownership. |
5
|
|
|
|
|
|
|
# The ASF licenses this file to you under the Apache License, Version 2.0 |
6
|
|
|
|
|
|
|
# (the "License"); you may not use this file except in compliance with |
7
|
|
|
|
|
|
|
# the License. You may obtain a copy of the License at: |
8
|
|
|
|
|
|
|
# |
9
|
|
|
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0 |
10
|
|
|
|
|
|
|
# |
11
|
|
|
|
|
|
|
# Unless required by applicable law or agreed to in writing, software |
12
|
|
|
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS, |
13
|
|
|
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14
|
|
|
|
|
|
|
# See the License for the specific language governing permissions and |
15
|
|
|
|
|
|
|
# limitations under the License. |
16
|
|
|
|
|
|
|
# </@LICENSE> |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head1 NAME |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
Mail::SpamAssassin::Timeout - safe, reliable timeouts in perl |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=head1 SYNOPSIS |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# non-timeout code... |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
my $t = Mail::SpamAssassin::Timeout->new({ secs => 5, deadline => $when }); |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
$t->run(sub { |
29
|
|
|
|
|
|
|
# code to run with a 5-second timeout... |
30
|
|
|
|
|
|
|
}); |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
if ($t->timed_out()) { |
33
|
|
|
|
|
|
|
# do something... |
34
|
|
|
|
|
|
|
} |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# more non-timeout code... |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=head1 DESCRIPTION |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
This module provides a safe, reliable and clean API to provide |
41
|
|
|
|
|
|
|
C<alarm(2)>-based timeouts for perl code. |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
Note that C<$SIG{ALRM}> is used to provide the timeout, so this will not |
44
|
|
|
|
|
|
|
interrupt out-of-control regular expression matches. |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
Nested timeouts are supported. |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
=head1 PUBLIC METHODS |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
=over 4 |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
=cut |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
use strict; |
56
|
42
|
|
|
42
|
|
24622
|
use warnings; |
|
42
|
|
|
|
|
83
|
|
|
42
|
|
|
|
|
1240
|
|
57
|
42
|
|
|
42
|
|
211
|
# use bytes; |
|
42
|
|
|
|
|
71
|
|
|
42
|
|
|
|
|
1530
|
|
58
|
|
|
|
|
|
|
use re 'taint'; |
59
|
42
|
|
|
42
|
|
255
|
|
|
42
|
|
|
|
|
99
|
|
|
42
|
|
|
|
|
1513
|
|
60
|
|
|
|
|
|
|
use Time::HiRes qw(time); |
61
|
42
|
|
|
42
|
|
216
|
use Mail::SpamAssassin::Logger; |
|
42
|
|
|
|
|
92
|
|
|
42
|
|
|
|
|
3137
|
|
62
|
42
|
|
|
42
|
|
4902
|
|
|
42
|
|
|
|
|
73
|
|
|
42
|
|
|
|
|
3576
|
|
63
|
|
|
|
|
|
|
our @ISA = qw(); |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
########################################################################### |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
=item my $t = Mail::SpamAssassin::Timeout->new({ ... options ... }); |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
Constructor. Options include: |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=over 4 |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=item secs => $seconds |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
time interval, in seconds. Optional; if neither C<secs> nor C<deadline> is |
76
|
|
|
|
|
|
|
specified, no timeouts will be applied. |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
=item deadline => $unix_timestamp |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
Unix timestamp (seconds since epoch) when a timeout is reached in the latest. |
81
|
|
|
|
|
|
|
Optional; if neither B<secs> nor B<deadline> is specified, no timeouts will |
82
|
|
|
|
|
|
|
be applied. If both are specified, the shorter interval of the two prevails. |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
=back |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=cut |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
our $id_gen; |
89
|
|
|
|
|
|
|
BEGIN { $id_gen = 0 } # unique generator of IDs for timer objects |
90
|
42
|
|
|
42
|
|
36580
|
our @expiration; # stack of expected expiration times, top at [0] |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
my ($class, $opts) = @_; |
93
|
|
|
|
|
|
|
$class = ref($class) || $class; |
94
|
2025
|
|
|
2025
|
1
|
19286
|
my %selfval = $opts ? %{$opts} : (); |
95
|
2025
|
|
33
|
|
|
5192
|
$selfval{id} = ++$id_gen; |
96
|
2025
|
100
|
|
|
|
3431
|
my($package, $filename, $line, $subroutine) = caller(1); |
|
2022
|
|
|
|
|
6425
|
|
97
|
2025
|
|
|
|
|
4289
|
if (defined $subroutine) { |
98
|
2025
|
|
|
|
|
12077
|
$subroutine =~ s/^Mail::SpamAssassin::/::/; |
99
|
2025
|
100
|
|
|
|
4940
|
$selfval{id} = join('/', $id_gen, $subroutine, $line); |
100
|
2000
|
|
|
|
|
7715
|
} |
101
|
2000
|
|
|
|
|
5164
|
my $self = \%selfval; |
102
|
|
|
|
|
|
|
|
103
|
2025
|
|
|
|
|
2912
|
bless ($self, $class); |
104
|
|
|
|
|
|
|
$self; |
105
|
2025
|
|
|
|
|
3057
|
} |
106
|
2025
|
|
|
|
|
5344
|
|
107
|
|
|
|
|
|
|
########################################################################### |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=item $t->run($coderef) |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
Run a code reference within the currently-defined timeout. |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
The timeout is as defined by the B<secs> and B<deadline> parameters |
114
|
|
|
|
|
|
|
to the constructor. |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
Returns whatever the subroutine returns, or C<undef> on timeout. |
117
|
|
|
|
|
|
|
If the timer times out, C<$t-<gt>timed_out()> will return C<1>. |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
Time elapsed is not cumulative; multiple runs of C<run> will restart the |
120
|
|
|
|
|
|
|
timeout from scratch. On the other hand, nested timers do observe outer |
121
|
|
|
|
|
|
|
timeouts if they are shorter, resignalling a timeout to the level which |
122
|
|
|
|
|
|
|
established them, i.e. code running under an inner timer can not exceed |
123
|
|
|
|
|
|
|
the time limit established by an outer timer. When restarting an outer |
124
|
|
|
|
|
|
|
timer on return, elapsed time of a running code is taken into account. |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=item $t->run_and_catch($coderef) |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
Run a code reference, as per C<$t-<gt>run()>, but also catching any |
129
|
|
|
|
|
|
|
C<die()> calls within the code reference. |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
Returns C<undef> if no C<die()> call was executed and C<$@> was unset, or the |
132
|
|
|
|
|
|
|
value of C<$@> if it was set. (The timeout event doesn't count as a C<die()>.) |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=cut |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
|
138
|
2012
|
|
|
2012
|
1
|
5399
|
my ($self, $sub, $and_catch) = @_; |
139
|
|
|
|
|
|
|
|
140
|
13
|
|
|
13
|
1
|
72
|
delete $self->{timed_out}; |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
my $id = $self->{id}; |
143
|
2025
|
|
|
2025
|
|
3285
|
my $secs = $self->{secs}; |
144
|
|
|
|
|
|
|
my $deadline = $self->{deadline}; |
145
|
2025
|
|
|
|
|
2753
|
my $alarm_tinkered_with = 0; |
146
|
|
|
|
|
|
|
# dbg("timed: %s run", $id); |
147
|
2025
|
|
|
|
|
2669
|
|
148
|
2025
|
|
|
|
|
2678
|
# assertion |
149
|
2025
|
|
|
|
|
2517
|
if (defined $secs && $secs < 0) { |
150
|
2025
|
|
|
|
|
2352
|
die "Mail::SpamAssassin::Timeout: oops? neg value for 'secs': $secs"; |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
my $start_time = time; |
154
|
2025
|
50
|
66
|
|
|
3987
|
if (defined $deadline) { |
155
|
0
|
|
|
|
|
0
|
my $dt = $deadline - $start_time; |
156
|
|
|
|
|
|
|
$secs = $dt if !defined $secs || $dt < $secs; |
157
|
|
|
|
|
|
|
} |
158
|
2025
|
|
|
|
|
4122
|
|
159
|
2025
|
100
|
|
|
|
3350
|
# bug 4699: under heavy load, an alarm may fire while $@ will contain "", |
160
|
2008
|
|
|
|
|
2740
|
# which isn't very useful. this flag works around it safely, since |
161
|
2008
|
100
|
100
|
|
|
4672
|
# it will not require malloc() be called if it fires |
162
|
|
|
|
|
|
|
my $timedout = 0; |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
my($oldalarm, $handler); |
165
|
|
|
|
|
|
|
if (defined $secs) { |
166
|
|
|
|
|
|
|
# stop the timer, collect remaining time |
167
|
2025
|
|
|
|
|
2484
|
$oldalarm = alarm(0); # 0 when disarmed, undef on error |
168
|
|
|
|
|
|
|
$alarm_tinkered_with = 1; |
169
|
2025
|
|
|
|
|
2478
|
if (!@expiration) { |
170
|
2025
|
100
|
|
|
|
3196
|
# dbg("timed: %s no timer in evidence", $id); |
171
|
|
|
|
|
|
|
# dbg("timed: %s actual timer was running, time left %.3f s", |
172
|
2021
|
|
|
|
|
14241
|
# $id, $oldalarm) if $oldalarm; |
173
|
2021
|
|
|
|
|
4360
|
} elsif (!defined $expiration[0]) { |
174
|
2021
|
100
|
|
|
|
5256
|
# dbg("timed: %s timer not running according to evidence", $id); |
|
|
50
|
|
|
|
|
|
175
|
|
|
|
|
|
|
# dbg("timed: %s actual timer was running, time left %.3f s", |
176
|
|
|
|
|
|
|
# $id, $oldalarm) if $oldalarm; |
177
|
|
|
|
|
|
|
} else { |
178
|
|
|
|
|
|
|
my $oldalarm2 = $expiration[0] - $start_time; |
179
|
|
|
|
|
|
|
# dbg("timed: %s stopping timer, time left %.3f s%s", $id, $oldalarm2, |
180
|
|
|
|
|
|
|
# !$oldalarm ? '' : sprintf(", reported as %.3f s", $oldalarm)); |
181
|
|
|
|
|
|
|
$oldalarm = $oldalarm2 < 1 ? 1 : $oldalarm2; |
182
|
|
|
|
|
|
|
} |
183
|
1908
|
|
|
|
|
2481
|
$self->{end_time} = $start_time + $secs; # needed by reset() |
184
|
|
|
|
|
|
|
$handler = sub { $timedout = 1; die "__alarm__ignore__($id)\n" }; |
185
|
|
|
|
|
|
|
} |
186
|
1908
|
100
|
|
|
|
4373
|
|
187
|
|
|
|
|
|
|
my($ret, $eval_stat); |
188
|
2021
|
|
|
|
|
4237
|
unshift(@expiration, undef); |
189
|
2021
|
|
|
12
|
|
8248
|
eval { |
|
12
|
|
|
|
|
18002630
|
|
|
12
|
|
|
|
|
851
|
|
190
|
|
|
|
|
|
|
local $SIG{__DIE__}; # bug 4631 |
191
|
|
|
|
|
|
|
|
192
|
2025
|
|
|
|
|
3269
|
if (!defined $secs) { # no timeout specified, just call the sub |
193
|
2025
|
|
|
|
|
3350
|
$ret = &$sub; |
194
|
|
|
|
|
|
|
|
195
|
2025
|
|
|
|
|
6477
|
} elsif ($secs <= 0) { |
196
|
|
|
|
|
|
|
$self->{timed_out} = 1; |
197
|
2025
|
100
|
100
|
|
|
8589
|
&$handler; |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
198
|
4
|
|
|
|
|
8
|
|
199
|
|
|
|
|
|
|
} elsif ($oldalarm && $oldalarm < $secs) { # run under an outer timer |
200
|
|
|
|
|
|
|
# just restore outer timer, a timeout signal will be handled there |
201
|
0
|
|
|
|
|
0
|
# dbg("timed: %s alarm(%.3f) - outer", $id, $oldalarm); |
202
|
0
|
|
|
|
|
0
|
$expiration[0] = $start_time + $oldalarm; |
203
|
|
|
|
|
|
|
alarm($oldalarm); $alarm_tinkered_with = 1; |
204
|
|
|
|
|
|
|
$ret = &$sub; |
205
|
|
|
|
|
|
|
# dbg("timed: %s post-sub(outer)", $id); |
206
|
|
|
|
|
|
|
|
207
|
1
|
|
|
|
|
29
|
} else { # run under a timer specified with this call |
208
|
1
|
|
|
|
|
7
|
local $SIG{ALRM} = $handler; # ensure closed scope here |
|
1
|
|
|
|
|
14
|
|
209
|
1
|
|
|
|
|
4
|
my $isecs = int($secs); |
210
|
|
|
|
|
|
|
$isecs++ if $secs > int($isecs); # ceiling |
211
|
|
|
|
|
|
|
# dbg("timed: %s alarm(%d)", $id, $isecs); |
212
|
|
|
|
|
|
|
$expiration[0] = $start_time + $isecs; |
213
|
2020
|
|
|
|
|
23926
|
alarm($isecs); $alarm_tinkered_with = 1; |
214
|
2020
|
|
|
|
|
5057
|
$ret = &$sub; |
215
|
2020
|
100
|
|
|
|
4519
|
# dbg("timed: %s post-sub", $id); |
216
|
|
|
|
|
|
|
} |
217
|
2020
|
|
|
|
|
3127
|
|
218
|
2020
|
|
|
|
|
9525
|
# Unset the alarm() before we leave eval{ } scope, as that stack-pop |
|
2020
|
|
|
|
|
3832
|
|
219
|
2020
|
|
|
|
|
5086
|
# operation can take a second or two under load. Note: previous versions |
220
|
|
|
|
|
|
|
# restored $oldalarm here; however, that is NOT what we want to do, since |
221
|
|
|
|
|
|
|
# it creates a new race condition, namely that an old alarm could then fire |
222
|
|
|
|
|
|
|
# while the stack-pop was underway, thereby appearing to be *this* timeout |
223
|
|
|
|
|
|
|
# timing out. In terms of how we might possibly have nested timeouts in |
224
|
|
|
|
|
|
|
# SpamAssassin, this is an academic issue with little impact, but it's |
225
|
|
|
|
|
|
|
# still worth avoiding anyway. |
226
|
|
|
|
|
|
|
# |
227
|
|
|
|
|
|
|
alarm(0) if $alarm_tinkered_with; # disarm |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
1; |
230
|
|
|
|
|
|
|
} or do { |
231
|
|
|
|
|
|
|
$eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat; |
232
|
2005
|
100
|
|
|
|
7041633
|
# just in case we popped out for some other reason |
233
|
|
|
|
|
|
|
alarm(0) if $alarm_tinkered_with; # disarm |
234
|
2005
|
|
|
|
|
9876
|
}; |
235
|
2025
|
100
|
|
|
|
2853
|
|
236
|
20
|
50
|
|
|
|
2005043
|
delete $self->{end_time}; # reset() is only applicable within a &$sub |
|
20
|
|
|
|
|
93
|
|
237
|
|
|
|
|
|
|
|
238
|
20
|
100
|
|
|
|
297
|
# catch timedout return: |
239
|
|
|
|
|
|
|
# 0 0 $ret |
240
|
|
|
|
|
|
|
# 0 1 undef |
241
|
2025
|
|
|
|
|
3891
|
# 1 0 $eval_stat |
242
|
|
|
|
|
|
|
# 1 1 undef |
243
|
|
|
|
|
|
|
# |
244
|
|
|
|
|
|
|
my $return = $and_catch ? $eval_stat : $ret; |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
if (defined $eval_stat && $eval_stat =~ /__alarm__ignore__\Q($id)\E/) { |
247
|
|
|
|
|
|
|
$self->{timed_out} = 1; |
248
|
|
|
|
|
|
|
# dbg("timed: %s cought: %s", $id, $eval_stat); |
249
|
2025
|
100
|
|
|
|
4117
|
} elsif ($timedout) { |
250
|
|
|
|
|
|
|
# this happens occasionally; haven't figured out why. seems harmless |
251
|
2025
|
100
|
100
|
|
|
6296
|
# dbg("timed: %s timeout with empty eval status", $id); |
|
|
50
|
|
|
|
|
|
252
|
12
|
|
|
|
|
82
|
$self->{timed_out} = 1; |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
shift(@expiration); # pop off the stack |
256
|
|
|
|
|
|
|
|
257
|
0
|
|
|
|
|
0
|
# covers all cases, including where $self->{timed_out} is flagged by reset() |
258
|
|
|
|
|
|
|
undef $return if $self->{timed_out}; |
259
|
|
|
|
|
|
|
|
260
|
2025
|
|
|
|
|
2809
|
my $remaining_time; |
261
|
|
|
|
|
|
|
# restore previous timer if necessary |
262
|
|
|
|
|
|
|
if ($oldalarm) { # an outer alarm was already active when we were called |
263
|
2025
|
100
|
|
|
|
3680
|
$remaining_time = $start_time + $oldalarm - time; |
264
|
|
|
|
|
|
|
if ($remaining_time > 0) { # still in the future |
265
|
2025
|
|
|
|
|
2272
|
# restore the previously-active alarm, |
266
|
|
|
|
|
|
|
# taking into account the elapsed time we spent here |
267
|
2025
|
100
|
|
|
|
3320
|
my $iremaining_time = int($remaining_time); |
268
|
1908
|
|
|
|
|
4674
|
$iremaining_time++ if $remaining_time > int($remaining_time); # ceiling |
269
|
1908
|
100
|
|
|
|
3771
|
# dbg("timed: %s restoring outer alarm(%.3f)", $id, $iremaining_time); |
270
|
|
|
|
|
|
|
alarm($iremaining_time); $alarm_tinkered_with = 1; |
271
|
|
|
|
|
|
|
undef $remaining_time; # already taken care of |
272
|
1905
|
|
|
|
|
2700
|
} |
273
|
1905
|
50
|
|
|
|
3879
|
} |
274
|
|
|
|
|
|
|
if (!$and_catch && defined $eval_stat && |
275
|
1905
|
|
|
|
|
8856
|
$eval_stat !~ /__alarm__ignore__\Q($id)\E/) { |
|
1905
|
|
|
|
|
3870
|
|
276
|
1905
|
|
|
|
|
3028
|
# propagate "real" errors or outer timeouts |
277
|
|
|
|
|
|
|
die "Timeout::_run: $eval_stat\n"; |
278
|
|
|
|
|
|
|
} |
279
|
2025
|
100
|
100
|
|
|
7870
|
if (defined $remaining_time) { |
|
|
|
100
|
|
|
|
|
280
|
|
|
|
|
|
|
# dbg("timed: %s outer timer expired %.3f s ago", $id, -$remaining_time); |
281
|
|
|
|
|
|
|
# mercifully grant two additional seconds |
282
|
3
|
|
|
|
|
60
|
alarm(2); $alarm_tinkered_with = 1; |
283
|
|
|
|
|
|
|
} |
284
|
2022
|
100
|
|
|
|
3421
|
return $return; |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
|
287
|
2
|
|
|
|
|
27
|
########################################################################### |
|
2
|
|
|
|
|
9
|
|
288
|
|
|
|
|
|
|
|
289
|
2022
|
|
|
|
|
12873
|
=item $t->timed_out() |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
Returns C<1> if the most recent code executed in C<run()> timed out, or |
292
|
|
|
|
|
|
|
C<undef> if it did not. |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
=cut |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
my ($self) = @_; |
297
|
|
|
|
|
|
|
return $self->{timed_out}; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
########################################################################### |
301
|
|
|
|
|
|
|
|
302
|
1832
|
|
|
1832
|
1
|
3663
|
=item $t->reset() |
303
|
1832
|
|
|
|
|
11665
|
|
304
|
|
|
|
|
|
|
If called within a C<run()> code reference, causes the current alarm timer |
305
|
|
|
|
|
|
|
to be restored to its original setting (useful after our alarm setting was |
306
|
|
|
|
|
|
|
clobbered by some underlying module). |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
=back |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
=cut |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
my ($self) = @_; |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
my $id = $self->{id}; |
315
|
|
|
|
|
|
|
# dbg("timed: %s reset", $id); |
316
|
|
|
|
|
|
|
return if !defined $self->{end_time}; |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
my $secs = $self->{end_time} - time; |
319
|
3
|
|
|
3
|
1
|
6013799
|
if ($secs > 0) { |
320
|
|
|
|
|
|
|
my $isecs = int($secs); |
321
|
3
|
|
|
|
|
19
|
$isecs++ if $secs > int($isecs); # ceiling |
322
|
|
|
|
|
|
|
# dbg("timed: %s reset: alarm(%.3f)", $self->{id}, $isecs); |
323
|
3
|
50
|
|
|
|
21
|
alarm($isecs); |
324
|
|
|
|
|
|
|
} else { |
325
|
3
|
|
|
|
|
26
|
$self->{timed_out} = 1; |
326
|
3
|
100
|
|
|
|
24
|
# dbg("timed: %s reset, timer expired %.3f s ago", $id, -$secs); |
327
|
2
|
|
|
|
|
7
|
alarm(2); # mercifully grant two additional seconds |
328
|
2
|
50
|
|
|
|
9
|
} |
329
|
|
|
|
|
|
|
} |
330
|
2
|
|
|
|
|
28
|
|
331
|
|
|
|
|
|
|
########################################################################### |
332
|
1
|
|
|
|
|
9
|
|
333
|
|
|
|
|
|
|
1; |