Filename | /home/hejohns/perl5/lib/perl5/IPC/Run/Timer.pm |
Statements | Executed 26 statements in 1.02ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 9µs | 9µs | BEGIN@169 | IPC::Run::Timer::
1 | 1 | 1 | 9µs | 11µs | BEGIN@160 | IPC::Run::Timer::
1 | 1 | 1 | 5µs | 113µs | BEGIN@163 | IPC::Run::Timer::
1 | 1 | 1 | 4µs | 17µs | BEGIN@164 | IPC::Run::Timer::
1 | 1 | 1 | 4µs | 19µs | BEGIN@161 | IPC::Run::Timer::
1 | 1 | 1 | 4µs | 20µs | BEGIN@162 | IPC::Run::Timer::
1 | 1 | 1 | 4µs | 8µs | BEGIN@165 | IPC::Run::Timer::
1 | 1 | 1 | 3µs | 25µs | BEGIN@192 | IPC::Run::Timer::
1 | 1 | 1 | 3µs | 24µs | BEGIN@167 | IPC::Run::Timer::
1 | 1 | 1 | 1µs | 1µs | BEGIN@166 | IPC::Run::Timer::
0 | 0 | 0 | 0s | 0s | _calc_end_time | IPC::Run::Timer::
0 | 0 | 0 | 0s | 0s | _parse_time | IPC::Run::Timer::
0 | 0 | 0 | 0s | 0s | check | IPC::Run::Timer::
0 | 0 | 0 | 0s | 0s | debug | IPC::Run::Timer::
0 | 0 | 0 | 0s | 0s | end_time | IPC::Run::Timer::
0 | 0 | 0 | 0s | 0s | exception | IPC::Run::Timer::
0 | 0 | 0 | 0s | 0s | expire | IPC::Run::Timer::
0 | 0 | 0 | 0s | 0s | interval | IPC::Run::Timer::
0 | 0 | 0 | 0s | 0s | is_expired | IPC::Run::Timer::
0 | 0 | 0 | 0s | 0s | is_reset | IPC::Run::Timer::
0 | 0 | 0 | 0s | 0s | is_running | IPC::Run::Timer::
0 | 0 | 0 | 0s | 0s | name | IPC::Run::Timer::
0 | 0 | 0 | 0s | 0s | new | IPC::Run::Timer::
0 | 0 | 0 | 0s | 0s | reset | IPC::Run::Timer::
0 | 0 | 0 | 0s | 0s | start | IPC::Run::Timer::
0 | 0 | 0 | 0s | 0s | start_time | IPC::Run::Timer::
0 | 0 | 0 | 0s | 0s | state | IPC::Run::Timer::
0 | 0 | 0 | 0s | 0s | timeout | IPC::Run::Timer::
0 | 0 | 0 | 0s | 0s | timer | IPC::Run::Timer::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | package IPC::Run::Timer; | ||||
2 | |||||
3 | =pod | ||||
4 | |||||
5 | =head1 NAME | ||||
6 | |||||
7 | IPC::Run::Timer -- Timer channels for IPC::Run. | ||||
8 | |||||
9 | =head1 SYNOPSIS | ||||
10 | |||||
11 | use IPC::Run qw( run timer timeout ); | ||||
12 | ## or IPC::Run::Timer ( timer timeout ); | ||||
13 | ## or IPC::Run::Timer ( :all ); | ||||
14 | |||||
15 | ## A non-fatal timer: | ||||
16 | $t = timer( 5 ); # or... | ||||
17 | $t = IO::Run::Timer->new( 5 ); | ||||
18 | run $t, ...; | ||||
19 | |||||
20 | ## A timeout (which is a timer that dies on expiry): | ||||
21 | $t = timeout( 5 ); # or... | ||||
22 | $t = IO::Run::Timer->new( 5, exception => "harness timed out" ); | ||||
23 | |||||
24 | =head1 DESCRIPTION | ||||
25 | |||||
26 | This class and module allows timers and timeouts to be created for use | ||||
27 | by IPC::Run. A timer simply expires when it's time is up. A timeout | ||||
28 | is a timer that throws an exception when it expires. | ||||
29 | |||||
30 | Timeouts are usually a bit simpler to use than timers: they throw an | ||||
31 | exception on expiration so you don't need to check them: | ||||
32 | |||||
33 | ## Give @cmd 10 seconds to get started, then 5 seconds to respond | ||||
34 | my $t = timeout( 10 ); | ||||
35 | $h = start( | ||||
36 | \@cmd, \$in, \$out, | ||||
37 | $t, | ||||
38 | ); | ||||
39 | pump $h until $out =~ /prompt/; | ||||
40 | |||||
41 | $in = "some stimulus"; | ||||
42 | $out = ''; | ||||
43 | $t->time( 5 ) | ||||
44 | pump $h until $out =~ /expected response/; | ||||
45 | |||||
46 | You do need to check timers: | ||||
47 | |||||
48 | ## Give @cmd 10 seconds to get started, then 5 seconds to respond | ||||
49 | my $t = timer( 10 ); | ||||
50 | $h = start( | ||||
51 | \@cmd, \$in, \$out, | ||||
52 | $t, | ||||
53 | ); | ||||
54 | pump $h until $t->is_expired || $out =~ /prompt/; | ||||
55 | |||||
56 | $in = "some stimulus"; | ||||
57 | $out = ''; | ||||
58 | $t->time( 5 ) | ||||
59 | pump $h until $out =~ /expected response/ || $t->is_expired; | ||||
60 | |||||
61 | Timers and timeouts that are reset get started by start() and | ||||
62 | pump(). Timers change state only in pump(). Since run() and | ||||
63 | finish() both call pump(), they act like pump() with respect to | ||||
64 | timers. | ||||
65 | |||||
66 | Timers and timeouts have three states: reset, running, and expired. | ||||
67 | Setting the timeout value resets the timer, as does calling | ||||
68 | the reset() method. The start() method starts (or restarts) a | ||||
69 | timer with the most recently set time value, no matter what state | ||||
70 | it's in. | ||||
71 | |||||
72 | =head2 Time values | ||||
73 | |||||
74 | All time values are in seconds. Times may be any kind of perl number, | ||||
75 | e.g. as integer or floating point seconds, optionally preceded by | ||||
76 | punctuation-separated days, hours, and minutes. | ||||
77 | |||||
78 | Examples: | ||||
79 | |||||
80 | 1 1 second | ||||
81 | 1.1 1.1 seconds | ||||
82 | 60 60 seconds | ||||
83 | 1:0 1 minute | ||||
84 | 1:1 1 minute, 1 second | ||||
85 | 1:90 2 minutes, 30 seconds | ||||
86 | 1:2:3:4.5 1 day, 2 hours, 3 minutes, 4.5 seconds | ||||
87 | 'inf' the infinity perl special number (the timer never finishes) | ||||
88 | |||||
89 | Absolute date/time strings are *not* accepted: year, month and | ||||
90 | day-of-month parsing is not available (patches welcome :-). | ||||
91 | |||||
92 | =head2 Interval fudging | ||||
93 | |||||
94 | When calculating an end time from a start time and an interval, IPC::Run::Timer | ||||
95 | instances add a little fudge factor. This is to ensure that no time will | ||||
96 | expire before the interval is up. | ||||
97 | |||||
98 | First a little background. Time is sampled in discrete increments. We'll | ||||
99 | call the | ||||
100 | exact moment that the reported time increments from one interval to the | ||||
101 | next a tick, and the interval between ticks as the time period. Here's | ||||
102 | a diagram of three ticks and the periods between them: | ||||
103 | |||||
104 | |||||
105 | -0-0-0-0-0-0-0-0-0-0-1-1-1-1-1-1-1-1-1-1-2-... | ||||
106 | ^ ^ ^ | ||||
107 | |<--- period 0 ---->|<--- period 1 ---->| | ||||
108 | | | | | ||||
109 | tick 0 tick 1 tick 2 | ||||
110 | |||||
111 | To see why the fudge factor is necessary, consider what would happen | ||||
112 | when a timer with an interval of 1 second is started right at the end of | ||||
113 | period 0: | ||||
114 | |||||
115 | |||||
116 | -0-0-0-0-0-0-0-0-0-0-1-1-1-1-1-1-1-1-1-1-2-... | ||||
117 | ^ ^ ^ ^ | ||||
118 | | | | | | ||||
119 | | | | | | ||||
120 | tick 0 |tick 1 tick 2 | ||||
121 | | | ||||
122 | start $t | ||||
123 | |||||
124 | Assuming that check() is called many times per period, then the timer | ||||
125 | is likely to expire just after tick 1, since the time reported will have | ||||
126 | lept from the value '0' to the value '1': | ||||
127 | |||||
128 | -0-0-0-0-0-0-0-0-0-0-1-1-1-1-1-1-1-1-1-1-2-... | ||||
129 | ^ ^ ^ ^ ^ | ||||
130 | | | | | | | ||||
131 | | | | | | | ||||
132 | tick 0 |tick 1| tick 2 | ||||
133 | | | | ||||
134 | start $t | | ||||
135 | | | ||||
136 | check $t | ||||
137 | |||||
138 | Adding a fudge of '1' in this example means that the timer is guaranteed | ||||
139 | not to expire before tick 2. | ||||
140 | |||||
141 | The fudge is not added to an interval of '0'. | ||||
142 | |||||
143 | This means that intervals guarantee a minimum interval. Given that | ||||
144 | the process running perl may be suspended for some period of time, or that | ||||
145 | it gets busy doing something time-consuming, there are no other guarantees on | ||||
146 | how long it will take a timer to expire. | ||||
147 | |||||
148 | =head1 SUBCLASSING | ||||
149 | |||||
150 | INCOMPATIBLE CHANGE: Due to the awkwardness introduced by ripping | ||||
151 | pseudohashes out of Perl, this class I<no longer> uses the fields | ||||
152 | pragma. | ||||
153 | |||||
154 | =head1 FUNCTIONS & METHODS | ||||
155 | |||||
156 | =over | ||||
157 | |||||
158 | =cut | ||||
159 | |||||
160 | 2 | 19µs | 2 | 13µs | # spent 11µs (9+2) within IPC::Run::Timer::BEGIN@160 which was called:
# once (9µs+2µs) by main::BEGIN@29 at line 160 # spent 11µs making 1 call to IPC::Run::Timer::BEGIN@160
# spent 2µs making 1 call to strict::import |
161 | 2 | 13µs | 2 | 34µs | # spent 19µs (4+15) within IPC::Run::Timer::BEGIN@161 which was called:
# once (4µs+15µs) by main::BEGIN@29 at line 161 # spent 19µs making 1 call to IPC::Run::Timer::BEGIN@161
# spent 15µs making 1 call to warnings::import |
162 | 2 | 16µs | 2 | 37µs | # spent 20µs (4+17) within IPC::Run::Timer::BEGIN@162 which was called:
# once (4µs+17µs) by main::BEGIN@29 at line 162 # spent 20µs making 1 call to IPC::Run::Timer::BEGIN@162
# spent 17µs making 1 call to Exporter::import |
163 | 2 | 14µs | 2 | 220µs | # spent 113µs (5+108) within IPC::Run::Timer::BEGIN@163 which was called:
# once (5µs+108µs) by main::BEGIN@29 at line 163 # spent 113µs making 1 call to IPC::Run::Timer::BEGIN@163
# spent 108µs making 1 call to Exporter::import |
164 | 2 | 15µs | 2 | 30µs | # spent 17µs (4+13) within IPC::Run::Timer::BEGIN@164 which was called:
# once (4µs+13µs) by main::BEGIN@29 at line 164 # spent 17µs making 1 call to IPC::Run::Timer::BEGIN@164
# spent 13µs making 1 call to Exporter::import |
165 | 2 | 11µs | 2 | 12µs | # spent 8µs (4+4) within IPC::Run::Timer::BEGIN@165 which was called:
# once (4µs+4µs) by main::BEGIN@29 at line 165 # spent 8µs making 1 call to IPC::Run::Timer::BEGIN@165
# spent 4µs making 1 call to Exporter::import |
166 | 2 | 13µs | 1 | 1µs | # spent 1µs within IPC::Run::Timer::BEGIN@166 which was called:
# once (1µs+0s) by main::BEGIN@29 at line 166 # spent 1µs making 1 call to IPC::Run::Timer::BEGIN@166 |
167 | 2 | 51µs | 2 | 44µs | # spent 24µs (3+21) within IPC::Run::Timer::BEGIN@167 which was called:
# once (3µs+21µs) by main::BEGIN@29 at line 167 # spent 24µs making 1 call to IPC::Run::Timer::BEGIN@167
# spent 21µs making 1 call to vars::import |
168 | |||||
169 | # spent 9µs within IPC::Run::Timer::BEGIN@169 which was called:
# once (9µs+0s) by main::BEGIN@29 at line 189 | ||||
170 | 1 | 200ns | $VERSION = '20220807.0'; | ||
171 | 1 | 6µs | @ISA = qw( Exporter ); | ||
172 | 1 | 800ns | @EXPORT_OK = qw( | ||
173 | check | ||||
174 | end_time | ||||
175 | exception | ||||
176 | expire | ||||
177 | interval | ||||
178 | is_expired | ||||
179 | is_reset | ||||
180 | is_running | ||||
181 | name | ||||
182 | reset | ||||
183 | start | ||||
184 | timeout | ||||
185 | timer | ||||
186 | ); | ||||
187 | |||||
188 | 1 | 2µs | %EXPORT_TAGS = ( 'all' => \@EXPORT_OK ); | ||
189 | 1 | 15µs | 1 | 9µs | } # spent 9µs making 1 call to IPC::Run::Timer::BEGIN@169 |
190 | |||||
191 | 1 | 500ns | require IPC::Run; | ||
192 | 2 | 846µs | 2 | 47µs | # spent 25µs (3+22) within IPC::Run::Timer::BEGIN@192 which was called:
# once (3µs+22µs) by main::BEGIN@29 at line 192 # spent 25µs making 1 call to IPC::Run::Timer::BEGIN@192
# spent 22µs making 1 call to Exporter::import |
193 | |||||
194 | ## | ||||
195 | ## Some helpers | ||||
196 | ## | ||||
197 | 1 | 300ns | my $resolution = 1; | ||
198 | |||||
199 | sub _parse_time { | ||||
200 | for ( $_[0] ) { | ||||
201 | my $val; | ||||
202 | if ( not defined $_ ) { | ||||
203 | $val = $_; | ||||
204 | } | ||||
205 | else { | ||||
206 | my @f = split( /:/, $_, -1 ); | ||||
207 | if ( scalar @f > 4 ) { | ||||
208 | croak "IPC::Run: expected <= 4 elements in time string '$_'"; | ||||
209 | } | ||||
210 | for (@f) { | ||||
211 | if ( not Scalar::Util::looks_like_number($_) ) { | ||||
212 | croak "IPC::Run: non-numeric element '$_' in time string '$_'"; | ||||
213 | } | ||||
214 | } | ||||
215 | my ( $s, $m, $h, $d ) = reverse @f; | ||||
216 | $val = ( ( ( $d || 0 ) * 24 + ( $h || 0 ) ) * 60 + ( $m || 0 ) ) * 60 + ( $s || 0 ); | ||||
217 | } | ||||
218 | return $val; | ||||
219 | } | ||||
220 | } | ||||
221 | |||||
222 | sub _calc_end_time { | ||||
223 | my IPC::Run::Timer $self = shift; | ||||
224 | my $interval = $self->interval; | ||||
225 | $interval += $resolution if $interval; | ||||
226 | $self->end_time( $self->start_time + $interval ); | ||||
227 | } | ||||
228 | |||||
229 | =item timer | ||||
230 | |||||
231 | A constructor function (not method) of IPC::Run::Timer instances: | ||||
232 | |||||
233 | $t = timer( 5 ); | ||||
234 | $t = timer( 5, name => 'stall timer', debug => 1 ); | ||||
235 | |||||
236 | $t = timer; | ||||
237 | $t->interval( 5 ); | ||||
238 | |||||
239 | run ..., $t; | ||||
240 | run ..., $t = timer( 5 ); | ||||
241 | |||||
242 | This convenience function is a shortened spelling of | ||||
243 | |||||
244 | IPC::Run::Timer->new( ... ); | ||||
245 | |||||
246 | . It returns a timer in the reset state with a given interval. | ||||
247 | |||||
248 | If an exception is provided, it will be thrown when the timer notices that | ||||
249 | it has expired (in check()). The name is for debugging usage, if you plan on | ||||
250 | having multiple timers around. If no name is provided, a name like "timer #1" | ||||
251 | will be provided. | ||||
252 | |||||
253 | =cut | ||||
254 | |||||
255 | sub timer { | ||||
256 | return IPC::Run::Timer->new(@_); | ||||
257 | } | ||||
258 | |||||
259 | =item timeout | ||||
260 | |||||
261 | A constructor function (not method) of IPC::Run::Timer instances: | ||||
262 | |||||
263 | $t = timeout( 5 ); | ||||
264 | $t = timeout( 5, exception => "kablooey" ); | ||||
265 | $t = timeout( 5, name => "stall", exception => "kablooey" ); | ||||
266 | |||||
267 | $t = timeout; | ||||
268 | $t->interval( 5 ); | ||||
269 | |||||
270 | run ..., $t; | ||||
271 | run ..., $t = timeout( 5 ); | ||||
272 | |||||
273 | A This convenience function is a shortened spelling of | ||||
274 | |||||
275 | IPC::Run::Timer->new( exception => "IPC::Run: timeout ...", ... ); | ||||
276 | |||||
277 | . It returns a timer in the reset state that will throw an | ||||
278 | exception when it expires. | ||||
279 | |||||
280 | Takes the same parameters as L</timer>, any exception passed in overrides | ||||
281 | the default exception. | ||||
282 | |||||
283 | =cut | ||||
284 | |||||
285 | sub timeout { | ||||
286 | my $t = IPC::Run::Timer->new(@_); | ||||
287 | $t->exception( "IPC::Run: timeout on " . $t->name ) | ||||
288 | unless defined $t->exception; | ||||
289 | return $t; | ||||
290 | } | ||||
291 | |||||
292 | =item new | ||||
293 | |||||
294 | IPC::Run::Timer->new() ; | ||||
295 | IPC::Run::Timer->new( 5 ) ; | ||||
296 | IPC::Run::Timer->new( 5, exception => 'kablooey' ) ; | ||||
297 | |||||
298 | Constructor. See L</timer> for details. | ||||
299 | |||||
300 | =cut | ||||
301 | |||||
302 | 1 | 100ns | my $timer_counter; | ||
303 | |||||
304 | sub new { | ||||
305 | my $class = shift; | ||||
306 | $class = ref $class || $class; | ||||
307 | |||||
308 | my IPC::Run::Timer $self = bless {}, $class; | ||||
309 | |||||
310 | $self->{STATE} = 0; | ||||
311 | $self->{DEBUG} = 0; | ||||
312 | $self->{NAME} = "timer #" . ++$timer_counter; | ||||
313 | |||||
314 | while (@_) { | ||||
315 | my $arg = shift; | ||||
316 | if ( $arg eq 'exception' ) { | ||||
317 | $self->exception(shift); | ||||
318 | } | ||||
319 | elsif ( $arg eq 'name' ) { | ||||
320 | $self->name(shift); | ||||
321 | } | ||||
322 | elsif ( $arg eq 'debug' ) { | ||||
323 | $self->debug(shift); | ||||
324 | } | ||||
325 | else { | ||||
326 | $self->interval($arg); | ||||
327 | } | ||||
328 | } | ||||
329 | |||||
330 | _debug $self->name . ' constructed' | ||||
331 | if $self->{DEBUG} || _debugging_details; | ||||
332 | |||||
333 | return $self; | ||||
334 | } | ||||
335 | |||||
336 | =item check | ||||
337 | |||||
338 | check $t; | ||||
339 | check $t, $now; | ||||
340 | $t->check; | ||||
341 | |||||
342 | Checks to see if a timer has expired since the last check. Has no effect | ||||
343 | on non-running timers. This will throw an exception if one is defined. | ||||
344 | |||||
345 | IPC::Run::pump() calls this routine for any timers in the harness. | ||||
346 | |||||
347 | You may pass in a version of now, which is useful in case you have | ||||
348 | it lying around or you want to check several timers with a consistent | ||||
349 | concept of the current time. | ||||
350 | |||||
351 | Returns the time left before end_time or 0 if end_time is no longer | ||||
352 | in the future or the timer is not running | ||||
353 | (unless, of course, check() expire()s the timer and this | ||||
354 | results in an exception being thrown). | ||||
355 | |||||
356 | Returns undef if the timer is not running on entry, 0 if check() expires it, | ||||
357 | and the time left if it's left running. | ||||
358 | |||||
359 | =cut | ||||
360 | |||||
361 | sub check { | ||||
362 | my IPC::Run::Timer $self = shift; | ||||
363 | return undef if !$self->is_running; | ||||
364 | return 0 if $self->is_expired; | ||||
365 | |||||
366 | my ($now) = @_; | ||||
367 | $now = _parse_time($now); | ||||
368 | $now = time unless defined $now; | ||||
369 | |||||
370 | _debug( "checking ", $self->name, " (end time ", $self->end_time, ") at ", $now ) if $self->{DEBUG} || _debugging_details; | ||||
371 | |||||
372 | my $left = $self->end_time - $now; | ||||
373 | return $left if $left > 0; | ||||
374 | |||||
375 | $self->expire; | ||||
376 | return 0; | ||||
377 | } | ||||
378 | |||||
379 | =item debug | ||||
380 | |||||
381 | Sets/gets the current setting of the debugging flag for this timer. This | ||||
382 | has no effect if debugging is not enabled for the current harness. | ||||
383 | |||||
384 | =cut | ||||
385 | |||||
386 | sub debug { | ||||
387 | my IPC::Run::Timer $self = shift; | ||||
388 | $self->{DEBUG} = shift if @_; | ||||
389 | return $self->{DEBUG}; | ||||
390 | } | ||||
391 | |||||
392 | =item end_time | ||||
393 | |||||
394 | $et = $t->end_time; | ||||
395 | $et = end_time $t; | ||||
396 | |||||
397 | $t->end_time( time + 10 ); | ||||
398 | |||||
399 | Returns the time when this timer will or did expire. Even if this time is | ||||
400 | in the past, the timer may not be expired, since check() may not have been | ||||
401 | called yet. | ||||
402 | |||||
403 | Note that this end_time is not start_time($t) + interval($t), since some | ||||
404 | small extra amount of time is added to make sure that the timer does not | ||||
405 | expire before interval() elapses. If this were not so, then | ||||
406 | |||||
407 | Changing end_time() while a timer is running will set the expiration time. | ||||
408 | Changing it while it is expired has no affect, since reset()ing a timer always | ||||
409 | clears the end_time(). | ||||
410 | |||||
411 | =cut | ||||
412 | |||||
413 | sub end_time { | ||||
414 | my IPC::Run::Timer $self = shift; | ||||
415 | if (@_) { | ||||
416 | $self->{END_TIME} = shift; | ||||
417 | _debug $self->name, ' end_time set to ', $self->{END_TIME} | ||||
418 | if $self->{DEBUG} > 2 || _debugging_details; | ||||
419 | } | ||||
420 | return $self->{END_TIME}; | ||||
421 | } | ||||
422 | |||||
423 | =item exception | ||||
424 | |||||
425 | $x = $t->exception; | ||||
426 | $t->exception( $x ); | ||||
427 | $t->exception( undef ); | ||||
428 | |||||
429 | Sets/gets the exception to throw, if any. 'undef' means that no | ||||
430 | exception will be thrown. Exception does not need to be a scalar: you | ||||
431 | may ask that references be thrown. | ||||
432 | |||||
433 | =cut | ||||
434 | |||||
435 | sub exception { | ||||
436 | my IPC::Run::Timer $self = shift; | ||||
437 | if (@_) { | ||||
438 | $self->{EXCEPTION} = shift; | ||||
439 | _debug $self->name, ' exception set to ', $self->{EXCEPTION} | ||||
440 | if $self->{DEBUG} || _debugging_details; | ||||
441 | } | ||||
442 | return $self->{EXCEPTION}; | ||||
443 | } | ||||
444 | |||||
445 | =item interval | ||||
446 | |||||
447 | $i = interval $t; | ||||
448 | $i = $t->interval; | ||||
449 | $t->interval( $i ); | ||||
450 | |||||
451 | Sets the interval. Sets the end time based on the start_time() and the | ||||
452 | interval (and a little fudge) if the timer is running. | ||||
453 | |||||
454 | =cut | ||||
455 | |||||
456 | sub interval { | ||||
457 | my IPC::Run::Timer $self = shift; | ||||
458 | if (@_) { | ||||
459 | $self->{INTERVAL} = _parse_time(shift); | ||||
460 | _debug $self->name, ' interval set to ', $self->{INTERVAL} | ||||
461 | if $self->{DEBUG} > 2 || _debugging_details; | ||||
462 | |||||
463 | $self->_calc_end_time if $self->state; | ||||
464 | } | ||||
465 | return $self->{INTERVAL}; | ||||
466 | } | ||||
467 | |||||
468 | =item expire | ||||
469 | |||||
470 | expire $t; | ||||
471 | $t->expire; | ||||
472 | |||||
473 | Sets the state to expired (undef). | ||||
474 | Will throw an exception if one | ||||
475 | is defined and the timer was not already expired. You can expire a | ||||
476 | reset timer without starting it. | ||||
477 | |||||
478 | =cut | ||||
479 | |||||
480 | sub expire { | ||||
481 | my IPC::Run::Timer $self = shift; | ||||
482 | if ( defined $self->state ) { | ||||
483 | _debug $self->name . ' expired' | ||||
484 | if $self->{DEBUG} || _debugging; | ||||
485 | |||||
486 | $self->state(undef); | ||||
487 | croak $self->exception if $self->exception; | ||||
488 | } | ||||
489 | return undef; | ||||
490 | } | ||||
491 | |||||
492 | =item is_running | ||||
493 | |||||
494 | =cut | ||||
495 | |||||
496 | sub is_running { | ||||
497 | my IPC::Run::Timer $self = shift; | ||||
498 | return $self->state ? 1 : 0; | ||||
499 | } | ||||
500 | |||||
501 | =item is_reset | ||||
502 | |||||
503 | =cut | ||||
504 | |||||
505 | sub is_reset { | ||||
506 | my IPC::Run::Timer $self = shift; | ||||
507 | return defined $self->state && $self->state == 0; | ||||
508 | } | ||||
509 | |||||
510 | =item is_expired | ||||
511 | |||||
512 | =cut | ||||
513 | |||||
514 | sub is_expired { | ||||
515 | my IPC::Run::Timer $self = shift; | ||||
516 | return !defined $self->state; | ||||
517 | } | ||||
518 | |||||
519 | =item name | ||||
520 | |||||
521 | Sets/gets this timer's name. The name is only used for debugging | ||||
522 | purposes so you can tell which freakin' timer is doing what. | ||||
523 | |||||
524 | =cut | ||||
525 | |||||
526 | sub name { | ||||
527 | my IPC::Run::Timer $self = shift; | ||||
528 | |||||
529 | $self->{NAME} = shift if @_; | ||||
530 | return | ||||
531 | defined $self->{NAME} ? $self->{NAME} | ||||
532 | : defined $self->{EXCEPTION} ? 'timeout' | ||||
533 | : 'timer'; | ||||
534 | } | ||||
535 | |||||
536 | =item reset | ||||
537 | |||||
538 | reset $t; | ||||
539 | $t->reset; | ||||
540 | |||||
541 | Resets the timer to the non-running, non-expired state and clears | ||||
542 | the end_time(). | ||||
543 | |||||
544 | =cut | ||||
545 | |||||
546 | sub reset { | ||||
547 | my IPC::Run::Timer $self = shift; | ||||
548 | $self->state(0); | ||||
549 | $self->end_time(undef); | ||||
550 | _debug $self->name . ' reset' | ||||
551 | if $self->{DEBUG} || _debugging; | ||||
552 | |||||
553 | return undef; | ||||
554 | } | ||||
555 | |||||
556 | =item start | ||||
557 | |||||
558 | start $t; | ||||
559 | $t->start; | ||||
560 | start $t, $interval; | ||||
561 | start $t, $interval, $now; | ||||
562 | |||||
563 | Starts or restarts a timer. This always sets the start_time. It sets the | ||||
564 | end_time based on the interval if the timer is running or if no end time | ||||
565 | has been set. | ||||
566 | |||||
567 | You may pass an optional interval or current time value. | ||||
568 | |||||
569 | Not passing a defined interval causes the previous interval setting to be | ||||
570 | re-used unless the timer is reset and an end_time has been set | ||||
571 | (an exception is thrown if no interval has been set). | ||||
572 | |||||
573 | Not passing a defined current time value causes the current time to be used. | ||||
574 | |||||
575 | Passing a current time value is useful if you happen to have a time value | ||||
576 | lying around or if you want to make sure that several timers are started | ||||
577 | with the same concept of start time. You might even need to lie to an | ||||
578 | IPC::Run::Timer, occasionally. | ||||
579 | |||||
580 | =cut | ||||
581 | |||||
582 | sub start { | ||||
583 | my IPC::Run::Timer $self = shift; | ||||
584 | |||||
585 | my ( $interval, $now ) = map { _parse_time($_) } @_; | ||||
586 | $now = _parse_time($now); | ||||
587 | $now = time unless defined $now; | ||||
588 | |||||
589 | $self->interval($interval) if defined $interval; | ||||
590 | |||||
591 | ## start()ing a running or expired timer clears the end_time, so that the | ||||
592 | ## interval is used. So does specifying an interval. | ||||
593 | $self->end_time(undef) if !$self->is_reset || $interval; | ||||
594 | |||||
595 | croak "IPC::Run: no timer interval or end_time defined for " . $self->name | ||||
596 | unless defined $self->interval || defined $self->end_time; | ||||
597 | |||||
598 | $self->state(1); | ||||
599 | $self->start_time($now); | ||||
600 | ## The "+ 1" is in case the START_TIME was sampled at the end of a | ||||
601 | ## tick (which are one second long in this module). | ||||
602 | $self->_calc_end_time | ||||
603 | unless defined $self->end_time; | ||||
604 | |||||
605 | _debug( | ||||
606 | $self->name, " started at ", $self->start_time, | ||||
607 | ", with interval ", $self->interval, ", end_time ", $self->end_time | ||||
608 | ) if $self->{DEBUG} || _debugging; | ||||
609 | return undef; | ||||
610 | } | ||||
611 | |||||
612 | =item start_time | ||||
613 | |||||
614 | Sets/gets the start time, in seconds since the epoch. Setting this manually | ||||
615 | is a bad idea, it's better to call L</start>() at the correct time. | ||||
616 | |||||
617 | =cut | ||||
618 | |||||
619 | sub start_time { | ||||
620 | my IPC::Run::Timer $self = shift; | ||||
621 | if (@_) { | ||||
622 | $self->{START_TIME} = _parse_time(shift); | ||||
623 | _debug $self->name, ' start_time set to ', $self->{START_TIME} | ||||
624 | if $self->{DEBUG} > 2 || _debugging; | ||||
625 | } | ||||
626 | |||||
627 | return $self->{START_TIME}; | ||||
628 | } | ||||
629 | |||||
630 | =item state | ||||
631 | |||||
632 | $s = state $t; | ||||
633 | $t->state( $s ); | ||||
634 | |||||
635 | Get/Set the current state. Only use this if you really need to transfer the | ||||
636 | state to/from some variable. | ||||
637 | Use L</expire>, L</start>, L</reset>, L</is_expired>, L</is_running>, | ||||
638 | L</is_reset>. | ||||
639 | |||||
640 | Note: Setting the state to 'undef' to expire a timer will not throw an | ||||
641 | exception. | ||||
642 | |||||
643 | =back | ||||
644 | |||||
645 | =cut | ||||
646 | |||||
647 | sub state { | ||||
648 | my IPC::Run::Timer $self = shift; | ||||
649 | if (@_) { | ||||
650 | $self->{STATE} = shift; | ||||
651 | _debug $self->name, ' state set to ', $self->{STATE} | ||||
652 | if $self->{DEBUG} > 2 || _debugging; | ||||
653 | } | ||||
654 | return $self->{STATE}; | ||||
655 | } | ||||
656 | |||||
657 | 1 | 2µs | 1; | ||
658 | |||||
659 | =pod | ||||
660 | |||||
661 | =head1 TODO | ||||
662 | |||||
663 | use Time::HiRes; if it's present. | ||||
664 | |||||
665 | Add detection and parsing of [[[HH:]MM:]SS formatted times and intervals. | ||||
666 | |||||
667 | =head1 AUTHOR | ||||
668 | |||||
669 | Barrie Slaymaker <barries@slaysys.com> | ||||
670 | |||||
671 | =cut |