nio4r 0.3.1 → 0.3.2
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/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
|