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
|
|
|
|
|
|
|
package Mail::SpamAssassin::Timeout; |
55
|
|
|
|
|
|
|
|
56
|
41
|
|
|
41
|
|
21502
|
use strict; |
|
41
|
|
|
|
|
73
|
|
|
41
|
|
|
|
|
1352
|
|
57
|
41
|
|
|
41
|
|
241
|
use warnings; |
|
41
|
|
|
|
|
88
|
|
|
41
|
|
|
|
|
1566
|
|
58
|
|
|
|
|
|
|
# use bytes; |
59
|
41
|
|
|
41
|
|
255
|
use re 'taint'; |
|
41
|
|
|
|
|
101
|
|
|
41
|
|
|
|
|
1672
|
|
60
|
|
|
|
|
|
|
|
61
|
41
|
|
|
41
|
|
294
|
use Time::HiRes qw(time); |
|
41
|
|
|
|
|
90
|
|
|
41
|
|
|
|
|
372
|
|
62
|
41
|
|
|
41
|
|
4869
|
use Mail::SpamAssassin::Logger; |
|
41
|
|
|
|
|
100
|
|
|
41
|
|
|
|
|
7802
|
|
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
our @ISA = qw(); |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
########################################################################### |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
=item my $t = Mail::SpamAssassin::Timeout->new({ ... options ... }); |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
Constructor. Options include: |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=over 4 |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
=item secs => $seconds |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
time interval, in seconds. Optional; if neither C<secs> nor C<deadline> is |
77
|
|
|
|
|
|
|
specified, no timeouts will be applied. |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=item deadline => $unix_timestamp |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
Unix timestamp (seconds since epoch) when a timeout is reached in the latest. |
82
|
|
|
|
|
|
|
Optional; if neither B<secs> nor B<deadline> is specified, no timeouts will |
83
|
|
|
|
|
|
|
be applied. If both are specified, the shorter interval of the two prevails. |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=back |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
=cut |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
our $id_gen; |
90
|
41
|
|
|
41
|
|
39898
|
BEGIN { $id_gen = 0 } # unique generator of IDs for timer objects |
91
|
|
|
|
|
|
|
our @expiration; # stack of expected expiration times, top at [0] |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
sub new { |
94
|
2025
|
|
|
2025
|
1
|
14604
|
my ($class, $opts) = @_; |
95
|
2025
|
|
33
|
|
|
6436
|
$class = ref($class) || $class; |
96
|
2025
|
100
|
|
|
|
4811
|
my %selfval = $opts ? %{$opts} : (); |
|
2022
|
|
|
|
|
7411
|
|
97
|
2025
|
|
|
|
|
5338
|
$selfval{id} = ++$id_gen; |
98
|
2025
|
|
|
|
|
13888
|
my($package, $filename, $line, $subroutine) = caller(1); |
99
|
2025
|
100
|
|
|
|
5536
|
if (defined $subroutine) { |
100
|
2000
|
|
|
|
|
8973
|
$subroutine =~ s/^Mail::SpamAssassin::/::/; |
101
|
2000
|
|
|
|
|
6350
|
$selfval{id} = join('/', $id_gen, $subroutine, $line); |
102
|
|
|
|
|
|
|
} |
103
|
2025
|
|
|
|
|
3476
|
my $self = \%selfval; |
104
|
|
|
|
|
|
|
|
105
|
2025
|
|
|
|
|
3763
|
bless ($self, $class); |
106
|
2025
|
|
|
|
|
5881
|
$self; |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
########################################################################### |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
=item $t->run($coderef) |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
Run a code reference within the currently-defined timeout. |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
The timeout is as defined by the B<secs> and B<deadline> parameters |
116
|
|
|
|
|
|
|
to the constructor. |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
Returns whatever the subroutine returns, or C<undef> on timeout. |
119
|
|
|
|
|
|
|
If the timer times out, C<$t-<gt>timed_out()> will return C<1>. |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
Time elapsed is not cumulative; multiple runs of C<run> will restart the |
122
|
|
|
|
|
|
|
timeout from scratch. On the other hand, nested timers do observe outer |
123
|
|
|
|
|
|
|
timeouts if they are shorter, resignalling a timeout to the level which |
124
|
|
|
|
|
|
|
established them, i.e. code running under an inner timer can not exceed |
125
|
|
|
|
|
|
|
the time limit established by an outer timer. When restarting an outer |
126
|
|
|
|
|
|
|
timer on return, elapsed time of a running code is taken into account. |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=item $t->run_and_catch($coderef) |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
Run a code reference, as per C<$t-<gt>run()>, but also catching any |
131
|
|
|
|
|
|
|
C<die()> calls within the code reference. |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
Returns C<undef> if no C<die()> call was executed and C<$@> was unset, or the |
134
|
|
|
|
|
|
|
value of C<$@> if it was set. (The timeout event doesn't count as a C<die()>.) |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=cut |
137
|
|
|
|
|
|
|
|
138
|
2012
|
|
|
2012
|
1
|
6489
|
sub run { $_[0]->_run($_[1], 0); } |
139
|
|
|
|
|
|
|
|
140
|
13
|
|
|
13
|
1
|
64
|
sub run_and_catch { $_[0]->_run($_[1], 1); } |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
sub _run { # private |
143
|
2025
|
|
|
2025
|
|
3973
|
my ($self, $sub, $and_catch) = @_; |
144
|
|
|
|
|
|
|
|
145
|
2025
|
|
|
|
|
3175
|
delete $self->{timed_out}; |
146
|
|
|
|
|
|
|
|
147
|
2025
|
|
|
|
|
3224
|
my $id = $self->{id}; |
148
|
2025
|
|
|
|
|
2803
|
my $secs = $self->{secs}; |
149
|
2025
|
|
|
|
|
3101
|
my $deadline = $self->{deadline}; |
150
|
2025
|
|
|
|
|
2554
|
my $alarm_tinkered_with = 0; |
151
|
|
|
|
|
|
|
# dbg("timed: %s run", $id); |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
# assertion |
154
|
2025
|
50
|
66
|
|
|
4963
|
if (defined $secs && $secs < 0) { |
155
|
0
|
|
|
|
|
0
|
die "Mail::SpamAssassin::Timeout: oops? neg value for 'secs': $secs"; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
2025
|
|
|
|
|
4960
|
my $start_time = time; |
159
|
2025
|
100
|
|
|
|
3940
|
if (defined $deadline) { |
160
|
2008
|
|
|
|
|
3000
|
my $dt = $deadline - $start_time; |
161
|
2008
|
100
|
100
|
|
|
5365
|
$secs = $dt if !defined $secs || $dt < $secs; |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
# bug 4699: under heavy load, an alarm may fire while $@ will contain "", |
165
|
|
|
|
|
|
|
# which isn't very useful. this flag works around it safely, since |
166
|
|
|
|
|
|
|
# it will not require malloc() be called if it fires |
167
|
2025
|
|
|
|
|
2803
|
my $timedout = 0; |
168
|
|
|
|
|
|
|
|
169
|
2025
|
|
|
|
|
2979
|
my($oldalarm, $handler); |
170
|
2025
|
100
|
|
|
|
4189
|
if (defined $secs) { |
171
|
|
|
|
|
|
|
# stop the timer, collect remaining time |
172
|
2021
|
|
|
|
|
15398
|
$oldalarm = alarm(0); # 0 when disarmed, undef on error |
173
|
2021
|
|
|
|
|
4243
|
$alarm_tinkered_with = 1; |
174
|
2021
|
100
|
|
|
|
6995
|
if (!@expiration) { |
|
|
50
|
|
|
|
|
|
175
|
|
|
|
|
|
|
# dbg("timed: %s no timer in evidence", $id); |
176
|
|
|
|
|
|
|
# dbg("timed: %s actual timer was running, time left %.3f s", |
177
|
|
|
|
|
|
|
# $id, $oldalarm) if $oldalarm; |
178
|
|
|
|
|
|
|
} elsif (!defined $expiration[0]) { |
179
|
|
|
|
|
|
|
# dbg("timed: %s timer not running according to evidence", $id); |
180
|
|
|
|
|
|
|
# dbg("timed: %s actual timer was running, time left %.3f s", |
181
|
|
|
|
|
|
|
# $id, $oldalarm) if $oldalarm; |
182
|
|
|
|
|
|
|
} else { |
183
|
1908
|
|
|
|
|
3142
|
my $oldalarm2 = $expiration[0] - $start_time; |
184
|
|
|
|
|
|
|
# dbg("timed: %s stopping timer, time left %.3f s%s", $id, $oldalarm2, |
185
|
|
|
|
|
|
|
# !$oldalarm ? '' : sprintf(", reported as %.3f s", $oldalarm)); |
186
|
1908
|
100
|
|
|
|
4879
|
$oldalarm = $oldalarm2 < 1 ? 1 : $oldalarm2; |
187
|
|
|
|
|
|
|
} |
188
|
2021
|
|
|
|
|
4830
|
$self->{end_time} = $start_time + $secs; # needed by reset() |
189
|
2021
|
|
|
12
|
|
9258
|
$handler = sub { $timedout = 1; die "__alarm__ignore__($id)\n" }; |
|
12
|
|
|
|
|
18001918
|
|
|
12
|
|
|
|
|
752
|
|
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
2025
|
|
|
|
|
4168
|
my($ret, $eval_stat); |
193
|
2025
|
|
|
|
|
4090
|
unshift(@expiration, undef); |
194
|
|
|
|
|
|
|
eval { |
195
|
2025
|
|
|
|
|
7522
|
local $SIG{__DIE__}; # bug 4631 |
196
|
|
|
|
|
|
|
|
197
|
2025
|
100
|
100
|
|
|
10558
|
if (!defined $secs) { # no timeout specified, just call the sub |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
198
|
4
|
|
|
|
|
9
|
$ret = &$sub; |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
} elsif ($secs <= 0) { |
201
|
0
|
|
|
|
|
0
|
$self->{timed_out} = 1; |
202
|
0
|
|
|
|
|
0
|
&$handler; |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
} elsif ($oldalarm && $oldalarm < $secs) { # run under an outer timer |
205
|
|
|
|
|
|
|
# just restore outer timer, a timeout signal will be handled there |
206
|
|
|
|
|
|
|
# dbg("timed: %s alarm(%.3f) - outer", $id, $oldalarm); |
207
|
1
|
|
|
|
|
3
|
$expiration[0] = $start_time + $oldalarm; |
208
|
1
|
|
|
|
|
6
|
alarm($oldalarm); $alarm_tinkered_with = 1; |
|
1
|
|
|
|
|
4
|
|
209
|
1
|
|
|
|
|
4
|
$ret = &$sub; |
210
|
|
|
|
|
|
|
# dbg("timed: %s post-sub(outer)", $id); |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
} else { # run under a timer specified with this call |
213
|
2020
|
|
|
|
|
26469
|
local $SIG{ALRM} = $handler; # ensure closed scope here |
214
|
2020
|
|
|
|
|
5482
|
my $isecs = int($secs); |
215
|
2020
|
100
|
|
|
|
5137
|
$isecs++ if $secs > int($isecs); # ceiling |
216
|
|
|
|
|
|
|
# dbg("timed: %s alarm(%d)", $id, $isecs); |
217
|
2020
|
|
|
|
|
3597
|
$expiration[0] = $start_time + $isecs; |
218
|
2020
|
|
|
|
|
10394
|
alarm($isecs); $alarm_tinkered_with = 1; |
|
2020
|
|
|
|
|
3964
|
|
219
|
2020
|
|
|
|
|
5448
|
$ret = &$sub; |
220
|
|
|
|
|
|
|
# dbg("timed: %s post-sub", $id); |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
# Unset the alarm() before we leave eval{ } scope, as that stack-pop |
224
|
|
|
|
|
|
|
# operation can take a second or two under load. Note: previous versions |
225
|
|
|
|
|
|
|
# restored $oldalarm here; however, that is NOT what we want to do, since |
226
|
|
|
|
|
|
|
# it creates a new race condition, namely that an old alarm could then fire |
227
|
|
|
|
|
|
|
# while the stack-pop was underway, thereby appearing to be *this* timeout |
228
|
|
|
|
|
|
|
# timing out. In terms of how we might possibly have nested timeouts in |
229
|
|
|
|
|
|
|
# SpamAssassin, this is an academic issue with little impact, but it's |
230
|
|
|
|
|
|
|
# still worth avoiding anyway. |
231
|
|
|
|
|
|
|
# |
232
|
2005
|
100
|
|
|
|
7041192
|
alarm(0) if $alarm_tinkered_with; # disarm |
233
|
|
|
|
|
|
|
|
234
|
2005
|
|
|
|
|
10552
|
1; |
235
|
2025
|
100
|
|
|
|
3210
|
} or do { |
236
|
20
|
50
|
|
|
|
2004586
|
$eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat; |
|
20
|
|
|
|
|
94
|
|
237
|
|
|
|
|
|
|
# just in case we popped out for some other reason |
238
|
20
|
100
|
|
|
|
186
|
alarm(0) if $alarm_tinkered_with; # disarm |
239
|
|
|
|
|
|
|
}; |
240
|
|
|
|
|
|
|
|
241
|
2025
|
|
|
|
|
4660
|
delete $self->{end_time}; # reset() is only applicable within a &$sub |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
# catch timedout return: |
244
|
|
|
|
|
|
|
# 0 0 $ret |
245
|
|
|
|
|
|
|
# 0 1 undef |
246
|
|
|
|
|
|
|
# 1 0 $eval_stat |
247
|
|
|
|
|
|
|
# 1 1 undef |
248
|
|
|
|
|
|
|
# |
249
|
2025
|
100
|
|
|
|
4866
|
my $return = $and_catch ? $eval_stat : $ret; |
250
|
|
|
|
|
|
|
|
251
|
2025
|
100
|
100
|
|
|
7229
|
if (defined $eval_stat && $eval_stat =~ /__alarm__ignore__\Q($id)\E/) { |
|
|
50
|
|
|
|
|
|
252
|
12
|
|
|
|
|
51
|
$self->{timed_out} = 1; |
253
|
|
|
|
|
|
|
# dbg("timed: %s cought: %s", $id, $eval_stat); |
254
|
|
|
|
|
|
|
} elsif ($timedout) { |
255
|
|
|
|
|
|
|
# this happens occasionally; haven't figured out why. seems harmless |
256
|
|
|
|
|
|
|
# dbg("timed: %s timeout with empty eval status", $id); |
257
|
0
|
|
|
|
|
0
|
$self->{timed_out} = 1; |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
|
260
|
2025
|
|
|
|
|
3581
|
shift(@expiration); # pop off the stack |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
# covers all cases, including where $self->{timed_out} is flagged by reset() |
263
|
2025
|
100
|
|
|
|
4421
|
undef $return if $self->{timed_out}; |
264
|
|
|
|
|
|
|
|
265
|
2025
|
|
|
|
|
2807
|
my $remaining_time; |
266
|
|
|
|
|
|
|
# restore previous timer if necessary |
267
|
2025
|
100
|
|
|
|
4428
|
if ($oldalarm) { # an outer alarm was already active when we were called |
268
|
1908
|
|
|
|
|
5473
|
$remaining_time = $start_time + $oldalarm - time; |
269
|
1908
|
100
|
|
|
|
4526
|
if ($remaining_time > 0) { # still in the future |
270
|
|
|
|
|
|
|
# restore the previously-active alarm, |
271
|
|
|
|
|
|
|
# taking into account the elapsed time we spent here |
272
|
1905
|
|
|
|
|
3162
|
my $iremaining_time = int($remaining_time); |
273
|
1905
|
50
|
|
|
|
4611
|
$iremaining_time++ if $remaining_time > int($remaining_time); # ceiling |
274
|
|
|
|
|
|
|
# dbg("timed: %s restoring outer alarm(%.3f)", $id, $iremaining_time); |
275
|
1905
|
|
|
|
|
10039
|
alarm($iremaining_time); $alarm_tinkered_with = 1; |
|
1905
|
|
|
|
|
3974
|
|
276
|
1905
|
|
|
|
|
3425
|
undef $remaining_time; # already taken care of |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
} |
279
|
2025
|
100
|
100
|
|
|
8888
|
if (!$and_catch && defined $eval_stat && |
|
|
|
100
|
|
|
|
|
280
|
|
|
|
|
|
|
$eval_stat !~ /__alarm__ignore__\Q($id)\E/) { |
281
|
|
|
|
|
|
|
# propagate "real" errors or outer timeouts |
282
|
3
|
|
|
|
|
72
|
die "Timeout::_run: $eval_stat\n"; |
283
|
|
|
|
|
|
|
} |
284
|
2022
|
100
|
|
|
|
3982
|
if (defined $remaining_time) { |
285
|
|
|
|
|
|
|
# dbg("timed: %s outer timer expired %.3f s ago", $id, -$remaining_time); |
286
|
|
|
|
|
|
|
# mercifully grant two additional seconds |
287
|
2
|
|
|
|
|
19
|
alarm(2); $alarm_tinkered_with = 1; |
|
2
|
|
|
|
|
7
|
|
288
|
|
|
|
|
|
|
} |
289
|
2022
|
|
|
|
|
14350
|
return $return; |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
########################################################################### |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
=item $t->timed_out() |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
Returns C<1> if the most recent code executed in C<run()> timed out, or |
297
|
|
|
|
|
|
|
C<undef> if it did not. |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=cut |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
sub timed_out { |
302
|
1832
|
|
|
1832
|
1
|
4004
|
my ($self) = @_; |
303
|
1832
|
|
|
|
|
13765
|
return $self->{timed_out}; |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
########################################################################### |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
=item $t->reset() |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
If called within a C<run()> code reference, causes the current alarm timer |
311
|
|
|
|
|
|
|
to be restored to its original setting (useful after our alarm setting was |
312
|
|
|
|
|
|
|
clobbered by some underlying module). |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
=back |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
=cut |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
sub reset { |
319
|
3
|
|
|
3
|
1
|
6012709
|
my ($self) = @_; |
320
|
|
|
|
|
|
|
|
321
|
3
|
|
|
|
|
23
|
my $id = $self->{id}; |
322
|
|
|
|
|
|
|
# dbg("timed: %s reset", $id); |
323
|
3
|
50
|
|
|
|
31
|
return if !defined $self->{end_time}; |
324
|
|
|
|
|
|
|
|
325
|
3
|
|
|
|
|
29
|
my $secs = $self->{end_time} - time; |
326
|
3
|
100
|
|
|
|
28
|
if ($secs > 0) { |
327
|
2
|
|
|
|
|
10
|
my $isecs = int($secs); |
328
|
2
|
50
|
|
|
|
18
|
$isecs++ if $secs > int($isecs); # ceiling |
329
|
|
|
|
|
|
|
# dbg("timed: %s reset: alarm(%.3f)", $self->{id}, $isecs); |
330
|
2
|
|
|
|
|
36
|
alarm($isecs); |
331
|
|
|
|
|
|
|
} else { |
332
|
1
|
|
|
|
|
8
|
$self->{timed_out} = 1; |
333
|
|
|
|
|
|
|
# dbg("timed: %s reset, timer expired %.3f s ago", $id, -$secs); |
334
|
1
|
|
|
|
|
15
|
alarm(2); # mercifully grant two additional seconds |
335
|
|
|
|
|
|
|
} |
336
|
|
|
|
|
|
|
} |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
########################################################################### |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
1; |