sleepy_penguin 3.4.1 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.document +1 -0
  3. data/.olddoc.yml +3 -4
  4. data/GIT-VERSION-GEN +1 -1
  5. data/LICENSE +3 -3
  6. data/README +7 -4
  7. data/TODO +1 -0
  8. data/ext/sleepy_penguin/cfr.c +62 -0
  9. data/ext/sleepy_penguin/epoll.c +34 -24
  10. data/ext/sleepy_penguin/eventfd.c +6 -5
  11. data/ext/sleepy_penguin/extconf.rb +6 -0
  12. data/ext/sleepy_penguin/init.c +83 -12
  13. data/ext/sleepy_penguin/inotify.c +48 -36
  14. data/ext/sleepy_penguin/kqueue.c +22 -21
  15. data/ext/sleepy_penguin/sendfile.c +120 -0
  16. data/ext/sleepy_penguin/sleepy_penguin.h +15 -28
  17. data/ext/sleepy_penguin/sp_copy.h +33 -0
  18. data/ext/sleepy_penguin/splice.c +174 -0
  19. data/ext/sleepy_penguin/timerfd.c +1 -5
  20. data/ext/sleepy_penguin/util.c +12 -0
  21. data/lib/sleepy_penguin.rb +28 -0
  22. data/lib/sleepy_penguin/cfr.rb +29 -0
  23. data/lib/sleepy_penguin/epoll.rb +13 -10
  24. data/lib/sleepy_penguin/kqueue.rb +6 -6
  25. data/lib/sleepy_penguin/sp.rb +1 -1
  26. data/lib/sleepy_penguin/splice.rb +125 -0
  27. data/pkg.mk +5 -12
  28. data/sleepy_penguin.gemspec +13 -15
  29. data/test/helper.rb +2 -7
  30. data/test/test_cfr.rb +35 -0
  31. data/test/test_constants.rb +2 -4
  32. data/test/test_epoll.rb +35 -6
  33. data/test/test_epoll_gc.rb +2 -5
  34. data/test/test_epoll_io.rb +3 -6
  35. data/test/test_epoll_optimizations.rb +2 -2
  36. data/test/test_eventfd.rb +2 -5
  37. data/test/test_inotify.rb +2 -4
  38. data/test/test_kqueue.rb +35 -7
  39. data/test/test_kqueue_io.rb +2 -5
  40. data/test/test_pipesize.rb +22 -0
  41. data/test/test_sendfile.rb +26 -0
  42. data/test/test_splice.rb +250 -0
  43. data/test/test_splice_eintr.rb +31 -0
  44. data/test/test_timerfd.rb +2 -5
  45. metadata +27 -34
  46. data/lib/sleepy_penguin/epoll/io.rb +0 -28
  47. data/lib/sleepy_penguin/kqueue/io.rb +0 -30
@@ -26,10 +26,8 @@ static VALUE s_new(int argc, VALUE *argv, VALUE klass)
26
26
 
27
27
  fd = inotify_init1(flags);
28
28
  if (fd < 0) {
29
- if (errno == EMFILE || errno == ENFILE || errno == ENOMEM) {
30
- rb_gc();
29
+ if (rb_sp_gc_for_fd(errno))
31
30
  fd = inotify_init1(flags);
32
- }
33
31
  if (fd < 0)
34
32
  rb_sys_fail("inotify_init1");
35
33
  }
@@ -136,8 +134,11 @@ static VALUE event_new(struct inotify_event *e)
136
134
  }
137
135
 
138
136
  struct inread_args {
137
+ VALUE self;
139
138
  int fd;
139
+ int nonblock_p;
140
140
  size_t size;
141
+ VALUE tmp;
141
142
  void *buf;
142
143
  };
143
144
 
@@ -160,6 +161,7 @@ static void resize_internal_buffer(struct inread_args *args)
160
161
 
161
162
  if (newlen > 0) {
162
163
  args->size = (size_t)newlen;
164
+ rb_sp_puttlsbuf((VALUE)args->buf);
163
165
  args->buf = rb_sp_gettlsbuf(&args->size);
164
166
  }
165
167
 
@@ -171,56 +173,35 @@ static void resize_internal_buffer(struct inread_args *args)
171
173
  newlen);
172
174
  }
173
175
 
