sleepy_penguin 3.0.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/GIT-VERSION-GEN CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v3.0.1.GIT
4
+ DEF_VER=v3.1.0.GIT
5
5
 
6
6
  LF='
7
7
  '
data/README CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  sleepy_penguin provides access to newer, Linux-only system calls to wait
4
4
  on events from traditionally non-I/O sources. Bindings to the eventfd,
5
- timerfd, inotify, signalfd and epoll interfaces are provided.
5
+ timerfd, inotify, and epoll interfaces are provided.
6
6
 
7
7
  == Features
8
8
 
@@ -57,6 +57,6 @@ don't email the git mailing list or maintainer with sleepy_penguin patches.
57
57
  == Contact
58
58
 
59
59
  All feedback (bug reports, user/development discussion, patches, pull
60
- requests) go to the mailing list: mailto:sleepy.penguin@librelist.com
60
+ requests) go to the mailing list: mailto:sleepy.penguin@librelist.org
61
61
 
62
62
  * http://bogomips.org/sleepy_penguin/archives/
@@ -9,8 +9,10 @@
9
9
  # include <st.h>
10
10
  #endif
11
11
  #include "missing_rb_thread_fd_close.h"
12
+ #include "missing_rb_update_max_fd.h"
12
13
  #define EP_RECREATE (-2)
13
14
 
15
+ static pthread_key_t epoll_key;
14
16
  static st_table *active;
15
17
  static const int step = 64; /* unlikely to grow unless you're huge */
16
18
  static VALUE cEpoll_IO;
@@ -35,18 +37,60 @@ static VALUE unpack_event_data(struct epoll_event *event)
35
37
  return (VALUE)event->data.ptr;
36
38
  }
37
39
 
40
+ #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
41
+ # define FLEX_ARRAY
42
+ #elif defined(__GNUC__)
43
+ # if (__GNUC__ >= 3)
44
+ # define FLEX_ARRAY
45
+ # else
46
+ # define FLEX_ARRAY 0
47
+ # endif
48
+ #endif
49
+
38
50
  struct rb_epoll {
39
51
  int fd;
40
- int timeout;
41
- int maxevents;
42
- int capa;
43
- struct epoll_event *events;
44
52
  VALUE io;
45
53
  VALUE marks;
46
54
  VALUE flag_cache;
47
55
  int flags;
48
56
  };
49
57
 
58
+ struct ep_per_thread {
59
+ struct rb_epoll *ep;
60
+ int timeout;
61
+ int maxevents;
62
+ int capa;
63
+ struct epoll_event events[FLEX_ARRAY];
64
+ };
65
+
66
+ static struct ep_per_thread *ept_get(int maxevents)
67
+ {
68
+ struct ep_per_thread *ept = pthread_getspecific(epoll_key);
69
+ int err;
70
+ size_t size;
71
+
72
+ if (ept && ept->capa >= maxevents)
73
+ goto out;
74
+
75
+ size = sizeof(struct ep_per_thread) +
76
+ sizeof(struct epoll_event) * maxevents;
77
+
78
+ free(ept); /* free(NULL) is POSIX and works on glibc */
79
+ ept = malloc(size);
80
+ if (ept == NULL)
81
+ rb_memerror();
82
+ err = pthread_setspecific(epoll_key, ept);
83
+ if (err != 0) {
84
+ errno = err;
85
+ rb_sys_fail("pthread_setspecific");
86
+ }
87
+ ept->capa = maxevents;
88
+ out:
89
+ ept->maxevents = maxevents;
90
+
91
+ return ept;
92
+ }
93
+
50
94
  static struct rb_epoll *ep_get(VALUE self)
