sleepy_penguin 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 */