sleepy_penguin 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document CHANGED
@@ -4,7 +4,6 @@ TODO
4
4
  NEWS
5
5
  ChangeLog
6
6
  lib
7
- ext/sleepy_penguin/init.c
8
7
  ext/sleepy_penguin/epoll.c
9
8
  ext/sleepy_penguin/eventfd.c
10
9
  ext/sleepy_penguin/init.c
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v2.0.0.GIT
4
+ DEF_VER=v3.0.0.GIT
5
5
 
6
6
  LF='
7
7
  '
@@ -1,13 +1,14 @@
1
1
  #include "sleepy_penguin.h"
2
2
  #include <sys/epoll.h>
3
3
  #include <pthread.h>
4
+ #include <time.h>
4
5
  #include "missing_epoll.h"
5
6
  #ifdef HAVE_RUBY_ST_H
6
7
  # include <ruby/st.h>
7
8
  #else
8
9
  # include <st.h>
9
10
  #endif
10
-
11
+ #include "missing_rb_thread_fd_close.h"
11
12
  #define EP_RECREATE (-2)
12
13
 
13
14
  static st_table *active;
@@ -15,6 +16,15 @@ static const int step = 64; /* unlikely to grow unless you're huge */
15
16
  static VALUE cEpoll_IO;
16
17
  static ID id_for_fd;
17
18
 
19
+ static uint64_t now_ms(void)
20
+ {
21
+ struct timespec now;
22
+
23
+ clock_gettime(CLOCK_MONOTONIC, &now);
24
+
25
+ return now.tv_sec * 1000 + (now.tv_nsec + 500000) / 1000000;
26
+ }
27
+
18
28
  static void pack_event_data(struct epoll_event *event, VALUE obj)
19
29
  {
20
30
  event->data.ptr = (void *)obj;
@@ -65,7 +75,7 @@ static void gcfree(void *ptr)
65
75
  st_delete(active, &key, NULL);
66
76
  }
67
77
  if (NIL_P(ep->io) && ep->fd >= 0) {
68
- /* can't raise during GC */
78
+ /* can't raise during GC, and close() never fails in Linux */
69
79
  (void)close(ep->fd);
70
80
  errno = 0;
71
81
  }
@@ -108,12 +118,18 @@ static void my_epoll_create(struct rb_epoll *ep)
108
118
  ep->flag_cache = rb_ary_new();
109
119
  }
110
120
 
121
+ static int ep_fd_check(struct rb_epoll *ep)
122
+ {
123
+ if (ep->fd == -1)
124
+ rb_raise(rb_eIOError, "closed epoll descriptor");
125
+ return 1;
126
+ }
127
+
111
128
  static void ep_check(struct rb_epoll *ep)
112
129
  {
113
130
  if (ep->fd == EP_RECREATE)
114
131
  my_epoll_create(ep);
115
- if (ep->fd == -1)
116
- rb_raise(rb_eIOError, "closed");
132
+ ep_fd_check(ep);
117
133
  assert(TYPE(ep->marks) == T_ARRAY && "marks not initialized");
118
134
  assert(TYPE(ep->flag_cache) == T_ARRAY && "flag_cache not initialized");
119
135
  }
@@ -303,6 +319,23 @@ static VALUE epwait_result(struct rb_epoll *ep, int n)
303
319
  return INT2NUM(n);
304
320
  }
305
321
 
322
+ static int epoll_expired_p(uint64_t expire_at, struct rb_epoll *ep)
323
+ {
324
+ uint64_t now;
325
+
326
+ ep_fd_check(ep);
327
+ if (ep->timeout < 0)
328
+ return 0;
329
+ if (ep->timeout == 0)
330
+ return 1;
331
+
332
+ now = now_ms();
333
+ if (now > expire_at)
334
+ return 1;
335
+ ep->timeout = (int)(expire_at - now);
336
+ return 0;
337
+ }
338
+
306
339
  #if defined(HAVE_RB_THREAD_BLOCKING_REGION)
307
340
  static VALUE nogvl_wait(void *args)
308
341
  {
@@ -314,7 +347,12 @@ static VALUE nogvl_wait(void *args)
314
347
 
315
348
  static VALUE real_epwait(struct rb_epoll *ep)
316
349
  {
317
- int n = (int)rb_sp_io_region(nogvl_wait, ep);
350
+ int n;
351
+ uint64_t expire_at = ep->timeout > 0 ? now_ms() + ep->timeout : 0;
352
+
353
+ do {
354
+ n = (int)rb_sp_fd_region(nogvl_wait, ep, ep->fd);
355
+ } while (n == -1 && errno == EINTR && ! epoll_expired_p(expire_at, ep));
318
356
 
319
357
  return epwait_result(ep, n);
320
358
  }
@@ -343,9 +381,11 @@ static int safe_epoll_wait(struct rb_epoll *ep)
343
381
  {
344
382
  int n;
345
383
 
346
- TRAP_BEG;
347
- n = epoll_wait(ep->fd, ep->events, ep->maxevents, 0);
348
- TRAP_END;
384
+ do {
385
+ TRAP_BEG;
386
+ n = epoll_wait(ep->fd, ep->events, ep->maxevents, 0);
387
+ TRAP_END;
388
+ } while (n == -1 && errno == EINTR && ep_fd_check(ep));
349
389
 
350
390
  return n;
351
391
  }
@@ -515,15 +555,18 @@ static VALUE epclose(VALUE self)
515
555
  }