51
95
  {
52
96
  struct rb_epoll *ep;
@@ -69,7 +113,6 @@ static void gcfree(void *ptr)
69
113
  {
70
114
  struct rb_epoll *ep = ptr;
71
115
 
72
- xfree(ep->events);
73
116
  if (ep->fd >= 0) {
74
117
  st_data_t key = ep->fd;
75
118
  st_delete(active, &key, NULL);
@@ -94,9 +137,7 @@ static VALUE alloc(VALUE klass)
94
137
  ep->io = Qnil;
95
138
  ep->marks = Qnil;
96
139
  ep->flag_cache = Qnil;
97
- ep->capa = step;
98
140
  ep->flags = 0;
99
- ep->events = xmalloc(sizeof(struct epoll_event) * ep->capa);
100
141
 
101
142
  return self;
102
143
  }
@@ -113,6 +154,7 @@ static void my_epoll_create(struct rb_epoll *ep)
113
154
  if (ep->fd == -1)
114
155
  rb_sys_fail("epoll_create1");
115
156
  }
157
+ rb_update_max_fd(ep->fd);
116
158
  st_insert(active, (st_data_t)ep->fd, (st_data_t)ep);
117
159
  ep->marks = rb_ary_new();
118
160
  ep->flag_cache = rb_ary_new();
@@ -294,10 +336,10 @@ out:
294
336
  return io;
295
337
  }
296
338
 
297
- static VALUE epwait_result(struct rb_epoll *ep, int n)
339
+ static VALUE epwait_result(struct ep_per_thread *ept, int n)
298
340
  {
299
341
  int i;
300
- struct epoll_event *epoll_event = ep->events;
342
+ struct epoll_event *epoll_event = ept->events;
301
343
  VALUE obj_events, obj;
302
344
 
303
345
  if (n == -1)
@@ -309,144 +351,47 @@ static VALUE epwait_result(struct rb_epoll *ep, int n)
309
351
  rb_yield_values(2, obj_events, obj);
310
352
  }
311
353
 
312
- /* grow our event buffer for the next epoll_wait call */
313
- if (n == ep->capa) {
314
- xfree(ep->events);
315
- ep->capa += step;
316
- ep->events = xmalloc(sizeof(struct epoll_event) * ep->capa);
317
- }
318
-
319
354
  return INT2NUM(n);
320
355
  }
321
356
 
322
- static int epoll_resume_p(uint64_t expire_at, struct rb_epoll *ep)
357
+ static int epoll_resume_p(uint64_t expire_at, struct ep_per_thread *ept)
323
358
  {
324
359
  uint64_t now;
325
360
 
326
- ep_fd_check(ep);
361
+ ep_fd_check(ept->ep);
327
362
 
328
363
  if (errno != EINTR)
329
364
  return 0;
330
- if (ep->timeout < 0)
365
+ if (ept->timeout < 0)
331
366
  return 1;
332
367
  now = now_ms();
333
- ep->timeout = now > expire_at ? 0 : (int)(expire_at - now);
368
+ ept->timeout = now > expire_at ? 0 : (int)(expire_at - now);
334
369
  return 1;
335
370
  }
336
371
 
337
372
  #if defined(HAVE_RB_THREAD_BLOCKING_REGION)
338
373
  static VALUE nogvl_wait(void *args)
339
374
  {
340
- struct rb_epoll *ep = args;
341
- int n = epoll_wait(ep->fd, ep->events, ep->maxevents, ep->timeout);
375
+ struct ep_per_thread *ept = args;
376
+ int fd = ept->ep->fd;
377
+ int n = epoll_wait(fd, ept->events, ept->maxevents, ept->timeout);
342
378
 
343
379
  return (VALUE)n;
344
380
  }
345
381
 
