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.
@@ -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 */