nio4r 0.2.0 → 0.2.1

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