File Coverage

blib/lib/IPC/Run/Timer.pm
Criterion Covered Total %
statement 134 140 95.7
branch 53 80 66.2
condition 26 50 52.0
subroutine 28 29 96.5
pod 17 17 100.0
total 258 316 81.6


line stmt bran cond sub pod time 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 = IPC::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 = IPC::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 its 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 uses the fields
152             pragma.
153              
154             =head1 FUNCTIONS & METHODS
155              
156             =over
157              
158             =cut
159              
160 231     231   1309 use strict;
  231         397  
  231         7525  
161 231     231   784 use warnings;
  231         348  
  231         7770  
162 231     231   858 use Carp;
  231         323  
  231         11107  
163 231     231   848 use Fcntl;
  231         457  
  231         36842  
164 231     231   1045 use Symbol;
  231         350  
  231         9398  
165 231     231   855 use Exporter;
  231         358  
  231         8925  
166 231     231   3482 use Scalar::Util ();
  231         2127  
  231         4429  
167 231     231   861 use vars qw( $VERSION @ISA @EXPORT_OK %EXPORT_TAGS );
  231         253  
  231         21958  
168              
169             BEGIN {
170 231     231   655 $VERSION = '20260402.0';
171 231         3101 @ISA = qw( Exporter );
172 231         740 @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 231         5815 %EXPORT_TAGS = ( 'all' => \@EXPORT_OK );
189             }
190              
191             require IPC::Run;
192 231     231   981 use IPC::Run::Debug;
  231         330  
  231         311795  
