nio4r 2.0.0.pre-java → 2.1.0-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/.rubocop.yml +31 -38
- data/.ruby-version +1 -0
- data/.travis.yml +9 -19
- data/CHANGES.md +94 -42
- data/Gemfile +11 -3
- data/Guardfile +10 -0
- data/LICENSE.txt +1 -1
- data/README.md +43 -136
- data/Rakefile +2 -0
- data/examples/echo_server.rb +1 -0
- data/ext/libev/Changes +9 -13
- data/ext/libev/ev.c +100 -74
- data/ext/libev/ev.h +4 -9
- data/ext/libev/ev_epoll.c +6 -3
- data/ext/libev/ev_kqueue.c +8 -4
- data/ext/libev/ev_poll.c +6 -3
- data/ext/libev/ev_port.c +8 -4
- data/ext/libev/ev_select.c +4 -2
- data/ext/nio4r/bytebuffer.c +265 -257
- data/ext/nio4r/extconf.rb +3 -9
- data/ext/nio4r/monitor.c +93 -46
- data/ext/nio4r/nio4r.h +6 -16
- data/ext/nio4r/org/nio4r/ByteBuffer.java +193 -209
- data/ext/nio4r/org/nio4r/Monitor.java +164 -0
- data/ext/nio4r/org/nio4r/Nio4r.java +13 -391
- data/ext/nio4r/org/nio4r/Selector.java +278 -0
- data/ext/nio4r/selector.c +72 -64
- data/lib/nio.rb +3 -3
- data/lib/nio/bytebuffer.rb +179 -132
- data/lib/nio/monitor.rb +64 -4
- data/lib/nio/selector.rb +36 -13
- data/lib/nio/version.rb +1 -1
- data/nio4r.gemspec +25 -19
- data/spec/nio/acceptables_spec.rb +6 -4
- data/spec/nio/bytebuffer_spec.rb +323 -51
- data/spec/nio/monitor_spec.rb +122 -79
- data/spec/nio/selectables/pipe_spec.rb +5 -1
- data/spec/nio/selectables/ssl_socket_spec.rb +15 -12
- data/spec/nio/selectables/tcp_socket_spec.rb +42 -31
- data/spec/nio/selectables/udp_socket_spec.rb +2 -0
- data/spec/nio/selector_spec.rb +10 -4
- data/spec/spec_helper.rb +24 -3
- data/spec/support/selectable_examples.rb +7 -5
- data/tasks/extension.rake +2 -0
- data/tasks/rspec.rake +2 -0
- data/tasks/rubocop.rake +2 -0
- metadata +18 -15
- data/.rubocop_todo.yml +0 -35
| @@ -0,0 +1,278 @@ | |
| 1 | 
            +
            package org.nio4r;
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            import java.util.Iterator;
         | 
| 4 | 
            +
            import java.util.Map;
         | 
| 5 | 
            +
            import java.util.HashMap;
         | 
| 6 | 
            +
            import java.io.IOException;
         | 
| 7 | 
            +
            import java.nio.channels.Channel;
         | 
| 8 | 
            +
            import java.nio.channels.SelectableChannel;
         | 
| 9 | 
            +
            import java.nio.channels.SelectionKey;
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            import org.jruby.Ruby;
         | 
| 12 | 
            +
            import org.jruby.RubyArray;
         | 
| 13 | 
            +
            import org.jruby.RubyClass;
         | 
| 14 | 
            +
            import org.jruby.RubyIO;
         | 
| 15 | 
            +
            import org.jruby.RubyNumeric;
         | 
| 16 | 
            +
            import org.jruby.RubyObject;
         | 
| 17 | 
            +
            import org.jruby.anno.JRubyMethod;
         | 
| 18 | 
            +
            import org.jruby.runtime.Block;
         | 
| 19 | 
            +
            import org.jruby.runtime.ThreadContext;
         | 