516
556
 
517
557
  if (NIL_P(ep->io)) {
558
+ ep_fd_check(ep);
559
+
518
560
  if (ep->fd == EP_RECREATE) {
519
- ep->fd = -1;
520
- } else if (ep->fd == -1) {
521
- rb_raise(rb_eIOError, "closed");
561
+ ep->fd = -1; /* success */
522
562
  } else {
523
- int e = close(ep->fd);
563
+ int err;
564
+ int fd = ep->fd;
524
565
 
525
566
  ep->fd = -1;
526
- if (e == -1)
567
+ rb_thread_fd_close(fd);
568
+ err = close(fd);
569
+ if (err == -1)
527
570
  rb_sys_fail("close");
528
571
  }
529
572
  } else {
@@ -599,6 +642,15 @@ static VALUE init_copy(VALUE copy, VALUE orig)
599
642
  return copy;
600
643
  }
601
644
 
645
+ /* occasionally it's still useful to lookup aliased IO objects
646
+ * based on for debugging */
647
+ static int my_fileno(VALUE obj)
648
+ {
649
+ if (T_FIXNUM == TYPE(obj))
650
+ return FIX2INT(obj);
651
+ return rb_sp_fileno(obj);
652
+ }
653
+
602
654
  /*
603
655
  * call-seq:
604
656
  * epoll.io_for(io) -> object
@@ -611,7 +663,7 @@ static VALUE io_for(VALUE self, VALUE obj)
611
663
  {
612
664
  struct rb_epoll *ep = ep_get(self);
613
665
 
614
- return rb_ary_entry(ep->marks, rb_sp_fileno(obj));
666
+ return rb_ary_entry(ep->marks, my_fileno(obj));
615
667
  }
616
668
 
617
669
  /*
@@ -625,7 +677,7 @@ static VALUE flags_for(VALUE self, VALUE obj)
625
677
  {
626
678
  struct rb_epoll *ep = ep_get(self);
627
679
 
628
- return rb_ary_entry(ep->flag_cache, rb_sp_fileno(obj));
680
+ return rb_ary_entry(ep->flag_cache, my_fileno(obj));
629
681
  }
630
682
 
631
683
  /*
@@ -640,7 +692,7 @@ static VALUE include_p(VALUE self, VALUE obj)
640
692
  {
641
693
  struct rb_epoll *ep = ep_get(self);
642
694
 
643
- return NIL_P(rb_ary_entry(ep->marks, rb_sp_fileno(obj))) ? Qfalse : Qtrue;
695
+ return NIL_P(rb_ary_entry(ep->marks, my_fileno(obj))) ? Qfalse : Qtrue;
644
696
  }
645
697
 
646
698
  /*
@@ -713,8 +765,8 @@ void sleepy_penguin_init_epoll(void)
713
765
  * Document-class: SleepyPenguin::Epoll::IO
714
766
  *
715
767
  * Epoll::IO is an internal class. Its only purpose is to be
716
- * compatible with IO.select and related methods and should not
717
- * be used directly, use Epoll instead.
768
+ * compatible with IO.select and related methods and should
769
+ * never be used directly, use Epoll instead.
718
770
  */
719
771
  cEpoll_IO = rb_define_class_under(cEpoll, "IO", rb_cIO);
720
772
  rb_define_method(cEpoll, "initialize", init, -1);
@@ -1,7 +1,6 @@
1
1
  #ifdef HAVE_SYS_EVENTFD_H
2
2
  #include "sleepy_penguin.h"
3
3
  #include <sys/eventfd.h>
4
- static ID id_for_fd;
5
4
 
6
5
  /*
7
6
  * call-seq:
@@ -21,7 +20,7 @@ static ID id_for_fd;
21
20
  */
22
21
  static VALUE s_new(int argc, VALUE *argv, VALUE klass)
23
22
  {
24
- VALUE _initval, _flags;
23
+ VALUE _initval, _flags, rv;
25
24
  unsigned initval;
26
25
  int flags;
27
26
  int fd;
@@ -40,7 +39,8 @@ static VALUE s_new(int argc, VALUE *argv, VALUE klass)
40
39
  rb_sys_fail("eventfd");
41
40
  }
42
41
 
43
- return rb_funcall(klass, id_for_fd, 1, INT2NUM(fd));
42
+ rv = INT2FIX(fd);
43
+ return rb_call_super(1, &rv);
44
44
  }
45
45
 
