class Selector
Enforces the selector interface and delegates operations to a wrapped selector instance.
You can enable this in the default selector by setting the IO_EVENT_DEBUG_SELECTOR environment variable. In addition, you can log all selector operations to a file by setting the IO_EVENT_DEBUG_SELECTOR_LOG environment variable. This is useful for debugging and understanding the behavior of the event loop.
Nested Classes and Modules
module ForwardersForwarders for optional selector hooks that not every backing selector implements (e.g.
io_closeis only provided byURing). Each method here is mixed into the wrapper's singleton class only when the wrapped selector actually defines a method of the same name, so feature detection viarespond_to?continues to reflect the real backend.
Definitions
def self.wrap(selector, env = ENV)
Wrap the given selector with debugging.
Signature
-
parameter
selectorSelector The selector to wrap.
-
parameter
envHash The environment to read configuration from.
Implementation
def self.wrap(selector, env = ENV)
log = nil
if log_path = env["IO_EVENT_DEBUG_SELECTOR_LOG"]
log = File.open(log_path, "w")
end
return self.new(selector, log: log)
end
def initialize(selector, log: nil)
Initialize the debug selector with the given selector and optional log.
Signature
-
parameter
selectorSelector The selector to wrap.
-
parameter
logIO The log to write debug messages to.
Implementation
def initialize(selector, log: nil)
@selector = selector
@readable = {}
@writable = {}
@priority = {}
unless Fiber.current == selector.loop
Kernel::raise "Selector must be initialized on event loop fiber!"
end
@log = log
install_optional_forwarders(selector)
end
def idle_duration
The idle duration of the underlying selector.
Signature
-
returns
Numeric The idle duration.
Implementation
def idle_duration
@selector.idle_duration
end
def now
The current time.
Signature
-
returns
Numeric The current time.
Implementation
def now
Process.clock_gettime(Process::CLOCK_MONOTONIC)
end
def log(message)
- asynchronous
Log the given message.
Signature
- asynchronous
Will block the calling fiber and the entire event loop.
Implementation
def log(message)
return unless @log
Fiber.blocking do
@log.puts("T+%10.1f; %s" % [now, message])
end
end
def wakeup
Wakeup the the selector.
Implementation
def wakeup
@selector.wakeup
end
def close
Close the selector.
Implementation
def close
log("Closing selector")
if @selector.nil?
Kernel::raise "Selector already closed!"
end
@selector.close
@selector = nil
@log&.flush
end
def closed?
Signature
-
returns
Boolean Whether the wrapped selector is closed.
Implementation
def closed?
@selector.nil? || @selector.closed?
end
def transfer
Transfer from the calling fiber to the selector.
Implementation
def transfer
log("Transfering to event loop")
@selector.transfer
end
def resume(*arguments)
Resume the given fiber with the given arguments.
Implementation
def resume(*arguments)
log("Resuming fiber with #{arguments.inspect}")
@selector.resume(*arguments)
end
def yield
Yield to the selector.
Implementation
def yield
log("Yielding to event loop")
@selector.yield
end
def push(fiber)
Push the given fiber to the selector ready list, such that it will be resumed on the next call to IO::Event::Debug::Selector#select.
Signature
-
parameter
fiberFiber The fiber that is ready.
Implementation
def push(fiber)
log("Pushing fiber #{fiber.inspect} to ready list")
@selector.push(fiber)
end
def raise(fiber, *arguments, **options)
Raise the given exception on the given fiber.
Signature
-
parameter
fiberFiber The fiber to raise the exception on.
-
parameter
argumentsArray The arguments to use when raising the exception.
Implementation
def raise(fiber, *arguments, **options)
log("Raising exception on fiber #{fiber.inspect} with #{arguments.inspect}")
@selector.raise(fiber, *arguments, **options)
end
def ready?
Check if the selector is ready.
Signature
-
returns
Boolean Whether the selector is ready.
Implementation
def ready?
@selector.ready?
end
def blocking_operation_wait(operation)
Run the given blocking operation and wait for its completion.
Implementation
def blocking_operation_wait(operation)
log("Waiting for blocking operation #{operation.inspect}")
@selector.blocking_operation_wait(operation)
end
def process_wait(*arguments)
Wait for the given process, forwarded to the underlying selector.
Implementation
def process_wait(*arguments)
log("Waiting for process with #{arguments.inspect}")
@selector.process_wait(*arguments)
end
def io_wait(fiber, io, events)
Wait for the given IO, forwarded to the underlying selector.
Implementation
def io_wait(fiber, io, events)
log("Waiting for IO #{io.inspect} for events #{events.inspect}")
@selector.io_wait(fiber, io, events)
end
def io_read(fiber, io, buffer, *arguments)
Read from the given IO, forwarded to the underlying selector.
Implementation
def io_read(fiber, io, buffer, *arguments)
log("Reading from IO #{io.inspect} with buffer #{buffer}; arguments #{arguments.inspect}")
@selector.io_read(fiber, io, buffer, *arguments)
end
def io_write(fiber, io, buffer, *arguments)
Write to the given IO, forwarded to the underlying selector.
Implementation
def io_write(fiber, io, buffer, *arguments)
log("Writing to IO #{io.inspect} with buffer #{buffer}; arguments #{arguments.inspect}")
@selector.io_write(fiber, io, buffer, *arguments)
end
def respond_to?(name, include_private = false)
Forward the given method to the underlying selector.
Implementation
def respond_to?(name, include_private = false)
@selector.respond_to?(name, include_private)
end
def select(duration = nil)
Select for the given duration, forwarded to the underlying selector.
Implementation
def select(duration = nil)
log("Selecting for #{duration.inspect}")
unless Fiber.current == @selector.loop
Kernel::raise "Selector must be run on event loop fiber!"
end
@selector.select(duration)
end