| 20 | 
            +
            import org.jruby.runtime.builtin.IRubyObject;
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            import org.nio4r.Monitor;
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            public class Selector extends RubyObject {
         | 
| 25 | 
            +
                private java.nio.channels.Selector selector;
         | 
| 26 | 
            +
                private HashMap<SelectableChannel,SelectionKey> cancelledKeys;
         | 
| 27 | 
            +
                private volatile boolean wakeupFired;
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                public Selector(final Ruby ruby, RubyClass rubyClass) {
         | 
| 30 | 
            +
                    super(ruby, rubyClass);
         | 
| 31 | 
            +
                }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                @JRubyMethod
         | 
| 34 | 
            +
                public IRubyObject initialize(ThreadContext context) {
         | 
| 35 | 
            +
                    this.cancelledKeys = new HashMap<SelectableChannel,SelectionKey>();
         | 
| 36 | 
            +
                    this.wakeupFired = false;
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    try {
         | 
| 39 | 
            +
                        this.selector = java.nio.channels.Selector.open();
         | 
| 40 | 
            +
                    } catch(IOException ie) {
         | 
| 41 | 
            +
                        throw context.runtime.newIOError(ie.getLocalizedMessage());
         | 
| 42 | 
            +
                    }
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    return context.nil;
         | 
| 45 | 
            +
                }
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                @JRubyMethod
         | 
| 48 | 
            +
                public IRubyObject backend(ThreadContext context) {
         | 
| 49 | 
            +
                    return context.runtime.newSymbol("java");
         | 
| 50 | 
            +
                }
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                @JRubyMethod
         | 
| 53 | 
            +
                public IRubyObject close(ThreadContext context) {
         | 
| 54 | 
            +
                    try {
         | 
| 55 | 
            +
                        this.selector.close();
         | 
| 56 | 
            +
                    } catch(IOException ie) {
         | 
| 57 | 
            +
                        throw context.runtime.newIOError(ie.getLocalizedMessage());
         | 
| 58 | 
            +
                    }
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    return context.nil;
         | 
| 61 | 
            +
                }
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                @JRubyMethod(name = "closed?")
         | 
| 64 | 
            +
                public IRubyObject isClosed(ThreadContext context) {
         | 
| 65 | 
            +
                    Ruby runtime = context.getRuntime();
         | 
| 66 | 
            +
                    return this.selector.isOpen() ? runtime.getFalse() : runtime.getTrue();
         | 
| 67 | 
            +
                }
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                @JRubyMethod(name = "empty?")
         | 
| 70 | 
            +
                public IRubyObject isEmpty(ThreadContext context) {
         | 
| 71 | 
            +
                    Ruby runtime = context.getRuntime();
         | 
| 72 | 
            +
                    return this.selector.keys().isEmpty() ? runtime.getTrue() : runtime.getFalse();
         | 
| 73 | 
            +
                }
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                @JRubyMethod
         | 
| 76 | 
            +
                public IRubyObject register(ThreadContext context, IRubyObject io, IRubyObject interests) {
         | 
| 77 | 
            +
                    Ruby runtime = context.getRuntime();
         | 
| 78 | 
            +
                    Channel rawChannel = RubyIO.convertToIO(context, io).getChannel();
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                    if(!this.selector.isOpen()) {
         | 
| 81 | 
            +
                        throw context.getRuntime().newIOError("selector is closed");
         | 
| 82 | 
            +
                    }
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                    if(!(rawChannel instanceof SelectableChannel)) {
         | 
| 85 | 
            +
                        throw runtime.newArgumentError("not a selectable IO object");
         | 
| 86 | 
            +
                    }
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                    SelectableChannel channel = (SelectableChannel)rawChannel;
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                    try {
         | 
| 91 | 
            +
                        channel.configureBlocking(false);
         | 
| 92 | 
            +
                    } catch(IOException ie) {
         | 
| 93 | 
            +
                        throw runtime.newIOError(ie.getLocalizedMessage());
         | 
| 94 | 
            +
                    }
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                    int interestOps = Nio4r.symbolToInterestOps(runtime, channel, interests);
         | 
| 97 | 
            +
                    SelectionKey key;
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                    key = this.cancelledKeys.remove(channel);
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                    if(key != null) {
         | 
| 102 | 
            +
                        key.interestOps(interestOps);
         | 
| 103 | 
            +
                    } else {
         | 
| 104 | 
            +
                        try {
         | 
| 105 | 
            +
                            key = channel.register(this.selector, interestOps);
         | 
| 106 | 
            +
                        } catch(java.lang.IllegalArgumentException ia) {
         | 
| 107 | 
            +
                            throw runtime.newArgumentError("mode not supported for this object: " + interests);
         | 
| 108 | 
            +
                        } catch(java.nio.channels.ClosedChannelException cce) {
         | 
| 109 | 
            +
                            throw context.runtime.newIOError(cce.getLocalizedMessage());
         | 
| 110 | 
            +
                        }
         | 
| 111 | 
            +
                    }
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                    RubyClass monitorClass = runtime.getModule("NIO").getClass("Monitor");
         | 
| 114 | 
            +
                    Monitor monitor = (Monitor)monitorClass.newInstance(context, io, interests, this, null);
         | 
| 115 | 
            +
                    monitor.setSelectionKey(key);
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                    return monitor;
         | 
| 118 | 
            +
                }
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                @JRubyMethod
         | 
| 121 | 
            +
                public IRubyObject deregister(ThreadContext context, IRubyObject io) {
         | 
| 122 | 
            +
                    Ruby runtime = context.getRuntime();
         | 
| 123 | 
            +
                    Channel rawChannel = RubyIO.convertToIO(context, io).getChannel();
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                    if(!(rawChannel instanceof SelectableChannel)) {
         | 
| 126 | 
            +
                        throw runtime.newArgumentError("not a selectable IO object");
         | 
| 127 | 
            +
                    }
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                    SelectableChannel channel = (SelectableChannel)rawChannel;
         | 
| 130 | 
            +
                    SelectionKey key = channel.keyFor(this.selector);
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                    if(key == null)
         | 
| 133 | 
            +
                        return context.nil;
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                    Monitor monitor = (Monitor)key.attachment();
         | 
| 136 | 
            +
                    monitor.close(context, runtime.getFalse());
         | 
| 137 | 
            +
                    cancelledKeys.put(channel, key);
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                    return monitor;
         | 
| 140 | 
            +
                }
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                @JRubyMethod(name = "registered?")
         | 
| 143 | 
            +
                public IRubyObject isRegistered(ThreadContext context, IRubyObject io) {
         | 
| 144 | 
            +
                    Ruby runtime = context.getRuntime();
         | 
| 145 | 
            +
                    Channel rawChannel = RubyIO.convertToIO(context, io).getChannel();
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                    if(!(rawChannel instanceof SelectableChannel)) {
         | 
| 148 | 
            +
                        throw runtime.newArgumentError("not a selectable IO object");
         | 
| 149 | 
            +
                    }
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                    SelectableChannel channel = (SelectableChannel)rawChannel;
         | 
| 152 | 
            +
                    SelectionKey key = channel.keyFor(this.selector);
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                    if(key == null)
         | 
| 155 | 
            +
                        return context.nil;
         | 
| 156 | 
            +
             | 
| 157 | 
            +
             | 
| 158 | 
            +
                    if(((Monitor)key.attachment()).isClosed(context) == runtime.getTrue()) {
         | 
| 159 | 
            +
                        return runtime.getFalse();
         | 
| 160 | 
            +
                    } else {
         | 
| 161 | 
            +
                        return runtime.getTrue();
         | 
| 162 | 
            +
                    }
         | 
| 163 | 
            +
                }
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                @JRubyMethod
         | 
| 166 | 
            +
                public synchronized IRubyObject select(ThreadContext context, Block block) {
         | 
| 167 | 
            +
                    return select(context, context.nil, block);
         | 
| 168 | 
            +
                }
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                @JRubyMethod
         | 
| 171 | 
            +
                public synchronized IRubyObject select(ThreadContext context, IRubyObject timeout, Block block) {
         | 
| 172 | 
            +
                    Ruby runtime = context.getRuntime();
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                    if(!this.selector.isOpen()) {
         | 
| 175 | 
            +
                        throw context.getRuntime().newIOError("selector is closed");
         | 
| 176 | 
            +
                    }
         | 
| 177 | 
            +
             | 
| 178 | 
            +
                    this.wakeupFired = false;
         | 
| 179 | 
            +
                    int ready = doSelect(runtime, context, timeout);
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                    /* Timeout */
         | 
| 182 | 
            +
                    if(ready <= 0 && !this.wakeupFired) {
         | 
| 183 | 
            +
                        return context.nil;
         | 
| 184 | 
            +
                    }
         | 
| 185 | 
            +
             | 
| 186 | 
            +
                    RubyArray array = null;
         | 
| 187 | 
            +
             | 
| 188 | 
            +
                    if(!block.isGiven()) {
         | 
| 189 | 
            +
                        array = runtime.newArray(this.selector.selectedKeys().size());
         | 
| 190 | 
            +
                    }
         | 
| 191 | 
            +
             | 
| 192 | 
            +
                    Iterator selectedKeys = this.selector.selectedKeys().iterator();
         | 
| 193 | 
            +
                    while(selectedKeys.hasNext()) {
         | 
| 194 | 
            +
                        SelectionKey key = (SelectionKey)selectedKeys.next();
         | 
| 195 | 
            +
                        processKey(key);
         | 
| 196 | 
            +
                        selectedKeys.remove();
         | 
| 197 | 
            +
             | 
| 198 | 
            +
                        if(block.isGiven()) {
         | 
| 199 | 
            +
                            block.call(context, (IRubyObject)key.attachment());
         | 
| 200 | 
            +
                        } else {
         | 
| 201 | 
            +
                            array.add(key.attachment());
         | 
| 202 | 
            +
                        }
         | 
| 203 | 
            +
                    }
         | 
| 204 | 
            +
             | 
| 205 | 
            +
                    if(block.isGiven()) {
         | 
| 206 | 
            +
                        return RubyNumeric.int2fix(runtime, ready);
         | 
| 207 | 
            +
                    } else {
         | 
| 208 | 
            +
                        return array;
         | 
| 209 | 
            +
                    }
         | 
| 210 | 
            +
                }
         | 
| 211 | 
            +
             | 
| 212 | 
            +
                /* Run the selector */
         | 
| 213 | 
            +
                private int doSelect(Ruby runtime, ThreadContext context, IRubyObject timeout) {
         | 
| 214 | 
            +
                    int result;
         | 
| 215 | 
            +
             | 
| 216 | 
            +
                    cancelKeys();
         | 
| 217 | 
            +
                    try {
         | 
| 218 | 
            +
                        context.getThread().beforeBlockingCall();
         | 
| 219 | 
            +
                        if(timeout.isNil()) {
         | 
| 220 | 
            +
                            result = this.selector.select();
         | 
| 221 | 
            +
                        } else {
         | 
| 222 | 
            +
                            double t = RubyNumeric.num2dbl(timeout);
         | 
| 223 | 
            +
                            if(t == 0) {
         | 
| 224 | 
            +
                                result = this.selector.selectNow();
         | 
| 225 | 
            +
                            } else if(t < 0) {
         | 
| 226 | 
            +
                                throw runtime.newArgumentError("time interval must be positive");
         | 
| 227 | 
            +
                            } else {
         | 
| 228 | 
            +
                                long timeoutMilliSeconds = (long)(t * 1000);
         | 
| 229 | 
            +
                                if(timeoutMilliSeconds == 0) {
         | 
| 230 | 
            +
                                  result = this.selector.selectNow();
         | 
| 231 | 
            +
                                } else {
         | 
| 232 | 
            +
                                  result = this.selector.select(timeoutMilliSeconds);
         | 
| 233 | 
            +
                                }
         | 
| 234 | 
            +
                            }
         | 
| 235 | 
            +
                        }
         | 
| 236 | 
            +
                        context.getThread().afterBlockingCall();
         | 
| 237 | 
            +
                        return result;
         | 
| 238 | 
            +
                    } catch(IOException ie) {
         | 
| 239 | 
            +
                        throw runtime.newIOError(ie.getLocalizedMessage());
         | 
| 240 | 
            +
                    }
         | 
| 241 | 
            +
                }
         | 
| 242 | 
            +
             | 
| 243 | 
            +
                /* Flush our internal buffer of cancelled keys */
         | 
| 244 | 
            +
                private void cancelKeys() {
         | 
| 245 | 
            +
                    Iterator cancelledKeys = this.cancelledKeys.entrySet().iterator();
         | 
| 246 | 
            +
                    while(cancelledKeys.hasNext()) {
         | 
| 247 | 
            +
                        Map.Entry entry = (Map.Entry)cancelledKeys.next();
         | 
| 248 | 
            +
                        SelectionKey key = (SelectionKey)entry.getValue();
         | 
| 249 | 
            +
                        key.cancel();
         | 
| 250 | 
            +
                        cancelledKeys.remove();
         | 
| 251 | 
            +
                    }
         | 
| 252 | 
            +
                }
         | 
| 253 | 
            +
             | 
| 254 | 
            +
                // Remove connect interest from connected sockets
         | 
| 255 | 
            +
                // See: http://stackoverflow.com/questions/204186/java-nio-select-returns-without-selected-keys-why
         | 
| 256 | 
            +
                private void processKey(SelectionKey key) {
         | 
| 257 | 
            +
                    if((key.readyOps() & SelectionKey.OP_CONNECT) != 0) {
         | 
| 258 | 
            +
                        int interestOps = key.interestOps();
         | 
| 259 | 
            +
             | 
| 260 | 
            +
                        interestOps &= ~SelectionKey.OP_CONNECT;
         | 
| 261 | 
            +
                        interestOps |=  SelectionKey.OP_WRITE;
         | 
| 262 | 
            +
             | 
| 263 | 
            +
                        key.interestOps(interestOps);
         | 
| 264 | 
            +
                    }
         | 
| 265 | 
            +
                }
         | 
| 266 | 
            +
             | 
| 267 | 
            +
                @JRubyMethod
         | 
| 268 | 
            +
                public IRubyObject wakeup(ThreadContext context) {
         | 
| 269 | 
            +
                    if(!this.selector.isOpen()) {
         | 
| 270 | 
            +
                        throw context.getRuntime().newIOError("selector is closed");
         | 
| 271 | 
            +
                    }
         | 
| 272 | 
            +
             | 
| 273 | 
            +
                    this.wakeupFired = true;
         | 
| 274 | 
            +
                    this.selector.wakeup();
         | 
| 275 | 
            +
             | 
| 276 | 
            +
                    return context.nil;
         | 
| 277 | 
            +
                }
         | 
| 278 | 
            +
            }
         | 
    
        data/ext/nio4r/selector.c
    CHANGED
    
    | @@ -29,6 +29,7 @@ static void NIO_Selector_free(struct NIO_Selector *loop); | |
