sleepy_penguin 1.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 +9 -0
- data/.gitignore +20 -0
- data/COPYING +165 -0
- data/GIT-VERSION-GEN +40 -0
- data/GNUmakefile +192 -0
- data/LICENSE +18 -0
- data/README +62 -0
- data/Rakefile +167 -0
- data/TODO +3 -0
- data/ext/sleepy_penguin/epoll.c +563 -0
- data/ext/sleepy_penguin/eventfd.c +177 -0
- data/ext/sleepy_penguin/extconf.rb +13 -0
- data/ext/sleepy_penguin/init.c +20 -0
- data/ext/sleepy_penguin/nonblock.h +19 -0
- data/ext/sleepy_penguin/sleepy_penguin.h +44 -0
- data/ext/sleepy_penguin/timerfd.c +128 -0
- data/ext/sleepy_penguin/value2timespec.h +64 -0
- data/lib/sleepy_penguin.rb +7 -0
- data/setup.rb +1586 -0
- data/sleepy_penguin.gemspec +36 -0
- data/test/test_epoll.rb +320 -0
- data/test/test_eventfd.rb +48 -0
- data/test/test_timerfd.rb +38 -0
- metadata +108 -0
@@ -0,0 +1,177 @@
|
|
1
|
+
#ifdef HAVE_SYS_EVENTFD_H
|
2
|
+
#include "sleepy_penguin.h"
|
3
|
+
#include <sys/eventfd.h>
|
4
|
+
#include "nonblock.h"
|
5
|
+
static ID id_for_fd;
|
6
|
+
|
7
|
+
static VALUE create(int argc, VALUE *argv, VALUE klass)
|
8
|
+
{
|
9
|
+
VALUE _initval, _flags;
|
10
|
+
unsigned initval;
|
11
|
+
int flags = 0;
|
12
|
+
int fd;
|
13
|
+
|
14
|
+
rb_scan_args(argc, argv, "11", &_initval, &_flags);
|
15
|
+
initval = NUM2UINT(_initval);
|
16
|
+
flags = NIL_P(_flags) ? 0 : NUM2INT(_flags);
|
17
|
+
|
18
|
+
fd = eventfd(initval, flags);
|
19
|
+
if (fd == -1) {
|
20
|
+
if (errno == EMFILE || errno == ENFILE || errno == ENOMEM) {
|
21
|
+
rb_gc();
|
22
|
+
fd = eventfd(initval, flags);
|
23
|
+
}
|
24
|
+
if (fd == -1)
|
25
|
+
rb_sys_fail("eventfd");
|
26
|
+
}
|
27
|
+
|
28
|
+
return rb_funcall(klass, id_for_fd, 1, INT2NUM(fd));
|
29
|
+
}
|
30
|
+
|
31
|
+
#ifdef HAVE_RB_THREAD_BLOCKING_REGION
|
32
|
+
struct efd_args {
|
33
|
+
int fd;
|
34
|
+
uint64_t val;
|
35
|
+
};
|
36
|
+
|
37
|
+
static VALUE efd_write(void *_args)
|
38
|
+
{
|
39
|
+
struct efd_args *args = _args;
|
40
|
+
ssize_t w = write(args->fd, &args->buf, sizeof(uint64_t));
|
41
|
+
|
42
|
+
return (VALUE)w;
|
43
|
+
}
|
44
|
+
|
45
|
+
static VALUE efd_read(void *_args)
|
46
|
+
{
|
47
|
+
struct efd_args *args = _args;
|
48
|
+
ssize_t r = read(args->fd, &args->buf, sizeof(uint64_t));
|
49
|
+
|
50
|
+
return (VALUE)r;
|
51
|
+
}
|
52
|
+
|
53
|
+
static VALUE incr(VALUE self, VALUE value)
|
54
|
+
{
|
55
|
+
struct efd_args x;
|
56
|
+
ssize_t w;
|
57
|
+
|
58
|
+
x.fd = my_fileno(self);
|
59
|
+
x.val = (uint64_t)NUM2ULL(value);
|
60
|
+
|
61
|
+
retry:
|
62
|
+
w = (ssize_t)rb_thread_blocking_region(efd_write, &x, RUBY_UBF_IO, 0);
|
63
|
+
if (w == -1) {
|
64
|
+
if (rb_io_wait_writable(x.fd))
|
65
|
+
goto retry;
|
66
|
+
rb_sys_fail("write(eventfd)");
|
67
|
+
}
|
68
|
+
|
69
|
+
return Qnil;
|
70
|
+
}
|
71
|
+
|
72
|
+
static VALUE getvalue(VALUE self)
|
73
|
+
{
|
74
|
+
struct efd_args x;
|
75
|
+
|
76
|
+
x.fd = my_fileno(self);
|
77
|
+
|
78
|
+
retry:
|
79
|
+
w = (ssize_t)rb_thread_blocking_region(efd_read, &x, RUBY_UBF_IO, 0);
|
80
|
+
if (w == -1) {
|
81
|
+
if (rb_io_wait_readable(x.fd))
|
82
|
+
goto retry;
|
83
|
+
rb_sys_fail("read(eventfd)");
|
84
|
+
}
|
85
|
+
|
86
|
+
return ULL2NUM(x.buf);
|
87
|
+
}
|
88
|
+
#else /* !HAVE_RB_THREAD_BLOCKING_REGION */
|
89
|
+
|
90
|
+
static VALUE incr(VALUE self, VALUE value)
|
91
|
+
{
|
92
|
+
int fd = my_fileno(self);
|
93
|
+
uint64_t val = (uint64_t)NUM2ULL(value);
|
94
|
+
ssize_t w;
|
95
|
+
|
96
|
+
set_nonblock(fd);
|
97
|
+
retry:
|
98
|
+
w = write(fd, &val, sizeof(uint64_t));
|
99
|
+
if (w == -1) {
|
100
|
+
if (rb_io_wait_writable(fd))
|
101
|
+
goto retry;
|
102
|
+
rb_sys_fail("write(eventfd)");
|
103
|
+
}
|
104
|
+
|
105
|
+
return Qnil;
|
106
|
+
}
|
107
|
+
|
108
|
+
static VALUE getvalue(VALUE self)
|
109
|
+
{
|
110
|
+
int fd = my_fileno(self);
|
111
|
+
uint64_t val;
|
112
|
+
ssize_t r;
|
113
|
+
|
114
|
+
set_nonblock(fd);
|
115
|
+
retry:
|
116
|
+
r = read(fd, &val, sizeof(uint64_t));
|
117
|
+
if (r == -1) {
|
118
|
+
if (rb_io_wait_readable(fd))
|
119
|
+
goto retry;
|
120
|
+
rb_sys_fail("read(eventfd)");
|
121
|
+
}
|
122
|
+
|
123
|
+
return ULL2NUM(val);
|
124
|
+
}
|
125
|
+
#endif /* !HAVE_RB_THREAD_BLOCKING_REGION */
|
126
|
+
|
127
|
+
static VALUE getvalue_nonblock(VALUE self)
|
128
|
+
{
|
129
|
+
int fd = my_fileno(self);
|
130
|
+
uint64_t val;
|
131
|
+
ssize_t r;
|
132
|
+
|
133
|
+
set_nonblock(fd);
|
134
|
+
r = read(fd, &val, sizeof(uint64_t));
|
135
|
+
if (r == -1)
|
136
|
+
rb_sys_fail("read(eventfd)");
|
137
|
+
|
138
|
+
return ULL2NUM(val);
|
139
|
+
}
|
140
|
+
|
141
|
+
static VALUE incr_nonblock(VALUE self, VALUE value)
|
142
|
+
{
|
143
|
+
int fd = my_fileno(self);
|
144
|
+
uint64_t val = (uint64_t)NUM2ULL(value);
|
145
|
+
ssize_t w;
|
146
|
+
|
147
|
+
set_nonblock(fd);
|
148
|
+
w = write(fd, &val, sizeof(uint64_t));
|
149
|
+
if (w == -1)
|
150
|
+
rb_sys_fail("write(eventfd)");
|
151
|
+
|
152
|
+
return Qnil;
|
153
|
+
}
|
154
|
+
|
155
|
+
void sleepy_penguin_init_eventfd(void)
|
156
|
+
{
|
157
|
+
VALUE mSleepyPenguin, cEventFD;
|
158
|
+
|
159
|
+
mSleepyPenguin = rb_const_get(rb_cObject, rb_intern("SleepyPenguin"));
|
160
|
+
cEventFD = rb_define_class_under(mSleepyPenguin, "EventFD", rb_cIO);
|
161
|
+
rb_define_singleton_method(cEventFD, "new", create, -1);
|
162
|
+
#ifdef EFD_NONBLOCK
|
163
|
+
rb_define_const(cEventFD, "NONBLOCK", UINT2NUM(EFD_NONBLOCK));
|
164
|
+
#endif
|
165
|
+
#ifdef EFD_CLOEXEC
|
166
|
+
rb_define_const(cEventFD, "CLOEXEC", UINT2NUM(EFD_CLOEXEC));
|
167
|
+
#endif
|
168
|
+
#ifdef EFD_SEMAPHORE
|
169
|
+
rb_define_const(cEventFD, "SEMAPHORE", UINT2NUM(EFD_SEMAPHORE));
|
170
|
+
#endif
|
171
|
+
rb_define_method(cEventFD, "value", getvalue, 0);
|
172
|
+
rb_define_method(cEventFD, "incr", incr, 1);
|
173
|
+
rb_define_method(cEventFD, "value_nonblock", getvalue_nonblock, 0);
|
174
|
+
rb_define_method(cEventFD, "incr_nonblock", incr_nonblock, 1);
|
175
|
+
id_for_fd = rb_intern("for_fd");
|
176
|
+
}
|
177
|
+
#endif /* HAVE_SYS_EVENTFD_H */
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
have_header('sys/epoll.h') or abort 'sys/epoll.h not found'
|
3
|
+
have_header("pthread.h") or abort 'pthread.h not found'
|
4
|
+
have_header('sys/eventfd.h')
|
5
|
+
have_header('sys/signalfd.h')
|
6
|
+
have_header('sys/timerfd.h')
|
7
|
+
have_func('rb_memerror')
|
8
|
+
have_func('rb_io_close')
|
9
|
+
have_func('epoll_create1', %w(sys/epoll.h))
|
10
|
+
have_func('rb_thread_blocking_region')
|
11
|
+
have_library('pthread')
|
12
|
+
dir_config('sleepy_penguin')
|
13
|
+
create_makefile('sleepy_penguin_ext')
|
@@ -0,0 +1,20 @@
|
|
1
|
+
void sleepy_penguin_init_epoll(void);
|
2
|
+
|
3
|
+
#ifdef HAVE_SYS_TIMERFD_H
|
4
|
+
void sleepy_penguin_init_timerfd(void);
|
5
|
+
#else
|
6
|
+
# define sleepy_penguin_init_timerfd() if(0)
|
7
|
+
#endif
|
8
|
+
|
9
|
+
#ifdef HAVE_SYS_EVENTFD_H
|
10
|
+
void sleepy_penguin_init_eventfd(void);
|
11
|
+
#else
|
12
|
+
# define sleepy_penguin_init_eventfd() if(0)
|
13
|
+
#endif
|
14
|
+
|
15
|
+
void Init_sleepy_penguin_ext(void)
|
16
|
+
{
|
17
|
+
sleepy_penguin_init_epoll();
|
18
|
+
sleepy_penguin_init_timerfd();
|
19
|
+
sleepy_penguin_init_eventfd();
|
20
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#ifndef SLEEPY_PENGUIN_NONBLOCK_H
|
2
|
+
#define SLEEPY_PENGUIN_NONBLOCK_H
|
3
|
+
#include <unistd.h>
|
4
|
+
#include <fcntl.h>
|
5
|
+
#include <ruby.h>
|
6
|
+
static void set_nonblock(int fd)
|
7
|
+
{
|
8
|
+
int flags = fcntl(fd, F_GETFL);
|
9
|
+
|
10
|
+
if (flags == -1)
|
11
|
+
rb_sys_fail("fcntl(F_GETFL)");
|
12
|
+
if ((flags & O_NONBLOCK) == O_NONBLOCK)
|
13
|
+
return;
|
14
|
+
flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
15
|
+
if (flags == -1)
|
16
|
+
rb_sys_fail("fcntl(F_SETFL)");
|
17
|
+
}
|
18
|
+
|
19
|
+
#endif /* SLEEPY_PENGUIN_NONBLOCK_H */
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#ifndef SLEEPY_PENGUIN_H
|
2
|
+
#define SLEEPY_PENGUIN_H
|
3
|
+
|
4
|
+
#include <ruby.h>
|
5
|
+
#ifdef HAVE_RUBY_IO_H
|
6
|
+
# include <ruby/io.h>
|
7
|
+
#else
|
8
|
+
# include <rubyio.h>
|
9
|
+
#endif
|
10
|
+
#include <errno.h>
|
11
|
+
#include <fcntl.h>
|
12
|
+
#include <assert.h>
|
13
|
+
#include <unistd.h>
|
14
|
+
|
15
|
+
#if ! HAVE_RB_IO_T
|
16
|
+
# define rb_io_t OpenFile
|
17
|
+
#endif
|
18
|
+
|
19
|
+
#ifdef GetReadFile
|
20
|
+
# define FPTR_TO_FD(fptr) (fileno(GetReadFile(fptr)))
|
21
|
+
#else
|
22
|
+
# if !HAVE_RB_IO_T || (RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8)
|
23
|
+
# define FPTR_TO_FD(fptr) fileno(fptr->f)
|
24
|
+
# else
|
25
|
+
# define FPTR_TO_FD(fptr) fptr->fd
|
26
|
+
# endif
|
27
|
+
#endif
|
28
|
+
|
29
|
+
static int my_fileno(VALUE io)
|
30
|
+
{
|
31
|
+
rb_io_t *fptr;
|
32
|
+
|
33
|
+
switch (TYPE(io)) {
|
34
|
+
case T_FIXNUM: return NUM2INT(io);
|
35
|
+
case T_FILE:
|
36
|
+
GetOpenFile(io, fptr);
|
37
|
+
return FPTR_TO_FD(fptr);
|
38
|
+
}
|
39
|
+
io = rb_convert_type(io, T_FILE, "IO", "to_io");
|
40
|
+
GetOpenFile(io, fptr);
|
41
|
+
return FPTR_TO_FD(fptr);
|
42
|
+
}
|
43
|
+
|
44
|
+
#endif /* SLEEPY_PENGUIN_H */
|
@@ -0,0 +1,128 @@
|
|
1
|
+
#ifdef HAVE_SYS_TIMERFD_H
|
2
|
+
#include "sleepy_penguin.h"
|
3
|
+
#include <sys/timerfd.h>
|
4
|
+
#include "value2timespec.h"
|
5
|
+
static ID id_for_fd;
|
6
|
+
|
7
|
+
static VALUE create(int argc, VALUE *argv, VALUE klass)
|
8
|
+
{
|
9
|
+
VALUE cid, fl;
|
10
|
+
int clockid, flags = 0;
|
11
|
+
int fd;
|
12
|
+
|
13
|
+
rb_scan_args(argc, argv, "02", &cid, &fl);
|
14
|
+
clockid = NIL_P(cid) ? CLOCK_MONOTONIC : NUM2INT(cid);
|
15
|
+
flags = NIL_P(fl) ? 0 : NUM2INT(fl);
|
16
|
+
|
17
|
+
fd = timerfd_create(clockid, flags);
|
18
|
+
if (fd == -1) {
|
19
|
+
if (errno == EMFILE || errno == ENFILE || errno == ENOMEM) {
|
20
|
+
rb_gc();
|
21
|
+
fd = timerfd_create(clockid, flags);
|
22
|
+
}
|
23
|
+
if (fd == -1)
|
24
|
+
rb_sys_fail("timerfd_create");
|
25
|
+
}
|
26
|
+
|
27
|
+
return rb_funcall(klass, id_for_fd, 1, INT2NUM(fd));
|
28
|
+
}
|
29
|
+
|
30
|
+
static VALUE itimerspec2ary(struct itimerspec *its)
|
31
|
+
{
|
32
|
+
VALUE interval = timespec2num(&its->it_interval);
|
33
|
+
VALUE value = timespec2num(&its->it_value);
|
34
|
+
|
35
|
+
return rb_ary_new3(2, interval, value);
|
36
|
+
}
|
37
|
+
|
38
|
+
static VALUE settime(VALUE self, VALUE fl, VALUE interval, VALUE value)
|
39
|
+
{
|
40
|
+
int fd = my_fileno(self);
|
41
|
+
int flags = NUM2INT(fl);
|
42
|
+
struct itimerspec old, new;
|
43
|
+
|
44
|
+
value2timespec(&new.it_interval, interval);
|
45
|
+
value2timespec(&new.it_value, value);
|
46
|
+
|
47
|
+
if (timerfd_settime(fd, flags, &new, &old) == -1)
|
48
|
+
rb_sys_fail("timerfd_settime");
|
49
|
+
|
50
|
+
return itimerspec2ary(&old);
|
51
|
+
}
|
52
|
+
|
53
|
+
static VALUE gettime(VALUE self)
|
54
|
+
{
|
55
|
+
int fd = my_fileno(self);
|
56
|
+
struct itimerspec curr;
|
57
|
+
|
58
|
+
if (timerfd_gettime(fd, &curr) == -1)
|
59
|
+
rb_sys_fail("timerfd_gettime");
|
60
|
+
|
61
|
+
return itimerspec2ary(&curr);
|
62
|
+
}
|
63
|
+
|
64
|
+
#ifdef HAVE_RB_THREAD_BLOCKING_REGION
|
65
|
+
static VALUE tfd_read(void *args)
|
66
|
+
{
|
67
|
+
uint64_t *buf = args;
|
68
|
+
int fd = (int)(*buf);
|
69
|
+
ssize_t r = read(fd, buf, sizeof(uint64_t));
|
70
|
+
|
71
|
+
return (VALUE)r;
|
72
|
+
}
|
73
|
+
|
74
|
+
static VALUE expirations(VALUE self)
|
75
|
+
{
|
76
|
+
ssize_t r;
|
77
|
+
uint64_t buf = (int)my_fileno(self);
|
78
|
+
|
79
|
+
r = (VALUE)rb_thread_blocking_region(tfd_read, &buf, RUBY_UBF_IO, 0);
|
80
|
+
if (r == -1)
|
81
|
+
rb_sys_fail("read(timerfd)");
|
82
|
+
|
83
|
+
return ULL2NUM(buf);
|
84
|
+
}
|
85
|
+
#else /* ! HAVE_RB_THREAD_BLOCKING_REGION */
|
86
|
+
#include "nonblock.h"
|
87
|
+
static VALUE expirations(VALUE self)
|
88
|
+
{
|
89
|
+
int fd = my_fileno(self);
|
90
|
+
uint64_t buf;
|
91
|
+
ssize_t r;
|
92
|
+
|
93
|
+
set_nonblock(fd);
|
94
|
+
retry:
|
95
|
+
r = read(fd, &buf, sizeof(uint64_t));
|
96
|
+
if (r == -1) {
|
97
|
+
if (rb_io_wait_readable(fd))
|
98
|
+
goto retry;
|
99
|
+
rb_sys_fail("read(timerfd)");
|
100
|
+
}
|
101
|
+
|
102
|
+
return ULL2NUM(buf);
|
103
|
+
}
|
104
|
+
#endif
|
105
|
+
|
106
|
+
void sleepy_penguin_init_timerfd(void)
|
107
|
+
{
|
108
|
+
VALUE mSleepyPenguin, cTimerFD;
|
109
|
+
|
110
|
+
mSleepyPenguin = rb_const_get(rb_cObject, rb_intern("SleepyPenguin"));
|
111
|
+
cTimerFD = rb_define_class_under(mSleepyPenguin, "TimerFD", rb_cIO);
|
112
|
+
rb_define_singleton_method(cTimerFD, "create", create, -1);
|
113
|
+
rb_define_singleton_method(cTimerFD, "new", create, -1);
|
114
|
+
rb_define_const(cTimerFD, "REALTIME", UINT2NUM(CLOCK_REALTIME));
|
115
|
+
rb_define_const(cTimerFD, "MONOTONIC", UINT2NUM(CLOCK_MONOTONIC));
|
116
|
+
rb_define_const(cTimerFD, "ABSTIME", UINT2NUM(TFD_TIMER_ABSTIME));
|
117
|
+
#ifdef TFD_NONBLOCK
|
118
|
+
rb_define_const(cTimerFD, "NONBLOCK", UINT2NUM(TFD_NONBLOCK));
|
119
|
+
#endif
|
120
|
+
#ifdef TFD_CLOEXEC
|
121
|
+
rb_define_const(cTimerFD, "CLOEXEC", UINT2NUM(TFD_CLOEXEC));
|
122
|
+
#endif
|
123
|
+
|
124
|
+
rb_define_method(cTimerFD, "settime", settime, 3);
|
125
|
+
rb_define_method(cTimerFD, "expirations", expirations, 0);
|
126
|
+
id_for_fd = rb_intern("for_fd");
|
127
|
+
}
|
128
|
+
#endif /* HAVE_SYS_TIMERFD_H */
|
@@ -0,0 +1,64 @@
|
|
1
|
+
#ifndef VALUE2TIMESPEC_H
|
2
|
+
#define VALUE2TIMESPEC_H
|
3
|
+
|
4
|
+
#include <ruby.h>
|
5
|
+
#include <math.h>
|
6
|
+
#include <time.h>
|
7
|
+
|
8
|
+
#ifndef NUM2TIMET
|
9
|
+
# define NUM2TIMET(n) NUM2LONG(n)
|
10
|
+
#endif
|
11
|
+
|
12
|
+
#ifndef RFLOAT_VALUE
|
13
|
+
# define RFLOAT_VALUE(v) (RFLOAT(v)->value)
|
14
|
+
#endif
|
15
|
+
|
16
|
+
static struct timespec *value2timespec(struct timespec *ts, VALUE num)
|
17
|
+
{
|
18
|
+
switch (TYPE(num)) {
|
19
|
+
case T_FIXNUM:
|
20
|
+
case T_BIGNUM:
|
21
|
+
ts->tv_sec = NUM2TIMET(num);
|
22
|
+
ts->tv_nsec = 0;
|
23
|
+
return ts;
|
24
|
+
case T_FLOAT: {
|
25
|
+
double orig = RFLOAT_VALUE(num);
|
26
|
+
double f, d;
|
27
|
+
|
28
|
+
d = modf(orig, &f);
|
29
|
+
if (d >= 0) {
|
30
|
+
ts->tv_nsec = (long)(d * 1e9 + 0.5);
|
31
|
+
} else {
|
32
|
+
ts->tv_nsec = (long)(-d * 1e9 + 0.5);
|
33
|
+
if (ts->tv_nsec > 0) {
|
34
|
+
ts->tv_nsec = (long)1e9 - ts->tv_nsec;
|
35
|
+
f -= 1;
|
36
|
+
}
|
37
|
+
}
|
38
|
+
ts->tv_sec = (time_t)f;
|
39
|
+
if (f != ts->tv_sec)
|
40
|
+
rb_raise(rb_eRangeError, "%f out of range", orig);
|
41
|
+
return ts;
|
42
|
+
}}
|
43
|
+
{
|
44
|
+
VALUE tmp = rb_inspect(num);
|
45
|
+
rb_raise(rb_eTypeError, "can't convert %s into timespec",
|
46
|
+
StringValuePtr(tmp));
|
47
|
+
}
|
48
|
+
rb_bug("rb_raise() failed, timespec failed");
|
49
|
+
return NULL;
|
50
|
+
}
|
51
|
+
|
52
|
+
#ifndef TIMET2NUM
|
53
|
+
# define TIMET2NUM(n) LONG2NUM(n)
|
54
|
+
#endif
|
55
|
+
|
56
|
+
static VALUE timespec2num(struct timespec *ts)
|
57
|
+
{
|
58
|
+
if (ts->tv_nsec == 0)
|
59
|
+
return TIMET2NUM(ts->tv_sec);
|
60
|
+
|
61
|
+
return rb_float_new(ts->tv_sec + ((double)ts->tv_nsec / 1e9));
|
62
|
+
}
|
63
|
+
|
64
|
+
#endif /* VALUE2TIMESPEC_H */
|