Skip to content

Selectors

Tony Arcieri edited this page Dec 29, 2016 · 2 revisions

The NIO::Selector class is the main API provided by nio4r. Use it where you might otherwise use Kernel.select, but want to monitor the same set of IO objects across multiple select calls rather than having to reregister them every single time:

require 'nio'

selector = NIO::Selector.new

Backends

Selectors use various platform-specific backends in order to select sockets that are ready for I/O. You can check which backend is in use with the #backend method:

>> selector.backend
 => :epoll

Registering IO objects

To monitor IO objects, attach them to the selector with the #register method, monitoring them for read readiness with the :r parameter, write readiness with the :w parameter, or both with :rw.

>> reader, writer = IO.pipe
 => [#<IO:0xf30>, #<IO:0xf34>]
>> monitor = selector.register(reader, :r)
 => #<NIO::Monitor:0xfbc>

After registering an IO object with the selector, you'll get a NIO::Monitor object which you can use for managing how a particular IO object is being monitored. Monitors will store an arbitrary value of your choice, which provides an easy way to implement callbacks:

>> monitor = selector.register(reader, :r)
 => #<NIO::Monitor:0xfbc>
>> monitor.value = proc { puts "Got some data: #{monitor.io.read_nonblock(4096)}" }
 => #<Proc:0x1000@(irb):4>

Deregistering IO objects

When you're done monitoring a particular IO object, just deregister it from the selector:

selector.deregister(reader)

Selecting IO objects for readiness

The main method of importance is #select, which monitors all registered IO objects and returns an array of monitors that are ready.

>> writer << "Hi there!"
 => #<IO:0x103c>
>> ready = selector.select
 => [#<NIO::Monitor:0xfbc>]
>> ready.each { |m| m.value.call }
Got some data: Hi there!
 => [#<NIO::Monitor:0xfbc>]

By default, #select will block indefinitely until one of the IO objects being monitored becomes ready. However, you can also pass a timeout to wait in to #select just like you can with Kernel.select:

ready = selector.select(15) # Wait 15 seconds

If a timeout occurs, ready will be nil.

You can avoid allocating an array each time you call #select by passing a block to select. The block will be called for each ready monitor object, with that object passed as an argument. The number of ready monitors is returned as a Fixnum:

>> selector.select { |m| m.value.call }
Got some data: Hi there!
 => 1

See Also

Clone this wiki locally