| 29 29 |  | 
| 30 30 | 
             
            /* Methods */
         | 
| 31 31 | 
             
            static VALUE NIO_Selector_initialize(VALUE self);
         | 
| 32 | 
            +
            static VALUE NIO_Selector_backend(VALUE self);
         | 
| 32 33 | 
             
            static VALUE NIO_Selector_register(VALUE self, VALUE selectable, VALUE interest);
         | 
| 33 34 | 
             
            static VALUE NIO_Selector_deregister(VALUE self, VALUE io);
         | 
| 34 35 | 
             
            static VALUE NIO_Selector_is_registered(VALUE self, VALUE io);
         | 
| @@ -48,8 +49,8 @@ static VALUE NIO_Selector_close_synchronized(VALUE *args); | |
| 48 49 | 
             
            static VALUE NIO_Selector_closed_synchronized(VALUE *args);
         | 
| 49 50 |  | 
| 50 51 | 
             
            static int NIO_Selector_run(struct NIO_Selector *selector, VALUE timeout);
         | 
| 51 | 
            -
            static void NIO_Selector_timeout_callback( | 
| 52 | 
            -
            static void NIO_Selector_wakeup_callback( | 
| 52 | 
            +
            static void NIO_Selector_timeout_callback(ev_loop *ev_loop, struct ev_timer *timer, int revents);
         | 
| 53 | 
            +
            static void NIO_Selector_wakeup_callback(ev_loop *ev_loop, struct ev_io *io, int revents);
         | 
| 53 54 |  | 
| 54 55 | 
             
            /* Default number of slots in the buffer for selected monitors */
         | 
| 55 56 | 
             
            #define INITIAL_READY_BUFFER 32
         | 
| @@ -65,6 +66,7 @@ void Init_NIO_Selector() | |
| 65 66 | 
             
                rb_define_alloc_func(cNIO_Selector, NIO_Selector_allocate);
         | 
| 66 67 |  | 
| 67 68 | 
             
                rb_define_method(cNIO_Selector, "initialize", NIO_Selector_initialize, 0);
         | 
| 69 | 
            +
                rb_define_method(cNIO_Selector, "backend", NIO_Selector_backend, 0);
         | 
| 68 70 | 
             
                rb_define_method(cNIO_Selector, "register", NIO_Selector_register, 2);
         | 
| 69 71 | 
             
                rb_define_method(cNIO_Selector, "deregister", NIO_Selector_deregister, 1);
         | 
| 70 72 | 
             
                rb_define_method(cNIO_Selector, "registered?", NIO_Selector_is_registered, 1);
         | 
| @@ -101,6 +103,7 @@ static VALUE NIO_Selector_allocate(VALUE klass) | |
| 101 103 |  | 
| 102 104 | 
             
                selector = (struct NIO_Selector *)xmalloc(sizeof(struct NIO_Selector));
         | 
| 103 105 | 
             
                selector->ev_loop = ev_loop_new(0);
         | 
| 106 | 
            +
             | 
| 104 107 | 
             
                ev_init(&selector->timer, NIO_Selector_timeout_callback);
         | 
| 105 108 |  | 
| 106 109 | 
             
                selector->wakeup_reader = fds[0];
         | 
| @@ -108,9 +111,10 @@ static VALUE NIO_Selector_allocate(VALUE klass) | |
| 108 111 |  | 
| 109 112 | 
             
                ev_io_init(&selector->wakeup, NIO_Selector_wakeup_callback, selector->wakeup_reader, EV_READ);
         | 
| 110 113 | 
             
                selector->wakeup.data = (void *)selector;
         | 
| 114 | 
            +
             | 
| 111 115 | 
             
                ev_io_start(selector->ev_loop, &selector->wakeup);
         | 
| 112 116 |  | 
| 113 | 
            -
                selector->closed = selector->selecting = selector->ready_count = 0;
         | 
| 117 | 
            +
                selector->closed = selector->selecting = selector->wakeup_fired = selector->ready_count = 0;
         | 
| 114 118 | 
             
                selector->ready_array = Qnil;
         | 
| 115 119 |  | 
| 116 120 | 
             
                return Data_Wrap_Struct(klass, NIO_Selector_mark, NIO_Selector_free, selector);
         | 
| @@ -139,6 +143,7 @@ static void NIO_Selector_shutdown(struct NIO_Selector *selector) | |
| 139 143 | 
             
                    ev_loop_destroy(selector->ev_loop);
         | 
| 140 144 | 
             
                    selector->ev_loop = 0;
         | 
| 141 145 | 
             
                }
         | 
| 146 | 
            +
             | 
| 142 147 | 
             
                selector->closed = 1;
         | 
| 143 148 | 
             
            }
         | 
