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 +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
|