sleepy_penguin 2.0.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|