require 'set' class Object module Pooling class MustImplementDisposeError < StandardError end def self.included(target) target.extend(ClassMethods) end def release @__pool.release(self) end class Pools attr_reader :type attr_accessor :size def initialize(type, size = 4) @type = type @size = size @pools = Hash.new { |h,k| h[k] = Pool.new(@size, @type, k) } end def flush! @pools.each_pair do |args,pool| pool.flush! end @pools.clear self end def [](*args) @pools[*args] end class Pool attr_reader :type, :available, :reserved def initialize(size, type, initializer) @size = size @type = type @initializer = initializer @lock = Mutex.new @available = [] @reserved = Set.new end def flush! @lock.synchronize do reserved.each do |instance| if @reserved.delete?(instance) @available << instance end end available.each do |instance| instance.dispose end @available = [] @reserved = Set.new end end def new if @available.empty? @lock.synchronize do instance = nil if @available.empty? if @reserved.size < @size instance = @type.allocate instance.send(:initialize, *@initializer) at_exit { instance.dispose } instance.instance_variable_set("@__pool", self) else # until(instance) do # TODO: Need to wait for an instance to become available, # but to do that we need to not use a synchronization block. # end instance = @type.allocate instance.send(:initialize, *@initializer) at_exit { instance.dispose } instance.instance_variable_set("@__pool", self) end else instance = @available.pop end @reserved << instance instance end else aquire_instance! end end def release(instance) @lock.synchronize do if @reserved.delete?(instance) @available << instance end end return nil end private def aquire_instance! instance = nil @lock.synchronize do instance = @available.pop raise StandardError.new("Concurrency Error!") unless instance @reserved << instance end instance end end end module ClassMethods def new(*args) unless instance_methods.include?("dispose") raise MustImplementDisposeError.new("#{self.name} must implement a `dispose' instance-method.") end pools[*args].new # uri = uri.is_a?(String) ? Addressable::URI::parse(uri) : uri # DataObjects.const_get(uri.scheme.capitalize)::Connection.acquire(uri) end def pools @pools ||= Pools.new(self) end end end end