nio4r 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES.md CHANGED
@@ -1,3 +1,9 @@
1
+ 0.2.1
2
+ -----
3
+ * Implement wakeup mechanism using raw pipes instead of ev_async, since
4
+ ev_async likes to cause segvs when used across threads (despite claims
5
+ in the documentation to the contrary)
6
+
1
7
  0.2.0
2
8
  -----
3
9
  * NIO::Monitor#readiness API to query readiness, along with #readable? and
data/ext/nio4r/nio4r.h CHANGED
@@ -14,8 +14,9 @@ struct NIO_Selector
14
14
  {
15
15
  struct ev_loop *ev_loop;
16
16
  struct ev_timer timer; /* for timeouts */
17
- struct ev_async wakeup;
17
+ struct ev_io wakeup;
18
18
 
19
+ int wakeup_reader, wakeup_writer;
19
20
  int closed, selecting;
20
21
  int ready_count;
21
22
  int ready_buffer_size;
data/ext/nio4r/selector.c CHANGED
@@ -5,6 +5,8 @@
5
5
 
6
6
  #include "nio4r.h"
7
7
  #include "rubysig.h"
8
+ #include <unistd.h>
9
+ #include <fcntl.h>
8
10
 
9
11
  static VALUE mNIO = Qnil;
10
12
  static VALUE cNIO_Channel = Qnil;
@@ -38,7 +40,7 @@ static VALUE NIO_Selector_select_each_synchronized(VALUE *args);
38
40
  static int NIO_Selector_fill_ready_buffer(VALUE *args);
39
41
  static VALUE NIO_Selector_run_evloop(void *ptr);
40
42
  static void NIO_Selector_timeout_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents);
41
- static void NIO_Selector_wakeup_callback(struct ev_loop *ev_loop, struct ev_async *async, int revents);
43
+ static void NIO_Selector_wakeup_callback(struct ev_loop *ev_loop, struct ev_io *io, int revents);
42
44
 
43
45
  /* Default number of slots in the buffer for selected monitors */
44
46
  #define INITIAL_READY_BUFFER 32
@@ -69,15 +71,33 @@ void Init_NIO_Selector()
69
71
  /* Create the libev event loop and incoming event buffer */
70
72
  static VALUE NIO_Selector_allocate(VALUE klass)
71
73
  {
72
- struct NIO_Selector *selector = (struct NIO_Selector *)xmalloc(sizeof(struct NIO_Selector));
74
+ struct NIO_Selector *selector;
75
+ int fds[2];
76
+
77
+ /* Use a pipe to implement the wakeup mechanism. I know libev provides
78
+ async watchers that implement this same behavior, but I'm getting
79
+ segvs trying to use that between threads, despite claims of thread
80
+ safety. Pipes are nice and safe to use between threads.
81
+
82
+ Note that Java NIO uses this same mechanism */
83
+ if(pipe(fds) < 0) {
84
+ rb_sys_fail("pipe");
85
+ }
86
+
87
+ if(fcntl(fds[0], F_SETFL, O_NONBLOCK) < 0) {
88
+ rb_sys_fail("fcntl");
89
+ }
73
90
 
91
+ selector = (struct NIO_Selector *)xmalloc(sizeof(struct NIO_Selector));
74
92
  selector->ev_loop = ev_loop_new(0);
75
93
  ev_init(&selector->timer, NIO_Selector_timeout_callback);
76
94
 
77
- ev_async_init(&selector->wakeup, NIO_Selector_wakeup_callback);
78
- selector->wakeup.data = (void *)selector;
95
+ selector->wakeup_reader = fds[0];
96
+ selector->wakeup_writer = fds[1];
79
97
 
80
- ev_async_start(selector->ev_loop, &selector->wakeup);
98
+ ev_io_init(&selector->wakeup, NIO_Selector_wakeup_callback, selector->wakeup_reader, EV_READ);
99
+ selector->wakeup.data = (void *)selector;
100
+ ev_io_start(selector->ev_loop, &selector->wakeup);
81
101
 
82
102
  selector->closed = selector->selecting = selector->ready_count = 0;
83
103
  selector->ready_buffer_size = INITIAL_READY_BUFFER;
@@ -104,6 +124,9 @@ static void NIO_Selector_shutdown(struct NIO_Selector *selector)
104
124
  return;
105
125
  }
106
126
 
127
+ close(selector->wakeup_reader);
128
+ close(selector->wakeup_writer);
129
+
107
130
  selector->closed = 1;
108
131
  }
109
132
 
@@ -383,7 +406,7 @@ static VALUE NIO_Selector_wakeup(VALUE self)
383
406
  struct NIO_Selector *selector;
384
407
  Data_Get_Struct(self, struct NIO_Selector, selector);
385
408
 
386
- ev_async_send(selector->ev_loop, &selector->wakeup);
409
+ write(selector->wakeup_writer, "\0", 1);
387
410
 
388
411
  return Qnil;
389
412
  }
@@ -416,10 +439,14 @@ static void NIO_Selector_timeout_callback(struct ev_loop *ev_loop, struct ev_tim
416
439
  }
417
440
 
418
441
  /* Called whenever a wakeup request is sent to a selector */
419
- static void NIO_Selector_wakeup_callback(struct ev_loop *ev_loop, struct ev_async *async, int revents)
442
+ static void NIO_Selector_wakeup_callback(struct ev_loop *ev_loop, struct ev_io *io, int revents)
420
443
  {
421
- struct NIO_Selector *selector = (struct NIO_Selector *)async->data;
444
+ char buffer[128];
445
+ struct NIO_Selector *selector = (struct NIO_Selector *)io->data;
422
446
  selector->selecting = 0;
447
+
448
+ /* Drain the wakeup pipe, giving us level-triggered behavior */
449
+ while(read(selector->wakeup_reader, buffer, 128) > 0);
423
450
  }
424
451
 
425
452
  /* This gets called from individual monitors. We must be careful here because
data/lib/nio/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module NIO
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
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.2.0
4
+ version: 0.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ date: 2012-01-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake-compiler
16
- requirement: &70186951376840 !ruby/object:Gem::Requirement
16
+ requirement: &70205623851360 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 0.7.9
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70186951376840
24
+ version_requirements: *70205623851360
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &70186951376120 !ruby/object:Gem::Requirement
27
+ requirement: &70205623850800 !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: *70186951376120
35
+ version_requirements: *70205623850800
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &70186951375540 !ruby/object:Gem::Requirement
38
+ requirement: &70205623850040 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: 2.7.0
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70186951375540
46
+ version_requirements: *70205623850040
47
47
  description: New IO for Ruby
48
48
  email:
49
49
  - tony.arcieri@gmail.com