193              
194             ##
195             ## Some helpers
196             ##
197             my $resolution = 1;
198              
199             sub _parse_time {
200 2654     2654   6560 for ( $_[0] ) {
201 2654         4606 my $val;
202 2654 100       6055 if ( not defined $_ ) {
203 4         8 $val = $_;
204             }
205             else {
206 2650         5048 my $str = $_;
207 2650         11266 my @f = split( /:/, $str, -1 );
208 2650 100       9007 if ( scalar @f > 4 ) {
209 2         406 croak "IPC::Run: expected <= 4 elements in time string '$str'";
210             }
211 2648         5743 for (@f) {
212 2678 100       15986 if ( not Scalar::Util::looks_like_number($_) ) {
213 8         788 croak "IPC::Run: non-numeric element '$_' in time string '$str'";
214             }
215             }
216 2640         9280 my ( $s, $m, $h, $d ) = reverse @f;
217 2640   100     34329 $val = ( ( ( $d || 0 ) * 24 + ( $h || 0 ) ) * 60 + ( $m || 0 ) ) * 60 + ( $s || 0 );
      100        
      100        
      100        
218             }
219 2644         7351 return $val;
220             }
221             }
222              
223             sub _calc_end_time {
224 412     412   674 my IPC::Run::Timer $self = shift;
225 412         1221 my $interval = $self->interval;
226 412 50       1048 $interval += $resolution if $interval;
227 412         868 $self->end_time( $self->start_time + $interval );
228             }
229              
230             =item timer
231              
232             A constructor function (not method) of IPC::Run::Timer instances:
233              
234             $t = timer( 5 );
235             $t = timer( 5, name => 'stall timer', debug => 1 );
236              
237             $t = timer;
238             $t->interval( 5 );
239              
240             run ..., $t;
241             run ..., $t = timer( 5 );
242              
243             This convenience function is a shortened spelling of
244              
245             IPC::Run::Timer->new( ... );
246            
247             . It returns a timer in the reset state with a given interval.
248              
249             If an exception is provided, it will be thrown when the timer notices that
250             it has expired (in check()). The name is for debugging usage, if you plan on
251             having multiple timers around. If no name is provided, a name like "timer #1"
252             will be provided.
253              
254             =cut
255              
256             sub timer {
257 2     2 1 271274 return IPC::Run::Timer->new(@_);
258             }
259              
260             =item timeout
261              
262             A constructor function (not method) of IPC::Run::Timer instances:
263              
264             $t = timeout( 5 );
265             $t = timeout( 5, exception => "kablooey" );
266             $t = timeout( 5, name => "stall", exception => "kablooey" );
267              
268             $t = timeout;
269             $t->interval( 5 );
270              
271             run ..., $t;
272             run ..., $t = timeout( 5 );
273              
274             A This convenience function is a shortened spelling of
275              
276             IPC::Run::Timer->new( exception => "IPC::Run: timeout ...", ... );
277            
278             . It returns a timer in the reset state that will throw an
279             exception when it expires.
280              
281             Takes the same parameters as L, any exception passed in overrides
282             the default exception.
283              
284             =cut
285              
286             sub timeout {
287 400     400 1 6357648 my $t = IPC::Run::Timer->new(@_);
288 400 50       2075 $t->exception( "IPC::Run: timeout on " . $t->name )
289             unless defined $t->exception;
290 400         2956 return $t;
291             }
292              
293             =item new
294              
295             IPC::Run::Timer->new() ;
296             IPC::Run::Timer->new( 5 ) ;
297             IPC::Run::Timer->new( 5, exception => 'kablooey' ) ;
298              
299             Constructor. See L for details.
300              
301             =cut
302              
303             my $timer_counter;
304              
305             sub new {
306 402     402 1 1108 my $class = shift;
307 402   33     4484 $class = ref $class || $class;
308              
309 402         1323 my IPC::Run::Timer $self = bless {}, $class;
310              
311 402         2279 $self->{STATE} = 0;
312 402         1343 $self->{DEBUG} = 0;
313 402         2281 $self->{NAME} = "timer #" . ++$timer_counter;
314              
315 402         2131 while (@_) {
316 402         1303 my $arg = shift;
317 402 50       3670 if ( $arg eq 'exception' ) {
    50          
    50          
318 0         0 $self->exception(shift);
319             }
320             elsif ( $arg eq 'name' ) {
321 0         0 $self->name(shift);
322             }
323             elsif ( $arg eq 'debug' ) {
324 0         0 $self->debug(shift);
325             }
326             else {
327 402         2561 $self->interval($arg);
328             }
329             }
330              
331             _debug $self->name . ' constructed'
332 402 50 33     9499 if $self->{DEBUG} || _debugging_details;
333              
334 402         1072 return $self;
335             }
336              
337             =item check
338              
339             check $t;
340             check $t, $now;
341             $t->check;
342              
343             Checks to see if a timer has expired since the last check. Has no effect
344             on non-running timers. This will throw an exception if one is defined.
345              
346             IPC::Run::pump() calls this routine for any timers in the harness.
347              
348             You may pass in a version of now, which is useful in case you have
349             it lying around or you want to check several timers with a consistent
350             concept of the current time.
351              
352             Returns the time left before end_time or 0 if end_time is no longer
353             in the future or the timer is not running
354             (unless, of course, check() expire()s the timer and this
355             results in an exception being thrown).
356              
357             Returns undef if the timer is not running on entry, 0 if check() expires it,
358             and the time left if it's left running.
359              
360             =cut
361              
362             sub check {
363 1788     1788 1 3015 my IPC::Run::Timer $self = shift;
364 1788 100       3916 return undef if !$self->is_running;
365 1782 50       4636 return 0 if $self->is_expired;
366              
367 1782         4167 my ($now) = @_;
368 1782         7223 $now = _parse_time($now);
369 1782 50       4415 $now = time unless defined $now;
370              
371 1782 50 33     52080 _debug( "checking ", $self->name, " (end time ", $self->end_time, ") at ", $now ) if $self->{DEBUG} || _debugging_details;
372              
373 1782         10979 my $left = $self->end_time - $now;
374 1782 100       6216 return $left if $left > 0;
375              
376 16         73 $self->expire;
377 6         36 return 0;
378             }
379              
380             =item debug
381              
382             Sets/gets the current setting of the debugging flag for this timer. This
383             has no effect if debugging is not enabled for the current harness.
384              
385             =cut
386              
387             sub debug {
388 0     0 1 0 my IPC::Run::Timer $self = shift;
389 0 0       0 $self->{DEBUG} = shift if @_;
390 0         0 return $self->{DEBUG};
391             }
392              
393             =item end_time
394              
395             $et = $t->end_time;
396             $et = end_time $t;
397              
398             $t->end_time( time + 10 );
399              
400             Returns the time when this timer will or did expire. Even if this time is
401             in the past, the timer may not be expired, since check() may not have been
402             called yet.
403              
404             Note that this end_time is not start_time($t) + interval($t), since some
405             small extra amount of time is added to make sure that the timer does not
406             expire before interval() elapses. If this were not so, then
407              
408             Changing end_time() while a timer is running will set the expiration time.
409             Changing it while it is expired has no affect, since reset()ing a timer always
410             clears the end_time().
411              
412             =cut
413              
414             sub end_time {
415 2620     2620 1 4804 my IPC::Run::Timer $self = shift;
416 2620 100       6571 if (@_) {
417 426         1047 $self->{END_TIME} = shift;
418             _debug $self->name, ' end_time set to ', $self->{END_TIME}
419 426 50 33     8248 if $self->{DEBUG} > 2 || _debugging_details;
420             }
421 2620         7866 return $self->{END_TIME};
422             }
423              
424             =item exception
425              
426             $x = $t->exception;
427             $t->exception( $x );
428             $t->exception( undef );
429              
430             Sets/gets the exception to throw, if any. 'undef' means that no
431             exception will be thrown. Exception does not need to be a scalar: you
432             may ask that references be thrown.
433              
434             =cut
435              
436             sub exception {
437 826     826 1 1190 my IPC::Run::Timer $self = shift;
438 826 100       1915 if (@_) {
439 400         1505 $self->{EXCEPTION} = shift;
440             _debug $self->name, ' exception set to ', $self->{EXCEPTION}
441 400 50 33     7155 if $self->{DEBUG} || _debugging_details;
442             }
443 826         7435 return $self->{EXCEPTION};
444             }
445              
446             =item interval
447              
448             $i = interval $t;
449             $i = $t->interval;
450             $t->interval( $i );
451              
452             Sets the interval. Sets the end time based on the start_time() and the
453             interval (and a little fudge) if the timer is running.
454              
455             =cut
456              
457             sub interval {
458 1300     1300 1 10026 my IPC::Run::Timer $self = shift;
459 1300 100       2678 if (@_) {
460 444         2821 $self->{INTERVAL} = _parse_time(shift);
461             _debug $self->name, ' interval set to ', $self->{INTERVAL}
462 434 50 33     12784 if $self->{DEBUG} > 2 || _debugging_details;
463              
464 434 50       1602 $self->_calc_end_time if $self->state;
465             }
466 1290         3243 return $self->{INTERVAL};
467             }
468              
469             =item expire
470              
471             expire $t;
472             $t->expire;
473              
474             Sets the state to expired (undef).
475             Will throw an exception if one
476             is defined and the timer was not already expired. You can expire a
477             reset timer without starting it.
478              
479             =cut
480              
481             sub expire {
482 16     16 1 32 my IPC::Run::Timer $self = shift;
483 16 50       44 if ( defined $self->state ) {
484             _debug $self->name . ' expired'
485 16 50 33     474 if $self->{DEBUG} || _debugging;
486              
487 16         51 $self->state(undef);
488 16 100       85 croak $self->exception if $self->exception;
489             }
490 6         14 return undef;
491             }
492              
493             =item is_running
494              
495             =cut
496              
497             sub is_running {
498 3601     3601 1 6498 my IPC::Run::Timer $self = shift;
499 3601 100       11251 return $self->state ? 1 : 0;
500             }
501              
502             =item is_reset
503              
504             =cut
505              
506             sub is_reset {
507 461     461 1 4328 my IPC::Run::Timer $self = shift;
508 461   100     1178 return defined $self->state && $self->state == 0;
509             }
510              
511             =item is_expired
512              
513             =cut
514              
515             sub is_expired {
516 1831     1831 1 3604 my IPC::Run::Timer $self = shift;
517 1831         4941 return !defined $self->state;
518             }
519              
520             =item name
521              
522             Sets/gets this timer's name. The name is only used for debugging
523             purposes so you can tell which freakin' timer is doing what.
524              
525             =cut
526              
527             sub name {
528 400     400 1 640 my IPC::Run::Timer $self = shift;
529              
530 400 50       916 $self->{NAME} = shift if @_;
531             return
532             defined $self->{NAME} ? $self->{NAME}
533 400 0       2433 : defined $self->{EXCEPTION} ? 'timeout'
    50          
534             : 'timer';
535             }
536              
537             =item reset
538              
539             reset $t;
540             $t->reset;
541              
542             Resets the timer to the non-running, non-expired state and clears
543             the end_time().
544              
545             =cut
546              
547             sub reset {
548 2     2 1 486 my IPC::Run::Timer $self = shift;
549 2         6 $self->state(0);
550 2         8 $self->end_time(undef);
551             _debug $self->name . ' reset'
552 2 50 33     28 if $self->{DEBUG} || _debugging;
553              
554 2         4 return undef;
555             }
556              
557             =item start
558              
559             start $t;
560             $t->start;
561             start $t, $interval;
562             start $t, $interval, $now;
563              
564             Starts or restarts a timer. This always sets the start_time. It sets the
565             end_time based on the interval if the timer is running or if no end time
566             has been set.
567              
568             You may pass an optional interval or current time value.
569              
570             Not passing a defined interval causes the previous interval setting to be
571             re-used unless the timer is reset and an end_time has been set
572             (an exception is thrown if no interval has been set).
573              
574             Not passing a defined current time value causes the current time to be used.
575              
576             Passing a current time value is useful if you happen to have a time value
577             lying around or if you want to make sure that several timers are started
578             with the same concept of start time. You might even need to lie to an
579             IPC::Run::Timer, occasionally.
580              
581             =cut
582              
583             sub start {
584 412     412 1 695 my IPC::Run::Timer $self = shift;
585              
586 412         1146 my ( $interval, $now ) = map { _parse_time($_) } @_;
  16         32  
587 412 100       1121 $now = time unless defined $now;
588              
589 412 100       1059 $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 412 100 100     1687 $self->end_time(undef) if !$self->is_reset || $interval;
594              
595 412 50 33     1754 croak "IPC::Run: no timer interval or end_time defined for " . $self->name
596             unless defined $self->interval || defined $self->end_time;
597              
598 412         1813 $self->state(1);
599 412         1908 $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 412 50       1432 $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 412 50 33     7732 ) if $self->{DEBUG} || _debugging;
609 412         1128 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() at the correct time.
616              
617             =cut
618              
619             sub start_time {
620 824     824 1 1004 my IPC::Run::Timer $self = shift;
621 824 100       1796 if (@_) {
622 412         1057 $self->{START_TIME} = _parse_time(shift);
623             _debug $self->name, ' start_time set to ', $self->{START_TIME}
624 412 50 33     9117 if $self->{DEBUG} > 2 || _debugging;
625             }
626              
627 824         1593 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, L, L, L, L,
638             L.
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 7207     7207 1 10204 my IPC::Run::Timer $self = shift;
649 7207 100       13625 if (@_) {
650 430         774 $self->{STATE} = shift;
651             _debug $self->name, ' state set to ', $self->{STATE}
652 430 50 33     8682 if $self->{DEBUG} > 2 || _debugging;
653             }
654 7207         28659 return $self->{STATE};
655             }
656              
657             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
670              
671             =cut