nio4r 0.2.0 → 2.7.5

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.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/ext/libev/Changes +232 -3
  4. data/ext/libev/LICENSE +2 -1
  5. data/ext/libev/README +11 -10
  6. data/ext/libev/ev.c +2249 -473
  7. data/ext/libev/ev.h +165 -135
  8. data/ext/libev/ev_epoll.c +68 -36
  9. data/ext/libev/ev_iouring.c +694 -0
  10. data/ext/libev/ev_kqueue.c +44 -18
  11. data/ext/libev/ev_linuxaio.c +620 -0
  12. data/ext/libev/ev_poll.c +28 -20
  13. data/ext/libev/ev_port.c +23 -10
  14. data/ext/libev/ev_select.c +18 -12
  15. data/ext/libev/ev_vars.h +65 -19
  16. data/ext/libev/ev_win32.c +16 -7
  17. data/ext/libev/ev_wrap.h +236 -160
  18. data/ext/nio4r/.clang-format +16 -0
  19. data/ext/nio4r/bytebuffer.c +465 -0
  20. data/ext/nio4r/extconf.rb +44 -35
  21. data/ext/nio4r/libev.h +3 -4
  22. data/ext/nio4r/monitor.c +186 -54
  23. data/ext/nio4r/nio4r.h +17 -23
  24. data/ext/nio4r/nio4r_ext.c +11 -3
  25. data/ext/nio4r/org/nio4r/ByteBuffer.java +295 -0
  26. data/ext/nio4r/org/nio4r/Monitor.java +176 -0
  27. data/ext/nio4r/org/nio4r/Nio4r.java +104 -0
  28. data/ext/nio4r/org/nio4r/Selector.java +297 -0
  29. data/ext/nio4r/selector.c +350 -182
  30. data/lib/nio/bytebuffer.rb +235 -0
  31. data/lib/nio/monitor.rb +100 -8
  32. data/lib/nio/selector.rb +110 -44
  33. data/lib/nio/version.rb +8 -1
  34. data/lib/nio.rb +47 -16
  35. data/lib/nio4r.rb +7 -0
  36. data/license.md +80 -0
  37. data/readme.md +91 -0
  38. data/releases.md +343 -0
  39. data.tar.gz.sig +2 -0
  40. metadata +114 -79
  41. metadata.gz.sig +0 -0
  42. data/.gitignore +0 -20
  43. data/.rspec +0 -4
  44. data/.travis.yml +0 -7
  45. data/CHANGES.md +0 -10
  46. data/Gemfile +0 -4
  47. data/LICENSE.txt +0 -20
  48. data/README.md +0 -171
  49. data/Rakefile +0 -9
  50. data/examples/echo_server.rb +0 -38
  51. data/ext/libev/README.embed +0 -3
  52. data/ext/libev/test_libev_win32.c +0 -123
  53. data/lib/nio/jruby/monitor.rb +0 -42
  54. data/lib/nio/jruby/selector.rb +0 -135
  55. data/nio4r.gemspec +0 -22
  56. data/spec/nio/monitor_spec.rb +0 -46
  57. data/spec/nio/selector_spec.rb +0 -269
  58. data/spec/spec_helper.rb +0 -3
  59. data/tasks/extension.rake +0 -10
  60. data/tasks/rspec.rake +0 -7
