class IO::Event::Debug::Selector

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.
Enforces the selector interface and delegates operations to a wrapped selector instance.

def self.wrap(selector, env = ENV)

@parameter env [Hash] The environment to read configuration from.
@parameter selector [Selector] The selector to wrap.

Wrap the given selector with debugging.
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 close

Close the selector.
def close
	log("Closing selector")
	
	if @selector.nil?
		Kernel::raise "Selector already closed!"
	end
	
	@selector.close
	@selector = nil
end

def idle_duration

@returns [Numeric] The idle duration.

The idle duration of the underlying selector.
def idle_duration
	@selector.idle_duration
end

def initialize(selector, log: nil)

@parameter log [IO] The log to write debug messages to.
@parameter selector [Selector] The selector to wrap.

Initialize the debug selector with the given selector and optional log.
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
end

def io_read(fiber, io, buffer, length, offset = 0)

Read from the given IO, forwarded to the underlying selector.
def io_read(fiber, io, buffer, length, offset = 0)
	log("Reading from IO #{io.inspect} with buffer #{buffer}; length #{length} offset #{offset}")
	@selector.io_read(fiber, io, buffer, length, offset)
end

def io_wait(fiber, io, events)

Wait for the given IO, forwarded to the underlying selector.
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_write(fiber, io, buffer, length, offset = 0)

Write to the given IO, forwarded to the underlying selector.
def io_write(fiber, io, buffer, length, offset = 0)
	log("Writing to IO #{io.inspect} with buffer #{buffer}; length #{length} offset #{offset}")
	@selector.io_write(fiber, io, buffer, length, offset)
end

def log(message)

@asynchronous Will block the calling fiber and the entire event loop.

Log the given message.
def log(message)
	return unless @log
	
	Fiber.blocking do
		@log.puts("T+%10.1f; %s" % [now, message])
	end
end

def now

@returns [Numeric] The current time.

The current time.
def now
	Process.clock_gettime(Process::CLOCK_MONOTONIC)
end

def process_wait(*arguments)

Wait for the given process, forwarded to the underlying selector.
def process_wait(*arguments)
	log("Waiting for process with #{arguments.inspect}")
	@selector.process_wait(*arguments)
end

def push(fiber)

@parameter fiber [Fiber] The fiber that is ready.

Push the given fiber to the selector ready list, such that it will be resumed on the next call to {select}.
def push(fiber)
	log("Pushing fiber #{fiber.inspect} to ready list")
	@selector.push(fiber)
end

def raise(fiber, *arguments)

@parameter arguments [Array] The arguments to use when raising the exception.
@parameter fiber [Fiber] The fiber to raise the exception on.

Raise the given exception on the given fiber.
def raise(fiber, *arguments)
	log("Raising exception on fiber #{fiber.inspect} with #{arguments.inspect}")
	@selector.raise(fiber, *arguments)
end

def ready?

@returns [Boolean] Whether the selector is ready.

Check if the selector is ready.
def ready?
	@selector.ready?
end

def respond_to?(name, include_private = false)

Forward the given method to the underlying selector.
def respond_to?(name, include_private = false)
	@selector.respond_to?(name, include_private)
end

def resume(*arguments)

Resume the given fiber with the given arguments.
def resume(*arguments)
	log("Resuming fiber with #{arguments.inspect}")
	@selector.resume(*arguments)
end

def select(duration = nil)

Select for the given duration, forwarded to the underlying selector.
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

def transfer

Transfer from the calling fiber to the selector.
def transfer
	log("Transfering to event loop")
	@selector.transfer
end

def wakeup

Wakeup the the selector.
def wakeup
	@selector.wakeup
end

def yield

Yield to the selector.
def yield
	log("Yielding to event loop")
	@selector.yield
end