class NIO::Selector

Selectors monitor IO objects for events of interest

Public Class Methods

backends() click to toggle source

Return an array of symbols for supported backends

static VALUE NIO_Selector_supported_backends(VALUE klass)
{
    unsigned int backends = ev_supported_backends();
    VALUE result = rb_ary_new();

    if (backends & EVBACKEND_EPOLL) {
        rb_ary_push(result, ID2SYM(rb_intern("epoll")));
    }

    if (backends & EVBACKEND_POLL) {
        rb_ary_push(result, ID2SYM(rb_intern("poll")));
    }

    if (backends & EVBACKEND_KQUEUE) {
        rb_ary_push(result, ID2SYM(rb_intern("kqueue")));
    }

    if (backends & EVBACKEND_SELECT) {
        rb_ary_push(result, ID2SYM(rb_intern("select")));
    }

    if (backends & EVBACKEND_PORT) {
        rb_ary_push(result, ID2SYM(rb_intern("port")));
    }

    if (backends & EVBACKEND_LINUXAIO) {
        rb_ary_push(result, ID2SYM(rb_intern("linuxaio")));
    }

    if (backends & EVBACKEND_IOURING) {
        rb_ary_push(result, ID2SYM(rb_intern("io_uring")));
    }

    return result;
}
new(p1 = v1) click to toggle source

Create a new selector. This is more or less the pure Ruby version translated into an MRI cext

static VALUE NIO_Selector_initialize(int argc, VALUE *argv, VALUE self)
{
    ID backend_id;
    VALUE backend;
    VALUE lock;

    struct NIO_Selector *selector;
    unsigned int flags = 0;

    Data_Get_Struct(self, struct NIO_Selector, selector);

    rb_scan_args(argc, argv, "01", &backend);

    if (backend != Qnil) {
        if (!rb_ary_includes(NIO_Selector_supported_backends(CLASS_OF(self)), backend)) {
            rb_raise(rb_eArgError, "unsupported backend: %s", RSTRING_PTR(rb_funcall(backend, rb_intern("inspect"), 0)));
        }

        backend_id = SYM2ID(backend);

        if (backend_id == rb_intern("epoll")) {
            flags = EVBACKEND_EPOLL;
        } else if (backend_id == rb_intern("poll")) {
            flags = EVBACKEND_POLL;
        } else if (backend_id == rb_intern("kqueue")) {
            flags = EVBACKEND_KQUEUE;
        } else if (backend_id == rb_intern("select")) {
            flags = EVBACKEND_SELECT;
        } else if (backend_id == rb_intern("port")) {
            flags = EVBACKEND_PORT;
        } else if (backend_id == rb_intern("linuxaio")) {
            flags = EVBACKEND_LINUXAIO;
        } else if (backend_id == rb_intern("io_uring")) {
            flags = EVBACKEND_IOURING;
        } else {
            rb_raise(rb_eArgError, "unsupported backend: %s", RSTRING_PTR(rb_funcall(backend, rb_intern("inspect"), 0)));
        }
    }

    /* Ensure the selector loop has not yet been initialized */
    assert(!selector->ev_loop);

    selector->ev_loop = ev_loop_new(flags);
    if (!selector->ev_loop) {
        rb_raise(rb_eIOError, "error initializing event loop");
    }

    ev_io_start(selector->ev_loop, &selector->wakeup);

    rb_ivar_set(self, rb_intern("selectables"), rb_hash_new());
    rb_ivar_set(self, rb_intern("lock_holder"), Qnil);

    lock = rb_class_new_instance(0, 0, rb_const_get(rb_cObject, rb_intern("Mutex")));
    rb_ivar_set(self, rb_intern("lock"), lock);
    rb_ivar_set(self, rb_intern("lock_holder"), Qnil);

    return Qnil;
}
new(backend = :ruby) click to toggle source

Create a new NIO::Selector

