nio4r 0.1.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/.gitignore +20 -0
- data/.rspec +4 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +20 -0
- data/README.md +123 -0
- data/Rakefile +9 -0
- data/ext/libev/Changes +388 -0
- data/ext/libev/LICENSE +36 -0
- data/ext/libev/README +58 -0
- data/ext/libev/README.embed +3 -0
- data/ext/libev/ev.c +3913 -0
- data/ext/libev/ev.h +829 -0
- data/ext/libev/ev_epoll.c +266 -0
- data/ext/libev/ev_kqueue.c +198 -0
- data/ext/libev/ev_poll.c +148 -0
- data/ext/libev/ev_port.c +179 -0
- data/ext/libev/ev_select.c +310 -0
- data/ext/libev/ev_vars.h +203 -0
- data/ext/libev/ev_win32.c +153 -0
- data/ext/libev/ev_wrap.h +196 -0
- data/ext/libev/test_libev_win32.c +123 -0
- data/ext/nio4r/extconf.rb +44 -0
- data/ext/nio4r/libev.h +8 -0
- data/ext/nio4r/monitor.c +164 -0
- data/ext/nio4r/nio4r.h +53 -0
- data/ext/nio4r/nio4r_ext.c +16 -0
- data/ext/nio4r/selector.c +370 -0
- data/lib/nio/jruby/monitor.rb +26 -0
- data/lib/nio/jruby/selector.rb +110 -0
- data/lib/nio/monitor.rb +21 -0
- data/lib/nio/selector.rb +101 -0
- data/lib/nio/version.rb +3 -0
- data/lib/nio.rb +30 -0
- data/nio4r.gemspec +23 -0
- data/spec/nio/monitor_spec.rb +36 -0
- data/spec/nio/selector_spec.rb +197 -0
- data/spec/spec_helper.rb +3 -0
- data/tasks/extension.rake +10 -0
- data/tasks/rspec.rake +7 -0
- metadata +121 -0
data/ext/nio4r/nio4r.h
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2011 Tony Arcieri. Distributed under the MIT License. See
|
3
|
+
* LICENSE.txt for further details.
|
4
|
+
*/
|
5
|
+
|
6
|
+
#ifndef NIO4R_H
|
7
|
+
#define NIO4R_H
|
8
|
+
|
9
|
+
#include "ruby.h"
|
10
|
+
#include "rubyio.h"
|
11
|
+
#include "libev.h"
|
12
|
+
|
13
|
+
struct NIO_Selector
|
14
|
+
{
|
15
|
+
struct ev_loop *ev_loop;
|
16
|
+
struct ev_timer timer; /* for timeouts */
|
17
|
+
struct ev_async wakeup;
|
18
|
+
|
19
|
+
int closed, selecting;
|
20
|
+
int ready_count;
|
21
|
+
int ready_buffer_size;
|
22
|
+
VALUE *ready_buffer;
|
23
|
+
};
|
24
|
+
|
25
|
+
struct NIO_callback_data
|
26
|
+
{
|
27
|
+
VALUE *monitor;
|
28
|
+
struct NIO_Selector *selector;
|
29
|
+
};
|
30
|
+
|
31
|
+
struct NIO_Monitor
|
32
|
+
{
|
33
|
+
VALUE self;
|
34
|
+
struct ev_io ev_io;
|
35
|
+
struct NIO_Selector *selector;
|
36
|
+
};
|
37
|
+
|
38
|
+
#ifdef GetReadFile
|
39
|
+
# define FPTR_TO_FD(fptr) (fileno(GetReadFile(fptr)))
|
40
|
+
#else
|
41
|
+
|
42
|
+
#if !HAVE_RB_IO_T || (RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8)
|
43
|
+
# define FPTR_TO_FD(fptr) fileno(fptr->f)
|
44
|
+
#else
|
45
|
+
# define FPTR_TO_FD(fptr) fptr->fd
|
46
|
+
#endif /* !HAVE_RB_IO_T */
|
47
|
+
|
48
|
+
#endif /* GetReadFile */
|
49
|
+
|
50
|
+
/* Thunk between libev callbacks in NIO::Monitors and NIO::Selectors */
|
51
|
+
void NIO_Selector_handle_event(struct NIO_Selector *selector, VALUE monitor, int revents);
|
52
|
+
|
53
|
+
#endif /* NIO4R_H */
|
@@ -0,0 +1,16 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2011 Tony Arcieri. Distributed under the MIT License. See
|
3
|
+
* LICENSE.txt for further details.
|
4
|
+
*/
|
5
|
+
|
6
|
+
#include "nio4r.h"
|
7
|
+
#include "../libev/ev.c"
|
8
|
+
|
9
|
+
void Init_NIO_Selector();
|
10
|
+
void Init_NIO_Monitor();
|
11
|
+
|
12
|
+
void Init_nio4r_ext()
|
13
|
+
{
|
14
|
+
Init_NIO_Selector();
|
15
|
+
Init_NIO_Monitor();
|
16
|
+
}
|
@@ -0,0 +1,370 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2011 Tony Arcieri. Distributed under the MIT License. See
|
3
|
+
* LICENSE.txt for further details.
|
4
|
+
*/
|
5
|
+
|
6
|
+
#include "nio4r.h"
|
7
|
+
#include "rubysig.h"
|
8
|
+
|
9
|
+
static VALUE mNIO = Qnil;
|
10
|
+
static VALUE cNIO_Channel = Qnil;
|
11
|
+
static VALUE cNIO_Monitor = Qnil;
|
12
|
+
static VALUE cNIO_Selector = Qnil;
|
13
|
+
|
14
|
+
/* Allocator/deallocator */
|
15
|
+
static VALUE NIO_Selector_allocate(VALUE klass);
|
16
|
+
static void NIO_Selector_mark(struct NIO_Selector *loop);
|
17
|
+
static void NIO_Selector_shutdown(struct NIO_Selector *selector);
|
18
|
+
static void NIO_Selector_free(struct NIO_Selector *loop);
|
19
|
+
|
20
|
+
/* Methods */
|
21
|
+
static VALUE NIO_Selector_initialize(VALUE self);
|
22
|
+
static VALUE NIO_Selector_register(VALUE self, VALUE selectable, VALUE interest);
|
23
|
+
static VALUE NIO_Selector_deregister(VALUE self, VALUE io);
|
24
|
+
static VALUE NIO_Selector_is_registered(VALUE self, VALUE io);
|
25
|
+
static VALUE NIO_Selector_select(int argc, VALUE *argv, VALUE self);
|
26
|
+
static VALUE NIO_Selector_wakeup(VALUE self);
|
27
|
+
static VALUE NIO_Selector_close(VALUE self);
|
28
|
+
static VALUE NIO_Selector_closed(VALUE self);
|
29
|
+
|
30
|
+
/* Internal functions */
|
31
|
+
static VALUE NIO_Selector_synchronize(VALUE self, VALUE (*func)(VALUE *args), VALUE *args);
|
32
|
+
static VALUE NIO_Selector_unlock(VALUE lock);
|
33
|
+
static VALUE NIO_Selector_register_synchronized(VALUE *args);
|
34
|
+
static VALUE NIO_Selector_deregister_synchronized(VALUE *args);
|
35
|
+
static VALUE NIO_Selector_select_synchronized(VALUE *args);
|
36
|
+
static VALUE NIO_Selector_run_evloop(void *ptr);
|
37
|
+
static void NIO_Selector_timeout_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents);
|
38
|
+
static void NIO_Selector_wakeup_callback(struct ev_loop *ev_loop, struct ev_async *async, int revents);
|
39
|
+
|
40
|
+
/* Default number of slots in the buffer for selected monitors */
|
41
|
+
#define INITIAL_READY_BUFFER 32
|
42
|
+
|
43
|
+
/* Ruby 1.8 needs us to busy wait and run the green threads scheduler every 10ms */
|
44
|
+
#define BUSYWAIT_INTERVAL 0.01
|
45
|
+
|
46
|
+
/* Selectors wait for events */
|
47
|
+
void Init_NIO_Selector()
|
48
|
+
{
|
49
|
+
mNIO = rb_define_module("NIO");
|
50
|
+
cNIO_Channel = rb_define_class_under(mNIO, "Channel", rb_cObject);
|
51
|
+
cNIO_Monitor = rb_define_class_under(mNIO, "Monitor", rb_cObject);
|
52
|
+
cNIO_Selector = rb_define_class_under(mNIO, "Selector", rb_cObject);
|
53
|
+
rb_define_alloc_func(cNIO_Selector, NIO_Selector_allocate);
|
54
|
+
|
55
|
+
rb_define_method(cNIO_Selector, "initialize", NIO_Selector_initialize, 0);
|
56
|
+
rb_define_method(cNIO_Selector, "register", NIO_Selector_register, 2);
|
57
|
+
rb_define_method(cNIO_Selector, "deregister", NIO_Selector_deregister, 1);
|
58
|
+
rb_define_method(cNIO_Selector, "registered?", NIO_Selector_is_registered, 1);
|
59
|
+
rb_define_method(cNIO_Selector, "select", NIO_Selector_select, -1);
|
60
|
+
rb_define_method(cNIO_Selector, "wakeup", NIO_Selector_wakeup, 0);
|
61
|
+
rb_define_method(cNIO_Selector, "close", NIO_Selector_close, 0);
|
62
|
+
rb_define_method(cNIO_Selector, "closed?", NIO_Selector_closed, 0);
|
63
|
+
}
|
64
|
+
|
65
|
+
/* Create the libev event loop and incoming event buffer */
|
66
|
+
static VALUE NIO_Selector_allocate(VALUE klass)
|
67
|
+
{
|
68
|
+
struct NIO_Selector *selector = (struct NIO_Selector *)xmalloc(sizeof(struct NIO_Selector));
|
69
|
+
|
70
|
+
selector->ev_loop = ev_loop_new(0);
|
71
|
+
ev_init(&selector->timer, NIO_Selector_timeout_callback);
|
72
|
+
|
73
|
+
ev_async_init(&selector->wakeup, NIO_Selector_wakeup_callback);
|
74
|
+
selector->wakeup.data = (void *)selector;
|
75
|
+
|
76
|
+
ev_async_start(selector->ev_loop, &selector->wakeup);
|
77
|
+
|
78
|
+
selector->closed = selector->selecting = selector->ready_count = 0;
|
79
|
+
selector->ready_buffer_size = INITIAL_READY_BUFFER;
|
80
|
+
selector->ready_buffer = (VALUE *)xmalloc(sizeof(VALUE) * INITIAL_READY_BUFFER);
|
81
|
+
|
82
|
+
return Data_Wrap_Struct(klass, NIO_Selector_mark, NIO_Selector_free, selector);
|
83
|
+
}
|
84
|
+
|
85
|
+
/* NIO selectors store all Ruby objects in instance variables so mark is a stub */
|
86
|
+
static void NIO_Selector_mark(struct NIO_Selector *selector)
|
87
|
+
{
|
88
|
+
}
|
89
|
+
|
90
|
+
/* Free a Selector's system resources.
|
91
|
+
Called by both NIO::Selector#close and the finalizer below */
|
92
|
+
static void NIO_Selector_shutdown(struct NIO_Selector *selector)
|
93
|
+
{
|
94
|
+
if(selector->ev_loop) {
|
95
|
+
ev_loop_destroy(selector->ev_loop);
|
96
|
+
selector->ev_loop = 0;
|
97
|
+
}
|
98
|
+
|
99
|
+
if(selector->closed) {
|
100
|
+
return;
|
101
|
+
}
|
102
|
+
|
103
|
+
selector->closed = 1;
|
104
|
+
}
|
105
|
+
|
106
|
+
/* Ruby finalizer for selector objects */
|
107
|
+
static void NIO_Selector_free(struct NIO_Selector *selector)
|
108
|
+
{
|
109
|
+
NIO_Selector_shutdown(selector);
|
110
|
+
|
111
|
+
xfree(selector->ready_buffer);
|
112
|
+
xfree(selector);
|
113
|
+
}
|
114
|
+
|
115
|
+
/* Create a new selector. This is more or less the pure Ruby version
|
116
|
+
translated into an MRI cext */
|
117
|
+
static VALUE NIO_Selector_initialize(VALUE self)
|
118
|
+
{
|
119
|
+
VALUE lock;
|
120
|
+
|
121
|
+
rb_ivar_set(self, rb_intern("selectables"), rb_hash_new());
|
122
|
+
|
123
|
+
lock = rb_class_new_instance(0, 0, rb_const_get(rb_cObject, rb_intern("Mutex")));
|
124
|
+
rb_ivar_set(self, rb_intern("lock"), lock);
|
125
|
+
|
126
|
+
return Qnil;
|
127
|
+
}
|
128
|
+
|
129
|
+
/* Synchronize the given function with the selector mutex */
|
130
|
+
static VALUE NIO_Selector_synchronize(VALUE self, VALUE (*func)(VALUE *args), VALUE *args)
|
131
|
+
{
|
132
|
+
VALUE lock;
|
133
|
+
|
134
|
+
lock = rb_ivar_get(self, rb_intern("lock"));
|
135
|
+
rb_funcall(lock, rb_intern("lock"), 0, 0);
|
136
|
+
return rb_ensure(func, (VALUE)args, NIO_Selector_unlock, lock);
|
137
|
+
}
|
138
|
+
|
139
|
+
/* Unlock the selector mutex */
|
140
|
+
static VALUE NIO_Selector_unlock(VALUE lock)
|
141
|
+
{
|
142
|
+
rb_funcall(lock, rb_intern("unlock"), 0, 0);
|
143
|
+
}
|
144
|
+
|
145
|
+
/* Register an IO object with the selector for the given interests */
|
146
|
+
static VALUE NIO_Selector_register(VALUE self, VALUE io, VALUE interests)
|
147
|
+
{
|
148
|
+
VALUE args[3] = {self, io, interests};
|
149
|
+
return NIO_Selector_synchronize(self, NIO_Selector_register_synchronized, args);
|
150
|
+
}
|
151
|
+
|
152
|
+
/* Internal implementation of register after acquiring mutex */
|
153
|
+
static VALUE NIO_Selector_register_synchronized(VALUE *args)
|
154
|
+
{
|
155
|
+
VALUE self, io, interests, selectables, monitor;
|
156
|
+
VALUE monitor_args[3];
|
157
|
+
|
158
|
+
self = args[0];
|
159
|
+
io = args[1];
|
160
|
+
interests = args[2];
|
161
|
+
|
162
|
+
selectables = rb_ivar_get(self, rb_intern("selectables"));
|
163
|
+
monitor = rb_hash_lookup(selectables, io);
|
164
|
+
|
165
|
+
if(monitor != Qnil)
|
166
|
+
rb_raise(rb_eArgError, "this IO is already registered with selector");
|
167
|
+
|
168
|
+
/* Create a new NIO::Monitor */
|
169
|
+
monitor_args[0] = self;
|
170
|
+
monitor_args[1] = io;
|
171
|
+
monitor_args[2] = interests;
|
172
|
+
|
173
|
+
monitor = rb_class_new_instance(3, monitor_args, cNIO_Monitor);
|
174
|
+
rb_hash_aset(selectables, io, monitor);
|
175
|
+
|
176
|
+
return monitor;
|
177
|
+
}
|
178
|
+
|
179
|
+
/* Deregister an IO object from the selector */
|
180
|
+
static VALUE NIO_Selector_deregister(VALUE self, VALUE io)
|
181
|
+
{
|
182
|
+
VALUE args[2] = {self, io};
|
183
|
+
return NIO_Selector_synchronize(self, NIO_Selector_deregister_synchronized, args);
|
184
|
+
}
|
185
|
+
|
186
|
+
/* Internal implementation of register after acquiring mutex */
|
187
|
+
static VALUE NIO_Selector_deregister_synchronized(VALUE *args)
|
188
|
+
{
|
189
|
+
VALUE self, io, interests, selectables, monitor;
|
190
|
+
VALUE monitor_args[3];
|
191
|
+
|
192
|
+
self = args[0];
|
193
|
+
io = args[1];
|
194
|
+
|
195
|
+
selectables = rb_ivar_get(self, rb_intern("selectables"));
|
196
|
+
monitor = rb_hash_delete(selectables, io);
|
197
|
+
|
198
|
+
if(monitor != Qnil) {
|
199
|
+
rb_funcall(monitor, rb_intern("close"), 0, 0);
|
200
|
+
}
|
201
|
+
|
202
|
+
return monitor;
|
203
|
+
}
|
204
|
+
|
205
|
+
/* Is the given IO object registered with the selector */
|
206
|
+
static VALUE NIO_Selector_is_registered(VALUE self, VALUE io)
|
207
|
+
{
|
208
|
+
VALUE selectables = rb_ivar_get(self, rb_intern("selectables"));
|
209
|
+
|
210
|
+
/* Perhaps this should be holding the mutex? */
|
211
|
+
return rb_funcall(selectables, rb_intern("has_key?"), 1, io);
|
212
|
+
}
|
213
|
+
|
214
|
+
/* Select from all registered IO objects */
|
215
|
+
static VALUE NIO_Selector_select(int argc, VALUE *argv, VALUE self)
|
216
|
+
{
|
217
|
+
VALUE timeout, array;
|
218
|
+
VALUE args[2];
|
219
|
+
|
220
|
+
rb_scan_args(argc, argv, "01", &timeout);
|
221
|
+
|
222
|
+
args[0] = self;
|
223
|
+
args[1] = timeout;
|
224
|
+
|
225
|
+
return NIO_Selector_synchronize(self, NIO_Selector_select_synchronized, args);
|
226
|
+
}
|
227
|
+
|
228
|
+
/* Internal implementation of select with the selector lock held */
|
229
|
+
static VALUE NIO_Selector_select_synchronized(VALUE *args)
|
230
|
+
{
|
231
|
+
VALUE self, timeout, result;
|
232
|
+
struct NIO_Selector *selector;
|
233
|
+
|
234
|
+
self = args[0];
|
235
|
+
timeout = args[1];
|
236
|
+
|
237
|
+
Data_Get_Struct(self, struct NIO_Selector, selector);
|
238
|
+
selector->selecting = 1;
|
239
|
+
|
240
|
+
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_ALONE)
|
241
|
+
/* Implement the optional timeout (if any) as a ev_timer */
|
242
|
+
if(timeout != Qnil) {
|
243
|
+
selector->timer.repeat = NUM2DBL(timeout);
|
244
|
+
ev_timer_again(selector->ev_loop, &selector->timer);
|
245
|
+
} else {
|
246
|
+
ev_timer_stop(selector->ev_loop, &selector->timer);
|
247
|
+
}
|
248
|
+
#else
|
249
|
+
/* Store when we started the loop so we can calculate the timeout */
|
250
|
+
ev_tstamp started_at = ev_now(selector->ev_loop);
|
251
|
+
#endif
|
252
|
+
|
253
|
+
#if defined(HAVE_RB_THREAD_BLOCKING_REGION)
|
254
|
+
/* Ruby 1.9 lets us release the GIL and make a blocking I/O call */
|
255
|
+
rb_thread_blocking_region(NIO_Selector_run_evloop, selector, RUBY_UBF_IO, 0);
|
256
|
+
#elif defined(HAVE_RB_THREAD_ALONE)
|
257
|
+
/* If we're the only thread we can make a blocking system call */
|
258
|
+
if(rb_thread_alone()) {
|
259
|
+
#else
|
260
|
+
/* If we don't have rb_thread_alone() we can't block */
|
261
|
+
if(0) {
|
262
|
+
#endif /* defined(HAVE_RB_THREAD_BLOCKING_REGION) */
|
263
|
+
|
264
|
+
#if !defined(HAVE_RB_THREAD_BLOCKING_REGION)
|
265
|
+
TRAP_BEG;
|
266
|
+
NIO_Selector_run_evloop(selector);
|
267
|
+
TRAP_END;
|
268
|
+
} else {
|
269
|
+
/* We need to busy wait as not to stall the green thread scheduler
|
270
|
+
Ruby 1.8: just say no! :( */
|
271
|
+
ev_timer_init(&selector->timer, NIO_Selector_timeout_callback, BUSYWAIT_INTERVAL, BUSYWAIT_INTERVAL);
|
272
|
+
ev_timer_start(selector->ev_loop, &selector->timer);
|
273
|
+
|
274
|
+
/* Loop until we receive events */
|
275
|
+
while(selector->selecting && !selector->ready_count) {
|
276
|
+
TRAP_BEG;
|
277
|
+
NIO_Selector_run_evloop(selector);
|
278
|
+
TRAP_END;
|
279
|
+
|
280
|
+
/* Run the next green thread */
|
281
|
+
rb_thread_schedule();
|
282
|
+
|
283
|
+
/* Break if the timeout has elapsed */
|
284
|
+
if(timeout != Qnil && ev_now(selector->ev_loop) - started_at >= NUM2DBL(timeout))
|
285
|
+
break;
|
286
|
+
}
|
287
|
+
|
288
|
+
ev_timer_stop(selector->ev_loop, &selector->timer);
|
289
|
+
}
|
290
|
+
#endif /* defined(HAVE_RB_THREAD_BLOCKING_REGION) */
|
291
|
+
|
292
|
+
if(!selector->ready_count) {
|
293
|
+
return Qnil;
|
294
|
+
}
|
295
|
+
|
296
|
+
result = rb_ary_new4(selector->ready_count, selector->ready_buffer);
|
297
|
+
selector->selecting = selector->ready_count = 0;
|
298
|
+
|
299
|
+
return result;
|
300
|
+
}
|
301
|
+
|
302
|
+
/* Run the libev event loop */
|
303
|
+
static VALUE NIO_Selector_run_evloop(void *ptr)
|
304
|
+
{
|
305
|
+
struct NIO_Selector *selector = (struct NIO_Selector *)ptr;
|
306
|
+
|
307
|
+
ev_loop(selector->ev_loop, EVLOOP_ONESHOT);
|
308
|
+
|
309
|
+
return Qnil;
|
310
|
+
}
|
311
|
+
|
312
|
+
/* Wake the selector up from another thread */
|
313
|
+
static VALUE NIO_Selector_wakeup(VALUE self)
|
314
|
+
{
|
315
|
+
struct NIO_Selector *selector;
|
316
|
+
Data_Get_Struct(self, struct NIO_Selector, selector);
|
317
|
+
|
318
|
+
ev_async_send(selector->ev_loop, &selector->wakeup);
|
319
|
+
|
320
|
+
return Qnil;
|
321
|
+
}
|
322
|
+
|
323
|
+
/* Close the selector and free system resources */
|
324
|
+
static VALUE NIO_Selector_close(VALUE self)
|
325
|
+
{
|
326
|
+
struct NIO_Selector *selector;
|
327
|
+
Data_Get_Struct(self, struct NIO_Selector, selector);
|
328
|
+
|
329
|
+
NIO_Selector_shutdown(selector);
|
330
|
+
|
331
|
+
return Qnil;
|
332
|
+
}
|
333
|
+
|
334
|
+
/* Is the selector closed? */
|
335
|
+
static VALUE NIO_Selector_closed(VALUE self)
|
336
|
+
{
|
337
|
+
struct NIO_Selector *selector;
|
338
|
+
Data_Get_Struct(self, struct NIO_Selector, selector);
|
339
|
+
|
340
|
+
return selector->closed ? Qtrue : Qfalse;
|
341
|
+
}
|
342
|
+
|
343
|
+
/* Called whenever a timeout fires on the event loop */
|
344
|
+
static void NIO_Selector_timeout_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents)
|
345
|
+
{
|
346
|
+
/* We don't actually need to do anything here, the mere firing of the
|
347
|
+
timer is sufficient to interrupt the selector. However, libev still wants a callback */
|
348
|
+
}
|
349
|
+
|
350
|
+
/* Called whenever a wakeup request is sent to a selector */
|
351
|
+
static void NIO_Selector_wakeup_callback(struct ev_loop *ev_loop, struct ev_async *async, int revents)
|
352
|
+
{
|
353
|
+
struct NIO_Selector *selector = (struct NIO_Selector *)async->data;
|
354
|
+
selector->selecting = 0;
|
355
|
+
}
|
356
|
+
|
357
|
+
/* This gets called from individual monitors. We must be careful here because
|
358
|
+
the GIL isn't held, so we must rely only on standard C and can't touch
|
359
|
+
anything Ruby-related */
|
360
|
+
void NIO_Selector_handle_event(struct NIO_Selector *selector, VALUE monitor, int revents)
|
361
|
+
{
|
362
|
+
/* Grow the ready buffer if it's too small */
|
363
|
+
if(selector->ready_count >= selector->ready_buffer_size) {
|
364
|
+
selector->ready_buffer_size *= 2;
|
365
|
+
selector->ready_buffer = (VALUE *)xrealloc(selector->ready_buffer, sizeof(VALUE) * selector->ready_buffer_size);
|
366
|
+
}
|
367
|
+
|
368
|
+
selector->ready_buffer[selector->ready_count] = monitor;
|
369
|
+
selector->ready_count++;
|
370
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module NIO
|
2
|
+
# Monitors watch Channels for specific events
|
3
|
+
class Monitor
|
4
|
+
attr_accessor :value
|
5
|
+
|
6
|
+
# :nodoc
|
7
|
+
def initialize(io, selection_key)
|
8
|
+
@io, @key = io, selection_key
|
9
|
+
selection_key.attach self
|
10
|
+
@closed = false
|
11
|
+
end
|
12
|
+
|
13
|
+
# Obtain the interests for this monitor
|
14
|
+
def interests
|
15
|
+
Selector.iops2sym @key.interestOps
|
16
|
+
end
|
17
|
+
|
18
|
+
# Is this monitor closed?
|
19
|
+
def closed?; @closed; end
|
20
|
+
|
21
|
+
# Deactivate this monitor
|
22
|
+
def close
|
23
|
+
@closed = true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module NIO
|
2
|
+
# Selectors monitor IO objects for events of interest
|
3
|
+
class Selector
|
4
|
+
java_import "java.nio.channels.Selector"
|
5
|
+
java_import "java.nio.channels.SelectionKey"
|
6
|
+
|
7
|
+
# Convert nio4r interest symbols to Java NIO interest ops
|
8
|
+
def self.sym2iops(interest)
|
9
|
+
case interest
|
10
|
+
when :r
|
11
|
+
interest = SelectionKey::OP_READ
|
12
|
+
when :w
|
13
|
+
interest = SelectionKey::OP_WRITE
|
14
|
+
when :rw
|
15
|
+
interest = SelectionKey::OP_READ | SelectionKey::OP_WRITE
|
16
|
+
else raise ArgumentError, "invalid interest type: #{interest}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Convert Java NIO interest ops to the corresponding Ruby symbols
|
21
|
+
def self.iops2sym(interest_ops)
|
22
|
+
case interest_ops
|
23
|
+
when SelectionKey::OP_READ
|
24
|
+
:r
|
25
|
+
when SelectionKey::OP_WRITE
|
26
|
+
:w
|
27
|
+
when SelectionKey::OP_READ | SelectionKey::OP_WRITE
|
28
|
+
:rw
|
29
|
+
else raise ArgumentError, "unknown interest op combination: 0x#{interest_ops.to_s(16)}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Create a new NIO::Selector
|
34
|
+
def initialize
|
35
|
+
@java_selector = Selector.open
|
36
|
+
@select_lock = Mutex.new
|
37
|
+
end
|
38
|
+
|
39
|
+
# Register interest in an IO object with the selector for the given types
|
40
|
+
# of events. Valid event types for interest are:
|
41
|
+
# * :r - is the IO readable?
|
42
|
+
# * :w - is the IO writeable?
|
43
|
+
# * :rw - is the IO either readable or writeable?
|
44
|
+
def register(io, interest)
|
45
|
+
java_channel = io.to_channel
|
46
|
+
java_channel.configureBlocking(false)
|
47
|
+
|
48
|
+
interest_ops = self.class.sym2iops(interest)
|
49
|
+
|
50
|
+
begin
|
51
|
+
selector_key = java_channel.register @java_selector, interest_ops
|
52
|
+
rescue NativeException => ex
|
53
|
+
case ex.cause
|
54
|
+
when java.lang.IllegalArgumentException
|
55
|
+
raise ArgumentError, "invalid interest type for #{channel}: #{interest}"
|
56
|
+
else raise
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
NIO::Monitor.new(io, selector_key)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Deregister the given IO object from the selector
|
64
|
+
def deregister(io)
|
65
|
+
key = io.to_channel.keyFor(@java_selector)
|
66
|
+
return unless key
|
67
|
+
|
68
|
+
monitor = key.attachment
|
69
|
+
monitor.close
|
70
|
+
monitor
|
71
|
+
end
|
72
|
+
|
73
|
+
# Is the given IO object registered with the selector?
|
74
|
+
def registered?(io)
|
75
|
+
key = io.to_channel.keyFor(@java_selector)
|
76
|
+
return unless key
|
77
|
+
!key.attachment.closed?
|
78
|
+
end
|
79
|
+
|
80
|
+
# Select which monitors are ready
|
81
|
+
def select(timeout = nil)
|
82
|
+
@select_lock.synchronize do
|
83
|
+
if timeout
|
84
|
+
ready = @java_selector.select(timeout * 1000)
|
85
|
+
else
|
86
|
+
ready = @java_selector.select
|
87
|
+
end
|
88
|
+
|
89
|
+
return unless ready > 0 # timeout or wakeup
|
90
|
+
@java_selector.selectedKeys.map { |key| key.attachment }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Wake up the other thread that's currently blocking on this selector
|
95
|
+
def wakeup
|
96
|
+
@java_selector.wakeup
|
97
|
+
nil
|
98
|
+
end
|
99
|
+
|
100
|
+
# Close this selector
|
101
|
+
def close
|
102
|
+
@java_selector.close
|
103
|
+
end
|
104
|
+
|
105
|
+
# Is this selector closed?
|
106
|
+
def closed?
|
107
|
+
!@java_selector.isOpen
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/lib/nio/monitor.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module NIO
|
2
|
+
# Monitors watch IO objects for specific events
|
3
|
+
class Monitor
|
4
|
+
attr_reader :io, :interests
|
5
|
+
attr_accessor :value
|
6
|
+
|
7
|
+
# :nodoc
|
8
|
+
def initialize(io, interests)
|
9
|
+
@io, @interests = io, interests
|
10
|
+
@closed = false
|
11
|
+
end
|
12
|
+
|
13
|
+
# Is this monitor closed?
|
14
|
+
def closed?; @closed; end
|
15
|
+
|
16
|
+
# Deactivate this monitor
|
17
|
+
def close
|
18
|
+
@closed = true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/nio/selector.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
module NIO
|
2
|
+
# Selectors monitor IO objects for events of interest
|
3
|
+
class Selector
|
4
|
+
# Create a new NIO::Selector
|
5
|
+
def initialize
|
6
|
+
@selectables = {}
|
7
|
+
@lock = Mutex.new
|
8
|
+
|
9
|
+
# Other threads can wake up a selector
|
10
|
+
@wakeup, @waker = IO.pipe
|
11
|
+
@closed = false
|
12
|
+
end
|
13
|
+
|
14
|
+
# Register interest in an IO object with the selector for the given types
|
15
|
+
# of events. Valid event types for interest are:
|
16
|
+
# * :r - is the IO readable?
|
17
|
+
# * :w - is the IO writeable?
|
18
|
+
# * :rw - is the IO either readable or writeable?
|
19
|
+
def register(io, interest)
|
20
|
+
@lock.synchronize do
|
21
|
+
raise ArgumentError, "this IO is already registered with the selector" if @selectables[io]
|
22
|
+
|
23
|
+
monitor = Monitor.new(io, interest)
|
24
|
+
@selectables[io] = monitor
|
25
|
+
|
26
|
+
monitor
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Deregister the given IO object from the selector
|
31
|
+
def deregister(io)
|
32
|
+
@lock.synchronize do
|
33
|
+
monitor = @selectables.delete io
|
34
|
+
monitor.close if monitor
|
35
|
+
monitor
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Is the given IO object registered with the selector?
|
40
|
+
def registered?(io)
|
41
|
+
@lock.synchronize { @selectables.has_key? io }
|
42
|
+
end
|
43
|
+
|
44
|
+
# Select which monitors are ready
|
45
|
+
def select(timeout = nil)
|
46
|
+
@lock.synchronize do
|
47
|
+
readers, writers = [@wakeup], []
|
48
|
+
|
49
|
+
@selectables.each do |io, monitor|
|
50
|
+
readers << io if monitor.interests == :r || monitor.interests == :rw
|
51
|
+
writers << io if monitor.interests == :w || monitor.interests == :rw
|
52
|
+
end
|
53
|
+
|
54
|
+
ready_readers, ready_writers = Kernel.select readers, writers, [], timeout
|
55
|
+
return unless ready_readers # timeout or wakeup
|
56
|
+
|
57
|
+
results = ready_readers
|
58
|
+
results.concat ready_writers if ready_writers
|
59
|
+
|
60
|
+
results.map! do |io|
|
61
|
+
if io == @wakeup
|
62
|
+
# Clear all wakeup signals we've received by reading them
|
63
|
+
# Wakeups should have level triggered behavior
|
64
|
+
begin
|
65
|
+
@wakeup.read_nonblock(1024)
|
66
|
+
|
67
|
+
# Loop until we've drained all incoming events
|
68
|
+
redo
|
69
|
+
rescue Errno::EWOULDBLOCK
|
70
|
+
end
|
71
|
+
|
72
|
+
return
|
73
|
+
else
|
74
|
+
@selectables[io]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Wake up other threads waiting on this selector
|
81
|
+
def wakeup
|
82
|
+
# Send the selector a signal in the form of writing data to a pipe
|
83
|
+
@waker << "\0"
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
|
87
|
+
# Close this selector and free its resources
|
88
|
+
def close
|
89
|
+
@lock.synchronize do
|
90
|
+
return if @closed
|
91
|
+
|
92
|
+
@wakeup.close rescue nil
|
93
|
+
@waker.close rescue nil
|
94
|
+
@closed = true
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Is this selector closed?
|
99
|
+
def closed?; @closed end
|
100
|
+
end
|
101
|
+
end
|
data/lib/nio/version.rb
ADDED