| 144 149 |  | 
| @@ -165,6 +170,30 @@ static VALUE NIO_Selector_initialize(VALUE self) | |
| 165 170 | 
             
                return Qnil;
         | 
| 166 171 | 
             
            }
         | 
| 167 172 |  | 
| 173 | 
            +
            static VALUE NIO_Selector_backend(VALUE self) {
         | 
| 174 | 
            +
                struct NIO_Selector *selector;
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                Data_Get_Struct(self, struct NIO_Selector, selector);
         | 
| 177 | 
            +
                if(selector->closed) {
         | 
| 178 | 
            +
                    rb_raise(rb_eIOError, "selector is closed");
         | 
| 179 | 
            +
                }
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                switch (ev_backend(selector->ev_loop)) {
         | 
| 182 | 
            +
                    case EVBACKEND_EPOLL:
         | 
| 183 | 
            +
                        return ID2SYM(rb_intern("epoll"));
         | 
| 184 | 
            +
                    case EVBACKEND_POLL:
         | 
| 185 | 
            +
                        return ID2SYM(rb_intern("poll"));
         | 
| 186 | 
            +
                    case EVBACKEND_KQUEUE:
         | 
| 187 | 
            +
                        return ID2SYM(rb_intern("kqueue"));
         | 
| 188 | 
            +
                    case EVBACKEND_SELECT:
         | 
| 189 | 
            +
                        return ID2SYM(rb_intern("select"));
         | 
| 190 | 
            +
                    case EVBACKEND_PORT:
         | 
| 191 | 
            +
                        return ID2SYM(rb_intern("port"));
         | 
| 192 | 
            +
                }
         | 
| 193 | 
            +
             | 
| 194 | 
            +
                return ID2SYM(rb_intern("unknown"));
         | 
| 195 | 
            +
            }
         | 