# File lib/nio/selector.rb, line 31
def initialize(backend = :ruby)
  raise ArgumentError, "unsupported backend: #{backend}" unless [:ruby, nil].include?(backend)

  @selectables = {}
  @lock = Mutex.new

  # Other threads can wake up a selector
  @wakeup, @waker = IO.pipe
  @closed = false
end

Public Instance Methods

backend() click to toggle source
static VALUE NIO_Selector_backend(VALUE self)
{
    struct NIO_Selector *selector;

    Data_Get_Struct(self, struct NIO_Selector, selector);
    if (selector->closed) {
        rb_raise(rb_eIOError, "selector is closed");
    }

    switch (ev_backend(selector->ev_loop)) {
        case EVBACKEND_EPOLL:
            return ID2SYM(rb_intern("epoll"));
        case EVBACKEND_POLL:
            return ID2SYM(rb_intern("poll"));
        case EVBACKEND_KQUEUE:
            return ID2SYM(rb_intern("kqueue"));
        case EVBACKEND_SELECT:
            return ID2SYM(rb_intern("select"));
        case EVBACKEND_PORT:
            return ID2SYM(rb_intern("port"));
        case EVBACKEND_LINUXAIO:
            return ID2SYM(rb_intern("linuxaio"));
        case EVBACKEND_IOURING:
            return ID2SYM(rb_intern("io_uring"));
    }

    return ID2SYM(rb_intern("unknown"));
}
close() click to toggle source

Close the selector and free system resources

static VALUE NIO_Selector_close(VALUE self)
{
    return NIO_Selector_synchronize(self, NIO_Selector_close_synchronized, self);
}
closed?() click to toggle source

Is the selector closed?

static VALUE NIO_Selector_closed(VALUE self)
{
    return NIO_Selector_synchronize(self, NIO_Selector_closed_synchronized, self);
}
deregister(p1) click to toggle source

Deregister an IO object from the selector

static VALUE NIO_Selector_deregister(VALUE self, VALUE io)
{
    VALUE args[2] = {self, io};
    return NIO_Selector_synchronize(self, NIO_Selector_deregister_synchronized, (VALUE)args);
}
empty?() click to toggle source

True if there are monitors on the loop

static VALUE NIO_Selector_is_empty(VALUE self)
{
    VALUE selectables = rb_ivar_get(self, rb_intern("selectables"));

    return rb_funcall(selectables, rb_intern("empty?"), 0) == Qtrue ? Qtrue : Qfalse;
}
register(p1, p2) click to toggle source

Register an IO object with the selector for the given interests

static VALUE NIO_Selector_register(VALUE self, VALUE io, VALUE interests)
{
    VALUE args[3] = {self, io, interests};
    return NIO_Selector_synchronize(self, NIO_Selector_register_synchronized, (VALUE)args);
}
registered?(p1) click to toggle source

Is the given IO object registered with the selector

static VALUE NIO_Selector_is_registered(VALUE self, VALUE io)
{
    VALUE selectables = rb_ivar_get(self, rb_intern("selectables"));

    /* Perhaps this should be holding the mutex? */
    return rb_funcall(selectables, rb_intern("has_key?"), 1, io);
}
select(p1 = v1) click to toggle source

Select from all registered IO objects

static VALUE NIO_Selector_select(int argc, VALUE *argv, VALUE self)
{
    VALUE timeout;

    rb_scan_args(argc, argv, "01", &timeout);

    if (timeout != Qnil && NUM2DBL(timeout) < 0) {
        rb_raise(rb_eArgError, "time interval must be positive");
    }

    VALUE args[2] = {self, timeout};
    return NIO_Selector_synchronize(self, NIO_Selector_select_synchronized, (VALUE)args);
}
wakeup() click to toggle source

Wake the selector up from another thread

static VALUE NIO_Selector_wakeup(VALUE self)
{
    struct NIO_Selector *selector;
    Data_Get_Struct(self, struct NIO_Selector, selector);

    if (selector->closed) {
        rb_raise(rb_eIOError, "selector is closed");
    }

    selector->wakeup_fired = 1;
    write(selector->wakeup_writer, "\0", 1);

    return Qnil;
}