1b8851fccSafresh1use strict; 2b8851fccSafresh1 3014083a1Safresh1use Test::More tests => 10; 4*9f11ffb7Safresh1BEGIN { push @INC, '.' } 5b8851fccSafresh1use t::Watchdog; 6b8851fccSafresh1 7b8851fccSafresh1BEGIN { require_ok "Time::HiRes"; } 8b8851fccSafresh1 9b8851fccSafresh1use Config; 10b8851fccSafresh1 11b8851fccSafresh1my $limit = 0.25; # 25% is acceptable slosh for testing timers 12b8851fccSafresh1 13b8851fccSafresh1my $xdefine = ''; 14*9f11ffb7Safresh1if (open(XDEFINE, "<", "xdefine")) { 15b8851fccSafresh1 chomp($xdefine = <XDEFINE> || ""); 16b8851fccSafresh1 close(XDEFINE); 17b8851fccSafresh1} 18b8851fccSafresh1 19b8851fccSafresh1my $can_subsecond_alarm = 20b8851fccSafresh1 defined &Time::HiRes::gettimeofday && 21b8851fccSafresh1 defined &Time::HiRes::ualarm && 22b8851fccSafresh1 defined &Time::HiRes::usleep && 23b8851fccSafresh1 ($Config{d_ualarm} || $xdefine =~ /-DHAS_UALARM/); 24b8851fccSafresh1 25b8851fccSafresh1SKIP: { 26b8851fccSafresh1 skip "no subsecond alarm", 1 unless $can_subsecond_alarm; 27b8851fccSafresh1 eval { require POSIX }; 28b8851fccSafresh1 my $use_sigaction = 29b8851fccSafresh1 !$@ && defined &POSIX::sigaction && &POSIX::SIGALRM > 0; 30b8851fccSafresh1 31b8851fccSafresh1 my ($r, $i, $not, $ok); 32b8851fccSafresh1 33014083a1Safresh1 $not = ""; 34014083a1Safresh1 35b8851fccSafresh1 $r = [Time::HiRes::gettimeofday()]; 36b8851fccSafresh1 $i = 5; 37b8851fccSafresh1 my $oldaction; 38b8851fccSafresh1 if ($use_sigaction) { 39b8851fccSafresh1 $oldaction = new POSIX::SigAction; 40014083a1Safresh1 printf("# sigaction tick, ALRM = %d\n", &POSIX::SIGALRM); 41b8851fccSafresh1 42b8851fccSafresh1 # Perl's deferred signals may be too wimpy to break through 43b8851fccSafresh1 # a restartable select(), so use POSIX::sigaction if available. 44b8851fccSafresh1 45*9f11ffb7Safresh1 # In perl 5.6.2 you will get a likely bogus warning of 46*9f11ffb7Safresh1 # "Use of uninitialized value in subroutine entry" from 47*9f11ffb7Safresh1 # the following line. 48b8851fccSafresh1 POSIX::sigaction(&POSIX::SIGALRM, 49b8851fccSafresh1 POSIX::SigAction->new("tick"), 50b8851fccSafresh1 $oldaction) 51b8851fccSafresh1 or die "Error setting SIGALRM handler with sigaction: $!\n"; 52b8851fccSafresh1 } else { 53014083a1Safresh1 print("# SIG tick\n"); 54b8851fccSafresh1 $SIG{ALRM} = "tick"; 55b8851fccSafresh1 } 56b8851fccSafresh1 57b8851fccSafresh1 # On VMS timers can not interrupt select. 58b8851fccSafresh1 if ($^O eq 'VMS') { 59b8851fccSafresh1 $ok = "Skip: VMS select() does not get interrupted."; 60b8851fccSafresh1 } else { 61b8851fccSafresh1 while ($i > 0) { 62b8851fccSafresh1 Time::HiRes::alarm(0.3); 63b8851fccSafresh1 select (undef, undef, undef, 3); 64b8851fccSafresh1 my $ival = Time::HiRes::tv_interval ($r); 65014083a1Safresh1 print("# Select returned! $i $ival\n"); 66014083a1Safresh1 printf("# %s\n", abs($ival/3 - 1)); 67b8851fccSafresh1 # Whether select() gets restarted after signals is 68b8851fccSafresh1 # implementation dependent. If it is restarted, we 69b8851fccSafresh1 # will get about 3.3 seconds: 3 from the select, 0.3 70b8851fccSafresh1 # from the alarm. If this happens, let's just skip 71b8851fccSafresh1 # this particular test. --jhi 72b8851fccSafresh1 if (abs($ival/3.3 - 1) < $limit) { 73b8851fccSafresh1 $ok = "Skip: your select() may get restarted by your SIGALRM (or just retry test)"; 74b8851fccSafresh1 undef $not; 75b8851fccSafresh1 last; 76b8851fccSafresh1 } 77b8851fccSafresh1 my $exp = 0.3 * (5 - $i); 78b8851fccSafresh1 if ($exp == 0) { 79b8851fccSafresh1 $not = "while: divisor became zero"; 80b8851fccSafresh1 last; 81b8851fccSafresh1 } 82b8851fccSafresh1 # This test is more sensitive, so impose a softer limit. 83b8851fccSafresh1 if (abs($ival/$exp - 1) > 4*$limit) { 84b8851fccSafresh1 my $ratio = abs($ival/$exp); 85b8851fccSafresh1 $not = "while: $exp sleep took $ival ratio $ratio"; 86b8851fccSafresh1 last; 87b8851fccSafresh1 } 88b8851fccSafresh1 $ok = $i; 89b8851fccSafresh1 } 90b8851fccSafresh1 } 91b8851fccSafresh1 92b8851fccSafresh1 sub tick { 93b8851fccSafresh1 $i--; 94b8851fccSafresh1 my $ival = Time::HiRes::tv_interval ($r); 95014083a1Safresh1 print("# Tick! $i $ival\n"); 96b8851fccSafresh1 my $exp = 0.3 * (5 - $i); 97b8851fccSafresh1 if ($exp == 0) { 98b8851fccSafresh1 $not = "tick: divisor became zero"; 99b8851fccSafresh1 last; 100b8851fccSafresh1 } 101b8851fccSafresh1 # This test is more sensitive, so impose a softer limit. 102b8851fccSafresh1 if (abs($ival/$exp - 1) > 4*$limit) { 103b8851fccSafresh1 my $ratio = abs($ival/$exp); 104b8851fccSafresh1 $not = "tick: $exp sleep took $ival ratio $ratio"; 105b8851fccSafresh1 $i = 0; 106b8851fccSafresh1 } 107b8851fccSafresh1 } 108b8851fccSafresh1 109b8851fccSafresh1 if ($use_sigaction) { 110b8851fccSafresh1 POSIX::sigaction(&POSIX::SIGALRM, $oldaction); 111b8851fccSafresh1 } else { 112b8851fccSafresh1 Time::HiRes::alarm(0); # can't cancel usig %SIG 113b8851fccSafresh1 } 114b8851fccSafresh1 115014083a1Safresh1 print("# $not\n"); 116b8851fccSafresh1 ok !$not; 117b8851fccSafresh1} 118b8851fccSafresh1 119b8851fccSafresh1SKIP: { 120b8851fccSafresh1 skip "no ualarm", 1 unless &Time::HiRes::d_ualarm; 121b8851fccSafresh1 eval { Time::HiRes::alarm(-3) }; 122b8851fccSafresh1 like $@, qr/::alarm\(-3, 0\): negative time not invented yet/, 123b8851fccSafresh1 "negative time error"; 124b8851fccSafresh1} 125b8851fccSafresh1 126b8851fccSafresh1# Find the loop size N (a for() loop 0..N-1) 127b8851fccSafresh1# that will take more than T seconds. 128b8851fccSafresh1 129b8851fccSafresh1SKIP: { 130b8851fccSafresh1 skip "no ualarm", 1 unless &Time::HiRes::d_ualarm; 131b8851fccSafresh1 skip "perl bug", 1 unless $] >= 5.008001; 132b8851fccSafresh1 # http://groups.google.com/group/perl.perl5.porters/browse_thread/thread/adaffaaf939b042e/20dafc298df737f0%2320dafc298df737f0?sa=X&oi=groupsr&start=0&num=3 133b8851fccSafresh1 # Perl changes [18765] and [18770], perl bug [perl #20920] 134b8851fccSafresh1 135014083a1Safresh1 print("# Finding delay loop...\n"); 136b8851fccSafresh1 137b8851fccSafresh1 my $T = 0.01; 138b8851fccSafresh1 my $DelayN = 1024; 139b8851fccSafresh1 my $i; 140b8851fccSafresh1 N: { 141b8851fccSafresh1 do { 142b8851fccSafresh1 my $t0 = Time::HiRes::time(); 143b8851fccSafresh1 for ($i = 0; $i < $DelayN; $i++) { } 144b8851fccSafresh1 my $t1 = Time::HiRes::time(); 145b8851fccSafresh1 my $dt = $t1 - $t0; 146014083a1Safresh1 print("# N = $DelayN, t1 = $t1, t0 = $t0, dt = $dt\n"); 147b8851fccSafresh1 last N if $dt > $T; 148b8851fccSafresh1 $DelayN *= 2; 149b8851fccSafresh1 } while (1); 150b8851fccSafresh1 } 151b8851fccSafresh1 152b8851fccSafresh1 # The time-burner which takes at least T (default 1) seconds. 153b8851fccSafresh1 my $Delay = sub { 154b8851fccSafresh1 my $c = @_ ? shift : 1; 155b8851fccSafresh1 my $n = $c * $DelayN; 156b8851fccSafresh1 my $i; 157b8851fccSafresh1 for ($i = 0; $i < $n; $i++) { } 158b8851fccSafresh1 }; 159b8851fccSafresh1 160b8851fccSafresh1 # Next setup a periodic timer (the two-argument alarm() of 161b8851fccSafresh1 # Time::HiRes, behind the curtains the libc getitimer() or 162b8851fccSafresh1 # ualarm()) which has a signal handler that takes so much time (on 163b8851fccSafresh1 # the first initial invocation) that the first periodic invocation 164b8851fccSafresh1 # (second invocation) will happen before the first invocation has 165b8851fccSafresh1 # finished. In Perl 5.8.0 the "safe signals" concept was 166b8851fccSafresh1 # implemented, with unfortunately at least one bug that caused a 167b8851fccSafresh1 # core dump on reentering the handler. This bug was fixed by the 168b8851fccSafresh1 # time of Perl 5.8.1. 169b8851fccSafresh1 170b8851fccSafresh1 # Do not try mixing sleep() and alarm() for testing this. 171b8851fccSafresh1 172b8851fccSafresh1 my $a = 0; # Number of alarms we receive. 173b8851fccSafresh1 my $A = 2; # Number of alarms we will handle before disarming. 174b8851fccSafresh1 # (We may well get $A + 1 alarms.) 175b8851fccSafresh1 176b8851fccSafresh1 $SIG{ALRM} = sub { 177b8851fccSafresh1 $a++; 178014083a1Safresh1 printf("# Alarm $a - %s\n", Time::HiRes::time()); 179b8851fccSafresh1 Time::HiRes::alarm(0) if $a >= $A; # Disarm the alarm. 180b8851fccSafresh1 $Delay->(2); # Try burning CPU at least for 2T seconds. 181b8851fccSafresh1 }; 182b8851fccSafresh1 183b8851fccSafresh1 Time::HiRes::alarm($T, $T); # Arm the alarm. 184b8851fccSafresh1 185b8851fccSafresh1 $Delay->(10); # Try burning CPU at least for 10T seconds. 186b8851fccSafresh1 187b8851fccSafresh1 ok 1; # Not core dumping by now is considered to be the success. 188b8851fccSafresh1} 189b8851fccSafresh1 190b8851fccSafresh1SKIP: { 191b8851fccSafresh1 skip "no subsecond alarm", 6 unless $can_subsecond_alarm; 192b8851fccSafresh1 { 193b8851fccSafresh1 my $alrm; 194b8851fccSafresh1 $SIG{ALRM} = sub { $alrm++ }; 195b8851fccSafresh1 Time::HiRes::alarm(0.1); 196b8851fccSafresh1 my $t0 = Time::HiRes::time(); 197b8851fccSafresh1 1 while Time::HiRes::time() - $t0 <= 1; 198b8851fccSafresh1 ok $alrm; 199b8851fccSafresh1 } 200b8851fccSafresh1 { 201b8851fccSafresh1 my $alrm; 202b8851fccSafresh1 $SIG{ALRM} = sub { $alrm++ }; 203b8851fccSafresh1 Time::HiRes::alarm(1.1); 204b8851fccSafresh1 my $t0 = Time::HiRes::time(); 205b8851fccSafresh1 1 while Time::HiRes::time() - $t0 <= 2; 206b8851fccSafresh1 ok $alrm; 207b8851fccSafresh1 } 208b8851fccSafresh1 209b8851fccSafresh1 { 210b8851fccSafresh1 my $alrm = 0; 211b8851fccSafresh1 $SIG{ALRM} = sub { $alrm++ }; 212b8851fccSafresh1 my $got = Time::HiRes::alarm(2.7); 213014083a1Safresh1 ok $got == 0 or print("# $got\n"); 214b8851fccSafresh1 215b8851fccSafresh1 my $t0 = Time::HiRes::time(); 216b8851fccSafresh1 1 while Time::HiRes::time() - $t0 <= 1; 217b8851fccSafresh1 218b8851fccSafresh1 $got = Time::HiRes::alarm(0); 219014083a1Safresh1 ok $got > 0 && $got < 1.8 or print("# $got\n"); 220b8851fccSafresh1 221014083a1Safresh1 ok $alrm == 0 or print("# $alrm\n"); 222b8851fccSafresh1 223b8851fccSafresh1 $got = Time::HiRes::alarm(0); 224014083a1Safresh1 ok $got == 0 or print("# $got\n"); 225b8851fccSafresh1 } 226b8851fccSafresh1} 227b8851fccSafresh1 228b8851fccSafresh11; 229