Subject: | race condition with signals |
There's a race condition in the page locking code. If the page locking
is interrupted in just the right spot, it will render the cache unusable
-- all subsequent accesses will die with "page X is already locked,
can't lock multiple pages..." errors.
Here is a test which reproduces the issue on my system; you might have
to adjust the alarm length a bit to make the error reproduce elsewhere:
#!/usr/bin/env perl
use strict;
use warnings;
use Cache::FastMmap ();
use File::Temp ();
use Time::HiRes ();
use List::Util ();
use Test::More qw(no_plan);
my $share_file = File::Temp::tmpnam();
my $c = new Cache::FastMmap(
init_file => 1,
share_file => $share_file,
page_size => 65536,
num_pages => 89,
raw_values => 1,
compress => 0,
enable_stats => 0,
expire_time => 0,
unlink_on_exit => 0,
empty_on_exit => 0,
);
my @keys = qw(foo bar baz barney poot boot zoot toot);
for my $key (@keys) {
$c->set($key => 'the quick brown fox');
}
## this is an inherently unreliable test. In particular, the rand(3)
below is specific to the
## speed of the machine where I developed the test. I don't have a solution.
eval {
for (1 .. 100) {
eval {
local $SIG{ALRM} = sub { die 'alarm' };
while (1) {
Time::HiRes::alarm(0.000001 * int(rand(3)));
$c->get((List::Util::shuffle(@keys))[0]);
Time::HiRes::alarm(0);
}
};
ok($@ =~ m/^alarm /, "exited the loop by alarm...");
## if the race condition occurs, this will die() because it thinks
## it's still holding a lock. If the race does not occur, this will
## not die.
$c->get('foo');
}
};
ok($@ eq '', "edge cache remains in a usable state") or diag($@);