46
46
  struct efd_args {
@@ -87,7 +87,7 @@ static VALUE incr(int argc, VALUE *argv, VALUE self)
87
87
  RTEST(nonblock) ? rb_sp_set_nonblock(x.fd) : blocking_io_prepare(x.fd);
88
88
  x.val = (uint64_t)NUM2ULL(value);
89
89
  retry:
90
- w = (ssize_t)rb_sp_io_region(efd_write, &x);
90
+ w = (ssize_t)rb_sp_fd_region(efd_write, &x, x.fd);
91
91
  if (w == -1) {
92
92
  if (errno == EAGAIN && RTEST(nonblock))
93
93
  return Qfalse;
@@ -113,7 +113,7 @@ retry:
113
113
  * the counter becomes non-zero unless +nonblock+ is +true+, in which
114
114
  * case it returns +nil+.
115
115
  */
116
- static VALUE getvalue(int argc, VALUE argv, VALUE self)
116
+ static VALUE getvalue(int argc, VALUE *argv, VALUE self)
117
117
  {
118
118
  struct efd_args x;
119
119
  ssize_t w;
@@ -123,11 +123,11 @@ static VALUE getvalue(int argc, VALUE argv, VALUE self)
123
123
  x.fd = rb_sp_fileno(self);
124
124
  RTEST(nonblock) ? rb_sp_set_nonblock(x.fd) : blocking_io_prepare(x.fd);
125
125
  retry:
126
- w = (ssize_t)rb_sp_io_region(efd_read, &x);
126
+ w = (ssize_t)rb_sp_fd_region(efd_read, &x, x.fd);
127
127
  if (w == -1) {
128
128
  if (errno == EAGAIN && RTEST(nonblock))
129
129
  return Qnil;
130
- if (rb_io_wait_readable(x.fd))
130
+ if (rb_io_wait_readable(x.fd = rb_sp_fileno(self)))
131
131
  goto retry;
132
132
  rb_sys_fail("read(eventfd)");
133
133
  }
@@ -157,7 +157,7 @@ void sleepy_penguin_init_eventfd(void)
157
157
  * the maximum value that may be stored in an EventFD,
158
158
  * currently 0xfffffffffffffffe
159
159
  */
160
- rb_define_const(cEventFD, "MAX", ULL2NUM(0xfffffffffffffffe));
160
+ rb_define_const(cEventFD, "MAX", ULL2NUM(0xfffffffffffffffeULL));
161
161
 
162
162
  #ifdef EFD_NONBLOCK
163
163
  NODOC_CONST(cEventFD, "NONBLOCK", INT2NUM(EFD_NONBLOCK));
@@ -170,6 +170,5 @@ void sleepy_penguin_init_eventfd(void)
170
170
  #endif
171
171
  rb_define_method(cEventFD, "value", getvalue, -1);
172
172
  rb_define_method(cEventFD, "incr", incr, -1);
173
- id_for_fd = rb_intern("for_fd");
174
173
  }
175
174
  #endif /* HAVE_SYS_EVENTFD_H */
@@ -7,9 +7,9 @@ have_header('sys/timerfd.h')
7
7
  have_header('sys/inotify.h')
8
8
  have_header('sys/signalfd.h')
9
9
  have_header('ruby/io.h') and have_struct_member('rb_io_t', 'fd', 'ruby/io.h')
10
- have_func('rb_memerror') or abort 'need rb_memerror()'
11
- have_func('rb_io_close') or abort 'need rb_io_close()'
12
10
  have_func('epoll_create1', %w(sys/epoll.h))
13
11
  have_func('rb_thread_blocking_region')
12
+ have_func('rb_thread_io_blocking_region')
13
+ have_func('rb_thread_fd_close')
14
14
  have_library('pthread')
15
15
  create_makefile('sleepy_penguin_ext')
@@ -3,12 +3,8 @@
3
3
  #include <sys/inotify.h>
4
4
  #include <sys/ioctl.h>
5
5
  #include "missing_inotify.h"
6
- #if defined(RFILE) && defined(HAVE_ST_FD) && \
7
- defined(HAVE_RB_THREAD_BLOCKING_REGION)
8
- # define NOGVL_CLOSE
9
- #endif
10
6
 
11
- static ID id_for_fd, id_inotify_buf, id_inotify_tmp, id_mask;
7
+ static ID id_inotify_buf, id_inotify_tmp, id_mask;
12
8
  static VALUE cEvent, checks;
13
9
 
14
10
  /*
@@ -38,7 +34,8 @@ static VALUE s_new(int argc, VALUE *argv, VALUE klass)
38
34
  rb_sys_fail("inotify_init1");
39
35
  }
40
36
 
41
- rv = rb_funcall(klass, id_for_fd, 1, INT2NUM(fd));
37
+ rv = INT2FIX(fd);
38
+ rv = rb_call_super(1, &rv);
42
39
  rb_ivar_set(rv, id_inotify_buf, rb_str_new(0, 128));
43
40
  rb_ivar_set(rv, id_inotify_tmp, rb_ary_new());
44
41
 
@@ -184,7 +181,7 @@ static VALUE take(int argc, VALUE *argv, VALUE self)
184
181
  else
185
182
  blocking_io_prepare(args.fd);
186
183
  do {
187
- r = rb_sp_io_region(inread, &args);
184
+ r = rb_sp_fd_region(inread, &args, args.fd);
188
185
  if (r == 0 /* Linux < 2.6.21 */
189
186
  ||
190
187
  (r < 0 && errno == EINVAL) /* Linux >= 2.6.21 */
@@ -202,6 +199,7 @@ static VALUE take(int argc, VALUE *argv, VALUE self)
202
199
  if (errno == EAGAIN && RTEST(nonblock)) {
203
200
  return Qnil;
204
201
  } else {
202
+ args.fd = rb_sp_fileno(self);
205
203
  if (!rb_io_wait_readable(args.fd))
206
204
  rb_sys_fail("read(inotify)");
207
205
  }
@@ -260,9 +258,7 @@ static VALUE events(VALUE self)
260
258
  */
261
259
  static VALUE init_copy(VALUE dest, VALUE orig)
262
260
  {
263
- VALUE tmp;
264
-
265
- dest = rb_call_super(1, &orig); /* copy all other ivars as-is */
261
+ rb_call_super(1, &orig); /* copy all other ivars as-is */
266
262
  rb_ivar_set(dest, id_inotify_buf, rb_str_new(0, 128));
267
263
 
268
264
  return dest;
@@ -284,36 +280,6 @@ static VALUE each(VALUE self)
284
280
  return self;
285
281
  }
286
282
 
287
- #if defined(NOGVL_CLOSE)
288
- static VALUE fptr_close(void *ptr)
289
- {
290
- rb_io_t *fptr = ptr;
291
- return (VALUE)close(fptr->fd);
292
- }
293
-
294
- /*
295
- * call-seq:
296
- * ino.close -> nil
297
- *
298
- * Closes the underlying file descriptor and releases resources used by the
299
- * kernel. Unlike other file descriptors, Inotify descriptors can take
300
- * a long time to close(2). Calling this explicitly releases the GVL under
301
- * Ruby 1.9
302
- */
303
- static VALUE nogvl_close(VALUE self)
304
- {
305
- rb_io_t *fptr;
306
-
307
- GetOpenFile(self, fptr);
308
-
309
- if ((int)rb_sp_io_region(fptr_close, fptr) < 0)
310
- rb_sys_fail("close(inotify)");
311
- fptr->fd = -1;
312
-
313
- return Qnil;
314
- }
315
- #endif /* NOGVL_CLOSE */
316
-
317
283
  void sleepy_penguin_init_inotify(void)
318
284
  {
319
285
  VALUE mSleepyPenguin, cInotify;
@@ -346,9 +312,6 @@ void sleepy_penguin_init_inotify(void)
346
312
  rb_define_method(cInotify, "initialize_copy", init_copy, 1);
347
313
  rb_define_method(cInotify, "take", take, -1);
348
314
  rb_define_method(cInotify, "each", each, 0);
349
- #ifdef NOGVL_CLOSE
350
- rb_define_method(cInotify, "close", nogvl_close, 0);
351
- #endif
352
315
 
353
316
  /*
354
317
  * Document-class: SleepyPenguin::Inotify::Event
@@ -376,7 +339,6 @@ void sleepy_penguin_init_inotify(void)
376
339
  cEvent = rb_define_class_under(cInotify, "Event", cEvent);
377
340
  rb_define_method(cEvent, "events", events, 0);
378
341
  rb_define_singleton_method(cInotify, "new", s_new, -1);
379
- id_for_fd = rb_intern("for_fd");
380
342
  id_inotify_buf = rb_intern("@inotify_buf");
381
343
  id_inotify_tmp = rb_intern("@inotify_tmp");
382
344
  id_mask = rb_intern("mask");
@@ -0,0 +1,4 @@
1
+ #ifndef HAVE_RB_THREAD_FD_CLOSE
2
+ #define HAVE_RB_THREAD_FD_CLOSE
3
+ # define rb_thread_fd_close(fd) for (;0;)
4
+ #endif
@@ -2,7 +2,7 @@
2
2
  #include "sleepy_penguin.h"
3
3
  #include <signal.h>
4
4
  #include <sys/signalfd.h>
5
- static ID id_for_fd, id_list;
5
+ static ID id_list;
6
6
  static VALUE ssi_members;
7
7
  static VALUE cSigInfo;
8
8
 
@@ -123,7 +123,7 @@ static VALUE update_bang(int argc, VALUE *argv, VALUE self)
123
123
  */
124
124
  static VALUE s_new(int argc, VALUE *argv, VALUE klass)
125
125
  {
126
- VALUE vmask, vflags;
126
+ VALUE vmask, vflags, rv;
127
127
  sigset_t mask;
128
128
  int flags;
129
129
  int fd;
@@ -142,7 +142,9 @@ static VALUE s_new(int argc, VALUE *argv, VALUE klass)
142
142
  rb_sys_fail("signalfd");
143
143
  }
144
144
 
145
- return rb_funcall(klass, id_for_fd, 1, INT2NUM(fd));
145
+
146
+ rv = INT2FIX(fd);
147
+ return rb_call_super(1, &rv);
146
148
  }
147
149
 
148
150
  static VALUE ssi_alloc(VALUE klass)
@@ -193,11 +195,11 @@ static VALUE sfd_take(int argc, VALUE *argv, VALUE self)
193
195
  blocking_io_prepare(fd);
194
196
  retry:
195
197
  ssi->ssi_fd = fd;
196
- r = (ssize_t)rb_sp_io_region(sfd_read, ssi);
198
+ r = (ssize_t)rb_sp_fd_region(sfd_read, ssi, fd);
197
199
  if (r == -1) {
198
200
  if (errno == EAGAIN && RTEST(nonblock))
199
201
  return Qnil;
200
- if (rb_io_wait_readable(fd))
202
+ if (rb_io_wait_readable(fd = rb_sp_fileno(self)))
201
203
  goto retry;
202
204
  rb_sys_fail("read(signalfd)");
203
205
  }
@@ -238,6 +240,9 @@ void sleepy_penguin_init_signalfd(void)
238
240
  /*
239
241
  * Document-class: SleepyPenguin::SignalFD
240
242
  *
243
+ * Use of this class is NOT recommended. Ruby itself has a great
244
+ * signal handling API and its implementation conflicts with this.
245
+ *
241
246
  * A SignalFD is an IO object for accepting signals. It provides
242
247
  * an alternative to Signal.trap that may be monitored using
243
248
  * IO.select or Epoll.
@@ -290,7 +295,6 @@ void sleepy_penguin_init_signalfd(void)
290
295
 
291
296
  rb_define_method(cSignalFD, "take", sfd_take, -1);
292
297
  rb_define_method(cSignalFD, "update!", update_bang, -1);
293
- id_for_fd = rb_intern("for_fd");
294
298
  ssi_members = rb_ary_new();
295
299
 
296
300
  NODOC_CONST(cSigInfo, "MEMBERS", ssi_members);
@@ -30,6 +30,13 @@ VALUE rb_sp_io_region(rb_blocking_function_t *func, void *data);
30
30
  # define blocking_io_prepare(fd) rb_sp_set_nonblock((fd))
31
31
  #endif
32
32
 
33
+ #ifdef HAVE_RB_THREAD_IO_BLOCKING_REGION
34
+ # define rb_sp_fd_region(fn,data,fd) \
35
+ rb_thread_io_blocking_region((fn),(data),(fd))
36
+ #else
37
+ # define rb_sp_fd_region(fn,data,fd) rb_sp_io_region(fn,data)
38
+ #endif
39
+
33
40
  #define NODOC_CONST(klass,name,value) \
34
41
  rb_define_const((klass),(name),(value))
35
42
  #endif /* SLEEPY_PENGUIN_H */
@@ -2,7 +2,6 @@
2
2
  #include "sleepy_penguin.h"
3
3
  #include <sys/timerfd.h>
4
4
  #include "value2timespec.h"
5
- static ID id_for_fd;
6
5
 
7
6
  /*
8
7
  * call-seq:
@@ -22,7 +21,7 @@ static ID id_for_fd;
22
21
  */
23
22
  static VALUE s_new(int argc, VALUE *argv, VALUE klass)
24
23
  {
25
- VALUE cid, fl;
24
+ VALUE cid, fl, rv;
26
25
  int clockid, flags;
27
26
  int fd;
28
27
 
@@ -40,7 +39,8 @@ static VALUE s_new(int argc, VALUE *argv, VALUE klass)
40
39
  rb_sys_fail("timerfd_create");
41
40
  }
42
41
 
43
- return rb_funcall(klass, id_for_fd, 1, INT2NUM(fd));
42
+ rv = INT2FIX(fd);
43
+ return rb_call_super(1, &rv);
44
44
  }
45
45
 
46
46
  static VALUE itimerspec2ary(struct itimerspec *its)
@@ -125,11 +125,11 @@ static VALUE expirations(int argc, VALUE *argv, VALUE self)
125
125
  else
126
126
  blocking_io_prepare(fd);
127
127
  retry:
128
- r = (ssize_t)rb_sp_io_region(tfd_read, &buf);
128
+ r = (ssize_t)rb_sp_fd_region(tfd_read, &buf, fd);
129
129
  if (r == -1) {
130
130
  if (errno == EAGAIN && RTEST(nonblock))
131
131
  return Qnil;
132
- if (rb_io_wait_readable(fd))
132
+ if (rb_io_wait_readable(fd = rb_sp_fileno(self)))
133
133
  goto retry;
134
134
  rb_sys_fail("read(timerfd)");
135
135
  }
@@ -165,6 +165,5 @@ void sleepy_penguin_init_timerfd(void)
165
165
  rb_define_method(cTimerFD, "settime", settime, 3);
166
166
  rb_define_method(cTimerFD, "gettime", gettime, 0);
167
167
  rb_define_method(cTimerFD, "expirations", expirations, -1);
168
- id_for_fd = rb_intern("for_fd");
169
168
  }