174
- /*
175
- * call-seq:
176
- * ino.take([nonblock]) -> Inotify::Event or nil
177
- *
178
- * Returns the next Inotify::Event processed. May return +nil+ if +nonblock+
179
- * is +true+.
180
- */
181
- static VALUE take(int argc, VALUE *argv, VALUE self)
176
+ static VALUE do_take(VALUE p)
182
177
  {
183
- struct inread_args args;
184
- VALUE tmp = rb_ivar_get(self, id_inotify_tmp);
185
- struct inotify_event *e, *end;
186
- ssize_t r;
178
+ struct inread_args *args = (struct inread_args *)p;
187
179
  VALUE rv = Qnil;
188
- VALUE nonblock;
189
-
190
- if (RARRAY_LEN(tmp) > 0)
191
- return rb_ary_shift(tmp);
192
-
193
- rb_scan_args(argc, argv, "01", &nonblock);
194
-
195
- args.fd = rb_sp_fileno(self);
196
- args.size = 128;
197
- args.buf = rb_sp_gettlsbuf(&args.size);
180
+ struct inotify_event *e, *end;
198
181
 
199
- if (RTEST(nonblock))
200
- rb_sp_set_nonblock(args.fd);
201
- else
202
- blocking_io_prepare(args.fd);
182
+ args->buf = rb_sp_gettlsbuf(&args->size);
203
183
  do {
204
- r = (ssize_t)rb_sp_fd_region(inread, &args, args.fd);
184
+ ssize_t r = (ssize_t)rb_sp_fd_region(inread, args, args->fd);
205
185
  if (r == 0 /* Linux < 2.6.21 */
206
186
  ||
207
187
  (r < 0 && errno == EINVAL) /* Linux >= 2.6.21 */
208
188
  ) {
209
- resize_internal_buffer(&args);
189
+ resize_internal_buffer(args);
210
190
  } else if (r < 0) {
211
- if (errno == EAGAIN && RTEST(nonblock))
191
+ if (errno == EAGAIN && args->nonblock_p)
212
192
  return Qnil;
213
- if (!rb_sp_wait(rb_io_wait_readable, self, &args.fd))
193
+ if (!rb_sp_wait(rb_io_wait_readable, args->self,
194
+ &args->fd))
214
195
  rb_sys_fail("read(inotify)");
215
196
  } else {
216
197
  /* buffer in userspace to minimize read() calls */
217
- end = (struct inotify_event *)((char *)args.buf + r);
218
- for (e = args.buf; e < end; ) {
198
+ end = (struct inotify_event *)((char *)args->buf + r);
199
+ for (e = args->buf; e < end; ) {
219
200
  VALUE event = event_new(e);
220
201
  if (NIL_P(rv))
221
202
  rv = event;
222
203
  else
223
- rb_ary_push(tmp, event);
204
+ rb_ary_push(args->tmp, event);
224
205
  e = (struct inotify_event *)
225
206
  ((char *)e + event_len(e));
226
207
  }
@@ -230,6 +211,37 @@ static VALUE take(int argc, VALUE *argv, VALUE self)
230
211
  return rv;
231
212
  }
232
213
 
214
+ /*
215
+ * call-seq:
216
+ * ino.take([nonblock]) -> Inotify::Event or nil
217
+ *
218
+ * Returns the next Inotify::Event processed. May return +nil+ if +nonblock+
219
+ * is +true+.
220
+ */
221
+ static VALUE take(int argc, VALUE *argv, VALUE self)
222
+ {
223
+ struct inread_args args;
224
+ VALUE nonblock;
225
+
226
+ args.tmp = rb_ivar_get(self, id_inotify_tmp);
227
+ if (RARRAY_LEN(args.tmp) > 0)
228
+ return rb_ary_shift(args.tmp);
229
+
230
+ rb_scan_args(argc, argv, "01", &nonblock);
231
+
232
+ args.self = self;
233
+ args.fd = rb_sp_fileno(self);
234
+ args.size = 128;
235
+ args.nonblock_p = RTEST(nonblock);
236
+
237
+ if (args.nonblock_p)
238
+ rb_sp_set_nonblock(args.fd);
239
+
240
+ args.buf = 0;
241
+ return rb_ensure(do_take, (VALUE)&args,
242
+ rb_sp_puttlsbuf, (VALUE)args.buf);
243
+ }
244
+
233
245
  /*
234
246
  * call-seq:
235
247
  * inotify_event.events => [ :MOVED_TO, ... ]
@@ -43,6 +43,7 @@ static VALUE mEv, mEvFilt, mNote, mVQ;
43
43
 
44
44
  struct kq_per_thread {
45
45
  VALUE io;
46
+ VALUE changelist;
46
47
  int fd;
47
48
  int nchanges;
48
49
  int nevents;
@@ -72,7 +73,7 @@ static int kq_fd_check(struct kq_per_thread *kpt)
72
73
  return 1;
73
74
  }
74
75
 
75
- static struct kq_per_thread *kpt_get(VALUE self, int nchanges, int nevents)
76
+ static struct kq_per_thread *kpt_get(int nchanges, int nevents)
76
77
  {
77
78
  struct kq_per_thread *kpt;
78
79
  size_t size;
@@ -89,8 +90,6 @@ static struct kq_per_thread *kpt_get(VALUE self, int nchanges, int nevents)
89
90
  kpt->capa = max;
90
91
  kpt->nchanges = nchanges;
91
92
  kpt->nevents = nevents;
92
- kpt->io = self;
93
- kpt->fd = rb_sp_fileno(kpt->io);
94
93
 
95
94
  return kpt;
96
95
  }
@@ -102,11 +101,10 @@ static struct kq_per_thread *kpt_get(VALUE self, int nchanges, int nevents)
102
101
  * Creates a new Kqueue::IO object. This is a wrapper around the kqueue(2)
103
102
  * system call which creates a Ruby IO object around the kqueue descriptor.
104
103
  *
105
- * kqueue descriptors are automatically invalidated across fork, so care
106
- * must be taken when forking.
104
+ * kqueue descriptors are automatically invalidated by the OS across fork,
105
+ * so care must be taken when forking.
107
106
  * Setting IO#autoclose=false is recommended for applications which fork
108
- * after kqueue creation. Ruby 1.8 does not have IO#autoclose=, so using
109
- * this class is not recommended under Ruby 1.8
107
+ * after kqueue creation.
110
108
  */
111
109
  static VALUE s_new(VALUE klass)
112
110
  {
@@ -203,11 +201,16 @@ static VALUE nogvl_kevent(void *args)
203
201
  return (VALUE)nevents;
204
202
  }
205
203
 
204
+ static void changelist_prepare(struct kevent *, VALUE);
205
+
206
206
  static VALUE do_kevent(struct kq_per_thread *kpt)
207
207
  {
208
208
  long nevents;
209
209
  struct timespec expire_at;
210
210
 
211
+ if (kpt->nchanges)
212
+ changelist_prepare(kpt->events, kpt->changelist);
213
+
211
214
  if (kpt->ts) {
212
215
  clock_gettime(CLOCK_MONOTONIC, &expire_at);
213
216
 
@@ -333,7 +336,7 @@ static void changelist_prepare(struct kevent *events, VALUE changelist)
333
336
  */
334
337
  static VALUE sp_kevent(int argc, VALUE *argv, VALUE self)
335
338
  {
336
- struct timespec ts;
339
+ struct timespec ts, *t;
337
340
  VALUE changelist, events, timeout;
338
341
  struct kq_per_thread *kpt;
339
342
  int nchanges, nevents;
@@ -362,12 +365,14 @@ static VALUE sp_kevent(int argc, VALUE *argv, VALUE self)
362
365
  nevents = 0;
363
366
  }
364
367
 
365
- kpt = kpt_get(self, nchanges, nevents);
366
- kpt->ts = NIL_P(timeout) ? NULL : value2timespec(&ts, timeout);
367
- if (nchanges)
368
- changelist_prepare(kpt->events, changelist);
368
+ t = NIL_P(timeout) ? NULL : value2timespec(&ts, timeout);
369
+ kpt = kpt_get(nchanges, nevents);
370
+ kpt->ts = t;
371
+ kpt->changelist = changelist;
372
+ kpt->io = self;
373
+ kpt->fd = rb_sp_fileno(kpt->io);
369
374
 
370
- return do_kevent(kpt);
375
+ return rb_ensure(do_kevent, (VALUE)kpt, rb_sp_puttlsbuf, (VALUE)kpt);
371
376
  }
372
377
 
373
378
  /* initialize constants in the SleepyPenguin::Ev namespace */
@@ -651,13 +656,9 @@ void sleepy_penguin_init_kqueue(void)
651
656
 
652
657
  id_for_fd = rb_intern("for_fd");
653
658
 
654
- if (RB_SP_GREEN_THREAD)
655
- rb_require("sleepy_penguin/kqueue/io");
656
-
657
- /* the high-level interface is implemented in Ruby: */
658
- rb_require("sleepy_penguin/kqueue");
659
-
660
- /* Kevent helper struct */
661
- rb_require("sleepy_penguin/kevent");
659
+ /*
660
+ * the high-level interface is implemented in Ruby
661
+ * see lib/sleepy_penguin/kevent.rb
662
+ */
662
663
  }
663
664
  #endif /* HAVE_SYS_EVENT_H */
@@ -0,0 +1,120 @@
1
+ #include "sleepy_penguin.h"
2
+ #include <sys/types.h>
3
+ #include <sys/socket.h>
4
+ #include <sys/uio.h>
5
+
6
+ #if defined(HAVE_SYS_SENDFILE_H) && !defined(HAVE_BSD_SENDFILE)
7
+ # include <sys/sendfile.h>
8
+ #endif
9
+
10
+ #if defined(__linux__) && defined(HAVE_SENDFILE)
11
+ # define linux_sendfile(in_fd, out_fd, offset, count) \
12
+ sendfile((in_fd),(out_fd),(offset),(count))
13
+
14
+ /* all good */
15
+ #elif defined(HAVE_SENDFILE) && \
16
+ (defined(__FreeBSD__) || defined(__DragonFly__))
17
+ /*
18
+ * make BSD sendfile look like Linux for now...
19
+ * we can support SF_NODISKIO later
20
+ */
21
+ static ssize_t linux_sendfile(int sockfd, int filefd, off_t *off, size_t count)
22
+ {
23
+ off_t sbytes = 0;
24
+ off_t offset = off ? *off : lseek(filefd, 0, SEEK_CUR);
25
+
26
+ int rc = sendfile(filefd, sockfd, offset, count, NULL, &sbytes, 0);
27
+ if (sbytes > 0) {
28
+ if (off)
29
+ *off += sbytes;
30
+ else
31
+ lseek(filefd, sbytes, SEEK_CUR);
32
+ return (ssize_t)sbytes;
33
+ }
34
+
35
+ return (ssize_t)rc;
36
+ }
37
+ #else /* emulate sendfile using (read|pread) + write */
38
+ static ssize_t pread_sendfile(int sockfd, int filefd, off_t *off, size_t count)
39
+ {
40
+ size_t max_read = 16384;
41
+ void *buf;
42
+ ssize_t r;
43
+ ssize_t w;
44
+
45
+ max_read = count > max_read ? max_read : count;
46
+ buf = xmalloc(max_read);
47
+
48
+ do {
49
+ r = off ? pread(filefd, buf, max_read, *off) :
50
+ read(filefd, buf, max_read);
51
+ } while (r < 0 && errno == EINTR);
52
+
53
+ if (r <= 0) {
54
+ int err = errno;
55
+ xfree(buf);
56
+ errno = err;
57
+ return r;
58
+ }
59
+ w = write(sockfd, buf, r);
60
+ if (w > 0 && off)
61
+ *off += w;
62
+ xfree(buf);
63
+ return w;
64
+ }
65
+ # define linux_sendfile(out_fd, in_fd, offset, count) \
66
+ pread_sendfile((out_fd),(in_fd),(offset),(count))
67
+ #endif
68
+
69
+ struct sf_args {
70
+ int dst_fd;
71
+ int src_fd;
72
+ off_t *off;
73
+ size_t count;
74
+ };
75
+
76
+ static VALUE sym_wait_writable;
77
+
78
+ static VALUE nogvl_sf(void *ptr)
79
+ {
80
+ struct sf_args *a = ptr;
81
+
82
+ return (VALUE)linux_sendfile(a->dst_fd, a->src_fd, a->off, a->count);
83
+ }
84
+
85
+ static VALUE lsf(VALUE mod, VALUE dst, VALUE src, VALUE src_off, VALUE count)
86
+ {
87
+ off_t off = 0;
88
+ struct sf_args a;
89
+ ssize_t bytes;
90
+ int retried = 0;
91
+
92
+ a.off = NIL_P(src_off) ? NULL : (off = NUM2OFFT(src_off), &off);
93
+ a.count = NUM2SIZET(count);
94
+ again:
95
+ a.src_fd = rb_sp_fileno(src);
96
+ a.dst_fd = rb_sp_fileno(dst);
97
+ bytes = (ssize_t)rb_sp_fd_region(nogvl_sf, &a, a.dst_fd);
98
+ if (bytes < 0) {
99
+ switch (errno) {
100
+ case EAGAIN:
101
+ return sym_wait_writable;
102
+ case ENOMEM:
103
+ case ENOBUFS:
104
+ if (!retried) {
105
+ rb_gc();
106
+ retried = 1;
107
+ goto again;
108
+ }
109
+ }
110
+ rb_sys_fail("sendfile");
111
+ }
112
+ return SSIZET2NUM(bytes);
113
+ }
114
+
115
+ void sleepy_penguin_init_sendfile(void)
116
+ {
117
+ VALUE m = rb_define_module("SleepyPenguin");
118
+ rb_define_singleton_method(m, "__lsf", lsf, 4);
119
+ sym_wait_writable = ID2SYM(rb_intern("wait_writable"));
120
+ }
@@ -19,16 +19,6 @@ int rb_sp_io_closed(VALUE io);
19
19
  int rb_sp_fileno(VALUE io);
20
20
  void rb_sp_set_nonblock(int fd);
21
21
 
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
- # define blocking_io_prepare(fd) ((void)(fd))
27
- #else
28
- # define RB_SP_GREEN_THREAD 1
29
- # define blocking_io_prepare(fd) rb_sp_set_nonblock((fd))
30
- #endif
31
-
32
22
  #ifdef HAVE_RB_THREAD_IO_BLOCKING_REGION
33
23
  /* Ruby 1.9.3 and 2.0.0 */
34
24
  VALUE rb_thread_io_blocking_region(rb_blocking_function_t *, void *, int);
@@ -46,24 +36,7 @@ VALUE rb_thread_io_blocking_region(rb_blocking_function_t *, void *, int);
46
36
  # define rb_sp_fd_region(fn,data,fd) \
47
37
  rb_thread_blocking_region((fn),(data),RUBY_UBF_IO,NULL)
48
38
  #else
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
+ # error Ruby <= 1.8 not supported
67
40
  #endif
68
41
 
69
42
  #define NODOC_CONST(klass,name,value) \
@@ -78,6 +51,7 @@ static inline VALUE fake_blocking_region(VALUE (*fn)(void *), void *data)
78
51
  typedef int rb_sp_waitfn(int fd);
79
52
  int rb_sp_wait(rb_sp_waitfn waiter, VALUE obj, int *fd);
80
53
  void *rb_sp_gettlsbuf(size_t *size);
54
+ VALUE rb_sp_puttlsbuf(VALUE);
81
55
 
82
56
  /* Flexible array elements are standard in C99 */
83
57
  #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
@@ -90,4 +64,17 @@ void *rb_sp_gettlsbuf(size_t *size);
90
64
  # endif
91
65
  #endif
92
66
 
67
+ int rb_sp_gc_for_fd(int err);
68
+
69
+ #ifndef HAVE_COPY_FILE_RANGE
70
+ # include <sys/syscall.h>
71
+ # if !defined(__NR_copy_file_range) && defined(__linux__)
72
+ # if defined(__x86_64__)
73
+ # define __NR_copy_file_range 326
74
+ # elif defined(__i386__)
75
+ # define __NR_copy_file_range 377
76
+ # endif /* supported arches */
77
+ # endif /* __NR_copy_file_range */
78
+ #endif
79
+
93
80
  #endif /* SLEEPY_PENGUIN_H */
@@ -0,0 +1,33 @@
1
+ /* common splice and copy_file_range-related definitions */
2
+
3
+ #ifndef SSIZET2NUM
4
+ # define SSIZET2NUM(x) LONG2NUM(x)
5
+ #endif
6
+ #ifndef NUM2SIZET
7
+ # define NUM2SIZET(x) NUM2ULONG(x)
8
+ #endif
9
+
10
+ #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) && defined(HAVE_RUBY_THREAD_H)
11
+ /* Ruby 2.0+ */
12
+ # include <ruby/thread.h>
13
+ # define WITHOUT_GVL(fn,a,ubf,b) \
14
+ rb_thread_call_without_gvl((fn),(a),(ubf),(b))
15
+ #elif defined(HAVE_RB_THREAD_BLOCKING_REGION)
16
+ typedef VALUE (*my_blocking_fn_t)(void*);
17
+ # define WITHOUT_GVL(fn,a,ubf,b) \
18
+ rb_thread_blocking_region((my_blocking_fn_t)(fn),(a),(ubf),(b))
19
+
20
+ #else /* Ruby 1.8 */
21
+ # error Ruby 1.8 not supported
22
+ #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
23
+
24
+ #define IO_RUN(fn,data) WITHOUT_GVL((fn),(data),RUBY_UBF_IO,0)
25
+
26
+ struct copy_args {
27
+ int fd_in;
28
+ int fd_out;
29
+ off_t *off_in;
30
+ off_t *off_out;
31
+ size_t len;
32
+ unsigned flags;
33
+ };