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_next_available
	
	return resource unless block_given?
	
	begin
		yield resource
	ensure
		release(resource)
	end
end

def close

def close
	@available.each_key(&:close)
	@available.clear
end

def create

def create
	begin
		# This might fail, which is okay :)
		resource = @constructor.call
	rescue
		Async.logger.error "#{$!}: #{$!.backtrace}"
		return nil
	end
	
	@available[resource] = 1
	
	return resource
end

def empty?

def empty?
	@available.empty?
end

def initialize(limit = nil, &block)

def initialize(limit = nil, &block)
	@available = {} # resource => count
	@waiting = []
	
	@limit = limit
	
	@constructor = block
end

def next_available

def next_available
	@available.each do |resource, count|
		if count < resource.multiplex
			# We want to use this resource... but is it good?
			if resource.good?
				@available[resource] += 1
				
				return resource
			else
				retire(resource)
			end
		end
	end
	
	if !@limit or @available.count < @limit
		Async.logger.debug(self) {"No available resources, allocating new one..."}
		
		return create
	end
end

def release(resource)

Make the resource available and let waiting tasks know that there is something available.
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}"}
	
	@available.delete(resource)
	
	resource.close
end

def reuse(resource)

def reuse(resource)
	Async.logger.debug(self) {"Reuse #{resource}"}
	
	@available[resource] -= 1
	
	if task = @waiting.pop
		task.resume
	end
end

def wait_for_next_available

def wait_for_next_available
	until resource = next_available
		@waiting << Fiber.current
		Task.yield
	end
	
	return resource
end