nio4r 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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/spec/nio/selector_spec.rb +30 -20
- metadata +7 -7
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/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
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nio4r
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2012-02-12 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake-compiler
|
16
|
-
requirement: &
|
16
|
+
requirement: &70256512025560 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70256512025560
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rake
|
27
|
-
requirement: &
|
27
|
+
requirement: &70256512024940 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70256512024940
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rspec
|
38
|
-
requirement: &
|
38
|
+
requirement: &70256512040740 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70256512040740
|
47
47
|
description: New IO for Ruby
|
48
48
|
email:
|
49
49
|
- tony.arcieri@gmail.com
|