nio4r 2.0.0.pre-java → 2.1.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +0 -1
  3. data/.rubocop.yml +31 -38
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +9 -19
  6. data/CHANGES.md +94 -42
  7. data/Gemfile +11 -3
  8. data/Guardfile +10 -0
  9. data/LICENSE.txt +1 -1
  10. data/README.md +43 -136
  11. data/Rakefile +2 -0
  12. data/examples/echo_server.rb +1 -0
  13. data/ext/libev/Changes +9 -13
  14. data/ext/libev/ev.c +100 -74
  15. data/ext/libev/ev.h +4 -9
  16. data/ext/libev/ev_epoll.c +6 -3
  17. data/ext/libev/ev_kqueue.c +8 -4
  18. data/ext/libev/ev_poll.c +6 -3
  19. data/ext/libev/ev_port.c +8 -4
  20. data/ext/libev/ev_select.c +4 -2
  21. data/ext/nio4r/bytebuffer.c +265 -257
  22. data/ext/nio4r/extconf.rb +3 -9
  23. data/ext/nio4r/monitor.c +93 -46
  24. data/ext/nio4r/nio4r.h +6 -16
  25. data/ext/nio4r/org/nio4r/ByteBuffer.java +193 -209
  26. data/ext/nio4r/org/nio4r/Monitor.java +164 -0
  27. data/ext/nio4r/org/nio4r/Nio4r.java +13 -391
  28. data/ext/nio4r/org/nio4r/Selector.java +278 -0
  29. data/ext/nio4r/selector.c +72 -64
  30. data/lib/nio.rb +3 -3
  31. data/lib/nio/bytebuffer.rb +179 -132
  32. data/lib/nio/monitor.rb +64 -4
  33. data/lib/nio/selector.rb +36 -13
  34. data/lib/nio/version.rb +1 -1
  35. data/nio4r.gemspec +25 -19
  36. data/spec/nio/acceptables_spec.rb +6 -4
  37. data/spec/nio/bytebuffer_spec.rb +323 -51
  38. data/spec/nio/monitor_spec.rb +122 -79
  39. data/spec/nio/selectables/pipe_spec.rb +5 -1
  40. data/spec/nio/selectables/ssl_socket_spec.rb +15 -12
  41. data/spec/nio/selectables/tcp_socket_spec.rb +42 -31
  42. data/spec/nio/selectables/udp_socket_spec.rb +2 -0
  43. data/spec/nio/selector_spec.rb +10 -4
  44. data/spec/spec_helper.rb +24 -3
  45. data/spec/support/selectable_examples.rb +7 -5
  46. data/tasks/extension.rake +2 -0
  47. data/tasks/rspec.rake +2 -0
  48. data/tasks/rubocop.rake +2 -0
  49. metadata +18 -15
  50. data/.rubocop_todo.yml +0 -35