| 196 | 
            +
             | 
| 168 197 | 
             
            /* Synchronize around a reentrant selector lock */
         | 
| 169 198 | 
             
            static VALUE NIO_Selector_synchronize(VALUE self, VALUE (*func)(VALUE *args), VALUE *args)
         | 
| 170 199 | 
             
            {
         | 
| @@ -309,82 +338,61 @@ static VALUE NIO_Selector_select_synchronized(VALUE *args) | |
| 309 338 | 
             
                }
         | 
| 310 339 |  | 
| 311 340 | 
             
                ready = NIO_Selector_run(selector, args[1]);
         | 
| 312 | 
            -
             | 
| 313 | 
            -
             | 
| 314 | 
            -
             | 
| 315 | 
            -
                     | 
| 316 | 
            -
                        ready_array = selector->ready_array;
         | 
| 341 | 
            +
             | 
| 342 | 
            +
                /* Timeout */
         | 
| 343 | 
            +
                if(ready < 0) {
         | 
| 344 | 
            +
                    if(!rb_block_given_p()) {
         | 
| 317 345 | 
             
                        selector->ready_array = Qnil;
         | 
| 318 | 
            -
                        return ready_array;
         | 
| 319 346 | 
             
                    }
         | 
| 347 | 
            +
             | 
| 348 | 
            +
                    return Qnil;
         | 
| 349 | 
            +
                }
         | 