170
169
  #endif /* HAVE_SYS_TIMERFD_H */
@@ -110,12 +110,6 @@ int rb_sp_fileno(VALUE io)
110
110
  {
111
111
  rb_io_t *fptr;
112
112
 
113
- switch (TYPE(io)) {
114
- case T_FIXNUM: return FIX2INT(io);
115
- case T_FILE:
116
- GetOpenFile(io, fptr);
117
- return FPTR_TO_FD(fptr);
118
- }
119
113
  io = rb_convert_type(io, T_FILE, "IO", "to_io");
120
114
  GetOpenFile(io, fptr);
121
115
  return FPTR_TO_FD(fptr);
data/pkg.mk CHANGED
@@ -44,6 +44,8 @@ $(ext_dl): $(ext_src) $(ext_pfx_src) $(ext_pfx)/$(ext)/Makefile
44
44
  $(MAKE) -C $(@D)
45
45
  lib := $(lib):$(ext_pfx)/$(ext)
46
46
  build: $(ext_dl)
47
+ else
48
+ build:
47
49
  endif
48
50
 
49
51
  pkg_extra += GIT-VERSION-FILE NEWS ChangeLog LATEST
@@ -67,7 +69,7 @@ doc:: .document .wrongdoc.yml $(pkg_extra)
67
69
  $(RM) -r doc
68
70
  $(WRONGDOC) all
69
71
  install -m644 COPYING doc/COPYING
70
- install -m644 $(shell grep '^[A-Z]' .document) doc/
72
+ install -m644 $(shell LC_ALL=C grep '^[A-Z]' .document) doc/
71
73
 
72
74
  ifneq ($(VERSION),)
73
75
  pkggem := pkg/$(rfpackage)-$(VERSION).gem
@@ -145,7 +147,7 @@ test_units := $(wildcard test/test_*.rb)
145
147
  test: test-unit
146
148
  test-unit: $(test_units)
147
149
  $(test_units): build
148
- $(RUBY) -I $(lib) $@
150
+ $(RUBY) -I $(lib) $@ $(RUBY_TEST_OPTS)
149
151
 
150
152
  # this requires GNU coreutils variants
151
153
  ifneq ($(RSYNC_DEST),)
@@ -165,5 +167,9 @@ doc_gz: docs = $(shell find doc -type f ! -regex '^.*\.\(gif\|jpg\|png\|gz\)$$')
165
167
  doc_gz:
166
168
  for i in $(docs); do \
167
169
  gzip --rsyncable -9 < $$i > $$i.gz; touch -r $$i $$i.gz; done
170
+ check-warnings:
171
+ @(for i in $$(git ls-files '*.rb'| grep -v '^setup\.rb$$'); \
172
+ do $(RUBY) -d -W2 -c $$i; done) | grep -v '^Syntax OK$$' || :
168
173
 
169
174
  .PHONY: all .FORCE-GIT-VERSION-FILE doc test $(test_units) manifest
175
+ .PHONY: check-warnings
@@ -63,7 +63,7 @@ class TestEpoll < Test::Unit::TestCase
63
63
  exit!(1)
64
64
  }