@@ -0,0 +1,278 @@
1
+ package org.nio4r;
2
+
3
+ import java.util.Iterator;
4
+ import java.util.Map;
5
+ import java.util.HashMap;
6
+ import java.io.IOException;
7
+ import java.nio.channels.Channel;
8
+ import java.nio.channels.SelectableChannel;
9
+ import java.nio.channels.SelectionKey;
10
+
11
+ import org.jruby.Ruby;
12
+ import org.jruby.RubyArray;
13
+ import org.jruby.RubyClass;
14
+ import org.jruby.RubyIO;
15
+ import org.jruby.RubyNumeric;
16
+ import org.jruby.RubyObject;
17
+ import org.jruby.anno.JRubyMethod;
18
+ import org.jruby.runtime.Block;
19
+ import org.jruby.runtime.ThreadContext;
20
+ import org.jruby.runtime.builtin.IRubyObject;
21
+
22
+ import org.nio4r.Monitor;
23
+
24
+ public class Selector extends RubyObject {
25
+ private java.nio.channels.Selector selector;
26
+ private HashMap<SelectableChannel,SelectionKey> cancelledKeys;
27
+ private volatile boolean wakeupFired;
28
+
29
+ public Selector(final Ruby ruby, RubyClass rubyClass) {
30
+ super(ruby, rubyClass);
31
+ }
32
+
33
+ @JRubyMethod
34
+ public IRubyObject initialize(ThreadContext context) {
35
+ this.cancelledKeys = new HashMap<SelectableChannel,SelectionKey>();
36
+ this.wakeupFired = false;
37
+
38
+ try {
39
+ this.selector = java.nio.channels.Selector.open();
40
+ } catch(IOException ie) {
41
+ throw context.runtime.newIOError(ie.getLocalizedMessage());
42
+ }
43
+
44
+ return context.nil;
45
+ }
46
+
47
+ @JRubyMethod
48
+ public IRubyObject backend(ThreadContext context) {
49
+ return context.runtime.newSymbol("java");
50
+ }
51
+
52
+ @JRubyMethod
53
+ public IRubyObject close(ThreadContext context) {
54
+ try {
55
+ this.selector.close();
56
+ } catch(IOException ie) {
57
+ throw context.runtime.newIOError(ie.getLocalizedMessage());
58
+ }
59
+
60
+ return context.nil;
61
+ }
62
+
63
+ @JRubyMethod(name = "closed?")
64
+ public IRubyObject isClosed(ThreadContext context) {
65
+ Ruby runtime = context.getRuntime();
66
+ return this.selector.isOpen() ? runtime.getFalse() : runtime.getTrue();
67
+ }
68
+
69
+ @JRubyMethod(name = "empty?")
70
+ public IRubyObject isEmpty(ThreadContext context) {
71
+ Ruby runtime = context.getRuntime();
72
+ return this.selector.keys().isEmpty() ? runtime.getTrue() : runtime.getFalse();
73
+ }
74
+
75
+ @JRubyMethod
76
+ public IRubyObject register(ThreadContext context, IRubyObject io, IRubyObject interests) {
77
+ Ruby runtime = context.getRuntime();
78
+ Channel rawChannel = RubyIO.convertToIO(context, io).getChannel();
79
+
80
+ if(!this.selector.isOpen()) {
81
+ throw context.getRuntime().newIOError("selector is closed");
82
+ }
83
+
84
+ if(!(rawChannel instanceof SelectableChannel)) {
85
+ throw runtime.newArgumentError("not a selectable IO object");
86
+ }
87
+
88
+ SelectableChannel channel = (SelectableChannel)rawChannel;
89
+
90
+ try {
91
+ channel.configureBlocking(false);
92
+ } catch(IOException ie) {
93
+ throw runtime.newIOError(ie.getLocalizedMessage());
94
+ }
95
+
96
+ int interestOps = Nio4r.symbolToInterestOps(runtime, channel, interests);
97
+ SelectionKey key;
98
+
99
+ key = this.cancelledKeys.remove(channel);
100
+
101
+ if(key != null) {
102
+ key.interestOps(interestOps);
103
+ } else {
104
+ try {
105
+ key = channel.register(this.selector, interestOps);
106
+ } catch(java.lang.IllegalArgumentException ia) {
107
+ throw runtime.newArgumentError("mode not supported for this object: " + interests);
108
+ } catch(java.nio.channels.ClosedChannelException cce) {
109
+ throw context.runtime.newIOError(cce.getLocalizedMessage());
110
+ }
111
+ }
112
+
113
+ RubyClass monitorClass = runtime.getModule("NIO").getClass("Monitor");
114
+ Monitor monitor = (Monitor)monitorClass.newInstance(context, io, interests, this, null);
115
+ monitor.setSelectionKey(key);
116
+
117
+ return monitor;
118
+ }
119
+
120
+ @JRubyMethod
121
+ public IRubyObject deregister(ThreadContext context, IRubyObject io) {
122
+ Ruby runtime = context.getRuntime();
123
+ Channel rawChannel = RubyIO.convertToIO(context, io).getChannel();
124
+
125
+ if(!(rawChannel instanceof SelectableChannel)) {
126
+ throw runtime.newArgumentError("not a selectable IO object");
127
+ }
128
+
129
+ SelectableChannel channel = (SelectableChannel)rawChannel;
130
+ SelectionKey key = channel.keyFor(this.selector);
131
+
132
+ if(key == null)
133
+ return context.nil;
134
+
135
+ Monitor monitor = (Monitor)key.attachment();
136
+ monitor.close(context, runtime.getFalse());
137
+ cancelledKeys.put(channel, key);
138
+
139
+ return monitor;
140
+ }
141
+
142
+ @JRubyMethod(name = "registered?")
143
+ public IRubyObject isRegistered(ThreadContext context, IRubyObject io) {
144
+ Ruby runtime = context.getRuntime();
145
+ Channel rawChannel = RubyIO.convertToIO(context, io).getChannel();
146
+
147
+ if(!(rawChannel instanceof SelectableChannel)) {
148
+ throw runtime.newArgumentError("not a selectable IO object");
149
+ }
150
+
151
+ SelectableChannel channel = (SelectableChannel)rawChannel;
152
+ SelectionKey key = channel.keyFor(this.selector);
153
+
154
+ if(key == null)
155
+ return context.nil;
156
+
157
+
158
+ if(((Monitor)key.attachment()).isClosed(context) == runtime.getTrue()) {
159
+ return runtime.getFalse();
160
+ } else {
161
+ return runtime.getTrue();
162
+ }
163
+ }
164
+
165
+ @JRubyMethod
166
+ public synchronized IRubyObject select(ThreadContext context, Block block) {
167
+ return select(context, context.nil, block);
168
+ }
169
+
170
+ @JRubyMethod
171
+ public synchronized IRubyObject select(ThreadContext context, IRubyObject timeout, Block block) {
172
+ Ruby runtime = context.getRuntime();
173
+
174
+ if(!this.selector.isOpen()) {
175
+ throw context.getRuntime().newIOError("selector is closed");
176
+ }
177
+
178
+ this.wakeupFired = false;
179
+ int ready = doSelect(runtime, context, timeout);
180
+
181
+ /* Timeout */
182
+ if(ready <= 0 && !this.wakeupFired) {
183
+ return context.nil;
184
+ }
185
+
186
+ RubyArray array = null;
187
+
188
+ if(!block.isGiven()) {
189
+ array = runtime.newArray(this.selector.selectedKeys().size());
190
+ }
191
+
192
+ Iterator selectedKeys = this.selector.selectedKeys().iterator();
193
+ while(selectedKeys.hasNext()) {
194
+ SelectionKey key = (SelectionKey)selectedKeys.next();
195
+ processKey(key);
196
+ selectedKeys.remove();
197
+
198
+ if(block.isGiven()) {
199
+ block.call(context, (IRubyObject)key.attachment());
200
+ } else {
201
+ array.add(key.attachment());
202
+ }
203
+ }
204
+
205
+ if(block.isGiven()) {
206
+ return RubyNumeric.int2fix(runtime, ready);
207
+ } else {
208
+ return array;
209
+ }
210
+ }
211
+
212
+ /* Run the selector */
213
+ private int doSelect(Ruby runtime, ThreadContext context, IRubyObject timeout) {
214
+ int result;
215
+
216
+ cancelKeys();
217
+ try {
218
+ context.getThread().beforeBlockingCall();
219
+ if(timeout.isNil()) {
220
+ result = this.selector.select();
221
+ } else {
222
+ double t = RubyNumeric.num2dbl(timeout);
223
+ if(t == 0) {
224
+ result = this.selector.selectNow();
225
+ } else if(t < 0) {
226
+ throw runtime.newArgumentError("time interval must be positive");
227
+ } else {
228
+ long timeoutMilliSeconds = (long)(t * 1000);
229
+ if(timeoutMilliSeconds == 0) {
230
+ result = this.selector.selectNow();
231
+ } else {
232
+ result = this.selector.select(timeoutMilliSeconds);
233
+ }
234
+ }
235
+ }
236
+ context.getThread().afterBlockingCall();
237
+ return result;
238
+ } catch(IOException ie) {
239
+ throw runtime.newIOError(ie.getLocalizedMessage());
240
+ }
241
+ }
242
+
243
+ /* Flush our internal buffer of cancelled keys */
244
+ private void cancelKeys() {
245
+ Iterator cancelledKeys = this.cancelledKeys.entrySet().iterator();
246
+ while(cancelledKeys.hasNext()) {
247
+ Map.Entry entry = (Map.Entry)cancelledKeys.next();
248
+ SelectionKey key = (SelectionKey)entry.getValue();
249
+ key.cancel();
250
+ cancelledKeys.remove();
251
+ }
252
+ }
253
+
254
+ // Remove connect interest from connected sockets
255
+ // See: http://stackoverflow.com/questions/204186/java-nio-select-returns-without-selected-keys-why
256
+ private void processKey(SelectionKey key) {
257
+ if((key.readyOps() & SelectionKey.OP_CONNECT) != 0) {
258
+ int interestOps = key.interestOps();
259
+
260
+ interestOps &= ~SelectionKey.OP_CONNECT;
261
+ interestOps |= SelectionKey.OP_WRITE;
262
+
263
+ key.interestOps(interestOps);
264
+ }
265
+ }
266
+
267
+ @JRubyMethod
268
+ public IRubyObject wakeup(ThreadContext context) {
269
+ if(!this.selector.isOpen()) {
270
+ throw context.getRuntime().newIOError("selector is closed");
271
+ }
272
+
273
+ this.wakeupFired = true;
274
+ this.selector.wakeup();
275
+
276
+ return context.nil;
277
+ }
278
+ }
data/ext/nio4r/selector.c CHANGED
@@ -29,6 +29,7 @@ static void NIO_Selector_free(struct NIO_Selector *loop);
29
29
 