346
- static VALUE real_epwait(struct rb_epoll *ep)
347
- {
348
- int n;
349
- uint64_t expire_at = ep->timeout > 0 ? now_ms() + ep->timeout : 0;
350
-
351
- do
352
- n = (int)rb_sp_fd_region(nogvl_wait, ep, ep->fd);
353
- while (n == -1 && epoll_resume_p(expire_at, ep));
354
-
355
- return epwait_result(ep, n);
356
- }
357
- #else /* 1.8 Green thread compatible code */
358
- /*
359
- * we have to worry about green threads and always pass zero
360
- * as the timeout for epoll_wait :(
361
- */
362
- # include <rubysig.h>
363
- # include <sys/time.h>
364
-
365
- /* in case _BSD_SOURCE doesn't give us this macro */
366
- #ifndef timersub
367
- # define timersub(a, b, result) \
368
- do { \
369
- (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
370
- (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
371
- if ((result)->tv_usec < 0) { \
372
- --(result)->tv_sec; \
373
- (result)->tv_usec += 1000000; \
374
- } \
375
- } while (0)
376
- #endif
377
-
378
- static int safe_epoll_wait(struct rb_epoll *ep)
379
- {
380
- int n;
381
-
382
- do {
383
- TRAP_BEG;
384
- n = epoll_wait(ep->fd, ep->events, ep->maxevents, 0);
385
- TRAP_END;
386
- } while (n == -1 && errno == EINTR && ep_fd_check(ep));
387
-
388
- return n;
389
- }
390
-
391
- static int epwait_forever(struct rb_epoll *ep)
382
+ static VALUE real_epwait(struct ep_per_thread *ept)
392
383
  {
393
384
  int n;
385
+ uint64_t expire_at = ept->timeout > 0 ? now_ms() + ept->timeout : 0;
394
386
 
395
387
  do {
396
- (void)rb_io_wait_readable(ep->fd);
397
- n = safe_epoll_wait(ep);
398
- } while (n == 0);
399
-
400
- return n;
401
- }
402
-
403
- static int epwait_timed(struct rb_epoll *ep)
404
- {
405
- struct timeval tv;
406
-
407
- tv.tv_sec = ep->timeout / 1000;
408
- tv.tv_usec = (ep->timeout % 1000) * 1000;
409
-
410
- for (;;) {
411
- struct timeval t0, now, diff;
412
- int n;
413
- fd_set rfds;
414
-
415
- FD_ZERO(&rfds);
416
- FD_SET(ep->fd, &rfds);
388
+ n = (int)rb_sp_fd_region(nogvl_wait, ept, ept->ep->fd);
389
+ } while (n == -1 && epoll_resume_p(expire_at, ept));
417
390
 
418
- gettimeofday(&t0, NULL);
419
- (void)rb_thread_select(ep->fd + 1, &rfds, NULL, NULL, &tv);
420
- n = safe_epoll_wait(ep);
421
- if (n != 0)
422
- return n;
423
-
424
- /* XXX use CLOCK_MONOTONIC if people care about 1.8... */
425
- gettimeofday(&now, NULL);
426
- timersub(&now, &t0, &diff);
427
- timersub(&tv, &diff, &tv);
428
-
429
- if (tv.tv_usec < 0 || tv.tv_sec < 0)
430
- return (n == -1) ? 0 : n;
431
- }
432
-
433
- assert("should never get here (epwait_timed)");
434
- return -1;
435
- }
436
-
437
- static VALUE real_epwait(struct rb_epoll *ep)
438
- {
439
- int n;
440
-
441
- if (ep->timeout == -1)
442
- n = epwait_forever(ep);
443
- else if (ep->timeout == 0)
444
- n = safe_epoll_wait(ep);
445
- else
446
- n = epwait_timed(ep);
447
-
448
- return epwait_result(ep, n);
391
+ return epwait_result(ept, n);
449
392
  }
393
+ #else /* 1.8 Green thread compatible code */
394
+ # include "epoll_green.h"
450
395
  #endif /* 1.8 Green thread compatibility code */
451
396
 
452
397
  /*
@@ -463,20 +408,16 @@ static VALUE epwait(int argc, VALUE *argv, VALUE self)
463
408
  {
464
409
  VALUE timeout, maxevents;
465
410
  struct rb_epoll *ep = ep_get(self);
411
+ struct ep_per_thread *ept;
466
412
 
467
413
  ep_check(ep);
468
414
  rb_need_block();
469
415
  rb_scan_args(argc, argv, "02", &maxevents, &timeout);
470
- ep->timeout = NIL_P(timeout) ? -1 : NUM2INT(timeout);
471
- ep->maxevents = NIL_P(maxevents) ? ep->capa : NUM2INT(maxevents);
472
-
473
- if (ep->maxevents > ep->capa) {
474
- xfree(ep->events);
475
- ep->capa = ep->maxevents;
476
- ep->events = xmalloc(sizeof(struct epoll_event) * ep->capa);
477
- }
416
+ ept = ept_get(NIL_P(maxevents) ? 64 : NUM2INT(maxevents));
417
+ ept->timeout = NIL_P(timeout) ? -1 : NUM2INT(timeout);
418
+ ept->ep = ep;
478
419
 
479
- return real_epwait(ep);
420
+ return real_epwait(ept);
480
421
  }
481
422
 
482
423
  /*
@@ -615,8 +556,7 @@ static VALUE init_copy(VALUE copy, VALUE orig)
615
556
  struct rb_epoll *a = ep_get(orig);
616
557
  struct rb_epoll *b = ep_get(copy);
617
558
 
618
- assert(a->events && b->events && a->events != b->events &&
619
- NIL_P(b->io) && "Ruby broken?");
559
+ assert(NIL_P(b->io) && "Ruby broken?");
620
560
 
621
561
  ep_check(a);
622
562
  assert(NIL_P(b->marks) && "mark array not nil");
@@ -721,9 +661,34 @@ static void atfork_child(void)
721
661
  st_free_table(old);
722
662
  }
723
663
 
664
+ static void epoll_once(void)
665
+ {
666
+ int err = pthread_key_create(&epoll_key, free);
667
+
668
+ if (err) {
669
+ errno = err;
670
+ rb_sys_fail("pthread_key_create");
671
+ }
672
+
673
+ active = st_init_numtable();
674
+
675
+ if (pthread_atfork(NULL, NULL, atfork_child) != 0) {
676
+ rb_gc();
677
+ if (pthread_atfork(NULL, NULL, atfork_child) != 0)
678
+ rb_memerror();
679
+ }
680
+ }
681
+
724
682
  void sleepy_penguin_init_epoll(void)
725
683
  {
726
684
  VALUE mSleepyPenguin, cEpoll;
685
+ pthread_once_t once = PTHREAD_ONCE_INIT;
686
+ int err = pthread_once(&once, epoll_once);
687
+
688
+ if (err) {
689
+ errno = err;
690
+ rb_sys_fail("pthread_once(.., epoll_once)");
691
+ }
727
692
 
728
693
  /*
729
694
  * Document-module: SleepyPenguin
@@ -731,7 +696,7 @@ void sleepy_penguin_init_epoll(void)
731
696
  * require "sleepy_penguin"
732
697
  * include SleepyPenguin
733
698
  *
734
- * The SleepyPenguin namespace includes the Epoll, Inotify, SignalFD,
699
+ * The SleepyPenguin namespace includes the Epoll, Inotify,
735
700
  * TimerFD, EventFD classes in its top level and no other constants.
736
701
  *
737
702
  * If you are uncomfortable including SleepyPenguin, you may also
@@ -744,7 +709,6 @@ void sleepy_penguin_init_epoll(void)
744
709
  * - SP::Epoll
745
710
  * - SP::EventFD
746
711
  * - SP::Inotify
747
- * - SP::SignalFD
748
712
  * - SP::TimerFD
749
713
  */
750
714
  mSleepyPenguin = rb_define_module("SleepyPenguin");
@@ -822,11 +786,4 @@ void sleepy_penguin_init_epoll(void)
822
786
  rb_define_const(cEpoll, "ONESHOT", UINT2NUM(EPOLLONESHOT));
823
787
 
824
788
  id_for_fd = rb_intern("for_fd");
825
- active = st_init_numtable();
826
-
827
- if (pthread_atfork(NULL, NULL, atfork_child) != 0) {
828
- rb_gc();
829
- if (pthread_atfork(NULL, NULL, atfork_child) != 0)
830
- rb_memerror();
831
- }
832
789
  }
