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 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
- * ~200 lines of Ruby code
161
- * ~700 lines of "custom" C code (not counting libev)
162
- * ~400 lines of Java code
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
- try {
151
- key = channel.register(selector, interestOps);
152
- } catch(java.lang.IllegalArgumentException ia) {
153
- throw runtime.newArgumentError("mode not supported for this object: " + interests);
154
- } catch(java.nio.channels.ClosedChannelException cce) {
155
- throw context.runtime.newIOError(cce.getLocalizedMessage());
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 (selectedKeys.hasNext()) {
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 k) {
338
- key = k;
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
- key.cancel();
400
- closed = context.getRuntime().getTrue();
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
@@ -1,3 +1,3 @@
1
1
  module NIO
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.1"
3
3
  end
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
- # gem.extensions = ["ext/nio4r/extconf.rb"]
18
- gem.platform = 'java'
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"
@@ -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
- pipe, _ = IO.pipe
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
- pipe, _ = IO.pipe
31
+ subject.register(reader, :r)
32
32
 
33
- subject.register(pipe, :r)
34
- monitor = subject.deregister(pipe)
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
- pipe, _ = IO.pipe
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
- unreadable, _ = IO.pipe
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(readable_monitor)
105
- selected.should_not include(unreadable_monitor)
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
@@ -2,7 +2,7 @@
2
2
  name: nio4r
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.3.0
5
+ version: 0.3.1
6
6
  platform: java
7
7
  authors:
8
8
  - Tony Arcieri