class Async::HTTP::Pool


#reusable? -> can be used again.
#multiplex -> 1 or more.
Resources must respond to
This pool doesn’t impose a maximum number of open resources, but it WILL block if there are no available resources and trying to allocate another one fails.
In general we don’t know the policy until connection is established.
- Multiplex requests per connection (HTTP2)
- Multiple sequential requests per connection (HTTP1 with keep-alive)
- Single request per connection (HTTP/1 without keep-alive)
Pool behaviours

def acquire

def acquire
	resource = wait_for_resource
	
	return resource unless block_given?
	
	begin
		yield resource
	ensure
		release(resource)
	end
end

def availability_string

def availability_string
	@resources.collect do |resource,usage|
		"#{usage}/#{resource.multiplex}#{resource.connected? ? '' : '*'}/#{resource.count}"
	end.join(";")
end

def available_resource

def available_resource
	# This is a linear search... not idea, but simple for now.
	@resources.each do |resource, count|
		if count < resource.multiplex
			# We want to use this resource... but is it connected?
			if resource.connected?
				@resources[resource] += 1
				
				return resource
			else
				retire(resource)
			end
		end
	end
	
	if !@limit or @active < @limit
		Async.logger.debug(self) {"No resources resources, allocating new one..."}
		
		@active += 1
		
		return create
	end
	
	return nil
end

def busy?

Whether there are resources which are currently in use.
def busy?
	@resources.collect do |_,usage|
		return true if usage > 0
	end
	
	return false
end

def close

def close
	@resources.each_key(&:close)
	@resources.clear
	
	@active = 0
end

def create

def create
	# This might return nil, which means creating the resource failed.
	if resource = @constructor.call
		@resources[resource] = 1
	end
	
	return resource
end

def empty?

def empty?
	@resources.empty?
end

def initialize(limit = nil, &block)

def initialize(limit = nil, &block)
	@resources = {} # resource => count
	@available = Async::Notification.new
	
	@limit = limit
	@active = 0
	
	@constructor = block
end

def release(resource)

Make the resource resources and let waiting tasks know that there is something resources.
def release(resource)
	# A resource that is not good should also not be reusable.
	if resource.reusable?
		reuse(resource)
	else
		retire(resource)
	end
end

def retire(resource)

def retire(resource)
	Async.logger.debug(self) {"Retire #{resource}"}
	
	@resources.delete(resource)
	
	@active -= 1
	
	resource.close
	
	@available.signal
end

def reuse(resource)

def reuse(resource)
	Async.logger.debug(self) {"Reuse #{resource}"}
	
	@resources[resource] -= 1
	
	@available.signal
end

def to_s

def to_s
	"\#<#{self.class} resources=#{availability_string} limit=#{@limit.inspect}>"
end

def wait_for_resource

def wait_for_resource
	# If we fail to create a resource (below), we will end up waiting for one to become resources.
	until resource = available_resource
		@available.wait
	end
	
	Async.logger.debug(self) {"Wait for resource #{resource}"}
	
	return resource
end