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/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