sleepy_penguin 2.0.0 → 3.0.0

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