65
65
  res = Process.waitall
66
- res.each { |(pid,status)| assert status.success? }
66
+ res.each { |(_,status)| assert status.success? }
67
67
  end
68
68
 
69
69
  def test_tcp_connect_nonblock_edge
@@ -127,28 +127,33 @@ class TestEpoll < Test::Unit::TestCase
127
127
  assert_equal 2, tmp.size
128
128
  end
129
129
 
130
- def test_signal_safe
130
+ def test_signal_safe_wait_forever
131
131
  time = {}
132
- trap(:USR1) { time[:USR1] = Time.now; sleep 0.1; @wr.write '.' }
132
+ trap(:USR1) do
133
+ time[:USR1] = Time.now
134
+ sleep 0.5
135
+ @wr.write '.'
136
+ end
133
137
  @ep.add @rd, Epoll::IN
134
138
  tmp = []
135
139
  pid = fork do
136
- sleep 0.1 # slightly racy :<
140
+ sleep 0.5 # slightly racy :<
137
141
  Process.kill(:USR1, Process.ppid)
138
142
  end
139
143
  time[:START_WAIT] = Time.now
140
- begin
141
- @ep.wait { |flags, obj| tmp << [ flags, obj ]; time[:EP] = Time.now }
142
- rescue Errno::EINTR
143
- retry
144
+ assert_nothing_raised do
145
+ @ep.wait do |flags, obj|
146
+ tmp << [ flags, obj ]
147
+ time[:EP] = Time.now
148
+ end
144
149
  end
