sleepy_penguin 3.0.1 → 3.1.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/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