rf-web/vendor/bundle/gems/eventmachine-1.2.7/lib/em/pool.rb

152 lines
4.1 KiB
Ruby
Raw Normal View History

2019-10-21 08:18:17 +00:00
module EventMachine
# A simple async resource pool based on a resource and work queue. Resources
# are enqueued and work waits for resources to become available.
#
# @example
# require 'em-http-request'
#
# EM.run do
# pool = EM::Pool.new
# spawn = lambda { pool.add EM::HttpRequest.new('http://example.org') }
# 10.times { spawn[] }
# done, scheduled = 0, 0
#
# check = lambda do
# done += 1
# if done >= scheduled
# EM.stop
# end
# end
#
# pool.on_error { |conn| spawn[] }
#
# 100.times do |i|
# scheduled += 1
# pool.perform do |conn|
# req = conn.get :path => '/', :keepalive => true
#
# req.callback do
# p [:success, conn.object_id, i, req.response.size]
# check[]
# end
#
# req.errback { check[] }
#
# req
# end
# end
# end
#
# Resources are expected to be controlled by an object responding to a
# deferrable/completion style API with callback and errback blocks.
#
class Pool
def initialize
@resources = EM::Queue.new
@removed = []
@contents = []
@on_error = nil
end
def add resource
@contents << resource
requeue resource
end
def remove resource
@contents.delete resource
@removed << resource
end
# Returns a list for introspection purposes only. You should *NEVER* call
# modification or work oriented methods on objects in this list. A good
# example use case is periodic statistics collection against a set of
# connection resources.
#
# @example
# pool.contents.inject(0) { |sum, connection| connection.num_bytes }
def contents
@contents.dup
end
# Define a default catch-all for when the deferrables returned by work
# blocks enter a failed state. By default all that happens is that the
# resource is returned to the pool. If on_error is defined, this block is
# responsible for re-adding the resource to the pool if it is still usable.
# In other words, it is generally assumed that on_error blocks explicitly
# handle the rest of the lifetime of the resource.
def on_error *a, &b
@on_error = EM::Callback(*a, &b)
end
# Perform a given #call-able object or block. The callable object will be
# called with a resource from the pool as soon as one is available, and is
# expected to return a deferrable.
#
# The deferrable will have callback and errback added such that when the
# deferrable enters a finished state, the object is returned to the pool.
#
# If on_error is defined, then objects are not automatically returned to the
# pool.
def perform(*a, &b)
work = EM::Callback(*a, &b)
@resources.pop do |resource|
if removed? resource
@removed.delete resource
reschedule work
else
process work, resource
end
end
end
alias reschedule perform
# A peek at the number of enqueued jobs waiting for resources
def num_waiting
@resources.num_waiting
end
# Removed will show resources in a partial pruned state. Resources in the
# removed list may not appear in the contents list if they are currently in
# use.
def removed? resource
@removed.include? resource
end
protected
def requeue resource
@resources.push resource
end
def failure resource
if @on_error
@contents.delete resource
@on_error.call resource
# Prevent users from calling a leak.
@removed.delete resource
else
requeue resource
end
end
def completion deferrable, resource
deferrable.callback { requeue resource }
deferrable.errback { failure resource }
end
def process work, resource
deferrable = work.call resource
if deferrable.kind_of?(EM::Deferrable)
completion deferrable, resource
else
raise ArgumentError, "deferrable expected from work"
end
rescue
failure resource
raise
end
end
end