30
30
  /* Methods */
31
31
  static VALUE NIO_Selector_initialize(VALUE self);
32
+ static VALUE NIO_Selector_backend(VALUE self);
32
33
  static VALUE NIO_Selector_register(VALUE self, VALUE selectable, VALUE interest);
33
34
  static VALUE NIO_Selector_deregister(VALUE self, VALUE io);
34
35
  static VALUE NIO_Selector_is_registered(VALUE self, VALUE io);
@@ -48,8 +49,8 @@ static VALUE NIO_Selector_close_synchronized(VALUE *args);
48
49
  static VALUE NIO_Selector_closed_synchronized(VALUE *args);
49
50
 
50
51
  static int NIO_Selector_run(struct NIO_Selector *selector, VALUE timeout);
51
- static void NIO_Selector_timeout_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents);
52
- static void NIO_Selector_wakeup_callback(struct ev_loop *ev_loop, struct ev_io *io, int revents);
52
+ static void NIO_Selector_timeout_callback(ev_loop *ev_loop, struct ev_timer *timer, int revents);
53
+ static void NIO_Selector_wakeup_callback(ev_loop *ev_loop, struct ev_io *io, int revents);
53
54
 
54
55
  /* Default number of slots in the buffer for selected monitors */
55
56
  #define INITIAL_READY_BUFFER 32
