1 #ifdef HAVE_SYS_TIMERFD_H
2 #include "sleepy_penguin.h"
3 #include <sys/timerfd.h>
4 #include "value2timespec.h"
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
)
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
);
34 if (rb_sp_gc_for_fd(errno
))
35 fd
= timerfd_create(clockid
, flags
);
37 rb_sys_fail("timerfd_create");
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
);
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
);
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
)
100 ssize_t r
= read(fd
, buf
, sizeof(uint64_t));
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
)
116 int fd
= rb_sp_fileno(self
);
117 uint64_t buf
= (uint64_t)fd
;
120 rb_scan_args(argc
, argv
, "01", &nonblock
);
122 rb_sp_set_nonblock(fd
);
124 r
= (ssize_t
)rb_sp_fd_region(tfd_read
, &buf
, fd
);
126 if (errno
== EAGAIN
&& RTEST(nonblock
))
128 if (rb_sp_wait(rb_io_wait_readable
, self
, &fd
))
130 rb_sys_fail("read(timerfd)");
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
));
161 NODOC_CONST(cTimerFD
, "NONBLOCK", UINT2NUM(TFD_NONBLOCK
));
164 NODOC_CONST(cTimerFD
, "CLOEXEC", UINT2NUM(TFD_CLOEXEC
));
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 */