145
150
  assert_equal([[Epoll::IN, @rd]], tmp)
146
151
  _, status = Process.waitpid2(pid)
147
- assert status.success?
148
- assert((time[:USR1] - time[:START_WAIT]) >= 0.1)
149
- assert((time[:USR1] - time[:START_WAIT]) < 0.15)
150
- assert((time[:EP] - time[:USR1]) >= 0.1)
151
- assert((time[:EP] - time[:USR1]) < 0.15)
152
+ assert status.success?, status.inspect
153
+ usr1_delay = time[:USR1] - time[:START_WAIT]
154
+ assert_in_delta(0.5, usr1_delay, 0.1, "usr1_delay=#{usr1_delay}")
155
+ ep_delay = time[:EP] - time[:USR1]
156
+ assert_in_delta(0.5, ep_delay, 0.1, "ep1_delay=#{ep_delay}")
152
157
  ensure
153
158
  trap(:USR1, 'DEFAULT')
154
159
  end unless RBX
@@ -212,7 +217,7 @@ class TestEpoll < Test::Unit::TestCase
212
217
  assert_nothing_raised do
213
218
  4096.times do
214
219
  ep = Epoll.new
215
- io = ep.to_io
220
+ assert_kind_of IO, ep.to_io
216
221
  end
217
222
  end