@@ -65,6 +66,7 @@ void Init_NIO_Selector()
65
66
  rb_define_alloc_func(cNIO_Selector, NIO_Selector_allocate);
66
67
 
67
68
  rb_define_method(cNIO_Selector, "initialize", NIO_Selector_initialize, 0);
69
+ rb_define_method(cNIO_Selector, "backend", NIO_Selector_backend, 0);
68
70
  rb_define_method(cNIO_Selector, "register", NIO_Selector_register, 2);
69
71
  rb_define_method(cNIO_Selector, "deregister", NIO_Selector_deregister, 1);
70
72
  rb_define_method(cNIO_Selector, "registered?", NIO_Selector_is_registered, 1);
@@ -101,6 +103,7 @@ static VALUE NIO_Selector_allocate(VALUE klass)
101
103
 
102
104
  selector = (struct NIO_Selector *)xmalloc(sizeof(struct NIO_Selector));
103
105
  selector->ev_loop = ev_loop_new(0);
106
+
104
107
  ev_init(&selector->timer, NIO_Selector_timeout_callback);
105
108
 
106
109
  selector->wakeup_reader = fds[0];
@@ -108,9 +111,10 @@ static VALUE NIO_Selector_allocate(VALUE klass)
108
111
 
109
112
  ev_io_init(&selector->wakeup, NIO_Selector_wakeup_callback, selector->wakeup_reader, EV_READ);
110
113
  selector->wakeup.data = (void *)selector;
114
+
111
115
  ev_io_start(selector->ev_loop, &selector->wakeup);
112
116
 
113
- selector->closed = selector->selecting = selector->ready_count = 0;
117
+ selector->closed = selector->selecting = selector->wakeup_fired = selector->ready_count = 0;
114
118
  selector->ready_array = Qnil;
115
119
 
116
120
  return Data_Wrap_Struct(klass, NIO_Selector_mark, NIO_Selector_free, selector);
@@ -139,6 +143,7 @@ static void NIO_Selector_shutdown(struct NIO_Selector *selector)
139
143
  ev_loop_destroy(selector->ev_loop);
