sleepy_penguin 3.1.0 → 3.1.0.26.g7181

Sign up to get free protection for your applications and to get access to all the features.
@@ -27,15 +27,15 @@ static VALUE s_new(int argc, VALUE *argv, VALUE klass)
27
27
 
28
28
  rb_scan_args(argc, argv, "11", &_initval, &_flags);
29
29
  initval = NUM2UINT(_initval);
30
- flags = rb_sp_get_flags(klass, _flags);
30
+ flags = rb_sp_get_flags(klass, _flags, RB_SP_CLOEXEC(EFD_CLOEXEC));
31
31
 
32
32
  fd = eventfd(initval, flags);
33
- if (fd == -1) {
33
+ if (fd < 0) {
34
34
  if (errno == EMFILE || errno == ENFILE || errno == ENOMEM) {
35
35
  rb_gc();
36
36
  fd = eventfd(initval, flags);
37
37
  }
38
- if (fd == -1)
38
+ if (fd < 0)
39
39
  rb_sys_fail("eventfd");
40
40
  }
41
41
 
@@ -88,10 +88,10 @@ static VALUE incr(int argc, VALUE *argv, VALUE self)
88
88
  x.val = (uint64_t)NUM2ULL(value);
89
89
  retry:
90
90
  w = (ssize_t)rb_sp_fd_region(efd_write, &x, x.fd);
91
- if (w == -1) {
91
+ if (w < 0) {
92
92
  if (errno == EAGAIN && RTEST(nonblock))
93
93
  return Qfalse;
94
- if (rb_io_wait_writable(x.fd))
94
+ if (rb_sp_wait(rb_io_wait_writable, self, &x.fd))
95
95
  goto retry;
96
96
  rb_sys_fail("write(eventfd)");
97
97
  }
@@ -124,10 +124,10 @@ static VALUE getvalue(int argc, VALUE *argv, VALUE self)
124
124
  RTEST(nonblock) ? rb_sp_set_nonblock(x.fd) : blocking_io_prepare(x.fd);
125
125
  retry:
126
126
  w = (ssize_t)rb_sp_fd_region(efd_read, &x, x.fd);
127
- if (w == -1) {
127
+ if (w < 0) {
128
128
  if (errno == EAGAIN && RTEST(nonblock))
129
129
  return Qnil;
130
- if (rb_io_wait_readable(x.fd = rb_sp_fileno(self)))
130
+ if (rb_sp_wait(rb_io_wait_readable, self, &x.fd))
131
131
  goto retry;
132
132
  rb_sys_fail("read(eventfd)");
133
133
  }
@@ -1,6 +1,5 @@
1
1
  require 'mkmf'
2
2
  have_header('sys/epoll.h') or abort 'sys/epoll.h not found'
3
- have_header("pthread.h") or abort 'pthread.h not found'
4
3
  have_header('sys/eventfd.h')
5
4
 
6
5
  # it's impossible to use signalfd reliably with Ruby since Ruby currently
@@ -11,9 +10,10 @@ have_header('sys/timerfd.h')
11
10
  have_header('sys/inotify.h')
12
11
  have_header('ruby/io.h') and have_struct_member('rb_io_t', 'fd', 'ruby/io.h')
13
12
  have_func('epoll_create1', %w(sys/epoll.h))
13
+ have_func('rb_thread_call_without_gvl')
14
14
  have_func('rb_thread_blocking_region')
15
15
  have_func('rb_thread_io_blocking_region')
16
16
  have_func('rb_thread_fd_close')
17
17
  have_func('rb_update_max_fd')
18
- have_library('pthread')
18
+ have_func('rb_fd_fix_cloexec')
19
19
  create_makefile('sleepy_penguin_ext')
@@ -1,3 +1,9 @@
1
+ #define _GNU_SOURCE
2
+ #include <unistd.h>
3
+ #include <sys/types.h>
4
+ #define L1_CACHE_LINE_MAX 128 /* largest I've seen (Pentium 4) */
5
+ size_t rb_sp_l1_cache_line_size;
6
+
1
7
  void sleepy_penguin_init_epoll(void);
2
8
 
3
9
  #ifdef HAVE_SYS_TIMERFD_H
@@ -24,8 +30,21 @@ void sleepy_penguin_init_signalfd(void);
24
30
  # define sleepy_penguin_init_signalfd() for(;0;)
25
31
  #endif
26
32
 
33
+ static size_t l1_cache_line_size_detect(void)
34
+ {
35
+ #ifdef _SC_LEVEL1_DCACHE_LINESIZE
36
+ long tmp = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
37
+
38
+ if (tmp > 0 && tmp <= L1_CACHE_LINE_MAX)
39
+ return (size_t)tmp;
40
+ #endif /* _SC_LEVEL1_DCACHE_LINESIZE */
41
+ return L1_CACHE_LINE_MAX;
42
+ }
43
+
27
44
  void Init_sleepy_penguin_ext(void)
28
45
  {
46
+ rb_sp_l1_cache_line_size = l1_cache_line_size_detect();
47
+
29
48
  sleepy_penguin_init_epoll();
30
49
  sleepy_penguin_init_timerfd();
31
50
  sleepy_penguin_init_eventfd();
@@ -4,7 +4,12 @@
4
4
  #include <sys/ioctl.h>
5
5
  #include "missing_inotify.h"
6
6
 
7
- static ID id_inotify_buf, id_inotify_tmp, id_mask;
7
+ struct inbuf {
8
+ size_t capa;
9
+ void *ptr;
10
+ };
11
+
12
+ static ID id_inotify_tmp, id_mask;
8
13
  static VALUE cEvent, checks;
9
14
 
10
15
  /*
@@ -22,21 +27,20 @@ static VALUE s_new(int argc, VALUE *argv, VALUE klass)
22
27
  int fd;
23
28
 
24
29
  rb_scan_args(argc, argv, "01", &_flags);
25
- flags = rb_sp_get_flags(klass, _flags);
30
+ flags = rb_sp_get_flags(klass, _flags, RB_SP_CLOEXEC(IN_CLOEXEC));
26
31
 
27
32
  fd = inotify_init1(flags);
28
- if (fd == -1) {
33
+ if (fd < 0) {
29
34
  if (errno == EMFILE || errno == ENFILE || errno == ENOMEM) {
30
35
  rb_gc();
31
36
  fd = inotify_init1(flags);
32
37
  }
33
- if (fd == -1)
38
+ if (fd < 0)
34
39
  rb_sys_fail("inotify_init1");
35
40
  }
36
41
 
37
42
  rv = INT2FIX(fd);
38
43
  rv = rb_call_super(1, &rv);
39
- rb_ivar_set(rv, id_inotify_buf, rb_str_new(0, 128));
40
44
  rb_ivar_set(rv, id_inotify_tmp, rb_ary_new());
41
45
 
42
46
  return rv;
@@ -89,14 +93,9 @@ static VALUE add_watch(VALUE self, VALUE path, VALUE vmask)
89
93
  uint32_t mask = rb_sp_get_uflags(self, vmask);
90
94
  int rc = inotify_add_watch(fd, pathname, mask);
91
95
 
92
- if (rc == -1) {
93
- if (errno == ENOMEM) {
94
- rb_gc();
95
- rc = inotify_add_watch(fd, pathname, mask);
96
- }
97
- if (rc == -1)
98
- rb_sys_fail("inotify_add_watch");
99
- }
96
+ if (rc < 0)
97
+ rb_sys_fail("inotify_add_watch");
98
+
100
99
  return UINT2NUM((uint32_t)rc);
101
100
  }
102
101
 
@@ -113,7 +112,7 @@ static VALUE rm_watch(VALUE self, VALUE vwd)
113
112
  int fd = rb_sp_fileno(self);
114
113
  int rc = inotify_rm_watch(fd, wd);
115
114
 
116
- if (rc == -1)
115
+ if (rc < 0)
117
116
  rb_sys_fail("inotify_rm_watch");
118
117
  return INT2NUM(rc);
119
118
  }
@@ -138,15 +137,50 @@ static VALUE event_new(struct inotify_event *e)
138
137
 
139
138
  struct inread_args {
140
139
  int fd;
141
- struct inotify_event *ptr;
142
- long len;
140
+ struct inbuf *inbuf;
143
141
  };
144
142
 
145
143
  static VALUE inread(void *ptr)
146
144
  {
147
145
  struct inread_args *args = ptr;
148
146
 
149
- return (VALUE)read(args->fd, args->ptr, args->len);
147
+ return (VALUE)read(args->fd, args->inbuf->ptr, args->inbuf->capa);
148
+ }
149
+
150
+ static void inbuf_grow(struct inbuf *inbuf, size_t size)
151
+ {
152
+ int err;
153
+
154
+ if (inbuf->capa >= size)
155
+ return;
156
+ free(inbuf->ptr);
157
+ err = posix_memalign(&inbuf->ptr, rb_sp_l1_cache_line_size, size);
158
+ if (err) {
159
+ errno = err;
160
+ rb_memerror();
161
+ }
162
+ inbuf->capa = size;
163
+ }
164
+
165
+ static void resize_internal_buffer(struct inread_args *args)
166
+ {
167
+ int newlen;
168
+
169
+ if (args->inbuf->capa > 0x10000)
170
+ rb_raise(rb_eRuntimeError, "path too long");
171
+
172
+ if (ioctl(args->fd, FIONREAD, &newlen) != 0)
173
+ rb_sys_fail("ioctl(inotify,FIONREAD)");
174
+
175
+ if (newlen > 0)
176
+ inbuf_grow(args->inbuf, (size_t)newlen);
177
+
178
+ if (newlen == 0) /* race: some other thread grabbed the data */
179
+ return;
180
+
181
+ rb_raise(rb_eRuntimeError,
182
+ "ioctl(inotify,FIONREAD) returned negative length: %d",
183
+ newlen);
150
184
  }
151
185
 
152
186
  /*
@@ -158,8 +192,9 @@ static VALUE inread(void *ptr)
158
192
  */
159
193
  static VALUE take(int argc, VALUE *argv, VALUE self)
160
194
  {
195
+ static __thread struct inbuf inbuf;
196
+
161
197
  struct inread_args args;
162
- VALUE buf;
163
198
  VALUE tmp = rb_ivar_get(self, id_inotify_tmp);
164
199
  struct inotify_event *e, *end;
165
200
  ssize_t r;
@@ -171,42 +206,31 @@ static VALUE take(int argc, VALUE *argv, VALUE self)
171
206
 
172
207
  rb_scan_args(argc, argv, "01", &nonblock);
173
208
 
209
+ inbuf_grow(&inbuf, 128);
174
210
  args.fd = rb_sp_fileno(self);
175
- buf = rb_ivar_get(self, id_inotify_buf);
176
- args.len = RSTRING_LEN(buf);
177
- args.ptr = (struct inotify_event *)RSTRING_PTR(buf);
211
+ args.inbuf = &inbuf;
178
212
 
179
213
  if (RTEST(nonblock))
180
214
  rb_sp_set_nonblock(args.fd);
181
215
  else
182
216
  blocking_io_prepare(args.fd);
183
217
  do {
184
- r = rb_sp_fd_region(inread, &args, args.fd);
218
+ r = (ssize_t)rb_sp_fd_region(inread, &args, args.fd);
185
219
  if (r == 0 /* Linux < 2.6.21 */
186
220
  ||
187
221
  (r < 0 && errno == EINVAL) /* Linux >= 2.6.21 */
188
222
  ) {
189
- /* resize internal buffer */
190
- int newlen;
191
- if (args.len > 0x10000)
192
- rb_raise(rb_eRuntimeError, "path too long");
193
- if (ioctl(args.fd, FIONREAD, &newlen) != 0)
194
- rb_sys_fail("ioctl(inotify,FIONREAD)");
195
- rb_str_resize(buf, newlen);
196
- args.ptr = (struct inotify_event *)RSTRING_PTR(buf);
197
- args.len = newlen;
223
+ resize_internal_buffer(&args);
198
224
  } else if (r < 0) {
199
- if (errno == EAGAIN && RTEST(nonblock)) {
225
+ if (errno == EAGAIN && RTEST(nonblock))
200
226
  return Qnil;
201
- } else {
202
- args.fd = rb_sp_fileno(self);
203
- if (!rb_io_wait_readable(args.fd))
204
- rb_sys_fail("read(inotify)");
205
- }
227
+ if (!rb_sp_wait(rb_io_wait_readable, self, &args.fd))
228
+ rb_sys_fail("read(inotify)");
206
229
  } else {
207
230
  /* buffer in userspace to minimize read() calls */
208
- end = (struct inotify_event *)((char *)args.ptr + r);
209
- for (e = args.ptr; e < end; ) {
231
+ end = (struct inotify_event *)
232
+ ((char *)args.inbuf->ptr + r);
233
+ for (e = args.inbuf->ptr; e < end; ) {
210
234
  VALUE event = event_new(e);
211
235
  if (NIL_P(rv))
212
236
  rv = event;
@@ -247,22 +271,6 @@ static VALUE events(VALUE self)
247
271
  return rv;
248
272
  }
249
273
 
250
- /*
251
- * call-seq:
252
- * inotify.dup -> another Inotify object
253
- *
254
- * Duplicates an Inotify object, allowing it to be used in a blocking
255
- * fashion in another thread. Ensures duplicated Inotify objects do
256
- * not share read buffers, but do share the userspace Array buffer.
257
- */
258
- static VALUE init_copy(VALUE dest, VALUE orig)
259
- {
260
- rb_call_super(1, &orig); /* copy all other ivars as-is */
261
- rb_ivar_set(dest, id_inotify_buf, rb_str_new(0, 128));
262
-
263
- return dest;
264
- }
265
-
266
274
  /*
267
275
  * call-seq:
268
276
  * ino.each { |event| ... } -> ino
@@ -308,7 +316,6 @@ void sleepy_penguin_init_inotify(void)
308
316
  cInotify = rb_define_class_under(mSleepyPenguin, "Inotify", rb_cIO);
309
317
  rb_define_method(cInotify, "add_watch", add_watch, 2);
310
318
  rb_define_method(cInotify, "rm_watch", rm_watch, 1);
311
- rb_define_method(cInotify, "initialize_copy", init_copy, 1);
312
319
  rb_define_method(cInotify, "take", take, -1);
313
320
  rb_define_method(cInotify, "each", each, 0);
314
321
 
@@ -338,7 +345,6 @@ void sleepy_penguin_init_inotify(void)
338
345
  cEvent = rb_define_class_under(cInotify, "Event", cEvent);
339
346
  rb_define_method(cEvent, "events", events, 0);
340
347
  rb_define_singleton_method(cInotify, "new", s_new, -1);
341
- id_inotify_buf = rb_intern("@inotify_buf");
342
348
  id_inotify_tmp = rb_intern("@inotify_tmp");
343
349
  id_mask = rb_intern("mask");
344
350
  checks = rb_ary_new();
@@ -15,7 +15,7 @@ static int my_epoll_create1(int flags)
15
15
  if (fd < 0 || flags == 0)
16
16
  return fd;
17
17
 
18
- if ((flags & EPOLL_CLOEXEC) && (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1))
18
+ if ((flags & EPOLL_CLOEXEC) && (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0))
19
19
  goto err;
20
20
  return fd;
21
21
  err:
@@ -36,13 +36,13 @@ static int my_inotify_init1(int flags)
36
36
 
37
37
  if (fd < 0)
38
38
  return fd;
39
- if ((flags & IN_CLOEXEC) && (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1))
39
+ if ((flags & IN_CLOEXEC) && (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0))
40
40
  goto fcntl_err;
41
41
  if (flags & IN_NONBLOCK) {
42
42
  tmp = fcntl(fd, F_GETFL);
43
43
  if (tmp == -1)
44
44
  goto fcntl_err;
45
- if ((fcntl(fd, F_SETFL, tmp | O_NONBLOCK) != 0))
45
+ if ((fcntl(fd, F_SETFL, tmp | O_NONBLOCK) < 0))
46
46
  goto fcntl_err;
47
47
  }
48
48
 
@@ -92,11 +92,12 @@ static VALUE update_bang(int argc, VALUE *argv, VALUE self)
92
92
  int rc;
93
93
 
94
94
  rb_scan_args(argc, argv, "02", &vmask, &vflags);
95
- flags = NIL_P(vflags) ? cur_flags(fd) : rb_sp_get_flags(self, vflags);
95
+ flags = NIL_P(vflags) ? cur_flags(fd)
96
+ : rb_sp_get_flags(self, vflags, 0);
96
97
  value2sigset(&mask, vmask);
97
98
 
98
99
  rc = signalfd(fd, &mask, flags);
99
- if (rc == -1)
100
+ if (rc < 0)
100
101
  rb_sys_fail("signalfd");
101
102
  return self;
102
103
  }
@@ -128,16 +129,16 @@ static VALUE s_new(int argc, VALUE *argv, VALUE klass)
128
129
  int fd;
129
130
 
130
131
  rb_scan_args(argc, argv, "02", &vmask, &vflags);
131
- flags = rb_sp_get_flags(klass, vflags);
132
+ flags = rb_sp_get_flags(klass, vflags, RB_SP_CLOEXEC(SFD_CLOEXEC));
132
133
  value2sigset(&mask, vmask);
133
134
 
134
135
  fd = signalfd(-1, &mask, flags);
135
- if (fd == -1) {
136
+ if (fd < 0) {
136
137
  if (errno == EMFILE || errno == ENFILE || errno == ENOMEM) {
137
138
  rb_gc();
138
139
  fd = signalfd(-1, &mask, flags);
139
140
  }
140
- if (fd == -1)
141
+ if (fd < 0)
141
142
  rb_sys_fail("signalfd");
142
143
  }
143
144
 
@@ -195,10 +196,10 @@ static VALUE sfd_take(int argc, VALUE *argv, VALUE self)
195
196
  retry:
196
197
  ssi->ssi_fd = fd;
197
198
  r = (ssize_t)rb_sp_fd_region(sfd_read, ssi, fd);
198
- if (r == -1) {
199
+ if (r < 0) {
199
200
  if (errno == EAGAIN && RTEST(nonblock))
200
201
  return Qnil;
201
- if (rb_io_wait_readable(fd = rb_sp_fileno(self)))
202
+ if (rb_sp_wait(rb_io_wait_readable, self, &fd))
202
203
  goto retry;
203
204
  rb_sys_fail("read(signalfd)");
204
205
  }
@@ -12,32 +12,69 @@
12
12
  #include <assert.h>
13
13
  #include <unistd.h>
14
14
 
15
+ extern size_t rb_sp_l1_cache_line_size;
15
16
  unsigned rb_sp_get_uflags(VALUE klass, VALUE flags);
16
- int rb_sp_get_flags(VALUE klass, VALUE flags);
17
+ int rb_sp_get_flags(VALUE klass, VALUE flags, int default_flags);
17
18
  int rb_sp_io_closed(VALUE io);
18
19
  int rb_sp_fileno(VALUE io);
19
20
  void rb_sp_set_nonblock(int fd);
20
21
 
21
- #ifdef HAVE_RB_THREAD_BLOCKING_REGION
22
- static inline VALUE rb_sp_io_region(rb_blocking_function_t *func, void *data)
23
- {
24
- return rb_thread_blocking_region(func, data, RUBY_UBF_IO, 0);
25
- }
22
+ #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || \
23
+ defined(HAVE_RB_THREAD_IO_BLOCKING_REGION) || \
24
+ defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
25
+ # define RB_SP_GREEN_THREAD 0
26
26
  # define blocking_io_prepare(fd) ((void)(fd))
27
27
  #else
28
- typedef VALUE rb_blocking_function_t(void *);
29
- VALUE rb_sp_io_region(rb_blocking_function_t *func, void *data);
28
+ # define RB_SP_GREEN_THREAD 1
30
29
  # define blocking_io_prepare(fd) rb_sp_set_nonblock((fd))
31
30
  #endif
32
31
 
33
32
  #ifdef HAVE_RB_THREAD_IO_BLOCKING_REGION
33
+ /* Ruby 1.9.3 and 2.0.0 */
34
34
  VALUE rb_thread_io_blocking_region(rb_blocking_function_t *, void *, int);
35
35
  # define rb_sp_fd_region(fn,data,fd) \
36
- rb_thread_io_blocking_region((fn),(data),(fd))
36
+ rb_thread_io_blocking_region((fn),(data),(fd))
37
+ #elif defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) && \
38
+ defined(HAVE_RUBY_THREAD_H) && HAVE_RUBY_THREAD_H
39
+ /* in case Ruby 2.0+ ever drops rb_thread_io_blocking_region: */
40
+ # include <ruby/thread.h>
41
+ # define COMPAT_FN (void *(*)(void *))
42
+ # define rb_sp_fd_region(fn,data,fd) \
43
+ rb_thread_call_without_gvl(COMPAT_FN(fn),(data),RUBY_UBF_IO,NULL)
44
+ #elif defined(HAVE_RB_THREAD_BLOCKING_REGION)
45
+ /* Ruby 1.9.2 */
46
+ # define rb_sp_fd_region(fn,data,fd) \
47
+ rb_thread_blocking_region((fn),(data),RUBY_UBF_IO,NULL)
37
48
  #else
38
- # define rb_sp_fd_region(fn,data,fd) rb_sp_io_region(fn,data)
49
+ /*
50
+ * Ruby 1.8 does not have a GVL, we'll just enable signal interrupts
51
+ * here in case we make interruptible syscalls.
52
+ *
53
+ * Note: epoll_wait with timeout=0 was interruptible until Linux 2.6.39
54
+ */
55
+ # include <rubysig.h>
56
+ static inline VALUE fake_blocking_region(VALUE (*fn)(void *), void *data)
57
+ {
58
+ VALUE rv;
59
+
60
+ TRAP_BEG;
61
+ rv = fn(data);
62
+ TRAP_END;
63
+
64
+ return rv;
65
+ }
66
+ # define rb_sp_fd_region(fn,data,fd) fake_blocking_region((fn),(data))
39
67
  #endif
40
68
 
41
69
  #define NODOC_CONST(klass,name,value) \
42
70
  rb_define_const((klass),(name),(value))
71
+
72
+ #ifdef HAVE_RB_FD_FIX_CLOEXEC
73
+ # define RB_SP_CLOEXEC(flag) (flag)
74
+ #else
75
+ # define RB_SP_CLOEXEC(flag) (0)
76
+ #endif
77
+
78
+ typedef int rb_sp_waitfn(int fd);
79
+ int rb_sp_wait(rb_sp_waitfn waiter, VALUE obj, int *fd);
43
80
  #endif /* SLEEPY_PENGUIN_H */