sleepy_penguin 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +0 -1
- data/GIT-VERSION-GEN +1 -1
- data/ext/sleepy_penguin/epoll.c +70 -18
- data/ext/sleepy_penguin/eventfd.c +8 -9
- data/ext/sleepy_penguin/extconf.rb +2 -2
- data/ext/sleepy_penguin/inotify.c +6 -44
- data/ext/sleepy_penguin/missing_rb_thread_fd_close.h +4 -0
- data/ext/sleepy_penguin/signalfd.c +10 -6
- data/ext/sleepy_penguin/sleepy_penguin.h +7 -0
- data/ext/sleepy_penguin/timerfd.c +5 -6
- data/ext/sleepy_penguin/util.c +0 -6
- data/pkg.mk +8 -2
- data/test/test_epoll.rb +28 -15
- data/test/test_epoll_gc.rb +1 -1
- data/test/test_epoll_optimizations.rb +6 -18
- data/test/test_inotify.rb +1 -24
- metadata +7 -8
data/.document
CHANGED
data/GIT-VERSION-GEN
CHANGED
data/ext/sleepy_penguin/epoll.c
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
#include "sleepy_penguin.h"
|
2
2
|
#include <sys/epoll.h>
|
3
3
|
#include <pthread.h>
|
4
|
+
#include <time.h>
|
4
5
|
#include "missing_epoll.h"
|
5
6
|
#ifdef HAVE_RUBY_ST_H
|
6
7
|
# include <ruby/st.h>
|
7
8
|
#else
|
8
9
|
# include <st.h>
|
9
10
|
#endif
|
10
|
-
|
11
|
+
#include "missing_rb_thread_fd_close.h"
|
11
12
|
#define EP_RECREATE (-2)
|
12
13
|
|
13
14
|
static st_table *active;
|
@@ -15,6 +16,15 @@ static const int step = 64; /* unlikely to grow unless you're huge */
|
|
15
16
|
static VALUE cEpoll_IO;
|
16
17
|
static ID id_for_fd;
|
17
18
|
|
19
|
+
static uint64_t now_ms(void)
|
20
|
+
{
|
21
|
+
struct timespec now;
|
22
|
+
|
23
|
+
clock_gettime(CLOCK_MONOTONIC, &now);
|
24
|
+
|
25
|
+
return now.tv_sec * 1000 + (now.tv_nsec + 500000) / 1000000;
|
26
|
+
}
|
27
|
+
|
18
28
|
static void pack_event_data(struct epoll_event *event, VALUE obj)
|
19
29
|
{
|
20
30
|
event->data.ptr = (void *)obj;
|
@@ -65,7 +75,7 @@ static void gcfree(void *ptr)
|
|
65
75
|
st_delete(active, &key, NULL);
|
66
76
|
}
|
67
77
|
if (NIL_P(ep->io) && ep->fd >= 0) {
|
68
|
-
/* can't raise during GC */
|
78
|
+
/* can't raise during GC, and close() never fails in Linux */
|
69
79
|
(void)close(ep->fd);
|
70
80
|
errno = 0;
|
71
81
|
}
|
@@ -108,12 +118,18 @@ static void my_epoll_create(struct rb_epoll *ep)
|
|
108
118
|
ep->flag_cache = rb_ary_new();
|
109
119
|
}
|
110
120
|
|
121
|
+
static int ep_fd_check(struct rb_epoll *ep)
|
122
|
+
{
|
123
|
+
if (ep->fd == -1)
|
124
|
+
rb_raise(rb_eIOError, "closed epoll descriptor");
|
125
|
+
return 1;
|
126
|
+
}
|
127
|
+
|
111
128
|
static void ep_check(struct rb_epoll *ep)
|
112
129
|
{
|
113
130
|
if (ep->fd == EP_RECREATE)
|
114
131
|
my_epoll_create(ep);
|
115
|
-
|
116
|
-
rb_raise(rb_eIOError, "closed");
|
132
|
+
ep_fd_check(ep);
|
117
133
|
assert(TYPE(ep->marks) == T_ARRAY && "marks not initialized");
|
118
134
|
assert(TYPE(ep->flag_cache) == T_ARRAY && "flag_cache not initialized");
|
119
135
|
}
|
@@ -303,6 +319,23 @@ static VALUE epwait_result(struct rb_epoll *ep, int n)
|
|
303
319
|
return INT2NUM(n);
|
304
320
|
}
|
305
321
|
|
322
|
+
static int epoll_expired_p(uint64_t expire_at, struct rb_epoll *ep)
|
323
|
+
{
|
324
|
+
uint64_t now;
|
325
|
+
|
326
|
+
ep_fd_check(ep);
|
327
|
+
if (ep->timeout < 0)
|
328
|
+
return 0;
|
329
|
+
if (ep->timeout == 0)
|
330
|
+
return 1;
|
331
|
+
|
332
|
+
now = now_ms();
|
333
|
+
if (now > expire_at)
|
334
|
+
return 1;
|
335
|
+
ep->timeout = (int)(expire_at - now);
|
336
|
+
return 0;
|
337
|
+
}
|
338
|
+
|
306
339
|
#if defined(HAVE_RB_THREAD_BLOCKING_REGION)
|
307
340
|
static VALUE nogvl_wait(void *args)
|
308
341
|
{
|
@@ -314,7 +347,12 @@ static VALUE nogvl_wait(void *args)
|
|
314
347
|
|
315
348
|
static VALUE real_epwait(struct rb_epoll *ep)
|
316
349
|
{
|
317
|
-
int n
|
350
|
+
int n;
|
351
|
+
uint64_t expire_at = ep->timeout > 0 ? now_ms() + ep->timeout : 0;
|
352
|
+
|
353
|
+
do {
|
354
|
+
n = (int)rb_sp_fd_region(nogvl_wait, ep, ep->fd);
|
355
|
+
} while (n == -1 && errno == EINTR && ! epoll_expired_p(expire_at, ep));
|
318
356
|
|
319
357
|
return epwait_result(ep, n);
|
320
358
|
}
|
@@ -343,9 +381,11 @@ static int safe_epoll_wait(struct rb_epoll *ep)
|
|
343
381
|
{
|
344
382
|
int n;
|
345
383
|
|
346
|
-
|
347
|
-
|
348
|
-
|
384
|
+
do {
|
385
|
+
TRAP_BEG;
|
386
|
+
n = epoll_wait(ep->fd, ep->events, ep->maxevents, 0);
|
387
|
+
TRAP_END;
|
388
|
+
} while (n == -1 && errno == EINTR && ep_fd_check(ep));
|
349
389
|
|
350
390
|
return n;
|
351
391
|
}
|
@@ -515,15 +555,18 @@ static VALUE epclose(VALUE self)
|
|
515
555
|
}
|
516
556
|
|
517
557
|
if (NIL_P(ep->io)) {
|
558
|
+
ep_fd_check(ep);
|
559
|
+
|
518
560
|
if (ep->fd == EP_RECREATE) {
|
519
|
-
ep->fd = -1;
|
520
|
-
} else if (ep->fd == -1) {
|
521
|
-
rb_raise(rb_eIOError, "closed");
|
561
|
+
ep->fd = -1; /* success */
|
522
562
|
} else {
|
523
|
-
int
|
563
|
+
int err;
|
564
|
+
int fd = ep->fd;
|
524
565
|
|
525
566
|
ep->fd = -1;
|
526
|
-
|
567
|
+
rb_thread_fd_close(fd);
|
568
|
+
err = close(fd);
|
569
|
+
if (err == -1)
|
527
570
|
rb_sys_fail("close");
|
528
571
|
}
|
529
572
|
} else {
|
@@ -599,6 +642,15 @@ static VALUE init_copy(VALUE copy, VALUE orig)
|
|
599
642
|
return copy;
|
600
643
|
}
|
601
644
|
|
645
|
+
/* occasionally it's still useful to lookup aliased IO objects
|
646
|
+
* based on for debugging */
|
647
|
+
static int my_fileno(VALUE obj)
|
648
|
+
{
|
649
|
+
if (T_FIXNUM == TYPE(obj))
|
650
|
+
return FIX2INT(obj);
|
651
|
+
return rb_sp_fileno(obj);
|
652
|
+
}
|
653
|
+
|
602
654
|
/*
|
603
655
|
* call-seq:
|
604
656
|
* epoll.io_for(io) -> object
|
@@ -611,7 +663,7 @@ static VALUE io_for(VALUE self, VALUE obj)
|
|
611
663
|
{
|
612
664
|
struct rb_epoll *ep = ep_get(self);
|
613
665
|
|
614
|
-
return rb_ary_entry(ep->marks,
|
666
|
+
return rb_ary_entry(ep->marks, my_fileno(obj));
|
615
667
|
}
|
616
668
|
|
617
669
|
/*
|
@@ -625,7 +677,7 @@ static VALUE flags_for(VALUE self, VALUE obj)
|
|
625
677
|
{
|
626
678
|
struct rb_epoll *ep = ep_get(self);
|
627
679
|
|
628
|
-
return rb_ary_entry(ep->flag_cache,
|
680
|
+
return rb_ary_entry(ep->flag_cache, my_fileno(obj));
|
629
681
|
}
|
630
682
|
|
631
683
|
/*
|
@@ -640,7 +692,7 @@ static VALUE include_p(VALUE self, VALUE obj)
|
|
640
692
|
{
|
641
693
|
struct rb_epoll *ep = ep_get(self);
|
642
694
|
|
643
|
-
return NIL_P(rb_ary_entry(ep->marks,
|
695
|
+
return NIL_P(rb_ary_entry(ep->marks, my_fileno(obj))) ? Qfalse : Qtrue;
|
644
696
|
}
|
645
697
|
|
646
698
|
/*
|
@@ -713,8 +765,8 @@ void sleepy_penguin_init_epoll(void)
|
|
713
765
|
* Document-class: SleepyPenguin::Epoll::IO
|
714
766
|
*
|
715
767
|
* Epoll::IO is an internal class. Its only purpose is to be
|
716
|
-
* compatible with IO.select and related methods
|
717
|
-
* be used directly, use Epoll instead.
|
768
|
+
* compatible with IO.select and related methods and should
|
769
|
+
* never be used directly, use Epoll instead.
|
718
770
|
*/
|
719
771
|
cEpoll_IO = rb_define_class_under(cEpoll, "IO", rb_cIO);
|
720
772
|
rb_define_method(cEpoll, "initialize", init, -1);
|
@@ -1,7 +1,6 @@
|
|
1
1
|
#ifdef HAVE_SYS_EVENTFD_H
|
2
2
|
#include "sleepy_penguin.h"
|
3
3
|
#include <sys/eventfd.h>
|
4
|
-
static ID id_for_fd;
|
5
4
|
|
6
5
|
/*
|
7
6
|
* call-seq:
|
@@ -21,7 +20,7 @@ static ID id_for_fd;
|
|
21
20
|
*/
|
22
21
|
static VALUE s_new(int argc, VALUE *argv, VALUE klass)
|
23
22
|
{
|
24
|
-
VALUE _initval, _flags;
|
23
|
+
VALUE _initval, _flags, rv;
|
25
24
|
unsigned initval;
|
26
25
|
int flags;
|
27
26
|
int fd;
|
@@ -40,7 +39,8 @@ static VALUE s_new(int argc, VALUE *argv, VALUE klass)
|
|
40
39
|
rb_sys_fail("eventfd");
|
41
40
|
}
|
42
41
|
|
43
|
-
|
42
|
+
rv = INT2FIX(fd);
|
43
|
+
return rb_call_super(1, &rv);
|
44
44
|
}
|
45
45
|
|
46
46
|
struct efd_args {
|
@@ -87,7 +87,7 @@ static VALUE incr(int argc, VALUE *argv, VALUE self)
|
|
87
87
|
RTEST(nonblock) ? rb_sp_set_nonblock(x.fd) : blocking_io_prepare(x.fd);
|
88
88
|
x.val = (uint64_t)NUM2ULL(value);
|
89
89
|
retry:
|
90
|
-
w = (ssize_t)
|
90
|
+
w = (ssize_t)rb_sp_fd_region(efd_write, &x, x.fd);
|
91
91
|
if (w == -1) {
|
92
92
|
if (errno == EAGAIN && RTEST(nonblock))
|
93
93
|
return Qfalse;
|
@@ -113,7 +113,7 @@ retry:
|
|
113
113
|
* the counter becomes non-zero unless +nonblock+ is +true+, in which
|
114
114
|
* case it returns +nil+.
|
115
115
|
*/
|
116
|
-
static VALUE getvalue(int argc, VALUE argv, VALUE self)
|
116
|
+
static VALUE getvalue(int argc, VALUE *argv, VALUE self)
|
117
117
|
{
|
118
118
|
struct efd_args x;
|
119
119
|
ssize_t w;
|
@@ -123,11 +123,11 @@ static VALUE getvalue(int argc, VALUE argv, VALUE self)
|
|
123
123
|
x.fd = rb_sp_fileno(self);
|
124
124
|
RTEST(nonblock) ? rb_sp_set_nonblock(x.fd) : blocking_io_prepare(x.fd);
|
125
125
|
retry:
|
126
|
-
w = (ssize_t)
|
126
|
+
w = (ssize_t)rb_sp_fd_region(efd_read, &x, x.fd);
|
127
127
|
if (w == -1) {
|
128
128
|
if (errno == EAGAIN && RTEST(nonblock))
|
129
129
|
return Qnil;
|
130
|
-
if (rb_io_wait_readable(x.fd))
|
130
|
+
if (rb_io_wait_readable(x.fd = rb_sp_fileno(self)))
|
131
131
|
goto retry;
|
132
132
|
rb_sys_fail("read(eventfd)");
|
133
133
|
}
|
@@ -157,7 +157,7 @@ void sleepy_penguin_init_eventfd(void)
|
|
157
157
|
* the maximum value that may be stored in an EventFD,
|
158
158
|
* currently 0xfffffffffffffffe
|
159
159
|
*/
|
160
|
-
rb_define_const(cEventFD, "MAX", ULL2NUM(
|
160
|
+
rb_define_const(cEventFD, "MAX", ULL2NUM(0xfffffffffffffffeULL));
|
161
161
|
|
162
162
|
#ifdef EFD_NONBLOCK
|
163
163
|
NODOC_CONST(cEventFD, "NONBLOCK", INT2NUM(EFD_NONBLOCK));
|
@@ -170,6 +170,5 @@ void sleepy_penguin_init_eventfd(void)
|
|
170
170
|
#endif
|
171
171
|
rb_define_method(cEventFD, "value", getvalue, -1);
|
172
172
|
rb_define_method(cEventFD, "incr", incr, -1);
|
173
|
-
id_for_fd = rb_intern("for_fd");
|
174
173
|
}
|
175
174
|
#endif /* HAVE_SYS_EVENTFD_H */
|
@@ -7,9 +7,9 @@ have_header('sys/timerfd.h')
|
|
7
7
|
have_header('sys/inotify.h')
|
8
8
|
have_header('sys/signalfd.h')
|
9
9
|
have_header('ruby/io.h') and have_struct_member('rb_io_t', 'fd', 'ruby/io.h')
|
10
|
-
have_func('rb_memerror') or abort 'need rb_memerror()'
|
11
|
-
have_func('rb_io_close') or abort 'need rb_io_close()'
|
12
10
|
have_func('epoll_create1', %w(sys/epoll.h))
|
13
11
|
have_func('rb_thread_blocking_region')
|
12
|
+
have_func('rb_thread_io_blocking_region')
|
13
|
+
have_func('rb_thread_fd_close')
|
14
14
|
have_library('pthread')
|
15
15
|
create_makefile('sleepy_penguin_ext')
|
@@ -3,12 +3,8 @@
|
|
3
3
|
#include <sys/inotify.h>
|
4
4
|
#include <sys/ioctl.h>
|
5
5
|
#include "missing_inotify.h"
|
6
|
-
#if defined(RFILE) && defined(HAVE_ST_FD) && \
|
7
|
-
defined(HAVE_RB_THREAD_BLOCKING_REGION)
|
8
|
-
# define NOGVL_CLOSE
|
9
|
-
#endif
|
10
6
|
|
11
|
-
static ID
|
7
|
+
static ID id_inotify_buf, id_inotify_tmp, id_mask;
|
12
8
|
static VALUE cEvent, checks;
|
13
9
|
|
14
10
|
/*
|
@@ -38,7 +34,8 @@ static VALUE s_new(int argc, VALUE *argv, VALUE klass)
|
|
38
34
|
rb_sys_fail("inotify_init1");
|
39
35
|
}
|
40
36
|
|
41
|
-
rv =
|
37
|
+
rv = INT2FIX(fd);
|
38
|
+
rv = rb_call_super(1, &rv);
|
42
39
|
rb_ivar_set(rv, id_inotify_buf, rb_str_new(0, 128));
|
43
40
|
rb_ivar_set(rv, id_inotify_tmp, rb_ary_new());
|
44
41
|
|
@@ -184,7 +181,7 @@ static VALUE take(int argc, VALUE *argv, VALUE self)
|
|
184
181
|
else
|
185
182
|
blocking_io_prepare(args.fd);
|
186
183
|
do {
|
187
|
-
r =
|
184
|
+
r = rb_sp_fd_region(inread, &args, args.fd);
|
188
185
|
if (r == 0 /* Linux < 2.6.21 */
|
189
186
|
||
|
190
187
|
(r < 0 && errno == EINVAL) /* Linux >= 2.6.21 */
|
@@ -202,6 +199,7 @@ static VALUE take(int argc, VALUE *argv, VALUE self)
|
|
202
199
|
if (errno == EAGAIN && RTEST(nonblock)) {
|
203
200
|
return Qnil;
|
204
201
|
} else {
|
202
|
+
args.fd = rb_sp_fileno(self);
|
205
203
|
if (!rb_io_wait_readable(args.fd))
|
206
204
|
rb_sys_fail("read(inotify)");
|
207
205
|
}
|
@@ -260,9 +258,7 @@ static VALUE events(VALUE self)
|
|
260
258
|
*/
|
261
259
|
static VALUE init_copy(VALUE dest, VALUE orig)
|
262
260
|
{
|
263
|
-
|
264
|
-
|
265
|
-
dest = rb_call_super(1, &orig); /* copy all other ivars as-is */
|
261
|
+
rb_call_super(1, &orig); /* copy all other ivars as-is */
|
266
262
|
rb_ivar_set(dest, id_inotify_buf, rb_str_new(0, 128));
|
267
263
|
|
268
264
|
return dest;
|
@@ -284,36 +280,6 @@ static VALUE each(VALUE self)
|
|
284
280
|
return self;
|
285
281
|
}
|
286
282
|
|
287
|
-
#if defined(NOGVL_CLOSE)
|
288
|
-
static VALUE fptr_close(void *ptr)
|
289
|
-
{
|
290
|
-
rb_io_t *fptr = ptr;
|
291
|
-
return (VALUE)close(fptr->fd);
|
292
|
-
}
|
293
|
-
|
294
|
-
/*
|
295
|
-
* call-seq:
|
296
|
-
* ino.close -> nil
|
297
|
-
*
|
298
|
-
* Closes the underlying file descriptor and releases resources used by the
|
299
|
-
* kernel. Unlike other file descriptors, Inotify descriptors can take
|
300
|
-
* a long time to close(2). Calling this explicitly releases the GVL under
|
301
|
-
* Ruby 1.9
|
302
|
-
*/
|
303
|
-
static VALUE nogvl_close(VALUE self)
|
304
|
-
{
|
305
|
-
rb_io_t *fptr;
|
306
|
-
|
307
|
-
GetOpenFile(self, fptr);
|
308
|
-
|
309
|
-
if ((int)rb_sp_io_region(fptr_close, fptr) < 0)
|
310
|
-
rb_sys_fail("close(inotify)");
|
311
|
-
fptr->fd = -1;
|
312
|
-
|
313
|
-
return Qnil;
|
314
|
-
}
|
315
|
-
#endif /* NOGVL_CLOSE */
|
316
|
-
|
317
283
|
void sleepy_penguin_init_inotify(void)
|
318
284
|
{
|
319
285
|
VALUE mSleepyPenguin, cInotify;
|
@@ -346,9 +312,6 @@ void sleepy_penguin_init_inotify(void)
|
|
346
312
|
rb_define_method(cInotify, "initialize_copy", init_copy, 1);
|
347
313
|
rb_define_method(cInotify, "take", take, -1);
|
348
314
|
rb_define_method(cInotify, "each", each, 0);
|
349
|
-
#ifdef NOGVL_CLOSE
|
350
|
-
rb_define_method(cInotify, "close", nogvl_close, 0);
|
351
|
-
#endif
|
352
315
|
|
353
316
|
/*
|
354
317
|
* Document-class: SleepyPenguin::Inotify::Event
|
@@ -376,7 +339,6 @@ void sleepy_penguin_init_inotify(void)
|
|
376
339
|
cEvent = rb_define_class_under(cInotify, "Event", cEvent);
|
377
340
|
rb_define_method(cEvent, "events", events, 0);
|
378
341
|
rb_define_singleton_method(cInotify, "new", s_new, -1);
|
379
|
-
id_for_fd = rb_intern("for_fd");
|
380
342
|
id_inotify_buf = rb_intern("@inotify_buf");
|
381
343
|
id_inotify_tmp = rb_intern("@inotify_tmp");
|
382
344
|
id_mask = rb_intern("mask");
|
@@ -2,7 +2,7 @@
|
|
2
2
|
#include "sleepy_penguin.h"
|
3
3
|
#include <signal.h>
|
4
4
|
#include <sys/signalfd.h>
|
5
|
-
static ID
|
5
|
+
static ID id_list;
|
6
6
|
static VALUE ssi_members;
|
7
7
|
static VALUE cSigInfo;
|
8
8
|
|
@@ -123,7 +123,7 @@ static VALUE update_bang(int argc, VALUE *argv, VALUE self)
|
|
123
123
|
*/
|
124
124
|
static VALUE s_new(int argc, VALUE *argv, VALUE klass)
|
125
125
|
{
|
126
|
-
VALUE vmask, vflags;
|
126
|
+
VALUE vmask, vflags, rv;
|
127
127
|
sigset_t mask;
|
128
128
|
int flags;
|
129
129
|
int fd;
|
@@ -142,7 +142,9 @@ static VALUE s_new(int argc, VALUE *argv, VALUE klass)
|
|
142
142
|
rb_sys_fail("signalfd");
|
143
143
|
}
|
144
144
|
|
145
|
-
|
145
|
+
|
146
|
+
rv = INT2FIX(fd);
|
147
|
+
return rb_call_super(1, &rv);
|
146
148
|
}
|
147
149
|
|
148
150
|
static VALUE ssi_alloc(VALUE klass)
|
@@ -193,11 +195,11 @@ static VALUE sfd_take(int argc, VALUE *argv, VALUE self)
|
|
193
195
|
blocking_io_prepare(fd);
|
194
196
|
retry:
|
195
197
|
ssi->ssi_fd = fd;
|
196
|
-
r = (ssize_t)
|
198
|
+
r = (ssize_t)rb_sp_fd_region(sfd_read, ssi, fd);
|
197
199
|
if (r == -1) {
|
198
200
|
if (errno == EAGAIN && RTEST(nonblock))
|
199
201
|
return Qnil;
|
200
|
-
if (rb_io_wait_readable(fd))
|
202
|
+
if (rb_io_wait_readable(fd = rb_sp_fileno(self)))
|
201
203
|
goto retry;
|
202
204
|
rb_sys_fail("read(signalfd)");
|
203
205
|
}
|
@@ -238,6 +240,9 @@ void sleepy_penguin_init_signalfd(void)
|
|
238
240
|
/*
|
239
241
|
* Document-class: SleepyPenguin::SignalFD
|
240
242
|
*
|
243
|
+
* Use of this class is NOT recommended. Ruby itself has a great
|
244
|
+
* signal handling API and its implementation conflicts with this.
|
245
|
+
*
|
241
246
|
* A SignalFD is an IO object for accepting signals. It provides
|
242
247
|
* an alternative to Signal.trap that may be monitored using
|
243
248
|
* IO.select or Epoll.
|
@@ -290,7 +295,6 @@ void sleepy_penguin_init_signalfd(void)
|
|
290
295
|
|
291
296
|
rb_define_method(cSignalFD, "take", sfd_take, -1);
|
292
297
|
rb_define_method(cSignalFD, "update!", update_bang, -1);
|
293
|
-
id_for_fd = rb_intern("for_fd");
|
294
298
|
ssi_members = rb_ary_new();
|
295
299
|
|
296
300
|
NODOC_CONST(cSigInfo, "MEMBERS", ssi_members);
|
@@ -30,6 +30,13 @@ VALUE rb_sp_io_region(rb_blocking_function_t *func, void *data);
|
|
30
30
|
# define blocking_io_prepare(fd) rb_sp_set_nonblock((fd))
|
31
31
|
#endif
|
32
32
|
|
33
|
+
#ifdef HAVE_RB_THREAD_IO_BLOCKING_REGION
|
34
|
+
# define rb_sp_fd_region(fn,data,fd) \
|
35
|
+
rb_thread_io_blocking_region((fn),(data),(fd))
|
36
|
+
#else
|
37
|
+
# define rb_sp_fd_region(fn,data,fd) rb_sp_io_region(fn,data)
|
38
|
+
#endif
|
39
|
+
|
33
40
|
#define NODOC_CONST(klass,name,value) \
|
34
41
|
rb_define_const((klass),(name),(value))
|
35
42
|
#endif /* SLEEPY_PENGUIN_H */
|
@@ -2,7 +2,6 @@
|
|
2
2
|
#include "sleepy_penguin.h"
|
3
3
|
#include <sys/timerfd.h>
|
4
4
|
#include "value2timespec.h"
|
5
|
-
static ID id_for_fd;
|
6
5
|
|
7
6
|
/*
|
8
7
|
* call-seq:
|
@@ -22,7 +21,7 @@ static ID id_for_fd;
|
|
22
21
|
*/
|
23
22
|
static VALUE s_new(int argc, VALUE *argv, VALUE klass)
|
24
23
|
{
|
25
|
-
VALUE cid, fl;
|
24
|
+
VALUE cid, fl, rv;
|
26
25
|
int clockid, flags;
|
27
26
|
int fd;
|
28
27
|
|
@@ -40,7 +39,8 @@ static VALUE s_new(int argc, VALUE *argv, VALUE klass)
|
|
40
39
|
rb_sys_fail("timerfd_create");
|
41
40
|
}
|
42
41
|
|
43
|
-
|
42
|
+
rv = INT2FIX(fd);
|
43
|
+
return rb_call_super(1, &rv);
|
44
44
|
}
|
45
45
|
|
46
46
|
static VALUE itimerspec2ary(struct itimerspec *its)
|
@@ -125,11 +125,11 @@ static VALUE expirations(int argc, VALUE *argv, VALUE self)
|
|
125
125
|
else
|
126
126
|
blocking_io_prepare(fd);
|
127
127
|
retry:
|
128
|
-
r = (ssize_t)
|
128
|
+
r = (ssize_t)rb_sp_fd_region(tfd_read, &buf, fd);
|
129
129
|
if (r == -1) {
|
130
130
|
if (errno == EAGAIN && RTEST(nonblock))
|
131
131
|
return Qnil;
|
132
|
-
if (rb_io_wait_readable(fd))
|
132
|
+
if (rb_io_wait_readable(fd = rb_sp_fileno(self)))
|
133
133
|
goto retry;
|
134
134
|
rb_sys_fail("read(timerfd)");
|
135
135
|
}
|
@@ -165,6 +165,5 @@ void sleepy_penguin_init_timerfd(void)
|
|
165
165
|
rb_define_method(cTimerFD, "settime", settime, 3);
|
166
166
|
rb_define_method(cTimerFD, "gettime", gettime, 0);
|
167
167
|
rb_define_method(cTimerFD, "expirations", expirations, -1);
|
168
|
-
id_for_fd = rb_intern("for_fd");
|
169
168
|
}
|
170
169
|
#endif /* HAVE_SYS_TIMERFD_H */
|
data/ext/sleepy_penguin/util.c
CHANGED
@@ -110,12 +110,6 @@ int rb_sp_fileno(VALUE io)
|
|
110
110
|
{
|
111
111
|
rb_io_t *fptr;
|
112
112
|
|
113
|
-
switch (TYPE(io)) {
|
114
|
-
case T_FIXNUM: return FIX2INT(io);
|
115
|
-
case T_FILE:
|
116
|
-
GetOpenFile(io, fptr);
|
117
|
-
return FPTR_TO_FD(fptr);
|
118
|
-
}
|
119
113
|
io = rb_convert_type(io, T_FILE, "IO", "to_io");
|
120
114
|
GetOpenFile(io, fptr);
|
121
115
|
return FPTR_TO_FD(fptr);
|
data/pkg.mk
CHANGED
@@ -44,6 +44,8 @@ $(ext_dl): $(ext_src) $(ext_pfx_src) $(ext_pfx)/$(ext)/Makefile
|
|
44
44
|
$(MAKE) -C $(@D)
|
45
45
|
lib := $(lib):$(ext_pfx)/$(ext)
|
46
46
|
build: $(ext_dl)
|
47
|
+
else
|
48
|
+
build:
|
47
49
|
endif
|
48
50
|
|
49
51
|
pkg_extra += GIT-VERSION-FILE NEWS ChangeLog LATEST
|
@@ -67,7 +69,7 @@ doc:: .document .wrongdoc.yml $(pkg_extra)
|
|
67
69
|
$(RM) -r doc
|
68
70
|
$(WRONGDOC) all
|
69
71
|
install -m644 COPYING doc/COPYING
|
70
|
-
install -m644 $(shell grep '^[A-Z]' .document) doc/
|
72
|
+
install -m644 $(shell LC_ALL=C grep '^[A-Z]' .document) doc/
|
71
73
|
|
72
74
|
ifneq ($(VERSION),)
|
73
75
|
pkggem := pkg/$(rfpackage)-$(VERSION).gem
|
@@ -145,7 +147,7 @@ test_units := $(wildcard test/test_*.rb)
|
|
145
147
|
test: test-unit
|
146
148
|
test-unit: $(test_units)
|
147
149
|
$(test_units): build
|
148
|
-
$(RUBY) -I $(lib) $@
|
150
|
+
$(RUBY) -I $(lib) $@ $(RUBY_TEST_OPTS)
|
149
151
|
|
150
152
|
# this requires GNU coreutils variants
|
151
153
|
ifneq ($(RSYNC_DEST),)
|
@@ -165,5 +167,9 @@ doc_gz: docs = $(shell find doc -type f ! -regex '^.*\.\(gif\|jpg\|png\|gz\)$$')
|
|
165
167
|
doc_gz:
|
166
168
|
for i in $(docs); do \
|
167
169
|
gzip --rsyncable -9 < $$i > $$i.gz; touch -r $$i $$i.gz; done
|
170
|
+
check-warnings:
|
171
|
+
@(for i in $$(git ls-files '*.rb'| grep -v '^setup\.rb$$'); \
|
172
|
+
do $(RUBY) -d -W2 -c $$i; done) | grep -v '^Syntax OK$$' || :
|
168
173
|
|
169
174
|
.PHONY: all .FORCE-GIT-VERSION-FILE doc test $(test_units) manifest
|
175
|
+
.PHONY: check-warnings
|
data/test/test_epoll.rb
CHANGED
@@ -63,7 +63,7 @@ class TestEpoll < Test::Unit::TestCase
|
|
63
63
|
exit!(1)
|
64
64
|
}
|
65
65
|
res = Process.waitall
|
66
|
-
res.each { |(
|
66
|
+
res.each { |(_,status)| assert status.success? }
|
67
67
|
end
|
68
68
|
|
69
69
|
def test_tcp_connect_nonblock_edge
|
@@ -127,28 +127,33 @@ class TestEpoll < Test::Unit::TestCase
|
|
127
127
|
assert_equal 2, tmp.size
|
128
128
|
end
|
129
129
|
|
130
|
-
def
|
130
|
+
def test_signal_safe_wait_forever
|
131
131
|
time = {}
|
132
|
-
trap(:USR1)
|
132
|
+
trap(:USR1) do
|
133
|
+
time[:USR1] = Time.now
|
134
|
+
sleep 0.5
|
135
|
+
@wr.write '.'
|
136
|
+
end
|
133
137
|
@ep.add @rd, Epoll::IN
|
134
138
|
tmp = []
|
135
139
|
pid = fork do
|
136
|
-
sleep 0.
|
140
|
+
sleep 0.5 # slightly racy :<
|
137
141
|
Process.kill(:USR1, Process.ppid)
|
138
142
|
end
|
139
143
|
time[:START_WAIT] = Time.now
|
140
|
-
|
141
|
-
@ep.wait
|
142
|
-
|
143
|
-
|
144
|
+
assert_nothing_raised do
|
145
|
+
@ep.wait do |flags, obj|
|
146
|
+
tmp << [ flags, obj ]
|
147
|
+
time[:EP] = Time.now
|
148
|
+
end
|
144
149
|
end
|
145
150
|
assert_equal([[Epoll::IN, @rd]], tmp)
|
146
151
|
_, status = Process.waitpid2(pid)
|
147
|
-
assert status.success
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
+
assert status.success?, status.inspect
|
153
|
+
usr1_delay = time[:USR1] - time[:START_WAIT]
|
154
|
+
assert_in_delta(0.5, usr1_delay, 0.1, "usr1_delay=#{usr1_delay}")
|
155
|
+
ep_delay = time[:EP] - time[:USR1]
|
156
|
+
assert_in_delta(0.5, ep_delay, 0.1, "ep1_delay=#{ep_delay}")
|
152
157
|
ensure
|
153
158
|
trap(:USR1, 'DEFAULT')
|
154
159
|
end unless RBX
|
@@ -212,7 +217,7 @@ class TestEpoll < Test::Unit::TestCase
|
|
212
217
|
assert_nothing_raised do
|
213
218
|
4096.times do
|
214
219
|
ep = Epoll.new
|
215
|
-
|
220
|
+
assert_kind_of IO, ep.to_io
|
216
221
|
end
|
217
222
|
end
|
218
223
|
assert ! @ep.closed?
|
@@ -295,7 +300,6 @@ class TestEpoll < Test::Unit::TestCase
|
|
295
300
|
break
|
296
301
|
end while true
|
297
302
|
@ep.add(@wr, Epoll::OUT | Epoll::IN)
|
298
|
-
i = 0
|
299
303
|
assert_equal 0, @ep.wait(nil, 0) { |flags,event| assert false }
|
300
304
|
end
|
301
305
|
|
@@ -384,4 +388,13 @@ class TestEpoll < Test::Unit::TestCase
|
|
384
388
|
assert ! @ep.include?(@wr)
|
385
389
|
assert ! @ep.include?(@wr.fileno)
|
386
390
|
end
|
391
|
+
|
392
|
+
def test_cross_thread_close
|
393
|
+
tmp = []
|
394
|
+
thr = Thread.new { sleep(1); @ep.close }
|
395
|
+
assert_raises(IOError) do
|
396
|
+
@ep.wait { |flags, obj| tmp << [ flags, obj ] }
|
397
|
+
end
|
398
|
+
assert_nil thr.value
|
399
|
+
end if RUBY_VERSION == "1.9.3"
|
387
400
|
end
|
data/test/test_epoll_gc.rb
CHANGED
@@ -29,19 +29,19 @@ class TestEpollOptimizations < Test::Unit::TestCase
|
|
29
29
|
assert_nil err
|
30
30
|
lines = io.readlines; io.close
|
31
31
|
assert_equal 1, lines.grep(/^epoll_ctl/).size
|
32
|
-
assert_match
|
32
|
+
assert_match(/EPOLL_CTL_ADD/, lines.grep(/^epoll_ctl/)[0])
|
33
33
|
|
34
34
|
io, err = Strace.me { @ep.set(@wr, Epoll::OUT | Epoll::ONESHOT) }
|
35
35
|
assert_nil err
|
36
36
|
lines = io.readlines; io.close
|
37
37
|
assert_equal 1, lines.grep(/^epoll_ctl/).size
|
38
|
-
assert_match
|
38
|
+
assert_match(/EPOLL_CTL_MOD/, lines.grep(/^epoll_ctl/)[0])
|
39
39
|
|
40
40
|
io, err = Strace.me { @ep.set(@wr, Epoll::OUT) }
|
41
41
|
assert_nil err
|
42
42
|
lines = io.readlines; io.close
|
43
43
|
assert_equal 1, lines.grep(/^epoll_ctl/).size
|
44
|
-
assert_match
|
44
|
+
assert_match(/EPOLL_CTL_MOD/, lines.grep(/^epoll_ctl/)[0])
|
45
45
|
@wr.close
|
46
46
|
@rd.close
|
47
47
|
|
@@ -50,7 +50,7 @@ class TestEpollOptimizations < Test::Unit::TestCase
|
|
50
50
|
assert_nil err
|
51
51
|
lines = io.readlines; io.close
|
52
52
|
assert_equal 1, lines.grep(/^epoll_ctl/).size
|
53
|
-
assert_match
|
53
|
+
assert_match(/EPOLL_CTL_ADD/, lines.grep(/^epoll_ctl/)[0])
|
54
54
|
end
|
55
55
|
|
56
56
|
def test_delete
|
@@ -61,7 +61,7 @@ class TestEpollOptimizations < Test::Unit::TestCase
|
|
61
61
|
assert_nil err
|
62
62
|
lines = io.readlines; io.close
|
63
63
|
assert_equal 1, lines.grep(/^epoll_ctl/).size
|
64
|
-
assert_match
|
64
|
+
assert_match(%r{=\s+0$}, lines.grep(/^epoll_ctl/)[0])
|
65
65
|
|
66
66
|
rv = true
|
67
67
|
io, err = Strace.me { rv = @ep.delete(@wr) }
|
@@ -98,18 +98,6 @@ class TestEpollOptimizations < Test::Unit::TestCase
|
|
98
98
|
assert_equal 0, lines.grep(/^epoll_ctl/).size
|
99
99
|
end
|
100
100
|
|
101
|
-
def test_delete_closed_fileno
|
102
|
-
fileno = @wr.fileno
|
103
|
-
@ep.add(fileno, Epoll::OUT)
|
104
|
-
@wr.close
|
105
|
-
rv = nil
|
106
|
-
io, err = Strace.me { rv = @ep.delete(fileno) }
|
107
|
-
lines = io.readlines; io.close
|
108
|
-
assert_nil err
|
109
|
-
assert_equal fileno, rv
|
110
|
-
assert_equal 0, lines.grep(/^epoll_ctl/).size
|
111
|
-
end
|
112
|
-
|
113
101
|
def test_delete_aliased_a
|
114
102
|
tmp = IO.for_fd @wr.fileno
|
115
103
|
IO_PURGATORY << tmp
|
@@ -134,7 +122,7 @@ class TestEpollOptimizations < Test::Unit::TestCase
|
|
134
122
|
assert_equal tmp, rv
|
135
123
|
assert_nil err
|
136
124
|
assert_equal 1, lines.grep(/^epoll_ctl/).size
|
137
|
-
assert_match
|
125
|
+
assert_match(%r{=\s+0$}, lines.grep(/^epoll_ctl/)[0])
|
138
126
|
assert_equal 0, lines.grep(/ENOENT/).size
|
139
127
|
end
|
140
128
|
|
data/test/test_inotify.rb
CHANGED
@@ -36,7 +36,7 @@ class TestInotify < Test::Unit::TestCase
|
|
36
36
|
assert a.fileno != b.fileno
|
37
37
|
abuf = a.instance_variable_get(:@inotify_buf)
|
38
38
|
bbuf = b.instance_variable_get(:@inotify_buf)
|
39
|
-
assert abuf.object_id != bbuf.object_id
|
39
|
+
assert abuf.object_id != bbuf.object_id, "#{a.inspect} #{b.inspect}"
|
40
40
|
|
41
41
|
atmp = a.instance_variable_get(:@inotify_tmp)
|
42
42
|
btmp = b.instance_variable_get(:@inotify_tmp)
|
@@ -115,27 +115,4 @@ class TestInotify < Test::Unit::TestCase
|
|
115
115
|
end
|
116
116
|
assert_equal 0, nr
|
117
117
|
end
|
118
|
-
|
119
|
-
def test_close_threadable
|
120
|
-
ino = Inotify.new
|
121
|
-
tmp = []
|
122
|
-
thr = Thread.new do
|
123
|
-
until ino.closed?
|
124
|
-
tmp << Time.now
|
125
|
-
Thread.pass
|
126
|
-
end
|
127
|
-
end
|
128
|
-
t0 = Time.now
|
129
|
-
ino.close
|
130
|
-
t1 = Time.now
|
131
|
-
between = []
|
132
|
-
thr.join
|
133
|
-
tmp.each do |t|
|
134
|
-
if t > t0 && t < t1
|
135
|
-
between << t
|
136
|
-
end
|
137
|
-
end
|
138
|
-
assert tmp.size > 0, "tmp.size=#{tmp.size}"
|
139
|
-
assert between.size > 0, "between.size=#{between.size}"
|
140
|
-
end if RUBY_VERSION.to_f >= 1.9
|
141
118
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sleepy_penguin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 7
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
|
-
-
|
7
|
+
- 3
|
8
8
|
- 0
|
9
9
|
- 0
|
10
|
-
version:
|
10
|
+
version: 3.0.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- sleepy_penguin hackers
|
@@ -15,8 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
19
|
-
default_executable:
|
18
|
+
date: 2011-05-21 00:00:00 Z
|
20
19
|
dependencies:
|
21
20
|
- !ruby/object:Gem::Dependency
|
22
21
|
name: wrongdoc
|
@@ -66,9 +65,9 @@ extra_rdoc_files:
|
|
66
65
|
- lib/sleepy_penguin.rb
|
67
66
|
- lib/sleepy_penguin/signalfd/sig_info.rb
|
68
67
|
- lib/sleepy_penguin/sp.rb
|
69
|
-
- ext/sleepy_penguin/init.c
|
70
68
|
- ext/sleepy_penguin/epoll.c
|
71
69
|
- ext/sleepy_penguin/eventfd.c
|
70
|
+
- ext/sleepy_penguin/init.c
|
72
71
|
- ext/sleepy_penguin/inotify.c
|
73
72
|
- ext/sleepy_penguin/signalfd.c
|
74
73
|
- ext/sleepy_penguin/timerfd.c
|
@@ -95,6 +94,7 @@ files:
|
|
95
94
|
- ext/sleepy_penguin/inotify.c
|
96
95
|
- ext/sleepy_penguin/missing_epoll.h
|
97
96
|
- ext/sleepy_penguin/missing_inotify.h
|
97
|
+
- ext/sleepy_penguin/missing_rb_thread_fd_close.h
|
98
98
|
- ext/sleepy_penguin/signalfd.c
|
99
99
|
- ext/sleepy_penguin/sleepy_penguin.h
|
100
100
|
- ext/sleepy_penguin/timerfd.c
|
@@ -114,7 +114,6 @@ files:
|
|
114
114
|
- test/test_signalfd.rb
|
115
115
|
- test/test_signalfd_siginfo.rb
|
116
116
|
- test/test_timerfd.rb
|
117
|
-
has_rdoc: true
|
118
117
|
homepage: http://bogomips.org/sleepy_penguin/
|
119
118
|
licenses: []
|
120
119
|
|
@@ -148,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
148
147
|
requirements: []
|
149
148
|
|
150
149
|
rubyforge_project: rainbows
|
151
|
-
rubygems_version: 1.
|
150
|
+
rubygems_version: 1.8.2
|
152
151
|
signing_key:
|
153
152
|
specification_version: 3
|
154
153
|
summary: Linux I/O events for Ruby
|