140
144
  selector->ev_loop = 0;
141
145
  }
146
+
142
147
  selector->closed = 1;
143
148
  }
144
149
 
@@ -165,6 +170,30 @@ static VALUE NIO_Selector_initialize(VALUE self)
165
170
  return Qnil;
166
171
  }
167
172
 
173
+ static VALUE NIO_Selector_backend(VALUE self) {
174
+ struct NIO_Selector *selector;
175
+
176
+ Data_Get_Struct(self, struct NIO_Selector, selector);
177
+ if(selector->closed) {
178
+ rb_raise(rb_eIOError, "selector is closed");
179
+ }
180
+
181
+ switch (ev_backend(selector->ev_loop)) {
182
+ case EVBACKEND_EPOLL:
183
+ return ID2SYM(rb_intern("epoll"));
184
+ case EVBACKEND_POLL:
185
+ return ID2SYM(rb_intern("poll"));
186
+ case EVBACKEND_KQUEUE:
187
+ return ID2SYM(rb_intern("kqueue"));
188
+ case EVBACKEND_SELECT:
189
+ return ID2SYM(rb_intern("select"));
190
+ case EVBACKEND_PORT:
191
+ return ID2SYM(rb_intern("port"));
192
+ }
193
+
194
+ return ID2SYM(rb_intern("unknown"));
195
+ }
196
+
168
197
  /* Synchronize around a reentrant selector lock */
169
198
  static VALUE NIO_Selector_synchronize(VALUE self, VALUE (*func)(VALUE *args), VALUE *args)
170
199
  {
@@ -309,82 +338,61 @@ static VALUE NIO_Selector_select_synchronized(VALUE *args)
309
338
  }
310
339
 
311
340
  ready = NIO_Selector_run(selector, args[1]);
