nio4r 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.md +10 -0
- data/examples/echo_server.rb +1 -1
- data/ext/libev/ev.c +70 -0
- data/ext/nio4r/monitor.c +2 -14
- data/ext/nio4r/nio4r.h +3 -3
- data/ext/nio4r/org/nio4r/Nio4r.java +22 -42
- data/ext/nio4r/selector.c +35 -100
- data/lib/nio/selector.rb +0 -8
- data/lib/nio/version.rb +1 -1
- data/spec/nio/selectables_spec.rb +6 -2
- data/spec/nio/selector_spec.rb +0 -39
- metadata +8 -8
data/CHANGES.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
0.3.2
|
2
|
+
-----
|
3
|
+
* NIO::Selector#select_each removed
|
4
|
+
* Remove event buffer
|
5
|
+
* Patch GIL unlock directly into libev
|
6
|
+
|
7
|
+
0.3.1
|
8
|
+
-----
|
9
|
+
* Prevent CancelledKeyExceptions on JRuby
|
10
|
+
|
1
11
|
0.3.0
|
2
12
|
-----
|
3
13
|
* NIO::Selector#select now takes a block and behaves like select_each
|
data/examples/echo_server.rb
CHANGED
data/ext/libev/ev.c
CHANGED
@@ -37,6 +37,10 @@
|
|
37
37
|
* either the BSD or the GPL.
|
38
38
|
*/
|
39
39
|
|
40
|
+
/* ########## NIO4R PATCHERY HO! ########## */
|
41
|
+
#include "ruby.h"
|
42
|
+
/* ######################################## */
|
43
|
+
|
40
44
|
/* this big block deduces configuration from config.h */
|
41
45
|
#ifndef EV_STANDALONE
|
42
46
|
# ifdef EV_CONFIG_H
|
@@ -2926,9 +2930,32 @@ time_update (EV_P_ ev_tstamp max_block)
|
|
2926
2930
|
}
|
2927
2931
|
}
|
2928
2932
|
|
2933
|
+
/* ########## NIO4R PATCHERY HO! ########## */
|
2934
|
+
#if defined(HAVE_RB_THREAD_BLOCKING_REGION)
|
2935
|
+
struct ev_poll_args {
|
2936
|
+
struct ev_loop *loop;
|
2937
|
+
ev_tstamp waittime;
|
2938
|
+
};
|
2939
|
+
|
2940
|
+
static
|
2941
|
+
VALUE ev_backend_poll(void *ptr)
|
2942
|
+
{
|
2943
|
+
struct ev_poll_args *args = (struct ev_poll_args *)ptr;
|
2944
|
+
struct ev_loop *loop = args->loop;
|
2945
|
+
backend_poll (EV_A_ args->waittime);
|
2946
|
+
}
|
2947
|
+
#endif
|
2948
|
+
/* ######################################## */
|
2949
|
+
|
2929
2950
|
void
|
2930
2951
|
ev_run (EV_P_ int flags)
|
2931
2952
|
{
|
2953
|
+
/* ########## NIO4R PATCHERY HO! ########## */
|
2954
|
+
#if defined(HAVE_RB_THREAD_BLOCKING_REGION)
|
2955
|
+
struct ev_poll_args poll_args;
|
2956
|
+
#endif
|
2957
|
+
/* ######################################## */
|
2958
|
+
|
2932
2959
|
#if EV_FEATURE_API
|
2933
2960
|
++loop_depth;
|
2934
2961
|
#endif
|
@@ -3046,7 +3073,50 @@ ev_run (EV_P_ int flags)
|
|
3046
3073
|
++loop_count;
|
3047
3074
|
#endif
|
3048
3075
|
assert ((loop_done = EVBREAK_RECURSE, 1)); /* assert for side effect */
|
3076
|
+
|
3077
|
+
/*
|
3078
|
+
########################## NIO4R PATCHERY HO! ##########################
|
3079
|
+
|
3080
|
+
According to the grandwizards of Ruby, locking and unlocking of the global
|
3081
|
+
interpreter lock are apparently too powerful a concept for a mere mortal to
|
3082
|
+
wield (although redefining what + and - do to numbers is totally cool).
|
3083
|
+
And so it came to pass that the only acceptable way to release the global
|
3084
|
+
interpreter lock is through a convoluted callback system that thakes a
|
3085
|
+
function pointer. While the grandwizard of libev foresaw this sort of scenario,
|
3086
|
+
he too attempted to place an API with callbacks on it, one that runs before
|
3087
|
+
the system call, and one that runs immediately after.
|
3088
|
+
|
3089
|
+
And so it came to pass that trying to wrap everything up in callbacks created
|
3090
|
+
two incompatible APIs, Ruby's which releases the global interpreter lock and
|
3091
|
+
reacquires it when the callback returns, and libev's, which wants two
|
3092
|
+
callbacks, one which runs before the polling operation starts, and one which
|
3093
|
+
runs after it finishes.
|
3094
|
+
|
3095
|
+
These two systems are incompatible as they both want to use callbacks to
|
3096
|
+
solve the same problem, however libev wants to use before/after callbacks,
|
3097
|
+
and Ruby wants to use an "around" callback. This presents a significant
|
3098
|
+
problem as these two patterns of callbacks are diametrical opposites of each
|
3099
|
+
other and thus cannot be composed.
|
3100
|
+
|
3101
|
+
And thus we are left with no choice but to patch the internals of libev in
|
3102
|
+
order to release a mutex at just the precise moment.
|
3103
|
+
|
3104
|
+
Let this be a lesson to the all: CALLBACKS FUCKING BLOW
|
3105
|
+
|
3106
|
+
#######################################################################
|
3107
|
+
*/
|
3108
|
+
|
3109
|
+
#if defined(HAVE_RB_THREAD_BLOCKING_REGION)
|
3110
|
+
poll_args.loop = loop;
|
3111
|
+
poll_args.waittime = waittime;
|
3112
|
+
rb_thread_blocking_region(ev_backend_poll, (void *)&poll_args, RUBY_UBF_IO, 0);
|
3113
|
+
#else
|
3049
3114
|
backend_poll (EV_A_ waittime);
|
3115
|
+
#endif
|
3116
|
+
/*
|
3117
|
+
############################# END PATCHERY ############################
|
3118
|
+
*/
|
3119
|
+
|
3050
3120
|
assert ((loop_done = EVBREAK_CANCEL, 1)); /* assert for side effect */
|
3051
3121
|
|
3052
3122
|
pipe_write_wanted = 0; /* just an optimisation, no fence needed */
|
data/ext/nio4r/monitor.c
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
*/
|
5
5
|
|
6
6
|
#include "nio4r.h"
|
7
|
-
#include <assert.h>
|
8
7
|
|
9
8
|
static VALUE mNIO = Qnil;
|
10
9
|
static VALUE cNIO_Monitor = Qnil;
|
@@ -101,7 +100,7 @@ static VALUE NIO_Monitor_initialize(VALUE self, VALUE io, VALUE interests, VALUE
|
|
101
100
|
}
|
102
101
|
|
103
102
|
GetOpenFile(rb_convert_type(io, T_FILE, "IO", "to_io"), fptr);
|
104
|
-
ev_io_init(&monitor->ev_io,
|
103
|
+
ev_io_init(&monitor->ev_io, NIO_Selector_monitor_callback, FPTR_TO_FD(fptr), monitor->interests);
|
105
104
|
|
106
105
|
rb_ivar_set(self, rb_intern("io"), io);
|
107
106
|
rb_ivar_set(self, rb_intern("interests"), interests);
|
@@ -215,15 +214,4 @@ static VALUE NIO_Monitor_is_writable(VALUE self)
|
|
215
214
|
} else {
|
216
215
|
return Qfalse;
|
217
216
|
}
|
218
|
-
}
|
219
|
-
|
220
|
-
/* libev callback fired whenever this monitor gets events */
|
221
|
-
static void NIO_Monitor_callback(struct ev_loop *ev_loop, struct ev_io *io, int revents)
|
222
|
-
{
|
223
|
-
struct NIO_Monitor *monitor = (struct NIO_Monitor *)io->data;
|
224
|
-
|
225
|
-
assert(monitor->selector != 0);
|
226
|
-
monitor->revents = revents;
|
227
|
-
|
228
|
-
NIO_Selector_handle_event(monitor->selector, monitor->self, revents);
|
229
|
-
}
|
217
|
+
}
|
data/ext/nio4r/nio4r.h
CHANGED
@@ -19,8 +19,8 @@ struct NIO_Selector
|
|
19
19
|
int wakeup_reader, wakeup_writer;
|
20
20
|
int closed, selecting;
|
21
21
|
int ready_count;
|
22
|
-
|
23
|
-
VALUE
|
22
|
+
|
23
|
+
VALUE ready_array;
|
24
24
|
};
|
25
25
|
|
26
26
|
struct NIO_callback_data
|
@@ -50,6 +50,6 @@ struct NIO_Monitor
|
|
50
50
|
#endif /* GetReadFile */
|
51
51
|
|
52
52
|
/* Thunk between libev callbacks in NIO::Monitors and NIO::Selectors */
|
53
|
-
void
|
53
|
+
void NIO_Selector_monitor_callback(struct ev_loop *ev_loop, struct ev_io *io, int revents);
|
54
54
|
|
55
55
|
#endif /* NIO4R_H */
|
@@ -134,13 +134,13 @@ public class Nio4r implements Library {
|
|
134
134
|
@JRubyMethod
|
135
135
|
public IRubyObject register(ThreadContext context, IRubyObject io, IRubyObject interests) {
|
136
136
|
Ruby runtime = context.getRuntime();
|
137
|
-
Channel
|
137
|
+
Channel rawChannel = RubyIO.convertToIO(context, io).getChannel();
|
138
138
|
|
139
|
-
if(!(
|
139
|
+
if(!(rawChannel instanceof SelectableChannel)) {
|
140
140
|
throw runtime.newArgumentError("not a selectable IO object");
|
141
141
|
}
|
142
142
|
|
143
|
-
SelectableChannel channel = (SelectableChannel)
|
143
|
+
SelectableChannel channel = (SelectableChannel)rawChannel;
|
144
144
|
|
145
145
|
try {
|
146
146
|
channel.configureBlocking(false);
|
@@ -175,13 +175,13 @@ public class Nio4r implements Library {
|
|
175
175
|
@JRubyMethod
|
176
176
|
public IRubyObject deregister(ThreadContext context, IRubyObject io) {
|
177
177
|
Ruby runtime = context.getRuntime();
|
178
|
-
Channel
|
178
|
+
Channel rawChannel = RubyIO.convertToIO(context, io).getChannel();
|
179
179
|
|
180
|
-
if(!(
|
180
|
+
if(!(rawChannel instanceof SelectableChannel)) {
|
181
181
|
throw runtime.newArgumentError("not a selectable IO object");
|
182
182
|
}
|
183
183
|
|
184
|
-
SelectableChannel channel = (SelectableChannel)
|
184
|
+
SelectableChannel channel = (SelectableChannel)rawChannel;
|
185
185
|
SelectionKey key = channel.keyFor(this.selector);
|
186
186
|
|
187
187
|
if(key == null)
|
@@ -197,13 +197,13 @@ public class Nio4r implements Library {
|
|
197
197
|
@JRubyMethod(name = "registered?")
|
198
198
|
public IRubyObject isRegistered(ThreadContext context, IRubyObject io) {
|
199
199
|
Ruby runtime = context.getRuntime();
|
200
|
-
Channel
|
200
|
+
Channel rawChannel = RubyIO.convertToIO(context, io).getChannel();
|
201
201
|
|
202
|
-
if(!(
|
202
|
+
if(!(rawChannel instanceof SelectableChannel)) {
|
203
203
|
throw runtime.newArgumentError("not a selectable IO object");
|
204
204
|
}
|
205
205
|
|
206
|
-
SelectableChannel channel = (SelectableChannel)
|
206
|
+
SelectableChannel channel = (SelectableChannel)rawChannel;
|
207
207
|
SelectionKey key = channel.keyFor(this.selector);
|
208
208
|
|
209
209
|
if(key == null)
|
@@ -256,40 +256,9 @@ public class Nio4r implements Library {
|
|
256
256
|
}
|
257
257
|
}
|
258
258
|
|
259
|
-
|
260
|
-
public synchronized IRubyObject select_each(ThreadContext context, Block block) {
|
261
|
-
return select_each(context, context.nil, block);
|
262
|
-
}
|
263
|
-
|
264
|
-
@JRubyMethod
|
265
|
-
public synchronized IRubyObject select_each(ThreadContext context, IRubyObject timeout, Block block) {
|
266
|
-
Ruby runtime = context.getRuntime();
|
267
|
-
int ready = doSelect(runtime, timeout);
|
268
|
-
|
269
|
-
/* Timeout or wakeup */
|
270
|
-
if(ready <= 0)
|
271
|
-
return context.nil;
|
272
|
-
|
273
|
-
Iterator selectedKeys = this.selector.selectedKeys().iterator();
|
274
|
-
while (selectedKeys.hasNext()) {
|
275
|
-
SelectionKey key = (SelectionKey)selectedKeys.next();
|
276
|
-
processKey(key);
|
277
|
-
selectedKeys.remove();
|
278
|
-
block.call(context, (IRubyObject)key.attachment());
|
279
|
-
}
|
280
|
-
|
281
|
-
return context.nil;
|
282
|
-
}
|
283
|
-
|
259
|
+
/* Run the selector */
|
284
260
|
private int doSelect(Ruby runtime, IRubyObject timeout) {
|
285
|
-
|
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
|
-
|
261
|
+
cancelKeys();
|
293
262
|
try {
|
294
263
|
if(timeout.isNil()) {
|
295
264
|
return this.selector.select();
|
@@ -308,6 +277,17 @@ public class Nio4r implements Library {
|
|
308
277
|
}
|
309
278
|
}
|
310
279
|
|
280
|
+
/* Flush our internal buffer of cancelled keys */
|
281
|
+
private void cancelKeys() {
|
282
|
+
Iterator cancelledKeys = this.cancelledKeys.entrySet().iterator();
|
283
|
+
while(cancelledKeys.hasNext()) {
|
284
|
+
Map.Entry entry = (Map.Entry)cancelledKeys.next();
|
285
|
+
SelectionKey key = (SelectionKey)entry.getValue();
|
286
|
+
key.cancel();
|
287
|
+
cancelledKeys.remove();
|
288
|
+
}
|
289
|
+
}
|
290
|
+
|
311
291
|
// Remove connect interest from connected sockets
|
312
292
|
// See: http://stackoverflow.com/questions/204186/java-nio-select-returns-without-selected-keys-why
|
313
293
|
private void processKey(SelectionKey key) {
|
data/ext/nio4r/selector.c
CHANGED
@@ -7,6 +7,7 @@
|
|
7
7
|
#include "rubysig.h"
|
8
8
|
#include <unistd.h>
|
9
9
|
#include <fcntl.h>
|
10
|
+
#include <assert.h>
|
10
11
|
|
11
12
|
static VALUE mNIO = Qnil;
|
12
13
|
static VALUE cNIO_Monitor = Qnil;
|
@@ -24,7 +25,6 @@ static VALUE NIO_Selector_register(VALUE self, VALUE selectable, VALUE interest)
|
|
24
25
|
static VALUE NIO_Selector_deregister(VALUE self, VALUE io);
|
25
26
|
static VALUE NIO_Selector_is_registered(VALUE self, VALUE io);
|
26
27
|
static VALUE NIO_Selector_select(int argc, VALUE *argv, VALUE self);
|
27
|
-
static VALUE NIO_Selector_select_each(int argc, VALUE *argv, VALUE self);
|
28
28
|
static VALUE NIO_Selector_wakeup(VALUE self);
|
29
29
|
static VALUE NIO_Selector_close(VALUE self);
|
30
30
|
static VALUE NIO_Selector_closed(VALUE self);
|
@@ -35,9 +35,7 @@ static VALUE NIO_Selector_unlock(VALUE lock);
|
|
35
35
|
static VALUE NIO_Selector_register_synchronized(VALUE *args);
|
36
36
|
static VALUE NIO_Selector_deregister_synchronized(VALUE *args);
|
37
37
|
static VALUE NIO_Selector_select_synchronized(VALUE *args);
|
38
|
-
static
|
39
|
-
static int NIO_Selector_fill_ready_buffer(VALUE *args);
|
40
|
-
static VALUE NIO_Selector_run_evloop(void *ptr);
|
38
|
+
static int NIO_Selector_run(struct NIO_Selector *selector, VALUE timeout);
|
41
39
|
static void NIO_Selector_timeout_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents);
|
42
40
|
static void NIO_Selector_wakeup_callback(struct ev_loop *ev_loop, struct ev_io *io, int revents);
|
43
41
|
|
@@ -59,7 +57,6 @@ void Init_NIO_Selector()
|
|
59
57
|
rb_define_method(cNIO_Selector, "deregister", NIO_Selector_deregister, 1);
|
60
58
|
rb_define_method(cNIO_Selector, "registered?", NIO_Selector_is_registered, 1);
|
61
59
|
rb_define_method(cNIO_Selector, "select", NIO_Selector_select, -1);
|
62
|
-
rb_define_method(cNIO_Selector, "select_each", NIO_Selector_select_each, -1);
|
63
60
|
rb_define_method(cNIO_Selector, "wakeup", NIO_Selector_wakeup, 0);
|
64
61
|
rb_define_method(cNIO_Selector, "close", NIO_Selector_close, 0);
|
65
62
|
rb_define_method(cNIO_Selector, "closed?", NIO_Selector_closed, 0);
|
@@ -99,8 +96,7 @@ static VALUE NIO_Selector_allocate(VALUE klass)
|
|
99
96
|
ev_io_start(selector->ev_loop, &selector->wakeup);
|
100
97
|
|
101
98
|
selector->closed = selector->selecting = selector->ready_count = 0;
|
102
|
-
selector->
|
103
|
-
selector->ready_buffer = (VALUE *)xmalloc(sizeof(VALUE) * INITIAL_READY_BUFFER);
|
99
|
+
selector->ready_array = Qnil;
|
104
100
|
|
105
101
|
return Data_Wrap_Struct(klass, NIO_Selector_mark, NIO_Selector_free, selector);
|
106
102
|
}
|
@@ -108,6 +104,9 @@ static VALUE NIO_Selector_allocate(VALUE klass)
|
|
108
104
|
/* NIO selectors store all Ruby objects in instance variables so mark is a stub */
|
109
105
|
static void NIO_Selector_mark(struct NIO_Selector *selector)
|
110
106
|
{
|
107
|
+
if(selector->ready_array != Qnil) {
|
108
|
+
rb_gc_mark(selector->ready_array);
|
109
|
+
}
|
111
110
|
}
|
112
111
|
|
113
112
|
/* Free a Selector's system resources.
|
@@ -133,8 +132,6 @@ static void NIO_Selector_shutdown(struct NIO_Selector *selector)
|
|
133
132
|
static void NIO_Selector_free(struct NIO_Selector *selector)
|
134
133
|
{
|
135
134
|
NIO_Selector_shutdown(selector);
|
136
|
-
|
137
|
-
xfree(selector->ready_buffer);
|
138
135
|
xfree(selector);
|
139
136
|
}
|
140
137
|
|
@@ -271,81 +268,36 @@ static VALUE NIO_Selector_select(int argc, VALUE *argv, VALUE self)
|
|
271
268
|
return NIO_Selector_synchronize(self, NIO_Selector_select_synchronized, args);
|
272
269
|
}
|
273
270
|
|
274
|
-
/* Select from all registered IO objects */
|
275
|
-
static VALUE NIO_Selector_select_each(int argc, VALUE *argv, VALUE self)
|
276
|
-
{
|
277
|
-
VALUE timeout, array;
|
278
|
-
VALUE args[2];
|
279
|
-
|
280
|
-
if(!rb_block_given_p()) {
|
281
|
-
rb_raise(rb_eArgError, "no block given");
|
282
|
-
}
|
283
|
-
|
284
|
-
rb_scan_args(argc, argv, "01", &timeout);
|
285
|
-
|
286
|
-
if(timeout != Qnil && NUM2DBL(timeout) < 0) {
|
287
|
-
rb_raise(rb_eArgError, "time interval must be positive");
|
288
|
-
}
|
289
|
-
|
290
|
-
args[0] = self;
|
291
|
-
args[1] = timeout;
|
292
|
-
|
293
|
-
return NIO_Selector_synchronize(self, NIO_Selector_select_each_synchronized, args);
|
294
|
-
}
|
295
|
-
|
296
271
|
/* Internal implementation of select with the selector lock held */
|
297
272
|
static VALUE NIO_Selector_select_synchronized(VALUE *args)
|
298
273
|
{
|
274
|
+
int i, ready;
|
275
|
+
VALUE ready_array;
|
299
276
|
struct NIO_Selector *selector;
|
300
|
-
int i, ready = NIO_Selector_fill_ready_buffer(args);
|
301
277
|
|
302
278
|
Data_Get_Struct(args[0], struct NIO_Selector, selector);
|
279
|
+
if(!rb_block_given_p()) {
|
280
|
+
selector->ready_array = rb_ary_new();
|
281
|
+
}
|
303
282
|
|
283
|
+
ready = NIO_Selector_run(selector, args[1]);
|
304
284
|
if(ready > 0) {
|
305
285
|
if(rb_block_given_p()) {
|
306
|
-
for(i = 0; i < ready; i++) {
|
307
|
-
rb_yield(selector->ready_buffer[i]);
|
308
|
-
}
|
309
|
-
|
310
286
|
return INT2NUM(ready);
|
311
287
|
} else {
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
} else {
|
316
|
-
return Qnil;
|
317
|
-
}
|
318
|
-
}
|
319
|
-
|
320
|
-
/* Internal implementation of select with the selector lock held */
|
321
|
-
static VALUE NIO_Selector_select_each_synchronized(VALUE *args)
|
322
|
-
{
|
323
|
-
struct NIO_Selector *selector;
|
324
|
-
int i, ready = NIO_Selector_fill_ready_buffer(args);
|
325
|
-
|
326
|
-
Data_Get_Struct(args[0], struct NIO_Selector, selector);
|
327
|
-
|
328
|
-
if(ready > 0) {
|
329
|
-
for(i = 0; i < ready; i++) {
|
330
|
-
rb_yield(selector->ready_buffer[i]);
|
288
|
+
ready_array = selector->ready_array;
|
289
|
+
selector->ready_array = Qnil;
|
290
|
+
return ready_array;
|
331
291
|
}
|
332
|
-
|
333
|
-
return INT2NUM(ready);
|
334
292
|
} else {
|
293
|
+
selector->ready_array = Qnil;
|
335
294
|
return Qnil;
|
336
295
|
}
|
337
296
|
}
|
338
297
|
|
339
|
-
static int
|
298
|
+
static int NIO_Selector_run(struct NIO_Selector *selector, VALUE timeout)
|
340
299
|
{
|
341
|
-
VALUE self, timeout;
|
342
|
-
struct NIO_Selector *selector;
|
343
300
|
int result;
|
344
|
-
|
345
|
-
self = args[0];
|
346
|
-
timeout = args[1];
|
347
|
-
|
348
|
-
Data_Get_Struct(self, struct NIO_Selector, selector);
|
349
301
|
selector->selecting = 1;
|
350
302
|
|
351
303
|
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_ALONE)
|
@@ -363,8 +315,8 @@ static int NIO_Selector_fill_ready_buffer(VALUE *args)
|
|
363
315
|
#endif
|
364
316
|
|
365
317
|
#if defined(HAVE_RB_THREAD_BLOCKING_REGION)
|
366
|
-
/*
|
367
|
-
|
318
|
+
/* libev is patched to release the GIL when it makes its system call */
|
319
|
+
ev_loop(selector->ev_loop, EVLOOP_ONESHOT);
|
368
320
|
#elif defined(HAVE_RB_THREAD_ALONE)
|
369
321
|
/* If we're the only thread we can make a blocking system call */
|
370
322
|
if(rb_thread_alone()) {
|
@@ -375,7 +327,7 @@ static int NIO_Selector_fill_ready_buffer(VALUE *args)
|
|
375
327
|
|
376
328
|
#if !defined(HAVE_RB_THREAD_BLOCKING_REGION)
|
377
329
|
TRAP_BEG;
|
378
|
-
|
330
|
+
ev_loop(selector->ev_loop, EVLOOP_ONESHOT);
|
379
331
|
TRAP_END;
|
380
332
|
} else {
|
381
333
|
/* We need to busy wait as not to stall the green thread scheduler
|
@@ -386,7 +338,7 @@ static int NIO_Selector_fill_ready_buffer(VALUE *args)
|
|
386
338
|
/* Loop until we receive events */
|
387
339
|
while(selector->selecting && !selector->ready_count) {
|
388
340
|
TRAP_BEG;
|
389
|
-
|
341
|
+
ev_loop(selector->ev_loop, EVLOOP_ONESHOT);
|
390
342
|
TRAP_END;
|
391
343
|
|
392
344
|
/* Run the next green thread */
|
@@ -407,16 +359,6 @@ static int NIO_Selector_fill_ready_buffer(VALUE *args)
|
|
407
359
|
return result;
|
408
360
|
}
|
409
361
|
|
410
|
-
/* Run the libev event loop */
|
411
|
-
static VALUE NIO_Selector_run_evloop(void *ptr)
|
412
|
-
{
|
413
|
-
struct NIO_Selector *selector = (struct NIO_Selector *)ptr;
|
414
|
-
|
415
|
-
ev_loop(selector->ev_loop, EVLOOP_ONESHOT);
|
416
|
-
|
417
|
-
return Qnil;
|
418
|
-
}
|
419
|
-
|
420
362
|
/* Wake the selector up from another thread */
|
421
363
|
static VALUE NIO_Selector_wakeup(VALUE self)
|
422
364
|
{
|
@@ -428,7 +370,6 @@ static VALUE NIO_Selector_wakeup(VALUE self)
|
|
428
370
|
}
|
429
371
|
|
430
372
|
write(selector->wakeup_writer, "\0", 1);
|
431
|
-
|
432
373
|
return Qnil;
|
433
374
|
}
|
434
375
|
|
@@ -470,27 +411,21 @@ static void NIO_Selector_wakeup_callback(struct ev_loop *ev_loop, struct ev_io *
|
|
470
411
|
while(read(selector->wakeup_reader, buffer, 128) > 0);
|
471
412
|
}
|
472
413
|
|
473
|
-
/*
|
474
|
-
|
475
|
-
anything Ruby-related.
|
476
|
-
|
477
|
-
It's scary because there's a VALUE here, and VALUEs are a Ruby thing,
|
478
|
-
however we're not going to dereference that VALUE or attempt to do anything
|
479
|
-
with it. We just treat it as completely opaque until we have the GIL back.
|
480
|
-
|
481
|
-
In order for this function to even get called, the monitor the VALUE points to
|
482
|
-
must be attached to this Selector, and if it's attached to this Selector
|
483
|
-
then we hold a reference to it in the @selectables instance variable, so
|
484
|
-
there's no danger of this monitor getting garbage collected before we have
|
485
|
-
the GIL back as we hold a reference. */
|
486
|
-
void NIO_Selector_handle_event(struct NIO_Selector *selector, VALUE monitor, int revents)
|
414
|
+
/* libev callback fired whenever a monitor gets an event */
|
415
|
+
void NIO_Selector_monitor_callback(struct ev_loop *ev_loop, struct ev_io *io, int revents)
|
487
416
|
{
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
selector->ready_buffer = (VALUE *)xrealloc(selector->ready_buffer, sizeof(VALUE) * selector->ready_buffer_size);
|
492
|
-
}
|
417
|
+
struct NIO_Monitor *monitor_data = (struct NIO_Monitor *)io->data;
|
418
|
+
struct NIO_Selector *selector = monitor_data->selector;
|
419
|
+
VALUE monitor = monitor_data->self;
|
493
420
|
|
494
|
-
selector
|
421
|
+
assert(selector != 0);
|
495
422
|
selector->ready_count++;
|
423
|
+
monitor_data->revents = revents;
|
424
|
+
|
425
|
+
if(rb_block_given_p()) {
|
426
|
+
rb_yield(monitor);
|
427
|
+
} else {
|
428
|
+
assert(selector->ready_array != Qnil);
|
429
|
+
rb_ary_push(selector->ready_array, monitor);
|
430
|
+
}
|
496
431
|
}
|
data/lib/nio/selector.rb
CHANGED
@@ -107,14 +107,6 @@ module NIO
|
|
107
107
|
end
|
108
108
|
end
|
109
109
|
|
110
|
-
# Select for ready monitors, successively yielding each one in a block
|
111
|
-
def select_each(timeout = nil, &block)
|
112
|
-
selected = select(timeout)
|
113
|
-
return unless selected
|
114
|
-
selected.each(&block)
|
115
|
-
selected.size
|
116
|
-
end
|
117
|
-
|
118
110
|
# Wake up a thread that's in the middle of selecting on this selector, if
|
119
111
|
# any such thread exists.
|
120
112
|
#
|
data/lib/nio/version.rb
CHANGED
@@ -39,14 +39,18 @@ describe "NIO selectables" do
|
|
39
39
|
describe "IO.pipe" do
|
40
40
|
let(:pair) { IO.pipe }
|
41
41
|
|
42
|
-
let :unreadable_subject do
|
42
|
+
let :unreadable_subject do
|
43
|
+
pair.first
|
44
|
+
end
|
43
45
|
let :readable_subject do
|
44
46
|
pipe, peer = pair
|
45
47
|
peer << "data"
|
46
48
|
pipe
|
47
49
|
end
|
48
50
|
|
49
|
-
let :writable_subject do
|
51
|
+
let :writable_subject do
|
52
|
+
pair.last
|
53
|
+
end
|
50
54
|
let :unwritable_subject do
|
51
55
|
reader, pipe = IO.pipe
|
52
56
|
|
data/spec/nio/selector_spec.rb
CHANGED
@@ -138,45 +138,6 @@ describe NIO::Selector do
|
|
138
138
|
end
|
139
139
|
end
|
140
140
|
|
141
|
-
context "select_each" do
|
142
|
-
it "iterates across ready selectables" do
|
143
|
-
readable1, writer = IO.pipe
|
144
|
-
writer << "ohai"
|
145
|
-
|
146
|
-
readable2, writer = IO.pipe
|
147
|
-
writer << "ohai"
|
148
|
-
|
149
|
-
unreadable, _ = IO.pipe
|
150
|
-
|
151
|
-
monitor1 = subject.register(readable1, :r)
|
152
|
-
monitor2 = subject.register(readable2, :r)
|
153
|
-
monitor3 = subject.register(unreadable, :r)
|
154
|
-
|
155
|
-
readables = []
|
156
|
-
subject.select_each { |monitor| readables << monitor }
|
157
|
-
|
158
|
-
readables.should include(monitor1)
|
159
|
-
readables.should include(monitor2)
|
160
|
-
readables.should_not include(monitor3)
|
161
|
-
end
|
162
|
-
|
163
|
-
it "allows new monitors to be registered in the select_each block" do
|
164
|
-
server = TCPServer.new("localhost", 10001)
|
165
|
-
|
166
|
-
monitor = subject.register(server, :r)
|
167
|
-
connector = TCPSocket.open("localhost", 10001)
|
168
|
-
|
169
|
-
block_fired = false
|
170
|
-
subject.select_each do |monitor|
|
171
|
-
block_fired = true
|
172
|
-
socket = server.accept
|
173
|
-
subject.register(socket, :r).should be_a NIO::Monitor
|
174
|
-
end
|
175
|
-
|
176
|
-
block_fired.should be_true
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
141
|
it "closes" do
|
181
142
|
subject.close
|
182
143
|
subject.should be_closed
|
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.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-03-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake-compiler
|
16
|
-
requirement: &
|
16
|
+
requirement: &70242529573760 !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: *70242529573760
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rake
|
27
|
-
requirement: &
|
27
|
+
requirement: &70242529573340 !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: *70242529573340
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rspec
|
38
|
-
requirement: &
|
38
|
+
requirement: &70242529572920 !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: *70242529572920
|
47
47
|
description: New IO for Ruby
|
48
48
|
email:
|
49
49
|
- tony.arcieri@gmail.com
|