# 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")));
        }

        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 {
                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 16
    def initialize(backend = :ruby)
      raise ArgumentError, "unsupported backend: #{backend}" unless backend == :ruby

      @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"));
        }

        return ID2SYM(rb_intern("unknown"));
    }

close() click to toggle source

Close the selector and free system resources

    static VALUE NIO_Selector_close(VALUE self)
    {
        VALUE args[1] = {self};
        return NIO_Selector_synchronize(self, NIO_Selector_close_synchronized, args);
    }

closed?() click to toggle source

Is the selector closed?

    static VALUE NIO_Selector_closed(VALUE self)
    {
        VALUE args[1] = {self};
        return NIO_Selector_synchronize(self, NIO_Selector_closed_synchronized, args);
    }

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, 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, 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;
        VALUE args[2];

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

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

        args[0] = self;
        args[1] = timeout;

        return NIO_Selector_synchronize(self, NIO_Selector_select_synchronized, 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;
    }