@@ -0,0 +1,95 @@
1
+ /* this file is only used by Matz Ruby 1.8 which used green threads */
2
+
3
+ /*
4
+ * we have to worry about green threads and always pass zero
5
+ * as the timeout for epoll_wait :(
6
+ */
7
+ #include <rubysig.h>
8
+ #include <sys/time.h>
9
+
10
+ /* in case _BSD_SOURCE doesn't give us this macro */
11
+ #ifndef timersub
12
+ # define timersub(a, b, result) \
13
+ do { \
14
+ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
15
+ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
16
+ if ((result)->tv_usec < 0) { \
17
+ --(result)->tv_sec; \
18
+ (result)->tv_usec += 1000000; \
19
+ } \
20
+ } while (0)
21
+ #endif
22
+
23
+ static int safe_epoll_wait(struct ep_per_thread *ept)
24
+ {
25
+ int n;
26
+
27
+ do {
28
+ TRAP_BEG;
29
+ n = epoll_wait(ept->ep->fd, ept->events, ept->maxevents, 0);
30
+ TRAP_END;
31
+ } while (n == -1 && errno == EINTR && ep_fd_check(ept->ep));
32
+
33
+ return n;
34
+ }
35
+
36
+ static int epwait_forever(struct ep_per_thread *ept)
37
+ {
38
+ int n;
39
+
40
+ do {
41
+ (void)rb_io_wait_readable(ept->ep->fd);
42
+ n = safe_epoll_wait(ept);
43
+ } while (n == 0);
44
+
45
+ return n;
46
+ }
47
+
48
+ static int epwait_timed(struct ep_per_thread *ept)
49
+ {
50
+ struct timeval tv;
51
+
52
+ tv.tv_sec = ept->timeout / 1000;
53
+ tv.tv_usec = (ept->timeout % 1000) * 1000;
54
+
55
+ for (;;) {
56
+ struct timeval t0, now, diff;
57
+ int n;
58
+ int fd = ept->ep->fd;
59
+ fd_set rfds;
60
+
61
+ FD_ZERO(&rfds);
62
+ FD_SET(fd, &rfds);
63
+
64
+ gettimeofday(&t0, NULL);
65
+ (void)rb_thread_select(fd + 1, &rfds, NULL, NULL, &tv);
66
+ n = safe_epoll_wait(ept);
67
+ if (n != 0)
68
+ return n;
69
+
70
+ /* XXX use CLOCK_MONOTONIC if people care about 1.8... */
71
+ gettimeofday(&now, NULL);
72
+ timersub(&now, &t0, &diff);
73
+ timersub(&tv, &diff, &tv);
74
+
75
+ if (tv.tv_usec < 0 || tv.tv_sec < 0)
76
+ return (n == -1) ? 0 : n;
77
+ }
78
+
79
+ assert("should never get here (epwait_timed)");
80
+ return -1;
81
+ }
82
+
83
+ static VALUE real_epwait(struct ep_per_thread *ept)
84
+ {
85
+ int n;
86
+
87
+ if (ept->timeout == -1)
88
+ n = epwait_forever(ept);
89
+ else if (ept->timeout == 0)
90
+ n = safe_epoll_wait(ept);
91
+ else
92
+ n = epwait_timed(ept);
93
+
94
+ return epwait_result(ept, n);
95
+ }
@@ -2,14 +2,18 @@ require 'mkmf'
2
2
  have_header('sys/epoll.h') or abort 'sys/epoll.h not found'
