CHI-Driver-SharedMem
view release on metacpan or search on metacpan
lib/CHI/Driver/SharedMem.pm view on Meta::CPAN
my $h = $self->_data();
$self->_unlock();
return(keys(%{$h->{$self->namespace()}}));
}
=head2 get_namespaces
Gets a list of the namespaces in the cache
=cut
sub get_namespaces {
my $self = shift;
$self->_lock(type => 'read');
my $rc = $self->_data();
$self->_unlock();
# Needs to be sorted for RT89892
my @rc = sort keys(%{$rc});
return @rc;
}
=head2 default_discard_policy
Use an LRU algorithm to discard items when the cache can't add anything
=cut
sub default_discard_policy { 'lru' }
=head2 discard_policy_lru
When the Shared memory area is getting close to full, discard the least recently used objects
=cut
sub discard_policy_lru {
my $self = shift;
if($ENV{'AUTHOR_TESTING'} && $self->{'is_size_aware'} && (my $timeout = $self->discard_timeout())) {
# Workaround for test_discard_timeout
# my $sleep_time = $timeout + 1;
# open(my $tulip, '>>', '/tmp/tulip');
# print $tulip "sleeping $sleep_time\n";
# close $tulip;
# sleep($sleep_time);
sleep(1);
}
$self->_lock(type => 'read');
my $last_used_time = $self->_data()->{CHI_Meta_Namespace()}->{last_used_time};
$self->_unlock();
my @keys_in_lru_order =
sort { $last_used_time->{$a} <=> $last_used_time->{$b} } $self->get_keys();
return sub {
shift(@keys_in_lru_order);
};
}
# Internal routines
# The area must be locked by the caller
sub _build_shm {
my $self = shift;
my $shm_size = $self->shm_size();
if((!defined($shm_size)) || ($shm_size == 0)) {
# Probably some strange condition in cleanup
# croak 'Size == 0';
return;
}
my $shm = IPC::SharedMem->new($self->shm_key(), $shm_size, S_IRUSR|S_IWUSR);
unless($shm) {
$shm = IPC::SharedMem->new($self->shm_key(), $shm_size, S_IRUSR|S_IWUSR|IPC_CREAT);
unless($shm) {
croak "Couldn't create a shared memory area of $shm_size bytes with key ",
$self->shm_key(), ": $!";
return;
}
$shm->write(pack('I', 0), 0, $Config{intsize});
}
$shm->attach();
return $shm;
}
sub _build_lock {
my $self = shift;
# open(my $fd, '<', $0) || croak("$0: $!");
# FIXME: make it unique for each object, not a singleton
$self->lock_file('/tmp/' . __PACKAGE__);
# open(my $tulip, '>>', '/tmp/tulip');
# print $tulip "build_lock\n", $self->lock_file(), "\n";
open(my $fd, '>', $self->lock_file()) || croak($self->lock_file(), ": $!");
# close $tulip;
return $fd;
}
sub _lock {
my ($self, %params) = @_;
# open(my $tulip, '>>', '/tmp/tulip');
# print $tulip $params{'type'}, ' lock ', $self->lock_file(), "\n";
# my $i = 0;
# while((my @call_details = (caller($i++)))) {
# print $tulip "\t", $call_details[1], ':', $call_details[2], ' in function ', $call_details[3], "\n";
# }
return unless $self->lock_file();
if(my $lock = $self->lock_fd()) {
# print $tulip "locking\n";
flock($lock, ($params{type} eq 'read') ? Fcntl::LOCK_SH : Fcntl::LOCK_EX);
} else {
# print $tulip 'lost lock ', $self->lock_file(), "\n";
croak('Lost lock: ', $self->lock_file());
}
# print $tulip "locked\n";
# close $tulip;
}
sub _unlock {
my $self = shift;
# open(my $tulip, '>>', '/tmp/tulip');
# print $tulip 'unlock ', $self->lock_file(), "\n";
# my $i = 0;
# while((my @call_details = (caller($i++)))) {
# print $tulip "\t", $call_details[1], ':', $call_details[2], ' in function ', $call_details[3], "\n";
# }
if(my $lock = $self->lock_fd()) {
flock($lock, Fcntl::LOCK_UN);
} else {
# print $tulip 'lost lock for unlock ', $self->lock_file(), "\n";
croak('Lost lock for unlock: ', $self->lock_file());
}
# close $tulip;
}
# The area must be locked by the caller
sub _data_size {
my($self, $value) = @_;
if(!$self->shm()) {
croak __PACKAGE__, ': panic: _data_size has lost the shared memory segment';
return 0;
}
if(defined($value)) {
$self->shm()->write(pack('I', $value), 0, $Config{intsize});
return $value;
}
my $size = $self->shm()->read(0, $Config{intsize});
unless(defined($size)) {
return 0;
}
return unpack('I', $size);
}
# The area must be locked by the caller
sub _data {
my($self, $h) = @_;
# open(my $tulip, '>>', '/tmp/tulip');
# print $tulip __LINE__, "\n";
if(defined($h)) {
my $f = JSON::MaybeXS->new()->ascii(1)->encode($h);
my $cur_size = length($f);
# print $tulip __LINE__, " cmp $cur_size > ", $self->shm_size(), "\n";
if($cur_size > ($self->shm_size() - $Config{intsize})) {
$self->_unlock();
croak("Sharedmem set failed - value too large? ($cur_size bytes) > ", $self->shm_size());
}
if($f !~ /\}$/) {
$self->_unlock();
croak("Encoding failed. ($cur_size bytes: $f) ");
}
$self->shm()->write($f, $Config{intsize}, $cur_size);
$self->_data_size($cur_size);
# print $tulip "set: $cur_size bytes\n";
# close $tulip;
return $h;
}
my $cur_size = $self->_data_size();
# print $tulip "get: $cur_size bytes\n";
if($cur_size) {
my $rc;
eval {
$rc = JSON::MaybeXS->new()->ascii(1)->decode($self->shm()->read($Config{intsize}, $cur_size));
};
if($@) {
$self->_lock(type => 'write');
$self->_data_size(0);
my $foo = $self->shm()->read($Config{intsize}, $cur_size);
# print $tulip "\tDecode fail $cur_size bytes $@\n\t$foo\n";
# my $i = 0;
# while((my @call_details = (caller($i++)))) {
# print $tulip "\t", $call_details[1], ':', $call_details[2], ' in function ', $call_details[3], "\n";
# }
croak($@);
$self->_unlock();
}
return $rc;
# return JSON::MaybeXS->new()->ascii(1)->decode($self->shm()->read($Config{intsize}, $cur_size));
}
# close $tulip;
return {};
}
=head2 BUILD
Constructor - validate arguments
=cut
sub BUILD {
my $self = shift;
unless($self->shm_key()) {
croak 'CHI::Driver::SharedMem - no shm_key given';
( run in 1.379 second using v1.01-cache-2.11-cpan-39bf76dae61 )