kqueue: modernize Struct usage for Ruby 2.5.0dev
[sleepy_penguin.git] / ext / sleepy_penguin / timerfd.c
blob7957802585767731df93c9dd1f0322cde2ffbfc5
1 #ifdef HAVE_SYS_TIMERFD_H
2 #include "sleepy_penguin.h"
3 #include <sys/timerfd.h>
4 #include "value2timespec.h"
6 /*
7 * call-seq:
8 * TimerFD.new([clockid[, flags]]) -> TimerFD IO object
10 * Creates a new timer as an IO object.
12 * If set +clockid+ must be be one of the following:
13 * - :REALTIME - use the settable clock
14 * - :MONOTONIC - use the non-settable clock unaffected by manual changes
16 * +clockid+ defaults to :MONOTONIC if unspecified
17 * +flags+ may be any or none of the following:
19 * - :CLOEXEC - set the close-on-exec flag on the new object
20 * - :NONBLOCK - set the non-blocking I/O flag on the new object
22 static VALUE s_new(int argc, VALUE *argv, VALUE klass)
24 VALUE cid, fl, rv;
25 int clockid, flags;
26 int fd;
28 rb_scan_args(argc, argv, "02", &cid, &fl);
29 clockid = NIL_P(cid) ? CLOCK_MONOTONIC : rb_sp_get_flags(klass, cid, 0);
30 flags = rb_sp_get_flags(klass, fl, RB_SP_CLOEXEC(TFD_CLOEXEC));
32 fd = timerfd_create(clockid, flags);
33 if (fd < 0) {
34 if (rb_sp_gc_for_fd(errno))
35 fd = timerfd_create(clockid, flags);
36 if (fd < 0)
37 rb_sys_fail("timerfd_create");
40 rv = INT2FIX(fd);
41 return rb_call_super(1, &rv);
44 static VALUE itimerspec2ary(struct itimerspec *its)
46 VALUE interval = timespec2num(&its->it_interval);
47 VALUE value = timespec2num(&its->it_value);
49 return rb_ary_new3(2, interval, value);
53 * call-seq:
54 * tfd.settime(flags, interval, value) -> [ old_interval, old_value ]
56 * Arms (starts) or disarms (stops) the timer referred by the TimerFD object
57 * and returns the old value of the timer.
59 * +flags+ is either zero (or nil) to start a relative timer or :ABSTIME
60 * to start an absolute timer. If the +interval+ is zero, the timer fires
61 * only once, otherwise the timer is fired every +interval+ seconds.
62 * +value+ is the time of the initial expiration in seconds.
64 static VALUE settime(VALUE self, VALUE fl, VALUE interval, VALUE value)
66 int fd = rb_sp_fileno(self);
67 int flags = rb_sp_get_flags(self, fl, 0);
68 struct itimerspec old, new;
70 value2timespec(&new.it_interval, interval);
71 value2timespec(&new.it_value, value);
73 if (timerfd_settime(fd, flags, &new, &old) < 0)
74 rb_sys_fail("timerfd_settime");
76 return itimerspec2ary(&old);
80 * call-seq:
81 * tfd#gettime -> [ interval, value ]
83 * Returns the current +interval+ and +value+ of the timer as an Array.
85 static VALUE gettime(VALUE self)
87 int fd = rb_sp_fileno(self);
88 struct itimerspec curr;
90 if (timerfd_gettime(fd, &curr) < 0)
91 rb_sys_fail("timerfd_gettime");
93 return itimerspec2ary(&curr);
96 static VALUE tfd_read(void *args)
98 uint64_t *buf = args;
99 int fd = (int)(*buf);
100 ssize_t r = read(fd, buf, sizeof(uint64_t));
102 return (VALUE)r;
106 * call-seq:
107 * tfd.expirations([nonblock]) -> Integer
109 * Returns the number of expirations that have occurred. This will block
110 * if no expirations have occurred at the time of the call. Returns +nil+
111 * if +nonblock+ is passed and is +true+
113 static VALUE expirations(int argc, VALUE *argv, VALUE self)
115 ssize_t r;
116 int fd = rb_sp_fileno(self);
117 uint64_t buf = (uint64_t)fd;
118 VALUE nonblock;
120 rb_scan_args(argc, argv, "01", &nonblock);
121 if (RTEST(nonblock))
122 rb_sp_set_nonblock(fd);
123 retry:
124 r = (ssize_t)rb_sp_fd_region(tfd_read, &buf, fd);
125 if (r < 0) {
126 if (errno == EAGAIN && RTEST(nonblock))
127 return Qnil;
128 if (rb_sp_wait(rb_io_wait_readable, self, &fd))
129 goto retry;
130 rb_sys_fail("read(timerfd)");
133 return ULL2NUM(buf);
136 void sleepy_penguin_init_timerfd(void)
138 VALUE mSleepyPenguin, cTimerFD;
140 mSleepyPenguin = rb_define_module("SleepyPenguin");
143 * Document-class: SleepyPenguin::TimerFD
145 * TimerFD exposes kernel timers as IO objects that may be monitored
146 * by IO.select or Epoll. IO#close disarms the timers and returns
147 * resources back to the kernel.
149 cTimerFD = rb_define_class_under(mSleepyPenguin, "TimerFD", rb_cIO);
150 rb_define_singleton_method(cTimerFD, "new", s_new, -1);
152 NODOC_CONST(cTimerFD, "REALTIME", UINT2NUM(CLOCK_REALTIME));
153 NODOC_CONST(cTimerFD, "MONOTONIC", UINT2NUM(CLOCK_MONOTONIC));
154 NODOC_CONST(cTimerFD, "ABSTIME", UINT2NUM(TFD_TIMER_ABSTIME));
155 #ifdef TFD_TIMER_CANCEL_ON_SET
156 NODOC_CONST(cTimerFD, "CANCEL_ON_SET",
157 UINT2NUM(TFD_TIMER_CANCEL_ON_SET));
158 #endif
160 #ifdef TFD_NONBLOCK
161 NODOC_CONST(cTimerFD, "NONBLOCK", UINT2NUM(TFD_NONBLOCK));
162 #endif
163 #ifdef TFD_CLOEXEC
164 NODOC_CONST(cTimerFD, "CLOEXEC", UINT2NUM(TFD_CLOEXEC));
165 #endif
167 rb_define_method(cTimerFD, "settime", settime, 3);
168 rb_define_method(cTimerFD, "gettime", gettime, 0);
169 rb_define_method(cTimerFD, "expirations", expirations, -1);
171 #endif /* HAVE_SYS_TIMERFD_H */