3
3
  have_header("pthread.h") or abort 'pthread.h not found'
4
4
  have_header('sys/eventfd.h')
5
- have_header('sys/signalfd.h')
5
+
6
+ # it's impossible to use signalfd reliably with Ruby since Ruby currently
7
+ # manages # (and overrides) all signal handling
8
+ # have_header('sys/signalfd.h')
9
+
6
10
  have_header('sys/timerfd.h')
7
11
  have_header('sys/inotify.h')
8
- have_header('sys/signalfd.h')
9
12
  have_header('ruby/io.h') and have_struct_member('rb_io_t', 'fd', 'ruby/io.h')
10
13
  have_func('epoll_create1', %w(sys/epoll.h))
11
14
  have_func('rb_thread_blocking_region')
12
15
  have_func('rb_thread_io_blocking_region')
13
16
  have_func('rb_thread_fd_close')
17
+ have_func('rb_update_max_fd')
14
18
  have_library('pthread')
15
19
  create_makefile('sleepy_penguin_ext')
@@ -232,7 +232,6 @@ static VALUE events(VALUE self)
232
232
  {
233
233
  long len = RARRAY_LEN(checks);
234
234
  VALUE *ptr = RARRAY_PTR(checks);
235
- VALUE pair;
236
235
  VALUE sym;
237
236
  VALUE rv = rb_ary_new();
238
237
  uint32_t mask;
@@ -0,0 +1,3 @@
1
+ #ifndef HAVE_RB_UPDATE_MAX_FD
2
+ # define rb_update_max_fd(fd) for (;0;)
3
+ #endif
@@ -2,7 +2,6 @@
2
2
  #include "sleepy_penguin.h"
3
3
  #include <signal.h>
4
4
  #include <sys/signalfd.h>
5
- static ID id_list;
6
5
  static VALUE ssi_members;
7
6
  static VALUE cSigInfo;
8
7
 
@@ -243,6 +242,9 @@ void sleepy_penguin_init_signalfd(void)
243
242
  * Use of this class is NOT recommended. Ruby itself has a great
244
243
  * signal handling API and its implementation conflicts with this.
245
244
  *
245
+ * This class is currently disabled and the documentation is only
246
+ * provided to describe what it would look like.
247
+ *
246
248
  * A SignalFD is an IO object for accepting signals. It provides
247
249
  * an alternative to Signal.trap that may be monitored using
248
250
  * IO.select or Epoll.
@@ -252,7 +254,7 @@ void sleepy_penguin_init_signalfd(void)
252
254
  * decent signal handling interface anyways, this class is less useful
253
255
  * than signalfd() in a C-only environment.
254
256
  *
255
- * It is not supported at all under (Matz) Ruby 1.8.
257
+ * It is not supported at all.
256
258
  */
257
259
  cSignalFD = rb_define_class_under(mSleepyPenguin, "SignalFD", rb_cIO);
258
260
 
@@ -31,6 +31,7 @@ VALUE rb_sp_io_region(rb_blocking_function_t *func, void *data);
31
31
  #endif
32
32
 
33
33
  #ifdef HAVE_RB_THREAD_IO_BLOCKING_REGION
34
+ VALUE rb_thread_io_blocking_region(rb_blocking_function_t *, void *, int);
34
35
  # define rb_sp_fd_region(fn,data,fd) \
35
36
  rb_thread_io_blocking_region((fn),(data),(fd))
36
37
  #else
@@ -152,9 +152,15 @@ void sleepy_penguin_init_timerfd(void)
152
152
  */
153
153
  cTimerFD = rb_define_class_under(mSleepyPenguin, "TimerFD", rb_cIO);
154
154
  rb_define_singleton_method(cTimerFD, "new", s_new, -1);
155
+
155
156
  NODOC_CONST(cTimerFD, "REALTIME", UINT2NUM(CLOCK_REALTIME));
156
157
  NODOC_CONST(cTimerFD, "MONOTONIC", UINT2NUM(CLOCK_MONOTONIC));
157
158
  NODOC_CONST(cTimerFD, "ABSTIME", UINT2NUM(TFD_TIMER_ABSTIME));
159
+ #ifdef TFD_TIMER_CANCEL_ON_SET
160
+ NODOC_CONST(cTimerFD, "CANCEL_ON_SET",
161
+ UINT2NUM(TFD_TIMER_CANCEL_ON_SET));
162
+ #endif
163
+
158
164
  #ifdef TFD_NONBLOCK
159
165
  NODOC_CONST(cTimerFD, "NONBLOCK", UINT2NUM(TFD_NONBLOCK));
160
166
  #endif
@@ -1 +1,7 @@
1
+ # -*- encoding: binary -*-
2
+ module SleepyPenguin
3
+
4
+ # the version of sleepy_penguin, currently 3.1.0
5
+ SLEEPY_PENGUIN_VERSION = '3.1.0'
6
+ end
1
7
  require 'sleepy_penguin_ext'
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
11
11
  s.authors = ["#{name} hackers"]
12
12
  s.date = Time.now.utc.strftime('%Y-%m-%d')
13
13
  s.description = readme_description
14
- s.email = %q{sleepy.penguin@librelist.com}
14
+ s.email = %q{sleepy.penguin@librelist.org}
15
15
  s.extra_rdoc_files = extra_rdoc_files(manifest)
16
16
  s.files = manifest
17
17
  s.rdoc_options = rdoc_options
data/test/test_epoll.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  require 'test/unit'
2
2
  require 'fcntl'
3
3
  require 'socket'
4
+ require 'thread'
4
5
  $-w = true
6
+ Thread.abort_on_exception = true
5
7
 
6
8
  require 'sleepy_penguin'
7
9
 
@@ -93,6 +95,19 @@ class TestEpoll < Test::Unit::TestCase
93
95
  assert_equal [ [Epoll::OUT, sock] ], tmp
94
96
  end
95
97
 
98
+ def test_edge_accept
99
+ host = '127.0.0.1'
100
+ srv = TCPServer.new(host, 0)
101
+ port = srv.addr[1]
102
+ sock = TCPSocket.new(host, port)
103
+ asock = srv.accept
104
+ assert_equal 3, asock.syswrite("HI\n")
105
+ @ep.add(asock, Epoll::OUT| Epoll::ET | Epoll::ONESHOT)
106
+ tmp = []
107
+ @ep.wait(1) { |flags, obj| tmp << [ flags, obj ] }
108
+ assert_equal [ [Epoll::OUT, asock] ], tmp
109
+ end
110
+
96
111
  def teardown
97
112
  assert_nothing_raised do
98
113
  @rd.close unless @rd.closed?
@@ -399,10 +414,34 @@ class TestEpoll < Test::Unit::TestCase
399
414
  assert_nil thr.value
400
415
  end if RUBY_VERSION == "1.9.3"
401
416
 
417
+ def test_epoll_level_trigger
418
+ @ep.add(@wr, Epoll::OUT)
419
+
420
+ tmp = nil
421
+ @ep.wait { |flags, obj| tmp = obj }
422
+ assert_equal @wr, tmp
423
+
424
+ tmp = nil
425
+ @ep.wait { |flags, obj| tmp = obj }
426
+ assert_equal @wr, tmp
427
+
428
+ buf = '.' * 16384
429
+ begin
430
+ @wr.write_nonblock(buf)
431
+ rescue Errno::EAGAIN
432
+ break
433
+ end while true
434
+ @rd.read(16384)
435
+
436
+ tmp = nil
437
+ @ep.wait { |flags, obj| tmp = obj }
438
+ assert_equal @wr, tmp
439
+ end
440
+
402
441
  def test_epoll_wait_signal_torture
403
442
  usr1 = 0
404
443
  empty = 0
405
- nr = 1000
444
+ nr = 100
406
445
  @ep.add(@rd, Epoll::IN)
407
446
  tmp = []
408
447
  trap(:USR1) { usr1 += 1 }
@@ -423,5 +462,66 @@ class TestEpoll < Test::Unit::TestCase
423
462
  assert usr1 > 0, "usr1: #{usr1}"
424
463
  ensure
425
464
  trap(:USR1, "DEFAULT")
465
+ end if ENV["STRESS"].to_i != 0
466
+
467
+ def test_wait_one_event_per_thread
468
+ thr = []
469
+ pipes = {}
470
+ lock = Mutex.new
471
+ maxevents = 1
472
+ ok = []
473
+ nr = 10
474
+ nr.times do
475
+ r, w = IO.pipe
476
+ pipes[r] = w
477
+ @ep.add(r, Epoll::IN | Epoll::ET | Epoll::ONESHOT)
478
+
479
+ t = Thread.new do
480
+ sleep 2
481
+ events = 0
482
+ @ep.wait(maxevents) do |_,obj|
483
+ assert pipes.include?(obj), "#{obj.inspect} is unknown"
484
+ lock.synchronize { ok << obj }
485
+ events += 1
486
+ end
487
+ events
488
+ end
489
+ thr << t
490
+ end
491
+ pipes.each_value { |w| w.syswrite '.' }
492
+ thr.each do |t|
493
+ begin
494
+ t.run
495
+ rescue ThreadError
496
+ end
497
+ end
498
+
499
+ thr.each { |t| assert_equal 1, t.value }
500
+ assert_equal nr, ok.size, ok.inspect
501
+ assert_equal ok.size, ok.uniq.size, ok.inspect
502
+ assert_equal ok.map { |io| io.fileno }.sort,
503
+ pipes.keys.map { |io| io.fileno }.sort
504
+ ensure
505
+ pipes.each do |r,w|
506
+ r.close
507
+ w.close
508
+ end
509
+ end
510
+
511
+ def test_epoll_as_queue
512
+ fl = Epoll::OUT | Epoll::ET
513
+ first = nil
514
+ 500.times do
515
+ r, w = IO.pipe
516
+ @ep.add(w, fl)
517
+ first ||= begin
518
+ @ep.add(r, Epoll::IN | Epoll::ET)
519
+ [ r, w ]
520
+ end
521
+ end
522
+ 500.times do |i|
523
+ @ep.wait(1) { |flags, io| first[1].write('.') if i == 0 }
524
+ end
525
+ @ep.wait(1) { |flags, io| assert_equal(first[0], io) }
426
526
  end
427
527
  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: 5
4
+ hash: 3
5
5
  prerelease:
6
6
  segments:
7
7
  - 3
8
- - 0
9
8
  - 1
10
- version: 3.0.1
9
+ - 0
10
+ version: 3.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - sleepy_penguin hackers
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-05-21 00:00:00 Z
18
+ date: 2012-05-02 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: wrongdoc
@@ -50,8 +50,8 @@ dependencies:
50
50
  description: |-
51
51
  sleepy_penguin provides access to newer, Linux-only system calls to wait
52
52
  on events from traditionally non-I/O sources. Bindings to the eventfd,
53
- timerfd, inotify, signalfd and epoll interfaces are provided.
54
- email: sleepy.penguin@librelist.com
53
+ timerfd, inotify, and epoll interfaces are provided.
54
+ email: sleepy.penguin@librelist.org
55
55
  executables: []
56
56
 
57
57
  extensions:
@@ -88,6 +88,7 @@ files:
88
88
  - Rakefile
89
89
  - TODO
90
90
  - ext/sleepy_penguin/epoll.c
91
+ - ext/sleepy_penguin/epoll_green.h
91
92
  - ext/sleepy_penguin/eventfd.c
92
93
  - ext/sleepy_penguin/extconf.rb
93
94
  - ext/sleepy_penguin/init.c
@@ -95,6 +96,7 @@ files:
95
96
  - ext/sleepy_penguin/missing_epoll.h
96
97
  - ext/sleepy_penguin/missing_inotify.h
97
98
  - ext/sleepy_penguin/missing_rb_thread_fd_close.h
99
+ - ext/sleepy_penguin/missing_rb_update_max_fd.h
98
100
  - ext/sleepy_penguin/signalfd.c
99
101
  - ext/sleepy_penguin/sleepy_penguin.h
100
102
  - ext/sleepy_penguin/timerfd.c
@@ -147,7 +149,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
147
149
  requirements: []
148
150
 
149
151
  rubyforge_project: rainbows
150
- rubygems_version: 1.8.2
152
+ rubygems_version: 1.8.24
151
153
  signing_key:
152
154
  specification_version: 3
153
155
  summary: Linux I/O events for Ruby