mirror of
https://github.com/HoneyryderChuck/httpx.git
synced 2025-10-11 00:01:38 -04:00
selector: use poll when possible, instead of IO.select
Using IO.select is costly for the one-request/one-origin case, as we have to build array to pass, and we receive arrays, and that generates a lot of needless garbage when we only request once. Instead, it now uses IO#wait_readable and IO#wait_writable, which does not require extra arrays, and uses poll under the hood.
This commit is contained in:
parent
2c5757b1a5
commit
ed23525daf
@ -1,5 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "io/wait"
|
||||||
|
|
||||||
class HTTPX::Selector
|
class HTTPX::Selector
|
||||||
READABLE = %i[rw r].freeze
|
READABLE = %i[rw r].freeze
|
||||||
WRITABLE = %i[rw w].freeze
|
WRITABLE = %i[rw w].freeze
|
||||||
@ -11,7 +13,7 @@ class HTTPX::Selector
|
|||||||
# I/O monitor
|
# I/O monitor
|
||||||
#
|
#
|
||||||
class Monitor
|
class Monitor
|
||||||
attr_accessor :io, :readiness
|
attr_accessor :io
|
||||||
|
|
||||||
def initialize(io, reactor)
|
def initialize(io, reactor)
|
||||||
@io = io
|
@io = io
|
||||||
@ -19,18 +21,6 @@ class HTTPX::Selector
|
|||||||
@closed = false
|
@closed = false
|
||||||
end
|
end
|
||||||
|
|
||||||
def interests
|
|
||||||
@io.interests
|
|
||||||
end
|
|
||||||
|
|
||||||
def readable?
|
|
||||||
READABLE.include?(@io.interests)
|
|
||||||
end
|
|
||||||
|
|
||||||
def writable?
|
|
||||||
WRITABLE.include?(@io.interests)
|
|
||||||
end
|
|
||||||
|
|
||||||
# closes +@io+, deregisters from reactor (unless +deregister+ is false)
|
# closes +@io+, deregisters from reactor (unless +deregister+ is false)
|
||||||
def close(deregister = true)
|
def close(deregister = true)
|
||||||
return if @closed
|
return if @closed
|
||||||
@ -74,46 +64,68 @@ class HTTPX::Selector
|
|||||||
# waits for read/write events for +interval+. Yields for monitors of
|
# waits for read/write events for +interval+. Yields for monitors of
|
||||||
# selected IO objects.
|
# selected IO objects.
|
||||||
#
|
#
|
||||||
def select(interval)
|
def select(interval, &block)
|
||||||
|
return select_one(interval, &block) if @selectables.size == 1
|
||||||
|
|
||||||
begin
|
begin
|
||||||
r = nil
|
r = nil
|
||||||
w = nil
|
w = nil
|
||||||
|
|
||||||
@selectables.each do |io, monitor|
|
@selectables.each_key do |io|
|
||||||
(r ||= []) << io if monitor.interests == :r || monitor.interests == :rw
|
(r ||= []) << io if io.interests == :r || io.interests == :rw
|
||||||
(w ||= []) << io if monitor.interests == :w || monitor.interests == :rw
|
(w ||= []) << io if io.interests == :w || io.interests == :rw
|
||||||
monitor.readiness = nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
readers, writers = IO.select(r, w, nil, interval)
|
readers, writers = IO.select(r, w, nil, interval)
|
||||||
|
|
||||||
if readers.nil? && writers.nil?
|
raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select") if readers.nil? && writers.nil?
|
||||||
raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select")
|
|
||||||
end
|
|
||||||
rescue IOError, SystemCallError
|
rescue IOError, SystemCallError
|
||||||
@selectables.reject! { |io, _| io.closed? }
|
@selectables.reject! { |io, _| io.closed? }
|
||||||
retry
|
retry
|
||||||
end
|
end
|
||||||
|
|
||||||
readers.each do |io|
|
readers.each do |io|
|
||||||
monitor = io.closed? ? @selectables.delete(io) : @selectables[io]
|
monitor = @selectables[io]
|
||||||
next unless monitor
|
next unless monitor
|
||||||
|
|
||||||
monitor.readiness = writers.delete(io) ? :rw : :r
|
# so that we don't yield 2 times
|
||||||
|
writers.delete(io)
|
||||||
|
|
||||||
yield monitor
|
yield monitor
|
||||||
end if readers
|
end if readers
|
||||||
|
|
||||||
writers.each do |io|
|
writers.each do |io|
|
||||||
monitor = io.closed? ? @selectables.delete(io) : @selectables[io]
|
monitor = @selectables[io]
|
||||||
next unless monitor
|
next unless monitor
|
||||||
|
|
||||||
# don't double run this, the last iteration might have run this task already
|
|
||||||
monitor.readiness = :w
|
|
||||||
yield monitor
|
yield monitor
|
||||||
end if writers
|
end if writers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Closes the selector.
|
# Closes the selector.
|
||||||
#
|
#
|
||||||
def close ; end
|
def close; end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def select_one(interval)
|
||||||
|
io, monitor = @selectables.first
|
||||||
|
|
||||||
|
case io.interests
|
||||||
|
when :r
|
||||||
|
result = io.to_io.wait_readable(interval)
|
||||||
|
raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select") unless result
|
||||||
|
when :w
|
||||||
|
result = io.to_io.wait_writable(interval)
|
||||||
|
raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select") unless result
|
||||||
|
when :rw
|
||||||
|
readers, writers = IO.select([io], [io], nil, interval)
|
||||||
|
|
||||||
|
raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select") if readers.nil? && writers.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
yield monitor
|
||||||
|
rescue IOError, SystemCallError
|
||||||
|
@selectables.reject! { |ios, _| ios.closed? }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user