nio4r 0.1.0-java

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module NIO
2
+ VERSION = "0.1.0"
3
+ end