218
223
  assert ! @ep.closed?
@@ -295,7 +300,6 @@ class TestEpoll < Test::Unit::TestCase
295
300
  break
296
301
  end while true
297
302
  @ep.add(@wr, Epoll::OUT | Epoll::IN)
298
- i = 0
299
303
  assert_equal 0, @ep.wait(nil, 0) { |flags,event| assert false }
300
304
  end
301
305
 
@@ -384,4 +388,13 @@ class TestEpoll < Test::Unit::TestCase
384
388
  assert ! @ep.include?(@wr)
385
389
  assert ! @ep.include?(@wr.fileno)
386
390
  end
391
+
392
+ def test_cross_thread_close
393
+ tmp = []
394
+ thr = Thread.new { sleep(1); @ep.close }
395
+ assert_raises(IOError) do
396
+ @ep.wait { |flags, obj| tmp << [ flags, obj ] }
397
+ end
398
+ assert_nil thr.value
399
+ end if RUBY_VERSION == "1.9.3"
387
400
  end
@@ -21,7 +21,7 @@ class TestEpollGC < Test::Unit::TestCase
21
21
 
22
22
  def add_pipe(m, depth = 0)
23
23
  if depth > 6000
24
- rd, wr = IO.pipe
24
+ _, wr = IO.pipe
25
25
  warn "wr: #{wr.fileno}"
26
26
  @ep.__send__(m, wr, Epoll::OUT)
27
27
  else
@@ -29,19 +29,19 @@ class TestEpollOptimizations < Test::Unit::TestCase
29
29
  assert_nil err
30
30
  lines = io.readlines; io.close
31
31
  assert_equal 1, lines.grep(/^epoll_ctl/).size
32
- assert_match /EPOLL_CTL_ADD/, lines.grep(/^epoll_ctl/)[0]
32
+ assert_match(/EPOLL_CTL_ADD/, lines.grep(/^epoll_ctl/)[0])
33
33
 
34
34
  io, err = Strace.me { @ep.set(@wr, Epoll::OUT | Epoll::ONESHOT) }
35
35
  assert_nil err
36
36
  lines = io.readlines; io.close
37
37
  assert_equal 1, lines.grep(/^epoll_ctl/).size
38
- assert_match /EPOLL_CTL_MOD/, lines.grep(/^epoll_ctl/)[0]
38
+ assert_match(/EPOLL_CTL_MOD/, lines.grep(/^epoll_ctl/)[0])
39
39
 
40
40
  io, err = Strace.me { @ep.set(@wr, Epoll::OUT) }
41
41
  assert_nil err
42
42
  lines = io.readlines; io.close
43
43
  assert_equal 1, lines.grep(/^epoll_ctl/).size
44
- assert_match /EPOLL_CTL_MOD/, lines.grep(/^epoll_ctl/)[0]
44
+ assert_match(/EPOLL_CTL_MOD/, lines.grep(/^epoll_ctl/)[0])
45
45
  @wr.close
46
46
  @rd.close
47
47
 
@@ -50,7 +50,7 @@ class TestEpollOptimizations < Test::Unit::TestCase
50
50
  assert_nil err
51
51
  lines = io.readlines; io.close
52
52
  assert_equal 1, lines.grep(/^epoll_ctl/).size
53
- assert_match /EPOLL_CTL_ADD/, lines.grep(/^epoll_ctl/)[0]
53
+ assert_match(/EPOLL_CTL_ADD/, lines.grep(/^epoll_ctl/)[0])
54
54
  end
55
55
 
56
56
  def test_delete
@@ -61,7 +61,7 @@ class TestEpollOptimizations < Test::Unit::TestCase
61
61
  assert_nil err
62
62
  lines = io.readlines; io.close
63
63
  assert_equal 1, lines.grep(/^epoll_ctl/).size
64
- assert_match %r{=\s+0$}, lines.grep(/^epoll_ctl/)[0]
64
+ assert_match(%r{=\s+0$}, lines.grep(/^epoll_ctl/)[0])
65
65
 
66
66
  rv = true
67
67
  io, err = Strace.me { rv = @ep.delete(@wr) }
@@ -98,18 +98,6 @@ class TestEpollOptimizations < Test::Unit::TestCase
98
98
  assert_equal 0, lines.grep(/^epoll_ctl/).size
