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 +1 -1
- data/README +2 -2
- data/ext/sleepy_penguin/epoll.c +97 -140
- data/ext/sleepy_penguin/epoll_green.h +95 -0
- data/ext/sleepy_penguin/extconf.rb +6 -2
- data/ext/sleepy_penguin/inotify.c +0 -1
- data/ext/sleepy_penguin/missing_rb_update_max_fd.h +3 -0
- data/ext/sleepy_penguin/signalfd.c +4 -2
- data/ext/sleepy_penguin/sleepy_penguin.h +1 -0
- data/ext/sleepy_penguin/timerfd.c +6 -0
- data/lib/sleepy_penguin.rb +6 -0
- data/sleepy_penguin.gemspec +1 -1
- data/test/test_epoll.rb +101 -1
- metadata +9 -7
data/GIT-VERSION-GEN
CHANGED
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,
|
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.
|
60
|
+
requests) go to the mailing list: mailto:sleepy.penguin@librelist.org
|
61
61
|
|
62
62
|
* http://bogomips.org/sleepy_penguin/archives/
|
data/ext/sleepy_penguin/epoll.c
CHANGED
@@ -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
|
339
|
+
static VALUE epwait_result(struct ep_per_thread *ept, int n)
|
298
340
|
{
|
299
341
|
int i;
|
300
|
-
struct epoll_event *epoll_event =
|
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
|
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 (
|
365
|
+
if (ept->timeout < 0)
|
331
366
|
return 1;
|
332
367
|
now = now_ms();
|
333
|
-
|
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
|
341
|
-
int
|
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
|
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
|
-
(
|
397
|
-
|
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
|
-
|
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
|
-
|
471
|
-
|
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(
|
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(
|
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,
|
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
|
-
|
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')
|
@@ -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
|
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
|
data/lib/sleepy_penguin.rb
CHANGED
data/sleepy_penguin.gemspec
CHANGED
@@ -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.
|
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 =
|
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:
|
4
|
+
hash: 3
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 3
|
8
|
-
- 0
|
9
8
|
- 1
|
10
|
-
|
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:
|
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,
|
54
|
-
email: sleepy.penguin@librelist.
|
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.
|
152
|
+
rubygems_version: 1.8.24
|
151
153
|
signing_key:
|
152
154
|
specification_version: 3
|
153
155
|
summary: Linux I/O events for Ruby
|