@@ -0,0 +1,297 @@
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
+ import org.jruby.util.io.OpenFile;
22
+
23
+ public class Selector extends RubyObject {
24
+ private static final long serialVersionUID = -14562818539414873L;
25
+ private transient 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(meta = true)
34
+ public static IRubyObject backends(ThreadContext context, IRubyObject self) {
35
+ return context.runtime.newArray(context.runtime.newSymbol("java"));
36
+ }
37
+
38
+ @JRubyMethod
39
+ public IRubyObject initialize(ThreadContext context) {
40
+ initialize(context, context.runtime.newSymbol("java"));
41
+ return context.nil;
42
+ }
43
+
44
+ @JRubyMethod
45
+ public IRubyObject initialize(ThreadContext context, IRubyObject backend) {
46
+ if(backend != context.runtime.newSymbol("java") && !backend.isNil()) {
47
+ throw context.runtime.newArgumentError(":java is the only supported backend");
48
+ }
49
+
50
+ this.cancelledKeys = new HashMap<SelectableChannel,SelectionKey>();
51
+ this.wakeupFired = false;
52
+
53
+ try {
54
+ this.selector = java.nio.channels.Selector.open();
55
+ } catch(IOException ie) {
56
+ throw context.runtime.newIOError(ie.getLocalizedMessage());
57
+ }
58
+
59
+ return context.nil;
60
+ }
61
+
62
+ @JRubyMethod
63
+ public IRubyObject backend(ThreadContext context) {
64
+ return context.runtime.newSymbol("java");
65
+ }
66
+
67
+ @JRubyMethod
68
+ public IRubyObject close(ThreadContext context) {
69
+ try {
70
+ this.selector.close();
71
+ } catch(IOException ie) {
72
+ throw context.runtime.newIOError(ie.getLocalizedMessage());
73
+ }
74
+
75
+ return context.nil;
76
+ }
77
+
78
+ @JRubyMethod(name = "closed?")
79
+ public IRubyObject isClosed(ThreadContext context) {
80
+ Ruby runtime = context.getRuntime();
81
+ return this.selector.isOpen() ? runtime.getFalse() : runtime.getTrue();
82
+ }
83
+
84
+ @JRubyMethod(name = "empty?")
85
+ public IRubyObject isEmpty(ThreadContext context) {
86
+ Ruby runtime = context.getRuntime();
87
+ return this.selector.keys().isEmpty() ? runtime.getTrue() : runtime.getFalse();
88
+ }
89
+
90
+ @JRubyMethod
91
+ public IRubyObject register(ThreadContext context, IRubyObject io, IRubyObject interests) {
92
+ Ruby runtime = context.getRuntime();
93
+ Channel rawChannel = RubyIO.convertToIO(context, io).getChannel();
94
+
95
+ if(!this.selector.isOpen()) {
96
+ throw context.getRuntime().newIOError("selector is closed");
97
+ }
98
+
99
+ if(!(rawChannel instanceof SelectableChannel)) {
100
+ throw runtime.newArgumentError("not a selectable IO object");
101
+ }
102
+
103
+ SelectableChannel channel = (SelectableChannel)rawChannel;
104
+
105
+ try {
106
+ channel.configureBlocking(false);
107
+ } catch(IOException ie) {
108
+ throw runtime.newIOError(ie.getLocalizedMessage());
109
+ }
110
+
111
+ int interestOps = Nio4r.symbolToInterestOps(runtime, channel, interests);
112
+ SelectionKey key;
113
+
114
+ key = this.cancelledKeys.remove(channel);
115
+
116
+ if(key != null) {
117
+ key.interestOps(interestOps);
118
+ } else {
119
+ try {
120
+ key = channel.register(this.selector, interestOps);
121
+ } catch(java.lang.IllegalArgumentException ia) {
122
+ throw runtime.newArgumentError("mode not supported for this object: " + interests);
123
+ } catch(java.nio.channels.ClosedChannelException cce) {
124
+ throw context.runtime.newIOError(cce.getLocalizedMessage());
125
+ }
126
+ }
127
+
128
+ RubyClass monitorClass = runtime.getModule("NIO").getClass("Monitor");
129
+ Monitor monitor = (Monitor)monitorClass.newInstance(context, io, interests, this, null);
130
+ monitor.setSelectionKey(key);
131
+
132
+ return monitor;
133
+ }
134
+
135
+ @JRubyMethod
136
+ public IRubyObject deregister(ThreadContext context, IRubyObject io) {
137
+ Ruby runtime = context.getRuntime();
138
+ OpenFile file = RubyIO.convertToIO(context, io).getOpenFileInitialized();
139
+ if (file.fd() == null)
140
+ return context.nil;
141
+ Channel rawChannel = file.channel();
142
+
143
+ if(!(rawChannel instanceof SelectableChannel)) {
144
+ throw runtime.newArgumentError("not a selectable IO object");
145
+ }
146
+
147
+ SelectableChannel channel = (SelectableChannel)rawChannel;
148
+ SelectionKey key = channel.keyFor(this.selector);
149
+
150
+ if(key == null)
151
+ return context.nil;
152
+
153
+ Monitor monitor = (Monitor)key.attachment();
154
+ monitor.close(context, runtime.getFalse());
155
+ cancelledKeys.put(channel, key);
156
+
157
+ return monitor;
158
+ }
159
+
160
+ @JRubyMethod(name = "registered?")
161
+ public IRubyObject isRegistered(ThreadContext context, IRubyObject io) {
162
+ Ruby runtime = context.getRuntime();
163
+ Channel rawChannel = RubyIO.convertToIO(context, io).getChannel();
164
+
165
+ if(!(rawChannel instanceof SelectableChannel)) {
166
+ throw runtime.newArgumentError("not a selectable IO object");
167
+ }
168
+
169
+ SelectableChannel channel = (SelectableChannel)rawChannel;
170
+ SelectionKey key = channel.keyFor(this.selector);
171
+
172
+ if(key == null)
173
+ return context.nil;
174
+
175
+
176
+ if(((Monitor)key.attachment()).isClosed(context) == runtime.getTrue()) {
177
+ return runtime.getFalse();
178
+ } else {
179
+ return runtime.getTrue();
180
+ }
181
+ }
182
+
183
+ @JRubyMethod
184
+ public synchronized IRubyObject select(ThreadContext context, Block block) {
185
+ return select(context, context.nil, block);
186
+ }
187
+
188
+ @JRubyMethod
189
+ public synchronized IRubyObject select(ThreadContext context, IRubyObject timeout, Block block) {
190
+ Ruby runtime = context.getRuntime();
191
+
192
+ if(!this.selector.isOpen()) {
193
+ throw context.getRuntime().newIOError("selector is closed");
194
+ }
195
+
196
+ this.wakeupFired = false;
197
+ int ready = doSelect(runtime, context, timeout);
198
+
199
+ /* Timeout */
200
+ if(ready <= 0 && !this.wakeupFired) {
201
+ return context.nil;
202
+ }
203
+
204
+ RubyArray<?> array = null;
205
+
206
+ if(!block.isGiven()) {
207
+ array = runtime.newArray(this.selector.selectedKeys().size());
208
+ }
209
+
210
+ Iterator<SelectionKey> selectedKeys = this.selector.selectedKeys().iterator();
211
+ while(selectedKeys.hasNext()) {
212
+ SelectionKey key = selectedKeys.next();
213
+ processKey(key);
214
+
215
+ selectedKeys.remove();
216
+
217
+ if(block.isGiven()) {
218
+ block.call(context, (IRubyObject)key.attachment());
219
+ } else {
220
+ array.add(key.attachment());
221
+ }
222
+ }
223
+
224
+ if(block.isGiven()) {
225
+ return RubyNumeric.int2fix(runtime, ready);
226
+ } else {
227
+ return array;
228
+ }
229
+ }
230
+
231
+ /* Run the selector */
232
+ private int doSelect(Ruby runtime, ThreadContext context, IRubyObject timeout) {
233
+ int result;
234
+
235
+ cancelKeys();
236
+ try {
237
+ context.getThread().beforeBlockingCall(context);
238
+ if(timeout.isNil()) {
239
+ result = this.selector.select();
240
+ } else {
241
+ double t = RubyNumeric.num2dbl(timeout);
242
+ if(t == 0) {
243
+ result = this.selector.selectNow();
244
+ } else if(t < 0) {
245
+ throw runtime.newArgumentError("time interval must be positive");
246
+ } else {
247
+ long timeoutMilliSeconds = (long)(t * 1000);
248
+ if(timeoutMilliSeconds == 0) {
249
+ result = this.selector.selectNow();
250
+ } else {
251
+ result = this.selector.select(timeoutMilliSeconds);
252
+ }
253
+ }
254
+ }
255
+ context.getThread().afterBlockingCall();
256
+ return result;
257
+ } catch(IOException ie) {
258
+ throw runtime.newIOError(ie.getLocalizedMessage());
259
+ }
260
+ }
261
+
262
+ /* Flush our internal buffer of cancelled keys */
263
+ private void cancelKeys() {
264
+ Iterator<Map.Entry<SelectableChannel, SelectionKey>> cancelledKeys = this.cancelledKeys.entrySet().iterator();
265
+ while(cancelledKeys.hasNext()) {
266
+ Map.Entry<SelectableChannel, SelectionKey> entry = cancelledKeys.next();
267
+ SelectionKey key = entry.getValue();
268
+ key.cancel();
269
+ cancelledKeys.remove();
270
+ }
271
+ }
272
+
273
+ // Remove connect interest from connected sockets
274
+ // See: http://stackoverflow.com/questions/204186/java-nio-select-returns-without-selected-keys-why
275
+ private void processKey(SelectionKey key) {
276
+ if(key.isValid() && (key.readyOps() & SelectionKey.OP_CONNECT) != 0) {
277
+ int interestOps = key.interestOps();
278
+
279
+ interestOps &= ~SelectionKey.OP_CONNECT;
280
+ interestOps |= SelectionKey.OP_WRITE;
281
+
282
+ key.interestOps(interestOps);
283
+ }
284
+ }
285
+
286
+ @JRubyMethod
287
+ public IRubyObject wakeup(ThreadContext context) {
288
+ if(!this.selector.isOpen()) {
289
+ throw context.getRuntime().newIOError("selector is closed");
290
+ }
291
+
292
+ this.wakeupFired = true;
293
+ this.selector.wakeup();
294
+
295
+ return context.nil;
296
+ }
297
+ }