nio4r 0.3.0-java → 0.3.1-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.
- data/README.md +5 -4
- data/ext/nio4r/org/nio4r/Nio4r.java +61 -32
- data/ext/nio4r/selector.c +2 -3
- data/lib/nio/version.rb +1 -1
- data/nio4r.gemspec +2 -2
- data/spec/nio/selector_spec.rb +30 -20
- metadata +1 -1
data/README.md
CHANGED
@@ -156,10 +156,11 @@ top of. nio4r provides a minimal API such that individual Ruby implementers
|
|
156
156
|
may choose to produce optimized versions for their platform, without having
|
157
157
|
to maintain a large codebase.
|
158
158
|
|
159
|
-
As of the time of writing, the current implementation is
|
160
|
-
|
161
|
-
*
|
162
|
-
*
|
159
|
+
As of the time of writing, the current implementation is (approximately):
|
160
|
+
|
161
|
+
* 200 lines of Ruby code
|
162
|
+
* 700 lines of "custom" C code (not counting libev)
|
163
|
+
* 400 lines of Java code
|
163
164
|
|
164
165
|
nio4r is also not a replacement for Kinder Gentler IO (KGIO), a set of
|
165
166
|
advanced Ruby IO APIs. At some point in the future nio4r might provide a
|
@@ -1,6 +1,8 @@
|
|
1
1
|
package org.nio4r;
|
2
2
|
|
3
3
|
import java.util.Iterator;
|
4
|
+
import java.util.Map;
|
5
|
+
import java.util.HashMap;
|
4
6
|
import java.io.IOException;
|
5
7
|
import java.nio.channels.Channel;
|
6
8
|
import java.nio.channels.SocketChannel;
|
@@ -94,6 +96,7 @@ public class Nio4r implements Library {
|
|
94
96
|
|
95
97
|
public class Selector extends RubyObject {
|
96
98
|
private java.nio.channels.Selector selector;
|
99
|
+
private HashMap<SelectableChannel,SelectionKey> cancelledKeys;
|
97
100
|
|
98
101
|
public Selector(final Ruby ruby, RubyClass rubyClass) {
|
99
102
|
super(ruby, rubyClass);
|
@@ -101,8 +104,9 @@ public class Nio4r implements Library {
|
|
101
104
|
|
102
105
|
@JRubyMethod
|
103
106
|
public IRubyObject initialize(ThreadContext context) {
|
107
|
+
this.cancelledKeys = new HashMap<SelectableChannel,SelectionKey>();
|
104
108
|
try {
|
105
|
-
selector = java.nio.channels.Selector.open();
|
109
|
+
this.selector = java.nio.channels.Selector.open();
|
106
110
|
} catch(IOException ie) {
|
107
111
|
throw context.runtime.newIOError(ie.getLocalizedMessage());
|
108
112
|
}
|
@@ -113,7 +117,7 @@ public class Nio4r implements Library {
|
|
113
117
|
@JRubyMethod
|
114
118
|
public IRubyObject close(ThreadContext context) {
|
115
119
|
try {
|
116
|
-
selector.close();
|
120
|
+
this.selector.close();
|
117
121
|
} catch(IOException ie) {
|
118
122
|
throw context.runtime.newIOError(ie.getLocalizedMessage());
|
119
123
|
}
|
@@ -124,7 +128,7 @@ public class Nio4r implements Library {
|
|
124
128
|
@JRubyMethod(name = "closed?")
|
125
129
|
public IRubyObject isClosed(ThreadContext context) {
|
126
130
|
Ruby runtime = context.getRuntime();
|
127
|
-
return selector.isOpen() ? runtime.getFalse() : runtime.getTrue();
|
131
|
+
return this.selector.isOpen() ? runtime.getFalse() : runtime.getTrue();
|
128
132
|
}
|
129
133
|
|
130
134
|
@JRubyMethod
|
@@ -147,12 +151,18 @@ public class Nio4r implements Library {
|
|
147
151
|
int interestOps = Nio4r.symbolToInterestOps(runtime, channel, interests);
|
148
152
|
SelectionKey key;
|
149
153
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
}
|
155
|
-
|
154
|
+
key = this.cancelledKeys.remove(channel);
|
155
|
+
|
156
|
+
if(key != null) {
|
157
|
+
key.interestOps(interestOps);
|
158
|
+
} else {
|
159
|
+
try {
|
160
|
+
key = channel.register(this.selector, interestOps);
|
161
|
+
} catch(java.lang.IllegalArgumentException ia) {
|
162
|
+
throw runtime.newArgumentError("mode not supported for this object: " + interests);
|
163
|
+
} catch(java.nio.channels.ClosedChannelException cce) {
|
164
|
+
throw context.runtime.newIOError(cce.getLocalizedMessage());
|
165
|
+
}
|
156
166
|
}
|
157
167
|
|
158
168
|
RubyClass monitorClass = runtime.getModule("NIO").getClass("Monitor");
|
@@ -172,13 +182,14 @@ public class Nio4r implements Library {
|
|
172
182
|
}
|
173
183
|
|
174
184
|
SelectableChannel channel = (SelectableChannel)raw_channel;
|
175
|
-
SelectionKey key = channel.keyFor(selector);
|
185
|
+
SelectionKey key = channel.keyFor(this.selector);
|
176
186
|
|
177
187
|
if(key == null)
|
178
188
|
return context.nil;
|
179
189
|
|
180
190
|
Monitor monitor = (Monitor)key.attachment();
|
181
|
-
monitor.close(context);
|
191
|
+
monitor.close(context, runtime.getFalse());
|
192
|
+
cancelledKeys.put(channel, key);
|
182
193
|
|
183
194
|
return monitor;
|
184
195
|
}
|
@@ -193,7 +204,7 @@ public class Nio4r implements Library {
|
|
193
204
|
}
|
194
205
|
|
195
206
|
SelectableChannel channel = (SelectableChannel)raw_channel;
|
196
|
-
SelectionKey key = channel.keyFor(selector);
|
207
|
+
SelectionKey key = channel.keyFor(this.selector);
|
197
208
|
|
198
209
|
if(key == null)
|
199
210
|
return context.nil;
|
@@ -222,11 +233,11 @@ public class Nio4r implements Library {
|
|
222
233
|
|
223
234
|
RubyArray array = null;
|
224
235
|
if(!block.isGiven()) {
|
225
|
-
array = runtime.newArray(selector.selectedKeys().size());
|
236
|
+
array = runtime.newArray(this.selector.selectedKeys().size());
|
226
237
|
}
|
227
238
|
|
228
|
-
Iterator selectedKeys = selector.selectedKeys().iterator();
|
229
|
-
while
|
239
|
+
Iterator selectedKeys = this.selector.selectedKeys().iterator();
|
240
|
+
while(selectedKeys.hasNext()) {
|
230
241
|
SelectionKey key = (SelectionKey)selectedKeys.next();
|
231
242
|
processKey(key);
|
232
243
|
selectedKeys.remove();
|
@@ -259,7 +270,7 @@ public class Nio4r implements Library {
|
|
259
270
|
if(ready <= 0)
|
260
271
|
return context.nil;
|
261
272
|
|
262
|
-
Iterator selectedKeys = selector.selectedKeys().iterator();
|
273
|
+
Iterator selectedKeys = this.selector.selectedKeys().iterator();
|
263
274
|
while (selectedKeys.hasNext()) {
|
264
275
|
SelectionKey key = (SelectionKey)selectedKeys.next();
|
265
276
|
processKey(key);
|
@@ -271,17 +282,25 @@ public class Nio4r implements Library {
|
|
271
282
|
}
|
272
283
|
|
273
284
|
private int doSelect(Ruby runtime, IRubyObject timeout) {
|
285
|
+
Iterator cancelledKeys = this.cancelledKeys.entrySet().iterator();
|
286
|
+
while(cancelledKeys.hasNext()) {
|
287
|
+
Map.Entry entry = (Map.Entry)cancelledKeys.next();
|
288
|
+
SelectionKey key = (SelectionKey)entry.getValue();
|
289
|
+
key.cancel();
|
290
|
+
cancelledKeys.remove();
|
291
|
+
}
|
292
|
+
|
274
293
|
try {
|
275
294
|
if(timeout.isNil()) {
|
276
|
-
return selector.select();
|
295
|
+
return this.selector.select();
|
277
296
|
} else {
|
278
297
|
double t = RubyNumeric.num2dbl(timeout);
|
279
298
|
if(t == 0) {
|
280
|
-
return selector.selectNow();
|
299
|
+
return this.selector.selectNow();
|
281
300
|
} else if(t < 0) {
|
282
301
|
throw runtime.newArgumentError("time interval must be positive");
|
283
302
|
} else {
|
284
|
-
return selector.select((long)(t * 1000));
|
303
|
+
return this.selector.select((long)(t * 1000));
|
285
304
|
}
|
286
305
|
}
|
287
306
|
} catch(IOException ie) {
|
@@ -304,11 +323,11 @@ public class Nio4r implements Library {
|
|
304
323
|
|
305
324
|
@JRubyMethod
|
306
325
|
public IRubyObject wakeup(ThreadContext context) {
|
307
|
-
if(!selector.isOpen()) {
|
326
|
+
if(!this.selector.isOpen()) {
|
308
327
|
throw context.getRuntime().newIOError("selector is closed");
|
309
328
|
}
|
310
329
|
|
311
|
-
selector.wakeup();
|
330
|
+
this.selector.wakeup();
|
312
331
|
return context.nil;
|
313
332
|
}
|
314
333
|
}
|
@@ -328,14 +347,14 @@ public class Nio4r implements Library {
|
|
328
347
|
this.interests = interests;
|
329
348
|
this.selector = selector;
|
330
349
|
|
331
|
-
value = context.nil;
|
332
|
-
closed = context.getRuntime().getFalse();
|
350
|
+
this.value = context.nil;
|
351
|
+
this.closed = context.getRuntime().getFalse();
|
333
352
|
|
334
353
|
return context.nil;
|
335
354
|
}
|
336
355
|
|
337
|
-
public void setSelectionKey(SelectionKey
|
338
|
-
key =
|
356
|
+
public void setSelectionKey(SelectionKey key) {
|
357
|
+
this.key = key;
|
339
358
|
key.attach(this);
|
340
359
|
}
|
341
360
|
|
@@ -362,7 +381,7 @@ public class Nio4r implements Library {
|
|
362
381
|
@JRubyMethod(name = "readable?")
|
363
382
|
public IRubyObject isReadable(ThreadContext context) {
|
364
383
|
Ruby runtime = context.getRuntime();
|
365
|
-
int readyOps = key.readyOps();
|
384
|
+
int readyOps = this.key.readyOps();
|
366
385
|
|
367
386
|
if((readyOps & SelectionKey.OP_READ) != 0 || (readyOps & SelectionKey.OP_ACCEPT) != 0) {
|
368
387
|
return runtime.getTrue();
|
@@ -374,7 +393,7 @@ public class Nio4r implements Library {
|
|
374
393
|
@JRubyMethod(name = {"writable?", "writeable?"})
|
375
394
|
public IRubyObject writable(ThreadContext context) {
|
376
395
|
Ruby runtime = context.getRuntime();
|
377
|
-
int readyOps = key.readyOps();
|
396
|
+
int readyOps = this.key.readyOps();
|
378
397
|
|
379
398
|
if((readyOps & SelectionKey.OP_WRITE) != 0 || (readyOps & SelectionKey.OP_CONNECT) != 0) {
|
380
399
|
return runtime.getTrue();
|
@@ -385,25 +404,35 @@ public class Nio4r implements Library {
|
|
385
404
|
|
386
405
|
@JRubyMethod(name = "value")
|
387
406
|
public IRubyObject getValue(ThreadContext context) {
|
388
|
-
return value;
|
407
|
+
return this.value;
|
389
408
|
}
|
390
409
|
|
391
410
|
@JRubyMethod(name = "value=")
|
392
411
|
public IRubyObject setValue(ThreadContext context, IRubyObject obj) {
|
393
|
-
value = obj;
|
412
|
+
this.value = obj;
|
394
413
|
return context.nil;
|
395
414
|
}
|
396
415
|
|
397
416
|
@JRubyMethod
|
398
417
|
public IRubyObject close(ThreadContext context) {
|
399
|
-
|
400
|
-
|
418
|
+
return close(context, context.getRuntime().getTrue());
|
419
|
+
}
|
420
|
+
|
421
|
+
@JRubyMethod
|
422
|
+
public IRubyObject close(ThreadContext context, IRubyObject deregister) {
|
423
|
+
Ruby runtime = context.getRuntime();
|
424
|
+
this.closed = runtime.getTrue();
|
425
|
+
|
426
|
+
if(deregister == runtime.getTrue()) {
|
427
|
+
selector.callMethod(context, "deregister", io);
|
428
|
+
}
|
429
|
+
|
401
430
|
return context.nil;
|
402
431
|
}
|
403
432
|
|
404
433
|
@JRubyMethod(name = "closed?")
|
405
434
|
public IRubyObject isClosed(ThreadContext context) {
|
406
|
-
return closed;
|
435
|
+
return this.closed;
|
407
436
|
}
|
408
437
|
}
|
409
438
|
}
|
data/ext/nio4r/selector.c
CHANGED
@@ -9,7 +9,6 @@
|
|
9
9
|
#include <fcntl.h>
|
10
10
|
|
11
11
|
static VALUE mNIO = Qnil;
|
12
|
-
static VALUE cNIO_Channel = Qnil;
|
13
12
|
static VALUE cNIO_Monitor = Qnil;
|
14
13
|
static VALUE cNIO_Selector = Qnil;
|
15
14
|
|
@@ -52,8 +51,6 @@ static void NIO_Selector_wakeup_callback(struct ev_loop *ev_loop, struct ev_io *
|
|
52
51
|
void Init_NIO_Selector()
|
53
52
|
{
|
54
53
|
mNIO = rb_define_module("NIO");
|
55
|
-
cNIO_Channel = rb_define_class_under(mNIO, "Channel", rb_cObject);
|
56
|
-
cNIO_Monitor = rb_define_class_under(mNIO, "Monitor", rb_cObject);
|
57
54
|
cNIO_Selector = rb_define_class_under(mNIO, "Selector", rb_cObject);
|
58
55
|
rb_define_alloc_func(cNIO_Selector, NIO_Selector_allocate);
|
59
56
|
|
@@ -66,6 +63,8 @@ void Init_NIO_Selector()
|
|
66
63
|
rb_define_method(cNIO_Selector, "wakeup", NIO_Selector_wakeup, 0);
|
67
64
|
rb_define_method(cNIO_Selector, "close", NIO_Selector_close, 0);
|
68
65
|
rb_define_method(cNIO_Selector, "closed?", NIO_Selector_closed, 0);
|
66
|
+
|
67
|
+
cNIO_Monitor = rb_define_class_under(mNIO, "Monitor", rb_cObject);
|
69
68
|
}
|
70
69
|
|
71
70
|
/* Create the libev event loop and incoming event buffer */
|
data/lib/nio/version.rb
CHANGED
data/nio4r.gemspec
CHANGED
@@ -14,8 +14,8 @@ Gem::Specification.new do |gem|
|
|
14
14
|
gem.name = "nio4r"
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = NIO::VERSION
|
17
|
-
|
18
|
-
gem.
|
17
|
+
gem.platform = "java"
|
18
|
+
#gem.extensions = ["ext/nio4r/extconf.rb"]
|
19
19
|
|
20
20
|
gem.add_development_dependency "rake-compiler"
|
21
21
|
gem.add_development_dependency "rake"
|
data/spec/nio/selector_spec.rb
CHANGED
@@ -6,11 +6,13 @@ require 'spec_helper'
|
|
6
6
|
TIMEOUT_PRECISION = 0.1
|
7
7
|
|
8
8
|
describe NIO::Selector do
|
9
|
+
let(:pair) { IO.pipe }
|
10
|
+
let(:reader) { pair.first }
|
11
|
+
let(:writer) { pair.last }
|
12
|
+
|
9
13
|
context "register" do
|
10
14
|
it "registers IO objects" do
|
11
|
-
|
12
|
-
|
13
|
-
monitor = subject.register(pipe, :r)
|
15
|
+
monitor = subject.register(reader, :r)
|
14
16
|
monitor.should_not be_closed
|
15
17
|
end
|
16
18
|
|
@@ -20,25 +22,37 @@ describe NIO::Selector do
|
|
20
22
|
end
|
21
23
|
|
22
24
|
it "knows which IO objects are registered" do
|
23
|
-
reader, writer = IO.pipe
|
24
25
|
subject.register(reader, :r)
|
25
|
-
|
26
26
|
subject.should be_registered(reader)
|
27
27
|
subject.should_not be_registered(writer)
|
28
28
|
end
|
29
29
|
|
30
30
|
it "deregisters IO objects" do
|
31
|
-
|
31
|
+
subject.register(reader, :r)
|
32
32
|
|
33
|
-
subject.
|
34
|
-
|
35
|
-
subject.should_not be_registered(pipe)
|
33
|
+
monitor = subject.deregister(reader)
|
34
|
+
subject.should_not be_registered(reader)
|
36
35
|
monitor.should be_closed
|
37
36
|
end
|
38
37
|
|
38
|
+
# This spec might seem a bit silly, but this actually something the
|
39
|
+
# Java NIO API specifically precludes that we need to work around
|
40
|
+
it "allows reregistration of the same IO object across select calls" do
|
41
|
+
monitor = subject.register(reader, :r)
|
42
|
+
writer << "ohai"
|
43
|
+
|
44
|
+
subject.select.should include monitor
|
45
|
+
reader.read(4).should == "ohai"
|
46
|
+
subject.deregister(reader)
|
47
|
+
|
48
|
+
new_monitor = subject.register(reader, :r)
|
49
|
+
writer << "thar"
|
50
|
+
subject.select.should include new_monitor
|
51
|
+
reader.read(4).should == "thar"
|
52
|
+
end
|
53
|
+
|
39
54
|
context "timeouts" do
|
40
55
|
it "waits for a timeout when selecting" do
|
41
|
-
reader, writer = IO.pipe
|
42
56
|
monitor = subject.register(reader, :r)
|
43
57
|
|
44
58
|
payload = "hi there"
|
@@ -56,7 +70,6 @@ describe NIO::Selector do
|
|
56
70
|
end
|
57
71
|
|
58
72
|
it "raises ArgumentError if given a negative timeout" do
|
59
|
-
reader, _ = IO.pipe
|
60
73
|
subject.register(reader, :r)
|
61
74
|
|
62
75
|
expect { subject.select(-1) }.to raise_exception(ArgumentError)
|
@@ -65,8 +78,7 @@ describe NIO::Selector do
|
|
65
78
|
|
66
79
|
context "wakeup" do
|
67
80
|
it "wakes up if signaled to from another thread" do
|
68
|
-
|
69
|
-
subject.register(pipe, :r)
|
81
|
+
subject.register(reader, :r)
|
70
82
|
|
71
83
|
thread = Thread.new do
|
72
84
|
started_at = Time.now
|
@@ -91,18 +103,16 @@ describe NIO::Selector do
|
|
91
103
|
|
92
104
|
context "select" do
|
93
105
|
it "selects IO objects" do
|
94
|
-
readable, writer = IO.pipe
|
95
106
|
writer << "ohai"
|
107
|
+
unready, _ = IO.pipe
|
96
108
|
|
97
|
-
|
98
|
-
|
99
|
-
readable_monitor = subject.register(readable, :r)
|
100
|
-
unreadable_monitor = subject.register(unreadable, :r)
|
109
|
+
reader_monitor = subject.register(reader, :r)
|
110
|
+
unready_monitor = subject.register(unready, :r)
|
101
111
|
|
102
112
|
selected = subject.select(0)
|
103
113
|
selected.size.should == 1
|
104
|
-
selected.should include(
|
105
|
-
selected.should_not include(
|
114
|
+
selected.should include(reader_monitor)
|
115
|
+
selected.should_not include(unready_monitor)
|
106
116
|
end
|
107
117
|
|
108
118
|
it "iterates across selected objects with a block" do
|