Skip to content

Commit f86e19f

Browse files
committed
Benchmark.t - sanity check for a broken times()
If time() or times() is broken then Benchmark can infinite loop. This adds a sanity check that will die early if it appears that either are broken. This fixes the infinite loop part of GH Issue Perl#20839
1 parent b625025 commit f86e19f

File tree

1 file changed

+83
-0
lines changed

1 file changed

+83
-0
lines changed

lib/Benchmark.t

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,89 @@ sub cmp_delta {
4040
return $max/$min <= (1+$delta);
4141
}
4242

43+
sub splatter {
44+
my ($message) = @_;
45+
my $splatter = <<~'EOF_SPLATTER';
46+
Please file a ticket to report this. Our bug tracker can be found at
47+
48+
https://github.com/Perl/perl5/issues
49+
50+
Make sure you include the full output of perl -V, also uname -a,
51+
and the version details for the C compiler you are using are
52+
very helpful.
53+
54+
Please also try compiling and running the C program that can
55+
be found at
56+
57+
https://github.com/Perl/perl5/issues/20839#issuecomment-1439286875
58+
59+
and provide the results (or compile errors) as part of your
60+
bug report.
61+
62+
EOF_SPLATTER
63+
64+
if ( $message =~ s/\.\.\.//) {
65+
$splatter =~ s/Please/please/;
66+
}
67+
die $message, $splatter;
68+
}
69+
70+
{
71+
# Benchmark may end up "looping forever" if time() or times() are
72+
# broken such that they do not return different values over time.
73+
# The following crude test is intended to ensure that we can rely
74+
# on them and be confident that we won't infinite loop in the
75+
# following tests.
76+
#
77+
# You can simulate a broken time or times() function by setting
78+
# the appropriate env var to a true value:
79+
#
80+
# time() -> SIMULATE_BROKEN_TIME_FUNCTION
81+
# times() -> SIMULATE_BROKEN_TIMES_FUNCTION
82+
#
83+
# If you have a very fast box you may need to set the FAST_CPU env
84+
# var to a number larger than 1 to require these tests to perform
85+
# more iterations to see the time actually tick over. (You could
86+
# also set it to a value between 0 and 1 to speed this up, but I
87+
# don't see why you would...)
88+
#
89+
# See https://github.com/Perl/perl5/issues/20839 for the ticket
90+
# that motivated this test. - Yves
91+
92+
my @times0;
93+
for ( 1 .. 3 ) {
94+
my $end_time = time + 1;
95+
my $count = 0;
96+
my $scale = $ENV{FAST_CPU} || 1;
97+
my $count_threshold = 20_000;
98+
while ( $ENV{SIMULATE_BROKEN_TIME_FUNCTION} || time < $end_time ) {
99+
my $x = 0.0;
100+
for ( 1 .. 10_000 ) {
101+
$x += sqrt(time);
102+
}
103+
if (++$count > $count_threshold * $scale) {
104+
last;
105+
}
106+
}
107+
cmp_ok($count,"<",$count_threshold * $scale,
108+
"expecting \$count < ($count_threshold * $scale)")
109+
or splatter(<<~'EOF_SPLATTER');
110+
Either this system is extremely fast, or the time() function
111+
is broken.
112+
113+
If you think this system is extremely fast you may scale up the
114+
number of iterations allowed by this test by setting FAST_CPU=N
115+
in the environment. Higher N will allow more ops-per-second
116+
before we decide time() is broken.
117+
118+
If setting a higher FAST_CPU value does not fix this problem then ...
119+
EOF_SPLATTER
120+
push @times0, $ENV{SIMULATE_BROKEN_TIMES_FUNCTION} ? 0 : (times)[0];
121+
}
122+
isnt("@times0", "0 0 0", "Make sure times() does not always return 0.")
123+
or splatter("It appears you have a broken a times() function.\n\n");
124+
}
125+
43126
my $t0 = new Benchmark;
44127
isa_ok ($t0, 'Benchmark', "Ensure we can create a benchmark object");
45128

0 commit comments

Comments
 (0)