http://qs1969.pair.com?node_id=74429

   1: =head1 NAME
   2: 
   3: TimeLimit - limit execution time
   4: 
   5: =head1 SYNOPSIS
   6: 
   7:   use TimeLimit
   8:   time_limit {
   9:       # Some code which might take a long time
  10:   } or warn "Timeout";
  11: 
  12: =head1 DESCRIPTION
  13: 
  14: Provides time_limit for adding easy to use timeouts to your programs.
  15: 
  16: =head1 FUNCTIONS
  17: 
  18: =cut
  19: 
  20: ############################################################
  21: 
  22: package TimeLimit;
  23: 
  24: use strict;
  25: use Carp;
  26: use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  27: 
  28: require Exporter;
  29: 
  30: @ISA = qw(Exporter);
  31: @EXPORT_OK = ( );
  32: @EXPORT = qw(
  33:     time_limit
  34: );
  35: $VERSION = '1.00';
  36: 
  37: ############################################################
  38: 
  39: =head2 time_limit
  40: 
  41: Time out the subroutine or subroutine block using alarm, being careful
  42: to save and restore the alarm properly.
  43: 
  44: You may nest this function inside other time_limits or alarm calls.
  45: Inside a nested time_limit the outer time_limit is turned off for the
  46: duration of the inner timeout, and restored when the inner time_limit
  47: exits.
  48: 
  49: Input:
  50: 
  51:     { subroutine block }
  52:     timeout - optional defaults to 30 seconds
  53: 
  54: Output:
  55: 
  56:     $ok  - returns 1 for OK or undef for false
  57: 
  58: Use like this
  59: 
  60:     time_limit
  61:     {
  62:         long_function();
  63:         more_stuff();
  64:     } 60
  65:     or die "Stuff timedout after 60 seconds";
  66: 
  67: Or this where the 'or do { }' block acts like an 'else' block.
  68: 
  69:     time_limit
  70:     {
  71:         long_function(1);
  72:         long_function(2);
  73:     } or do
  74:     {
  75:         something_on_timeout();
  76:     };
  77: 
  78: =cut
  79: 
  80: ############################################################
  81: 
  82: my $time_limit_invocation = 0;
  83: 
  84: sub time_limit (&;$)
  85: {
  86:     my ($sub, $timeout) = @_;
  87:     my $die_text = "time_limit: " . $time_limit_invocation++ . "\n";
  88:     $timeout ||= 30;
  89:     my $old_alarm = alarm(0);        # turn alarm off and read old value
  90:     {
  91:         local $SIG{ALRM} = 'IGNORE'; # ignore alarms in this scope
  92: 
  93:         eval
  94:         {
  95:             local $SIG{__DIE__};     # turn die handler off in eval block
  96:             local $SIG{ALRM} = sub { die $die_text };
  97:             alarm($timeout);         # set alarm
  98:             $sub->();                # do the user's code
  99:         };
 100: 
 101:         # Note the alarm is still active here - however we assume that
 102:         # if we got here without an alarm the user's code succeeded -
 103:         # hence the IGNOREing of alarms in this scope
 104: 
 105:         alarm 0;                     # kill off alarm
 106:     }
 107: 
 108:     alarm $old_alarm;                # restore alarm
 109: 
 110:     if ($@)
 111:     {
 112:         # the eval returned an error
 113:         return 0 if $@ eq $die_text; # show we timed out ($@ is set)
 114:         die $@;                      # propagate error
 115:     }
 116: 
 117:     return 1;                        # all ok
 118: }
 119:     
 120: ############################################################
 121: 
 122: =head1 EXPORT
 123: 
 124:     time_limit
 125: 
 126: =head1 AUTHOR
 127: 
 128: Nick Craig-Wood
 129: 
 130: =head1 CHANGES
 131: 
 132: =head2 2001-04-21 ncw
 133: 
 134: Release 1.00
 135: 
 136: Created
 137: 
 138: =head1 SEE ALSO
 139: 
 140: perl(1).
 141: 
 142: =cut
 143: 
 144: ############################################################
 145: 1;
 146: __END__