| 350 | 
            +
             | 
| 351 | 
            +
                if(rb_block_given_p()) {
         | 
| 352 | 
            +
                    return INT2NUM(ready);
         | 
| 320 353 | 
             
                } else {
         | 
| 354 | 
            +
                    ready_array = selector->ready_array;
         | 
| 321 355 | 
             
                    selector->ready_array = Qnil;
         | 
| 322 | 
            -
                    return  | 
| 356 | 
            +
                    return ready_array;
         | 
| 323 357 | 
             
                }
         | 
| 324 358 | 
             
            }
         | 
| 325 359 |  | 
| 326 360 | 
             
            static int NIO_Selector_run(struct NIO_Selector *selector, VALUE timeout)
         | 
| 327 361 | 
             
            {
         | 
| 362 | 
            +
                int ev_run_flags = EVRUN_ONCE;
         | 
| 328 363 | 
             
                int result;
         | 
| 364 | 
            +
                double timeout_val;
         | 
| 365 | 
            +
             | 
| 329 366 | 
             
                selector->selecting = 1;
         | 
| 367 | 
            +
                selector->wakeup_fired = 0;
         | 
| 330 368 |  | 
| 331 | 
            -
             | 
| 332 | 
            -
             | 
| 333 | 
            -
                if(timeout != Qnil) {
         | 
| 334 | 
            -
                    /* It seems libev is not a fan of timers being zero, so fudge a little */
         | 
| 335 | 
            -
                    selector->timer.repeat = NUM2DBL(timeout) + 0.0001;
         | 
| 336 | 
            -
                    ev_timer_again(selector->ev_loop, &selector->timer);
         | 
| 337 | 
            -
                } else {
         | 
| 369 | 
            +
                if(timeout == Qnil) {
         | 
| 370 | 
            +
                    /* Don't fire a wakeup timeout if we weren't passed one */
         | 
| 338 371 | 
             
                    ev_timer_stop(selector->ev_loop, &selector->timer);
         | 
| 339 | 
            -
                }
         | 
| 340 | 
            -
            #else
         | 
| 341 | 
            -
                /* Store when we started the loop so we can calculate the timeout */
         | 
| 342 | 
            -
                ev_tstamp started_at = ev_now(selector->ev_loop);
         | 
| 343 | 
            -
            #endif
         | 
| 344 | 
            -
             | 
| 345 | 
            -
            #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
         | 
| 346 | 
            -
                /* libev is patched to release the GIL when it makes its system call */
         | 
| 347 | 
            -
                ev_loop(selector->ev_loop, EVLOOP_ONESHOT);
         | 
| 348 | 
            -
            #elif defined(HAVE_RB_THREAD_ALONE)
         | 
| 349 | 
            -
                /* If we're the only thread we can make a blocking system call */
         | 
| 350 | 
            -
                if(rb_thread_alone()) {
         | 
| 351 | 
            -
            #else
         | 
| 352 | 
            -
                /* If we don't have rb_thread_alone() we can't block */
         | 
| 353 | 
            -
                if(0) {
         | 
| 354 | 
            -
            #endif /* defined(HAVE_RB_THREAD_BLOCKING_REGION) */
         | 
| 355 | 
            -
             | 
| 356 | 
            -
            #if !defined(HAVE_RB_THREAD_BLOCKING_REGION) && !defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
         | 
| 357 | 
            -
                    TRAP_BEG;
         | 
| 358 | 
            -
                    ev_loop(selector->ev_loop, EVLOOP_ONESHOT);
         | 
| 359 | 
            -
                    TRAP_END;
         | 
| 360 372 | 
             
                } else {
         | 
| 361 | 
            -
                     | 
| 362 | 
            -
             | 
| 363 | 
            -
             | 
| 364 | 
            -
             | 
| 365 | 
            -
             | 
| 366 | 
            -
                     | 
| 367 | 
            -
             | 
| 368 | 
            -
                         | 
| 369 | 
            -
                        ev_loop(selector->ev_loop, EVLOOP_ONESHOT);
         | 
| 370 | 
            -
                        TRAP_END;
         | 
| 371 | 
            -
             | 
| 372 | 
            -
                        /* Run the next green thread */
         | 
| 373 | 
            -
                        rb_thread_schedule();
         | 
| 374 | 
            -
             | 
| 375 | 
            -
                        /* Break if the timeout has elapsed */
         | 
| 376 | 
            -
                        if(timeout != Qnil && ev_now(selector->ev_loop) - started_at >= NUM2DBL(timeout))
         | 
| 377 | 
            -
                            break;
         | 
| 373 | 
            +
                    timeout_val = NUM2DBL(timeout);
         | 
| 374 | 
            +
                    if(timeout_val == 0) {
         | 
| 375 | 
            +
                        /* If we've been given an explicit timeout of 0, perform a non-blocking
         | 
| 376 | 
            +
                           select operation */
         | 
| 377 | 
            +
                        ev_run_flags = EVRUN_NOWAIT;
         | 
| 378 | 
            +
                    } else {
         | 
| 379 | 
            +
                        selector->timer.repeat = timeout_val;
         | 
| 380 | 
            +
                        ev_timer_again(selector->ev_loop, &selector->timer);
         | 
| 378 381 | 
             
                    }
         | 
| 379 | 
            -
             | 
| 380 | 
            -
                    ev_timer_stop(selector->ev_loop, &selector->timer);
         | 
| 381 382 | 
             
                }
         | 
| 382 | 
            -
             | 
| 383 | 
            +
             | 
| 384 | 
            +
                /* libev is patched to release the GIL when it makes its system call */
         | 
| 385 | 
            +
                ev_run(selector->ev_loop, ev_run_flags);
         | 
| 383 386 |  | 
| 384 387 | 
             
                result = selector->ready_count;
         | 
| 385 388 | 
             
                selector->selecting = selector->ready_count = 0;
         | 
| 386 389 |  | 
| 387 | 
            -
                 | 
| 390 | 
            +
                if(result > 0 || selector->wakeup_fired) {
         | 
| 391 | 
            +
                    selector->wakeup_fired = 0;
         | 
| 392 | 
            +
                    return result;
         | 
| 393 | 
            +
                } else {
         | 
| 394 | 
            +
                    return -1;
         | 
| 395 | 
            +
                }
         | 
| 388 396 | 
             
            }
         | 
| 389 397 |  | 
| 390 398 | 
             
            /* Wake the selector up from another thread */
         | 
| @@ -397,7 +405,9 @@ static VALUE NIO_Selector_wakeup(VALUE self) | |
| 397 405 | 
             
                    rb_raise(rb_eIOError, "selector is closed");
         | 
| 398 406 | 
             
                }
         | 
| 399 407 |  | 
| 408 | 
            +
                selector->wakeup_fired = 1;
         | 
| 400 409 | 
             
                write(selector->wakeup_writer, "\0", 1);
         | 
| 410 | 
            +
             | 
| 401 411 | 
             
                return Qnil;
         | 
| 402 412 | 
             
            }
         | 
