xref: /openbsd-src/gnu/usr.bin/perl/dist/Time-HiRes/t/alarm.t (revision 56d68f1e19ff848c889ecfa71d3a06340ff64892)
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