nio4r 0.2.2-java → 0.3.0-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/.travis.yml +6 -2
- data/CHANGES.md +10 -0
- data/LICENSE.txt +1 -1
- data/README.md +16 -11
- data/Rakefile +2 -2
- data/examples/echo_server.rb +9 -0
- data/ext/libev/Changes +34 -2
- data/ext/libev/ev.c +694 -131
- data/ext/libev/ev.h +89 -79
- data/ext/libev/ev_epoll.c +23 -10
- data/ext/libev/ev_kqueue.c +4 -4
- data/ext/libev/ev_poll.c +4 -4
- data/ext/libev/ev_port.c +9 -3
- data/ext/libev/ev_select.c +10 -6
- data/ext/libev/ev_vars.h +3 -2
- data/ext/libev/ev_wrap.h +6 -4
- data/ext/nio4r/monitor.c +23 -6
- data/ext/nio4r/org/nio4r/Nio4r.java +409 -0
- data/ext/nio4r/selector.c +36 -8
- data/lib/nio.rb +4 -7
- data/lib/nio/monitor.rb +15 -4
- data/lib/nio/selector.rb +23 -6
- data/lib/nio/version.rb +1 -1
- data/lib/nio4r_ext.jar +0 -0
- data/nio4r.gemspec +4 -3
- data/spec/nio/acceptables_spec.rb +30 -0
- data/spec/nio/monitor_spec.rb +37 -22
- data/spec/nio/selectables_spec.rb +178 -0
- data/spec/nio/selector_spec.rb +71 -173
- data/tasks/extension.rake +6 -5
- metadata +33 -29
- data/lib/nio/jruby/monitor.rb +0 -42
- data/lib/nio/jruby/selector.rb +0 -136
data/ext/libev/ev_select.c
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
/*
|
2
2
|
* libev select fd activity backend
|
3
3
|
*
|
4
|
-
* Copyright (c) 2007,2008,2009,2010 Marc Alexander Lehmann <libev@schmorp.de>
|
4
|
+
* Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann <libev@schmorp.de>
|
5
5
|
* All rights reserved.
|
6
6
|
*
|
7
7
|
* Redistribution and use in source and binary forms, with or without modifica-
|
@@ -195,7 +195,12 @@ select_poll (EV_P_ ev_tstamp timeout)
|
|
195
195
|
*/
|
196
196
|
if (errno == EINVAL)
|
197
197
|
{
|
198
|
-
|
198
|
+
if (timeout)
|
199
|
+
{
|
200
|
+
unsigned long ms = timeout * 1e3;
|
201
|
+
Sleep (ms ? ms : 1);
|
202
|
+
}
|
203
|
+
|
199
204
|
return;
|
200
205
|
}
|
201
206
|
#endif
|
@@ -269,9 +274,9 @@ select_poll (EV_P_ ev_tstamp timeout)
|
|
269
274
|
int inline_size
|
270
275
|
select_init (EV_P_ int flags)
|
271
276
|
{
|
272
|
-
|
273
|
-
backend_modify
|
274
|
-
backend_poll
|
277
|
+
backend_mintime = 1e-6;
|
278
|
+
backend_modify = select_modify;
|
279
|
+
backend_poll = select_poll;
|
275
280
|
|
276
281
|
#if EV_SELECT_USE_FD_SET
|
277
282
|
vec_ri = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_ri);
|
@@ -307,4 +312,3 @@ select_destroy (EV_P)
|
|
307
312
|
#endif
|
308
313
|
}
|
309
314
|
|
310
|
-
|
data/ext/libev/ev_vars.h
CHANGED
@@ -51,7 +51,7 @@ VARx(int, activecnt) /* total number of active events ("refcount") */
|
|
51
51
|
VARx(EV_ATOMIC_T, loop_done) /* signal by ev_break */
|
52
52
|
|
53
53
|
VARx(int, backend_fd)
|
54
|
-
VARx(ev_tstamp,
|
54
|
+
VARx(ev_tstamp, backend_mintime) /* assumed typical timer resolution */
|
55
55
|
VAR (backend_modify, void (*backend_modify)(EV_P_ int fd, int oev, int nev))
|
56
56
|
VAR (backend_poll , void (*backend_poll)(EV_P_ ev_tstamp timeout))
|
57
57
|
|
@@ -73,6 +73,8 @@ VARx(int, evfd)
|
|
73
73
|
#endif
|
74
74
|
VAR (evpipe, int evpipe [2])
|
75
75
|
VARx(ev_io, pipe_w)
|
76
|
+
VARx(EV_ATOMIC_T, pipe_write_wanted)
|
77
|
+
VARx(EV_ATOMIC_T, pipe_write_skipped)
|
76
78
|
|
77
79
|
#if !defined(_WIN32) || EV_GENWRAP
|
78
80
|
VARx(pid_t, curpid)
|
@@ -180,7 +182,6 @@ VAR (fs_hash, ANFS fs_hash [EV_INOTIFY_HASHSIZE])
|
|
180
182
|
#endif
|
181
183
|
|
182
184
|
VARx(EV_ATOMIC_T, sig_pending)
|
183
|
-
VARx(int, nosigmask)
|
184
185
|
#if EV_USE_SIGNALFD || EV_GENWRAP
|
185
186
|
VARx(int, sigfd)
|
186
187
|
VARx(ev_io, sigfd_w)
|
data/ext/libev/ev_wrap.h
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
#define activecnt ((loop)->activecnt)
|
11
11
|
#define loop_done ((loop)->loop_done)
|
12
12
|
#define backend_fd ((loop)->backend_fd)
|
13
|
-
#define
|
13
|
+
#define backend_mintime ((loop)->backend_mintime)
|
14
14
|
#define backend_modify ((loop)->backend_modify)
|
15
15
|
#define backend_poll ((loop)->backend_poll)
|
16
16
|
#define anfds ((loop)->anfds)
|
@@ -25,6 +25,8 @@
|
|
25
25
|
#define evfd ((loop)->evfd)
|
26
26
|
#define evpipe ((loop)->evpipe)
|
27
27
|
#define pipe_w ((loop)->pipe_w)
|
28
|
+
#define pipe_write_wanted ((loop)->pipe_write_wanted)
|
29
|
+
#define pipe_write_skipped ((loop)->pipe_write_skipped)
|
28
30
|
#define curpid ((loop)->curpid)
|
29
31
|
#define postfork ((loop)->postfork)
|
30
32
|
#define vec_ri ((loop)->vec_ri)
|
@@ -85,7 +87,6 @@
|
|
85
87
|
#define fs_2625 ((loop)->fs_2625)
|
86
88
|
#define fs_hash ((loop)->fs_hash)
|
87
89
|
#define sig_pending ((loop)->sig_pending)
|
88
|
-
#define nosigmask ((loop)->nosigmask)
|
89
90
|
#define sigfd ((loop)->sigfd)
|
90
91
|
#define sigfd_w ((loop)->sigfd_w)
|
91
92
|
#define sigfd_set ((loop)->sigfd_set)
|
@@ -107,7 +108,7 @@
|
|
107
108
|
#undef activecnt
|
108
109
|
#undef loop_done
|
109
110
|
#undef backend_fd
|
110
|
-
#undef
|
111
|
+
#undef backend_mintime
|
111
112
|
#undef backend_modify
|
112
113
|
#undef backend_poll
|
113
114
|
#undef anfds
|
@@ -122,6 +123,8 @@
|
|
122
123
|
#undef evfd
|
123
124
|
#undef evpipe
|
124
125
|
#undef pipe_w
|
126
|
+
#undef pipe_write_wanted
|
127
|
+
#undef pipe_write_skipped
|
125
128
|
#undef curpid
|
126
129
|
#undef postfork
|
127
130
|
#undef vec_ri
|
@@ -182,7 +185,6 @@
|
|
182
185
|
#undef fs_2625
|
183
186
|
#undef fs_hash
|
184
187
|
#undef sig_pending
|
185
|
-
#undef nosigmask
|
186
188
|
#undef sigfd
|
187
189
|
#undef sigfd_w
|
188
190
|
#undef sigfd_set
|
data/ext/nio4r/monitor.c
CHANGED
@@ -16,10 +16,11 @@ static void NIO_Monitor_free(struct NIO_Monitor *monitor);
|
|
16
16
|
|
17
17
|
/* Methods */
|
18
18
|
static VALUE NIO_Monitor_initialize(VALUE self, VALUE selector, VALUE io, VALUE interests);
|
19
|
-
static VALUE NIO_Monitor_close(VALUE self);
|
19
|
+
static VALUE NIO_Monitor_close(int argc, VALUE *argv, VALUE self);
|
20
20
|
static VALUE NIO_Monitor_is_closed(VALUE self);
|
21
21
|
static VALUE NIO_Monitor_io(VALUE self);
|
22
22
|
static VALUE NIO_Monitor_interests(VALUE self);
|
23
|
+
static VALUE NIO_Monitor_selector(VALUE self);
|
23
24
|
static VALUE NIO_Monitor_is_readable(VALUE self);
|
24
25
|
static VALUE NIO_Monitor_is_writable(VALUE self);
|
25
26
|
static VALUE NIO_Monitor_value(VALUE self);
|
@@ -43,10 +44,11 @@ void Init_NIO_Monitor()
|
|
43
44
|
rb_define_alloc_func(cNIO_Monitor, NIO_Monitor_allocate);
|
44
45
|
|
45
46
|
rb_define_method(cNIO_Monitor, "initialize", NIO_Monitor_initialize, 3);
|
46
|
-
rb_define_method(cNIO_Monitor, "close", NIO_Monitor_close,
|
47
|
+
rb_define_method(cNIO_Monitor, "close", NIO_Monitor_close, -1);
|
47
48
|
rb_define_method(cNIO_Monitor, "closed?", NIO_Monitor_is_closed, 0);
|
48
49
|
rb_define_method(cNIO_Monitor, "io", NIO_Monitor_io, 0);
|
49
50
|
rb_define_method(cNIO_Monitor, "interests", NIO_Monitor_interests, 0);
|
51
|
+
rb_define_method(cNIO_Monitor, "selector", NIO_Monitor_selector, 0);
|
50
52
|
rb_define_method(cNIO_Monitor, "value", NIO_Monitor_value, 0);
|
51
53
|
rb_define_method(cNIO_Monitor, "value=", NIO_Monitor_set_value, 1);
|
52
54
|
rb_define_method(cNIO_Monitor, "readiness", NIO_Monitor_readiness, 0);
|
@@ -71,7 +73,7 @@ static void NIO_Monitor_free(struct NIO_Monitor *monitor)
|
|
71
73
|
xfree(monitor);
|
72
74
|
}
|
73
75
|
|
74
|
-
static VALUE NIO_Monitor_initialize(VALUE self, VALUE
|
76
|
+
static VALUE NIO_Monitor_initialize(VALUE self, VALUE io, VALUE interests, VALUE selector_obj)
|
75
77
|
{
|
76
78
|
struct NIO_Monitor *monitor;
|
77
79
|
struct NIO_Selector *selector;
|
@@ -101,9 +103,9 @@ static VALUE NIO_Monitor_initialize(VALUE self, VALUE selector_obj, VALUE io, VA
|
|
101
103
|
GetOpenFile(rb_convert_type(io, T_FILE, "IO", "to_io"), fptr);
|
102
104
|
ev_io_init(&monitor->ev_io, NIO_Monitor_callback, FPTR_TO_FD(fptr), monitor->interests);
|
103
105
|
|
104
|
-
rb_ivar_set(self, rb_intern("selector"), selector_obj);
|
105
106
|
rb_ivar_set(self, rb_intern("io"), io);
|
106
107
|
rb_ivar_set(self, rb_intern("interests"), interests);
|
108
|
+
rb_ivar_set(self, rb_intern("selector"), selector_obj);
|
107
109
|
|
108
110
|
Data_Get_Struct(selector_obj, struct NIO_Selector, selector);
|
109
111
|
|
@@ -119,14 +121,24 @@ static VALUE NIO_Monitor_initialize(VALUE self, VALUE selector_obj, VALUE io, VA
|
|
119
121
|
return Qnil;
|
120
122
|
}
|
121
123
|
|
122
|
-
static VALUE NIO_Monitor_close(VALUE self)
|
124
|
+
static VALUE NIO_Monitor_close(int argc, VALUE *argv, VALUE self)
|
123
125
|
{
|
126
|
+
VALUE deregister, selector;
|
124
127
|
struct NIO_Monitor *monitor;
|
125
128
|
Data_Get_Struct(self, struct NIO_Monitor, monitor);
|
126
129
|
|
127
|
-
|
130
|
+
rb_scan_args(argc, argv, "01", &deregister);
|
131
|
+
selector = rb_ivar_get(self, rb_intern("selector"));
|
132
|
+
|
133
|
+
if(selector != Qnil) {
|
128
134
|
ev_io_stop(monitor->selector->ev_loop, &monitor->ev_io);
|
129
135
|
monitor->selector = 0;
|
136
|
+
rb_ivar_set(self, rb_intern("selector"), Qnil);
|
137
|
+
|
138
|
+
/* Default value is true */
|
139
|
+
if(deregister == Qtrue || deregister == Qnil) {
|
140
|
+
rb_funcall(selector, rb_intern("deregister"), 1, rb_ivar_get(self, rb_intern("io")));
|
141
|
+
}
|
130
142
|
}
|
131
143
|
|
132
144
|
return Qnil;
|
@@ -150,6 +162,11 @@ static VALUE NIO_Monitor_interests(VALUE self)
|
|
150
162
|
return rb_ivar_get(self, rb_intern("interests"));
|
151
163
|
}
|
152
164
|
|
165
|
+
static VALUE NIO_Monitor_selector(VALUE self)
|
166
|
+
{
|
167
|
+
return rb_ivar_get(self, rb_intern("selector"));
|
168
|
+
}
|
169
|
+
|
153
170
|
static VALUE NIO_Monitor_value(VALUE self)
|
154
171
|
{
|
155
172
|
return rb_ivar_get(self, rb_intern("value"));
|
@@ -0,0 +1,409 @@
|
|
1
|
+
package org.nio4r;
|
2
|
+
|
3
|
+
import java.util.Iterator;
|
4
|
+
import java.io.IOException;
|
5
|
+
import java.nio.channels.Channel;
|
6
|
+
import java.nio.channels.SocketChannel;
|
7
|
+
import java.nio.channels.SelectableChannel;
|
8
|
+
import java.nio.channels.SelectionKey;
|
9
|
+
import org.jruby.Ruby;
|
10
|
+
import org.jruby.RubyModule;
|
11
|
+
import org.jruby.RubyClass;
|
12
|
+
import org.jruby.RubyObject;
|
13
|
+
import org.jruby.RubyIO;
|
14
|
+
import org.jruby.RubyNumeric;
|
15
|
+
import org.jruby.RubyArray;
|
16
|
+
import org.jruby.anno.JRubyMethod;
|
17
|
+
import org.jruby.runtime.ObjectAllocator;
|
18
|
+
import org.jruby.runtime.ThreadContext;
|
19
|
+
import org.jruby.runtime.load.Library;
|
20
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
21
|
+
import org.jruby.runtime.Block;
|
22
|
+
|
23
|
+
public class Nio4r implements Library {
|
24
|
+
private Ruby ruby;
|
25
|
+
|
26
|
+
public void load(final Ruby ruby, boolean bln) {
|
27
|
+
this.ruby = ruby;
|
28
|
+
|
29
|
+
RubyModule nio = ruby.defineModule("NIO");
|
30
|
+
|
31
|
+
RubyClass selector = ruby.defineClassUnder("Selector", ruby.getObject(), new ObjectAllocator() {
|
32
|
+
public IRubyObject allocate(Ruby ruby, RubyClass rc) {
|
33
|
+
return new Selector(ruby, rc);
|
34
|
+
}
|
35
|
+
}, nio);
|
36
|
+
|
37
|
+
selector.defineAnnotatedMethods(Selector.class);
|
38
|
+
|
39
|
+
RubyClass monitor = ruby.defineClassUnder("Monitor", ruby.getObject(), new ObjectAllocator() {
|
40
|
+
public IRubyObject allocate(Ruby ruby, RubyClass rc) {
|
41
|
+
return new Monitor(ruby, rc);
|
42
|
+
}
|
43
|
+
}, nio);
|
44
|
+
|
45
|
+
monitor.defineAnnotatedMethods(Monitor.class);
|
46
|
+
}
|
47
|
+
|
48
|
+
public static int symbolToInterestOps(Ruby ruby, SelectableChannel channel, IRubyObject interest) {
|
49
|
+
if(interest == ruby.newSymbol("r")) {
|
50
|
+
if((channel.validOps() & SelectionKey.OP_ACCEPT) != 0) {
|
51
|
+
return SelectionKey.OP_ACCEPT;
|
52
|
+
} else {
|
53
|
+
return SelectionKey.OP_READ;
|
54
|
+
}
|
55
|
+
} else if(interest == ruby.newSymbol("w")) {
|
56
|
+
if(channel instanceof SocketChannel && !((SocketChannel)channel).isConnected()) {
|
57
|
+
return SelectionKey.OP_CONNECT;
|
58
|
+
} else {
|
59
|
+
return SelectionKey.OP_WRITE;
|
60
|
+
}
|
61
|
+
} else if(interest == ruby.newSymbol("rw")) {
|
62
|
+
int interestOps = 0;
|
63
|
+
|
64
|
+
/* nio4r emulates the POSIX behavior, which is sloppy about allowed modes */
|
65
|
+
if((channel.validOps() & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0) {
|
66
|
+
interestOps |= symbolToInterestOps(ruby, channel, ruby.newSymbol("r"));
|
67
|
+
}
|
68
|
+
|
69
|
+
if((channel.validOps() & (SelectionKey.OP_WRITE | SelectionKey.OP_CONNECT)) != 0) {
|
70
|
+
interestOps |= symbolToInterestOps(ruby, channel, ruby.newSymbol("w"));
|
71
|
+
}
|
72
|
+
|
73
|
+
return interestOps;
|
74
|
+
} else {
|
75
|
+
throw ruby.newArgumentError("invalid interest type: " + interest);
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
public static IRubyObject interestOpsToSymbol(Ruby ruby, int interestOps) {
|
80
|
+
switch(interestOps) {
|
81
|
+
case SelectionKey.OP_READ:
|
82
|
+
case SelectionKey.OP_ACCEPT:
|
83
|
+
return ruby.newSymbol("r");
|
84
|
+
case SelectionKey.OP_WRITE:
|
85
|
+
case SelectionKey.OP_CONNECT:
|
86
|
+
return ruby.newSymbol("w");
|
87
|
+
case SelectionKey.OP_READ | SelectionKey.OP_CONNECT:
|
88
|
+
case SelectionKey.OP_READ | SelectionKey.OP_WRITE:
|
89
|
+
return ruby.newSymbol("rw");
|
90
|
+
default:
|
91
|
+
throw ruby.newArgumentError("unknown interest op combination");
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
public class Selector extends RubyObject {
|
96
|
+
private java.nio.channels.Selector selector;
|
97
|
+
|
98
|
+
public Selector(final Ruby ruby, RubyClass rubyClass) {
|
99
|
+
super(ruby, rubyClass);
|
100
|
+
}
|
101
|
+
|
102
|
+
@JRubyMethod
|
103
|
+
public IRubyObject initialize(ThreadContext context) {
|
104
|
+
try {
|
105
|
+
selector = java.nio.channels.Selector.open();
|
106
|
+
} catch(IOException ie) {
|
107
|
+
throw context.runtime.newIOError(ie.getLocalizedMessage());
|
108
|
+
}
|
109
|
+
|
110
|
+
return context.nil;
|
111
|
+
}
|
112
|
+
|
113
|
+
@JRubyMethod
|
114
|
+
public IRubyObject close(ThreadContext context) {
|
115
|
+
try {
|
116
|
+
selector.close();
|
117
|
+
} catch(IOException ie) {
|
118
|
+
throw context.runtime.newIOError(ie.getLocalizedMessage());
|
119
|
+
}
|
120
|
+
|
121
|
+
return context.nil;
|
122
|
+
}
|
123
|
+
|
124
|
+
@JRubyMethod(name = "closed?")
|
125
|
+
public IRubyObject isClosed(ThreadContext context) {
|
126
|
+
Ruby runtime = context.getRuntime();
|
127
|
+
return selector.isOpen() ? runtime.getFalse() : runtime.getTrue();
|
128
|
+
}
|
129
|
+
|
130
|
+
@JRubyMethod
|
131
|
+
public IRubyObject register(ThreadContext context, IRubyObject io, IRubyObject interests) {
|
132
|
+
Ruby runtime = context.getRuntime();
|
133
|
+
Channel raw_channel = RubyIO.convertToIO(context, io).getChannel();
|
134
|
+
|
135
|
+
if(!(raw_channel instanceof SelectableChannel)) {
|
136
|
+
throw runtime.newArgumentError("not a selectable IO object");
|
137
|
+
}
|
138
|
+
|
139
|
+
SelectableChannel channel = (SelectableChannel)raw_channel;
|
140
|
+
|
141
|
+
try {
|
142
|
+
channel.configureBlocking(false);
|
143
|
+
} catch(IOException ie) {
|
144
|
+
throw runtime.newIOError(ie.getLocalizedMessage());
|
145
|
+
}
|
146
|
+
|
147
|
+
int interestOps = Nio4r.symbolToInterestOps(runtime, channel, interests);
|
148
|
+
SelectionKey key;
|
149
|
+
|
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());
|
156
|
+
}
|
157
|
+
|
158
|
+
RubyClass monitorClass = runtime.getModule("NIO").getClass("Monitor");
|
159
|
+
Monitor monitor = (Monitor)monitorClass.newInstance(context, io, interests, this, null);
|
160
|
+
monitor.setSelectionKey(key);
|
161
|
+
|
162
|
+
return monitor;
|
163
|
+
}
|
164
|
+
|
165
|
+
@JRubyMethod
|
166
|
+
public IRubyObject deregister(ThreadContext context, IRubyObject io) {
|
167
|
+
Ruby runtime = context.getRuntime();
|
168
|
+
Channel raw_channel = RubyIO.convertToIO(context, io).getChannel();
|
169
|
+
|
170
|
+
if(!(raw_channel instanceof SelectableChannel)) {
|
171
|
+
throw runtime.newArgumentError("not a selectable IO object");
|
172
|
+
}
|
173
|
+
|
174
|
+
SelectableChannel channel = (SelectableChannel)raw_channel;
|
175
|
+
SelectionKey key = channel.keyFor(selector);
|
176
|
+
|
177
|
+
if(key == null)
|
178
|
+
return context.nil;
|
179
|
+
|
180
|
+
Monitor monitor = (Monitor)key.attachment();
|
181
|
+
monitor.close(context);
|
182
|
+
|
183
|
+
return monitor;
|
184
|
+
}
|
185
|
+
|
186
|
+
@JRubyMethod(name = "registered?")
|
187
|
+
public IRubyObject isRegistered(ThreadContext context, IRubyObject io) {
|
188
|
+
Ruby runtime = context.getRuntime();
|
189
|
+
Channel raw_channel = RubyIO.convertToIO(context, io).getChannel();
|
190
|
+
|
191
|
+
if(!(raw_channel instanceof SelectableChannel)) {
|
192
|
+
throw runtime.newArgumentError("not a selectable IO object");
|
193
|
+
}
|
194
|
+
|
195
|
+
SelectableChannel channel = (SelectableChannel)raw_channel;
|
196
|
+
SelectionKey key = channel.keyFor(selector);
|
197
|
+
|
198
|
+
if(key == null)
|
199
|
+
return context.nil;
|
200
|
+
|
201
|
+
|
202
|
+
if(((Monitor)key.attachment()).isClosed(context) == runtime.getTrue()) {
|
203
|
+
return runtime.getFalse();
|
204
|
+
} else {
|
205
|
+
return runtime.getTrue();
|
206
|
+
}
|
207
|
+
}
|
208
|
+
|
209
|
+
@JRubyMethod
|
210
|
+
public synchronized IRubyObject select(ThreadContext context, Block block) {
|
211
|
+
return select(context, context.nil, block);
|
212
|
+
}
|
213
|
+
|
214
|
+
@JRubyMethod
|
215
|
+
public synchronized IRubyObject select(ThreadContext context, IRubyObject timeout, Block block) {
|
216
|
+
Ruby runtime = context.getRuntime();
|
217
|
+
int ready = doSelect(runtime, timeout);
|
218
|
+
|
219
|
+
/* Timeout or wakeup */
|
220
|
+
if(ready <= 0)
|
221
|
+
return context.nil;
|
222
|
+
|
223
|
+
RubyArray array = null;
|
224
|
+
if(!block.isGiven()) {
|
225
|
+
array = runtime.newArray(selector.selectedKeys().size());
|
226
|
+
}
|
227
|
+
|
228
|
+
Iterator selectedKeys = selector.selectedKeys().iterator();
|
229
|
+
while (selectedKeys.hasNext()) {
|
230
|
+
SelectionKey key = (SelectionKey)selectedKeys.next();
|
231
|
+
processKey(key);
|
232
|
+
selectedKeys.remove();
|
233
|
+
|
234
|
+
if(block.isGiven()) {
|
235
|
+
block.call(context, (IRubyObject)key.attachment());
|
236
|
+
} else {
|
237
|
+
array.add(key.attachment());
|
238
|
+
}
|
239
|
+
}
|
240
|
+
|
241
|
+
if(block.isGiven()) {
|
242
|
+
return RubyNumeric.int2fix(runtime, ready);
|
243
|
+
} else {
|
244
|
+
return array;
|
245
|
+
}
|
246
|
+
}
|
247
|
+
|
248
|
+
@JRubyMethod
|
249
|
+
public synchronized IRubyObject select_each(ThreadContext context, Block block) {
|
250
|
+
return select_each(context, context.nil, block);
|
251
|
+
}
|
252
|
+
|
253
|
+
@JRubyMethod
|
254
|
+
public synchronized IRubyObject select_each(ThreadContext context, IRubyObject timeout, Block block) {
|
255
|
+
Ruby runtime = context.getRuntime();
|
256
|
+
int ready = doSelect(runtime, timeout);
|
257
|
+
|
258
|
+
/* Timeout or wakeup */
|
259
|
+
if(ready <= 0)
|
260
|
+
return context.nil;
|
261
|
+
|
262
|
+
Iterator selectedKeys = selector.selectedKeys().iterator();
|
263
|
+
while (selectedKeys.hasNext()) {
|
264
|
+
SelectionKey key = (SelectionKey)selectedKeys.next();
|
265
|
+
processKey(key);
|
266
|
+
selectedKeys.remove();
|
267
|
+
block.call(context, (IRubyObject)key.attachment());
|
268
|
+
}
|
269
|
+
|
270
|
+
return context.nil;
|
271
|
+
}
|
272
|
+
|
273
|
+
private int doSelect(Ruby runtime, IRubyObject timeout) {
|
274
|
+
try {
|
275
|
+
if(timeout.isNil()) {
|
276
|
+
return selector.select();
|
277
|
+
} else {
|
278
|
+
double t = RubyNumeric.num2dbl(timeout);
|
279
|
+
if(t == 0) {
|
280
|
+
return selector.selectNow();
|
281
|
+
} else if(t < 0) {
|
282
|
+
throw runtime.newArgumentError("time interval must be positive");
|
283
|
+
} else {
|
284
|
+
return selector.select((long)(t * 1000));
|
285
|
+
}
|
286
|
+
}
|
287
|
+
} catch(IOException ie) {
|
288
|
+
throw runtime.newIOError(ie.getLocalizedMessage());
|
289
|
+
}
|
290
|
+
}
|
291
|
+
|
292
|
+
// Remove connect interest from connected sockets
|
293
|
+
// See: http://stackoverflow.com/questions/204186/java-nio-select-returns-without-selected-keys-why
|
294
|
+
private void processKey(SelectionKey key) {
|
295
|
+
if((key.readyOps() & SelectionKey.OP_CONNECT) != 0) {
|
296
|
+
int interestOps = key.interestOps();
|
297
|
+
|
298
|
+
interestOps &= ~SelectionKey.OP_CONNECT;
|
299
|
+
interestOps |= SelectionKey.OP_WRITE;
|
300
|
+
|
301
|
+
key.interestOps(interestOps);
|
302
|
+
}
|
303
|
+
}
|
304
|
+
|
305
|
+
@JRubyMethod
|
306
|
+
public IRubyObject wakeup(ThreadContext context) {
|
307
|
+
if(!selector.isOpen()) {
|
308
|
+
throw context.getRuntime().newIOError("selector is closed");
|
309
|
+
}
|
310
|
+
|
311
|
+
selector.wakeup();
|
312
|
+
return context.nil;
|
313
|
+
}
|
314
|
+
}
|
315
|
+
|
316
|
+
public class Monitor extends RubyObject {
|
317
|
+
private SelectionKey key;
|
318
|
+
private RubyIO io;
|
319
|
+
private IRubyObject interests, selector, value, closed;
|
320
|
+
|
321
|
+
public Monitor(final Ruby ruby, RubyClass rubyClass) {
|
322
|
+
super(ruby, rubyClass);
|
323
|
+
}
|
324
|
+
|
325
|
+
@JRubyMethod
|
326
|
+
public IRubyObject initialize(ThreadContext context, IRubyObject selectable, IRubyObject interests, IRubyObject selector) {
|
327
|
+
this.io = RubyIO.convertToIO(context, selectable);
|
328
|
+
this.interests = interests;
|
329
|
+
this.selector = selector;
|
330
|
+
|
331
|
+
value = context.nil;
|
332
|
+
closed = context.getRuntime().getFalse();
|
333
|
+
|
334
|
+
return context.nil;
|
335
|
+
}
|
336
|
+
|
337
|
+
public void setSelectionKey(SelectionKey k) {
|
338
|
+
key = k;
|
339
|
+
key.attach(this);
|
340
|
+
}
|
341
|
+
|
342
|
+
@JRubyMethod
|
343
|
+
public IRubyObject io(ThreadContext context) {
|
344
|
+
return io;
|
345
|
+
}
|
346
|
+
|
347
|
+
@JRubyMethod
|
348
|
+
public IRubyObject selector(ThreadContext context) {
|
349
|
+
return selector;
|
350
|
+
}
|
351
|
+
|
352
|
+
@JRubyMethod
|
353
|
+
public IRubyObject interests(ThreadContext context) {
|
354
|
+
return interests;
|
355
|
+
}
|
356
|
+
|
357
|
+
@JRubyMethod
|
358
|
+
public IRubyObject readiness(ThreadContext context) {
|
359
|
+
return Nio4r.interestOpsToSymbol(context.getRuntime(), key.readyOps());
|
360
|
+
}
|
361
|
+
|
362
|
+
@JRubyMethod(name = "readable?")
|
363
|
+
public IRubyObject isReadable(ThreadContext context) {
|
364
|
+
Ruby runtime = context.getRuntime();
|
365
|
+
int readyOps = key.readyOps();
|
366
|
+
|
367
|
+
if((readyOps & SelectionKey.OP_READ) != 0 || (readyOps & SelectionKey.OP_ACCEPT) != 0) {
|
368
|
+
return runtime.getTrue();
|
369
|
+
} else {
|
370
|
+
return runtime.getFalse();
|
371
|
+
}
|
372
|
+
}
|
373
|
+
|
374
|
+
@JRubyMethod(name = {"writable?", "writeable?"})
|
375
|
+
public IRubyObject writable(ThreadContext context) {
|
376
|
+
Ruby runtime = context.getRuntime();
|
377
|
+
int readyOps = key.readyOps();
|
378
|
+
|
379
|
+
if((readyOps & SelectionKey.OP_WRITE) != 0 || (readyOps & SelectionKey.OP_CONNECT) != 0) {
|
380
|
+
return runtime.getTrue();
|
381
|
+
} else {
|
382
|
+
return runtime.getFalse();
|
383
|
+
}
|
384
|
+
}
|
385
|
+
|
386
|
+
@JRubyMethod(name = "value")
|
387
|
+
public IRubyObject getValue(ThreadContext context) {
|
388
|
+
return value;
|
389
|
+
}
|
390
|
+
|
391
|
+
@JRubyMethod(name = "value=")
|
392
|
+
public IRubyObject setValue(ThreadContext context, IRubyObject obj) {
|
393
|
+
value = obj;
|
394
|
+
return context.nil;
|
395
|
+
}
|
396
|
+
|
397
|
+
@JRubyMethod
|
398
|
+
public IRubyObject close(ThreadContext context) {
|
399
|
+
key.cancel();
|
400
|
+
closed = context.getRuntime().getTrue();
|
401
|
+
return context.nil;
|
402
|
+
}
|
403
|
+
|
404
|
+
@JRubyMethod(name = "closed?")
|
405
|
+
public IRubyObject isClosed(ThreadContext context) {
|
406
|
+
return closed;
|
407
|
+
}
|
408
|
+
}
|
409
|
+
}
|