nio4r 2.0.0.pre-java → 2.1.0-java
Sign up to get free protection for your applications and to get access to all the features.
- 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;
|