Implements the selector loop, where it registers and monitors “Selectable” objects.
A Selectable object is an object which can calculate the interests (:r, :w or :rw, respectively “read”, “write” or “read-write”) it wants to monitor for, and returns (via to_io method) an IO object which can be passed to functions such as IO.select . More exhaustively, a Selectable must implement the following methods:
| state |
returns the state as a Symbol, must return |
| to_io |
returns the IO object. |
| call |
gets called when the IO is ready. |
| interests |
returns the current interests to monitor for, as described above. |
| timeout |
returns nil or an integer, representing how long to wait for interests. |
| handle_socket_timeout(Numeric) |
called when waiting for interest times out. |
Methods
Public Class
Public Instance
Constants
| HAS_FIBER_SCHEDULER | = | Fiber.respond_to?(:scheduler) |
Public Class methods
# File lib/httpx/selector.rb 40 def initialize 41 @timers = Timers.new 42 @selectables = [] 43 @is_timer_interval = false 44 @busy_iterations = 0 45 @last_select_registered = nil 46 end
Public Instance methods
deregisters io from selectables.
# File lib/httpx/selector.rb 150 def deregister(io) 151 @selectables.delete(io) 152 end
# File lib/httpx/selector.rb 124 def each_connection(&block) 125 return enum_for(__method__) unless block 126 127 @selectables.each do |c| 128 case c 129 when Resolver::Resolver 130 c.each_connection(&block) 131 when Connection 132 yield c 133 end 134 end 135 end
# File lib/httpx/selector.rb 48 def empty? 49 @selectables.empty? && @timers.empty? 50 end
# File lib/httpx/selector.rb 137 def find_connection(request_uri, options) 138 each_connection.find do |connection| 139 connection.match?(request_uri, options) 140 end 141 end
# File lib/httpx/selector.rb 143 def find_mergeable_connection(connection) 144 each_connection.find do |ch| 145 ch != connection && ch.mergeable?(connection) 146 end 147 end
# File lib/httpx/selector.rb 115 def find_resolver(options) 116 res = @selectables.find do |c| 117 c.is_a?(Resolver::Resolver) && 118 options.resolver_options_match?(c.options) 119 end 120 121 res.multi if res 122 end
first time the registered selectables are added, there’s probably work to do.
# File lib/httpx/selector.rb 53 def initial_call 54 @selectables.each(&:initial_call) 55 end
# File lib/httpx/selector.rb 57 def next_tick 58 catch(:jump_tick) do 59 timeout = next_timeout 60 if timeout && timeout.negative? 61 @timers.fire 62 busy_loop_check(timeout) 63 throw(:jump_tick) 64 end 65 66 begin 67 selected = false 68 69 select(timeout) do |c| 70 selected = true 71 72 c.log(level: 2) { "[#{c.state}] selected from selector##{object_id}#{" after #{timeout} secs" unless timeout.nil?}..." } 73 74 c.call 75 end 76 77 @timers.fire 78 rescue TimeoutError => e 79 @timers.fire(e) 80 81 @busy_iterations = 0 82 else 83 # a tick is healthy if it either called a ready selectable, or actually blocked waiting for one. 84 # without a timeout, #select only blocks when something registered interests; with one, 85 # it always sleeps or selects, but a zero timeout just polls. 86 waited = timeout.nil? ? @last_select_registered.to_i.positive? : timeout.positive? 87 88 if selected || waited 89 @busy_iterations = 0 90 else 91 busy_loop_check(timeout) 92 end 93 end 94 end 95 end
register io.
# File lib/httpx/selector.rb 155 def register(io) 156 return if @selectables.include?(io) 157 158 @selectables << io 159 end
# File lib/httpx/selector.rb 97 def terminate(selectables = nil) 98 # array may change during iteration 99 selectables ||= @selectables.reject(&:inflight?) 100 101 selectables.delete_if do |sel| 102 sel.terminate 103 # TODO: this check is racy: connections check in to the pool on termination, and may check out and 104 # change state immediately after. find a more idempotent way of doing this. 105 sel.current_selector != self 106 end 107 108 until selectables.empty? 109 next_tick 110 111 selectables &= @selectables 112 end 113 end