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