/usr/local/CPAN/ResourcePool/ResourcePool.pm
#*********************************************************************
#*** ResourcePool
#*** Copyright (c) 2002,2003 by Markus Winand <mws@fatalmind.com>
#*** $Id: ResourcePool.pm,v 1.53 2009-11-25 14:40:22 mws Exp $
#*********************************************************************
######
# TODO
#
# -> statistics function
# -> DEBUG option to find "lost" resources (store backtrace of get() call
# and dump on DESTROY)
# -> NOTIFYing features
package ResourcePool;
use strict;
use vars qw($VERSION @ISA);
use ResourcePool::Singleton;
use ResourcePool::Command::Execute;
BEGIN {
# make script using Time::HiRes, but not fail if it isn't there
eval "use Time::HiRes qw(sleep)";
}
push @ISA, ("ResourcePool::Command::Execute", "ResourcePool::Singleton");
$VERSION = "1.0106";
sub new($$@) {
my $proto = shift;
my $class = ref($proto) || $proto;
my $factory = shift->singleton();
my $self = $class->SUPER::new($factory); # Singleton
if (!exists($self->{Factory})) {
$self->{Factory} = $factory;
$self->{FreePool} = [];
$self->{UsedPool} = {};
$self->{FreePoolSize} = 0;
$self->{UsedPoolSize} = 0;
my %options = (
Max => 5,
Min => 1,
MaxTry => 2,
MaxExecTry => 2,
PreCreate => 0,
SleepOnFail => [0]
);
if (scalar(@_) == 1) {
%options = ((%options), %{$_[0]});
} elsif (scalar(@_) > 1) {
%options = ((%options), @_);
}
if ($options{MaxTry} <= 1) {
$options{MaxTry} = 2;
}
# prepare SleepOnFail parameter, extend if neccessary
if (ref($options{SleepOnFail})) {
push (@{$options{SleepOnFail}},
($options{SleepOnFail}->[-1]) x
($options{MaxTry} - 1 - scalar(@{$options{SleepOnFail}})));
} else {
# convinience if you want set SleepOnFail to a scalar
$options{SleepOnFail}
= [($options{SleepOnFail}) x ($options{MaxTry} - 1)];
}
# truncate list if it is too long
$#{$options{SleepOnFail}} = $options{MaxTry} - 2;
$self->{Max} = $options{Max};
$self->{Min} = $options{Min};
$self->{MaxTry} = $options{MaxTry} - 1;
$self->{MaxExecTry} = $options{MaxExecTry} - 1;
$self->{PreCreate} = $options{PreCreate};
$self->{SleepOnFail} = [reverse @{$options{SleepOnFail}}];
bless($self, $class);
for (my $i = $self->{PreCreate}; $i > 0; $i--) {
$self->inc_pool();
}
}
return $self;
}
sub get($) {
my ($self) = @_;
my $rec = undef;
my $maxtry = $self->{MaxTry};
my $rv = undef;
do {
if (! $self->{FreePoolSize}) {
$self->inc_pool();
}
if ($self->{FreePoolSize}) {
$rec = shift @{$self->{FreePool}};
$self->{FreePoolSize}--;
if (! $rec->precheck()) {
swarn("ResourcePool(%s): precheck failed\n",
$self->{Factory}->info());
$rec->fail_close();
undef $rec;
}
if ($rec) {
$rv = $rec->get_plain_resource();
$self->{UsedPool}->{$rv} = $rec;
$self->{UsedPoolSize}++;
}
}
} while (! $rec && ($maxtry-- > 0) && ($self->sleepit($maxtry)));
return $rv;
}
sub free($$) {
my ($self, $plain_rec) = @_;
my $rec = $self->{UsedPool}->{$plain_rec};
if ($rec) {
undef $self->{UsedPool}->{$plain_rec};
$self->{UsedPoolSize}--;
if ($rec->postcheck()) {
push @{$self->{FreePool}}, $rec;
$self->{FreePoolSize}++;
} else {
$rec->fail_close();
}
return 1;
} else {
return 0;
}
}
sub fail($$) {
my ($self, $plain_rec) = @_;
swarn("ResourcePool(%s): got failed resource from client\n",
$self->{Factory}->info());
my $rec = $self->{UsedPool}->{$plain_rec};
if (defined $rec) {
undef $self->{UsedPool}->{$plain_rec};
$self->{UsedPoolSize}--;
$rec->fail_close();
return 1;
} else {
return 0;
}
}
sub downsize($) {
my ($self) = @_;
my $rec;
swarn("ResourcePool(%s): Downsizing\n", $self->{Factory}->info());
while ($rec = shift(@{$self->{FreePool}})) {
$rec->close();
}
$self->{FreePoolSize} = 0;
swarn("ResourcePool: Downsized... still %s open (%s)\n",
$self->{UsedPoolSize}, $self->{FreePoolSize});
}
sub postfork($) {
my ($self) = @_;
my $rec;
$self->{FreePool} = [];
$self->{UsedPool} = {};
$self->{FreePoolSize} = 0;
$self->{UsedPoolSize} = 0;
}
sub info($) {
my ($self) = @_;
return $self->{Factory}->info();
}
sub setMin($$) {
my ($self, $min) = @_;
$self->{Min} = $min;
return 1;
}
sub setMax($$) {
my ($self, $max) = @_;
$self->{Max} = $max;
return 1;
}
sub print_status($) {
my ($self) = @_;
printf("\t\t\t\t\tDB> FreePool: <%d>", $self->{FreePoolSize});
printf(" UsedPool: <%d>\n", $self->{UsedPoolSize});
}
sub get_stat_used($) {
my ($self) = @_;
return $self->{UsedPoolSize};
}
sub get_stat_free($) {
my ($self) = @_;
return $self->{FreePoolSize};
}
#*********************************************************************
#*** Private Part
#*********************************************************************
sub inc_pool($) {
my ($self) = @_;
my $rec;
my $PoolSize;
$PoolSize=$self->{FreePoolSize} + $self->{UsedPoolSize};
if ( (! defined $self->{Max}) || ($PoolSize < $self->{Max})) {
$rec = $self->{Factory}->create_resource();
if (defined $rec) {
push @{$self->{FreePool}}, $rec;
$self->{FreePoolSize}++;
}
}
}
sub sleepit($$) {
my ($self, $try) = @_;
swarn("ResourcePool> sleeping %s seconds...\n", $self->{SleepOnFail}->[$try]);
sleep($self->{SleepOnFail}->[$try]);
$self->downsize();
return 1;
}
#*********************************************************************
#*** Functional Part
#*********************************************************************
sub swarn($@) {
my $fmt = shift;
warn sprintf($fmt, @_);
}
1;