99
99
  end
100
100
 
101
- def test_delete_closed_fileno
102
- fileno = @wr.fileno
103
- @ep.add(fileno, Epoll::OUT)
104
- @wr.close
105
- rv = nil
106
- io, err = Strace.me { rv = @ep.delete(fileno) }
107
- lines = io.readlines; io.close
108
- assert_nil err
109
- assert_equal fileno, rv
110
- assert_equal 0, lines.grep(/^epoll_ctl/).size
111
- end
112
-
113
101
  def test_delete_aliased_a
114
102
  tmp = IO.for_fd @wr.fileno
115
103
  IO_PURGATORY << tmp
@@ -134,7 +122,7 @@ class TestEpollOptimizations < Test::Unit::TestCase
134
122
  assert_equal tmp, rv
135
123
  assert_nil err
136
124
  assert_equal 1, lines.grep(/^epoll_ctl/).size
137
- assert_match %r{=\s+0$}, lines.grep(/^epoll_ctl/)[0]
125
+ assert_match(%r{=\s+0$}, lines.grep(/^epoll_ctl/)[0])
138
126
  assert_equal 0, lines.grep(/ENOENT/).size
139
127
  end
140
128
 
@@ -36,7 +36,7 @@ class TestInotify < Test::Unit::TestCase
36
36
  assert a.fileno != b.fileno
37
37
  abuf = a.instance_variable_get(:@inotify_buf)
38
38
  bbuf = b.instance_variable_get(:@inotify_buf)
39
- assert abuf.object_id != bbuf.object_id
39
+ assert abuf.object_id != bbuf.object_id, "#{a.inspect} #{b.inspect}"
40
40
 
41
41
  atmp = a.instance_variable_get(:@inotify_tmp)
42
42
  btmp = b.instance_variable_get(:@inotify_tmp)
@@ -115,27 +115,4 @@ class TestInotify < Test::Unit::TestCase
115
115
  end
116
116
  assert_equal 0, nr
117
117
  end
118
-
119
- def test_close_threadable
120
- ino = Inotify.new
121
- tmp = []
122
- thr = Thread.new do
123
- until ino.closed?
124
- tmp << Time.now
125
- Thread.pass
126
- end
127
- end
128
- t0 = Time.now
129
- ino.close
130
- t1 = Time.now
131
- between = []
132
- thr.join
133
- tmp.each do |t|
134
- if t > t0 && t < t1
135
- between << t
136
- end
137
- end
138
- assert tmp.size > 0, "tmp.size=#{tmp.size}"
139
- assert between.size > 0, "between.size=#{between.size}"
140
- end if RUBY_VERSION.to_f >= 1.9
141
118
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sleepy_penguin
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
4
+ hash: 7
5
5
  prerelease:
6
6
  segments:
7
- - 2
7
+ - 3
8
8
  - 0
9
9
  - 0
10
- version: 2.0.0
10
+ version: 3.0.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - sleepy_penguin hackers
@@ -15,8 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-03-10 00:00:00 +00:00
19
- default_executable:
18
+ date: 2011-05-21 00:00:00 Z
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
22
21
  name: wrongdoc
@@ -66,9 +65,9 @@ extra_rdoc_files:
66
65
  - lib/sleepy_penguin.rb
67
66
  - lib/sleepy_penguin/signalfd/sig_info.rb
68
67
  - lib/sleepy_penguin/sp.rb
69
- - ext/sleepy_penguin/init.c
70
68
  - ext/sleepy_penguin/epoll.c
71
69
  - ext/sleepy_penguin/eventfd.c
70
+ - ext/sleepy_penguin/init.c
72
71
  - ext/sleepy_penguin/inotify.c
73
72
  - ext/sleepy_penguin/signalfd.c
74
73
  - ext/sleepy_penguin/timerfd.c
@@ -95,6 +94,7 @@ files:
95
94
  - ext/sleepy_penguin/inotify.c
96
95
  - ext/sleepy_penguin/missing_epoll.h
97
96
  - ext/sleepy_penguin/missing_inotify.h
97
+ - ext/sleepy_penguin/missing_rb_thread_fd_close.h
98
98
  - ext/sleepy_penguin/signalfd.c
99
99
  - ext/sleepy_penguin/sleepy_penguin.h
100
100
  - ext/sleepy_penguin/timerfd.c
@@ -114,7 +114,6 @@ files:
114
114
  - test/test_signalfd.rb
115
115
  - test/test_signalfd_siginfo.rb
116
116
  - test/test_timerfd.rb
117
- has_rdoc: true
118
117
  homepage: http://bogomips.org/sleepy_penguin/
119
118
  licenses: []
120
119
 
@@ -148,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
147
  requirements: []
149
148
 
150
149
  rubyforge_project: rainbows
151
- rubygems_version: 1.6.1
150
+ rubygems_version: 1.8.2
152
151
  signing_key:
153
152
  specification_version: 3
154
153
  summary: Linux I/O events for Ruby