require 'thread'
require 'timeout'
class ObjectPoolError < StandardError; end
class ObjectPoolFull < ObjectPoolError; end
class ObjectPoolTimeOut < ObjectPoolFull; end
class ObjectPoolConfig
attr_accessor :pool_size, :timeout
def initialize(pool_size, timeout)
@pool_size = pool_size
@timeout = timeout
end
def initialize(params)
@pool_size = params[:pool_size]
@timeout = params[:timeout]
end
end
class ObjectPoolCreator
def create
raise ObjectPoolError, "override this method"
end
def valid?(object)
true
end
def expire
end
end
class ObjectPool
def initialize(object_creator, config = nil)
@object_creator = object_creator
raise ObjectPoolError, "First Parameter should be a kind of ObjectPoolCreator" if !@object_creator.kind_of?(ObjectPoolCreator)
@config = config
self_configuration if @config.nil?
raise ObjectPoolError, "Second Parameter should be a kind of ObjectPoolConfig" if !@config.kind_of?(ObjectPoolConfig)
@mutex = Mutex.new
@cond = ConditionVariable.new
@activations = {}
@idles = []
end
def borrow_object
@mutex.synchronize do
result = nil
if @activations.size + @idles.size >= @config.pool_size and @idles.empty?
raise ObjectPoolFull, "%s is full" % self.class if @config.timeout == 0
begin
timeout(@config.timeout) { @cond.wait(@mutex) }
rescue TimeoutError
raise ObjectPoolTimeOut, "%s borrow_object is timeout" % self.class
end
result = @idles.pop
elsif !@idles.empty?
result = @idles.pop
else
result = create_object
end
if !@object_creator.valid?(result)
result.expire
result = create_object
end
@activations[result.__id__] = result
return result
end
end
def return_object(object)
@mutex.synchronize do
res = @activations.delete object.__id__
return if res.nil?
@activations.delete object.__id__
@idles << object
@cond.signal
end
end
private
def self_configuration
@config = ObjectPoolConfig.new(10, 1)
end
def create_object
@object_creator.create
end
end