home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / tsw / TSW_3.4.0.exe / Apache2 / perl / Timer.pm < prev    next >
Encoding:
Perl POD Document  |  2003-09-26  |  17.0 KB  |  711 lines

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