| 403 413 |  | 
| @@ -445,14 +455,12 @@ static VALUE NIO_Selector_is_empty(VALUE self) | |
| 445 455 |  | 
| 446 456 |  | 
| 447 457 | 
             
            /* Called whenever a timeout fires on the event loop */
         | 
| 448 | 
            -
            static void NIO_Selector_timeout_callback( | 
| 458 | 
            +
            static void NIO_Selector_timeout_callback(ev_loop *ev_loop, struct ev_timer *timer, int revents)
         | 
| 449 459 | 
             
            {
         | 
| 450 | 
            -
                /* We don't actually need to do anything here, the mere firing of the
         | 
| 451 | 
            -
                   timer is sufficient to interrupt the selector. However, libev still wants a callback */
         | 
| 452 460 | 
             
            }
         | 
| 453 461 |  | 
| 454 462 | 
             
            /* Called whenever a wakeup request is sent to a selector */
         | 
| 455 | 
            -
            static void NIO_Selector_wakeup_callback( | 
| 463 | 
            +
            static void NIO_Selector_wakeup_callback(ev_loop *ev_loop, struct ev_io *io, int revents)
         | 
| 456 464 | 
             
            {
         | 
| 457 465 | 
             
                char buffer[128];
         | 
| 458 466 | 
             
                struct NIO_Selector *selector = (struct NIO_Selector *)io->data;
         | 
| @@ -463,7 +471,7 @@ static void NIO_Selector_wakeup_callback(struct ev_loop *ev_loop, struct ev_io * | |
| 463 471 | 
             
            }
         | 
| 464 472 |  | 
| 465 473 | 
             
            /* libev callback fired whenever a monitor gets an event */
         | 
| 466 | 
            -
            void NIO_Selector_monitor_callback( | 
| 474 | 
            +
            void NIO_Selector_monitor_callback(ev_loop *ev_loop, struct ev_io *io, int revents)
         | 
| 467 475 | 
             
            {
         | 
| 468 476 | 
             
                struct NIO_Monitor *monitor_data = (struct NIO_Monitor *)io->data;
         | 
| 469 477 | 
             
                struct NIO_Selector *selector = monitor_data->selector;
         |