class HTTPX::Selector

  1. lib/httpx/selector.rb
Superclass: Object

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 :closed when disposed of resources.

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.

Constants

HAS_FIBER_SCHEDULER = Fiber.respond_to?(:scheduler)  

Public Class methods

new()
[show source]
   # 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

deregister(io)

deregisters io from selectables.

[show source]
    # File lib/httpx/selector.rb
150 def deregister(io)
151   @selectables.delete(io)
152 end
each_connection(&block)
[show source]
    # 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
empty?()
[show source]
   # File lib/httpx/selector.rb
48 def empty?
49   @selectables.empty? && @timers.empty?
50 end
find_connection(request_uri, options)
[show source]
    # 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
find_mergeable_connection(connection)
[show source]
    # 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
find_resolver(options)
[show source]
    # 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
initial_call()

first time the registered selectables are added, there’s probably work to do.

[show source]
   # File lib/httpx/selector.rb
53 def initial_call
54   @selectables.each(&:initial_call)
55 end
next_tick()
[show source]
   # 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)

register io.

[show source]
    # File lib/httpx/selector.rb
155 def register(io)
156   return if @selectables.include?(io)
157 
158   @selectables << io
159 end
terminate(selectables = nil)
[show source]
    # 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