312
- if(ready > 0) {
313
- if(rb_block_given_p()) {
314
- return INT2NUM(ready);
315
- } else {
316
- ready_array = selector->ready_array;
341
+
342
+ /* Timeout */
343
+ if(ready < 0) {
344
+ if(!rb_block_given_p()) {
317
345
  selector->ready_array = Qnil;
318
- return ready_array;
319
346
  }
347
+
348
+ return Qnil;
349
+ }
350
+
351
+ if(rb_block_given_p()) {
352
+ return INT2NUM(ready);
320
353
  } else {
354
+ ready_array = selector->ready_array;
321
355
  selector->ready_array = Qnil;
322
- return Qnil;
356
+ return ready_array;
323
357
  }
324
358
  }
325
359
 
326
360
  static int NIO_Selector_run(struct NIO_Selector *selector, VALUE timeout)
327
361
  {
362
+ int ev_run_flags = EVRUN_ONCE;
328
363
  int result;
364
+ double timeout_val;
365
+
329
366
  selector->selecting = 1;
367
+ selector->wakeup_fired = 0;
330
368
 
331
- #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) || defined(HAVE_RB_THREAD_ALONE)
332
- /* Implement the optional timeout (if any) as a ev_timer */
333
- if(timeout != Qnil) {
334
- /* It seems libev is not a fan of timers being zero, so fudge a little */
335
- selector->timer.repeat = NUM2DBL(timeout) + 0.0001;
336
- ev_timer_again(selector->ev_loop, &selector->timer);
337
- } else {
369
+ if(timeout == Qnil) {
370
+ /* Don't fire a wakeup timeout if we weren't passed one */
338
371
  ev_timer_stop(selector->ev_loop, &selector->timer);
339
- }
340
- #else
341
- /* Store when we started the loop so we can calculate the timeout */
342
- ev_tstamp started_at = ev_now(selector->ev_loop);
343
- #endif
344
-
345
- #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
346
- /* libev is patched to release the GIL when it makes its system call */
347
- ev_loop(selector->ev_loop, EVLOOP_ONESHOT);
348
- #elif defined(HAVE_RB_THREAD_ALONE)
349
- /* If we're the only thread we can make a blocking system call */
350
- if(rb_thread_alone()) {
351
- #else
352
- /* If we don't have rb_thread_alone() we can't block */
353
- if(0) {
354
- #endif /* defined(HAVE_RB_THREAD_BLOCKING_REGION) */
355
-
356
- #if !defined(HAVE_RB_THREAD_BLOCKING_REGION) && !defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
357
- TRAP_BEG;
358
- ev_loop(selector->ev_loop, EVLOOP_ONESHOT);
359
- TRAP_END;
360
372
  } else {
361
- /* We need to busy wait as not to stall the green thread scheduler
362
- Ruby 1.8: just say no! :( */
363
- ev_timer_init(&selector->timer, NIO_Selector_timeout_callback, BUSYWAIT_INTERVAL, BUSYWAIT_INTERVAL);
364
- ev_timer_start(selector->ev_loop, &selector->timer);
365
-
366
- /* Loop until we receive events */
367
- while(selector->selecting && !selector->ready_count) {
368
- TRAP_BEG;
369
- ev_loop(selector->ev_loop, EVLOOP_ONESHOT);
370
- TRAP_END;
371
-
372
- /* Run the next green thread */
373
- rb_thread_schedule();
374
-
375
- /* Break if the timeout has elapsed */
376
- if(timeout != Qnil && ev_now(selector->ev_loop) - started_at >= NUM2DBL(timeout))
377
- break;
373
+ timeout_val = NUM2DBL(timeout);
374
+ if(timeout_val == 0) {
375
+ /* If we've been given an explicit timeout of 0, perform a non-blocking
376
+ select operation */
377
+ ev_run_flags = EVRUN_NOWAIT;
378
+ } else {
379
+ selector->timer.repeat = timeout_val;
380
+ ev_timer_again(selector->ev_loop, &selector->timer);
378
381
  }
379
-
380
- ev_timer_stop(selector->ev_loop, &selector->timer);
381
382
  }
382
- #endif /* defined(HAVE_RB_THREAD_BLOCKING_REGION) */
383
+
384
+ /* libev is patched to release the GIL when it makes its system call */
385
+ ev_run(selector->ev_loop, ev_run_flags);
383
386
 
384
387
  result = selector->ready_count;
385
388
  selector->selecting = selector->ready_count = 0;
386
389
 
387
- return result;
390
+ if(result > 0 || selector->wakeup_fired) {
391
+ selector->wakeup_fired = 0;
392
+ return result;
393
+ } else {
394
+ return -1;
395
+ }
388
396
  }
389
397
 
390
398
  /* Wake the selector up from another thread */
@@ -397,7 +405,9 @@ static VALUE NIO_Selector_wakeup(VALUE self)
397
405
  rb_raise(rb_eIOError, "selector is closed");
398
406
  }
399
407
 
408
+ selector->wakeup_fired = 1;
400
409
  write(selector->wakeup_writer, "\0", 1);
410
+
401
411
  return Qnil;
402
412
  }
403
413
 
@@ -445,14 +455,12 @@ static VALUE NIO_Selector_is_empty(VALUE self)
445
455
 
446
456
 
447
457
  /* Called whenever a timeout fires on the event loop */
448
- static void NIO_Selector_timeout_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents)
458
+ static void NIO_Selector_timeout_callback(ev_loop *ev_loop, struct ev_timer *timer, int revents)
449
459
  {
450
- /* We don't actually need to do anything here, the mere firing of the
451
- timer is sufficient to interrupt the selector. However, libev still wants a callback */
452
460
  }
453
461
 
454
462
  /* Called whenever a wakeup request is sent to a selector */
455
- static void NIO_Selector_wakeup_callback(struct ev_loop *ev_loop, struct ev_io *io, int revents)
463
+ static void NIO_Selector_wakeup_callback(ev_loop *ev_loop, struct ev_io *io, int revents)
456
464
  {
457
465
  char buffer[128];
458
466
  struct NIO_Selector *selector = (struct NIO_Selector *)io->data;
@@ -463,7 +471,7 @@ static void NIO_Selector_wakeup_callback(struct ev_loop *ev_loop, struct ev_io *
463
471
  }
464
472
 
465
473
  /* libev callback fired whenever a monitor gets an event */
466
- void NIO_Selector_monitor_callback(struct ev_loop *ev_loop, struct ev_io *io, int revents)
474
+ void NIO_Selector_monitor_callback(ev_loop *ev_loop, struct ev_io *io, int revents)
467
475
  {
468
476
  struct NIO_Monitor *monitor_data = (struct NIO_Monitor *)io->data;
469
477
  struct NIO_Selector *selector = monitor_data->selector;