poll: deal with pollset changes on EINTR
[kgio.git] / ext / kgio / poll.c
blob2f101d13fcb35cebd80efe7665aeef05a2ace989
1 #include "kgio.h"
2 #if defined(USE_KGIO_POLL)
3 #include <time.h>
4 #include "broken_system_compat.h"
5 #include <poll.h>
6 #include <errno.h>
7 #ifdef HAVE_RUBY_ST_H
8 # include <ruby/st.h>
9 #else
10 # include <st.h>
11 #endif
13 static VALUE sym_wait_readable, sym_wait_writable;
14 static ID id_clear;
16 struct poll_args {
17 struct pollfd *fds;
18 nfds_t nfds;
19 int timeout;
20 VALUE ios;
21 st_table *fd_to_io;
22 struct timespec start;
25 static int interrupted(void)
27 switch (errno) {
28 case EINTR:
29 #ifdef ERESTART
30 case ERESTART:
31 #endif
32 return 1;
34 return 0;
37 static int retryable(struct poll_args *a)
39 struct timespec ts;
41 if (a->timeout < 0)
42 return 1;
43 if (a->timeout == 0)
44 return 0;
46 clock_gettime(hopefully_CLOCK_MONOTONIC, &ts);
48 ts.tv_sec -= a->start.tv_sec;
49 ts.tv_nsec -= a->start.tv_nsec;
50 if (ts.tv_nsec < 0) {
51 ts.tv_sec--;
52 ts.tv_nsec += 1000000000;
54 a->timeout -= ts.tv_sec * 1000;
55 a->timeout -= ts.tv_nsec / 1000000;
56 return (a->timeout >= 0);
59 static int num2timeout(VALUE timeout)
61 switch (TYPE(timeout)) {
62 case T_NIL: return -1;
63 case T_FIXNUM: return FIX2INT(timeout);
64 case T_BIGNUM: return NUM2INT(timeout);
66 rb_raise(rb_eTypeError, "timeout must be integer or nil");
67 return 0;
70 static VALUE poll_free(VALUE args)
72 struct poll_args *a = (struct poll_args *)args;
74 if (a->fds)
75 xfree(a->fds);
76 if (a->fd_to_io)
77 st_free_table(a->fd_to_io);
79 return Qnil;
82 static short value2events(VALUE event)
84 if (event == sym_wait_readable) return POLLIN;
85 if (event == sym_wait_writable) return POLLOUT;
86 if (TYPE(event) == T_FIXNUM) return (short)FIX2INT(event);
87 rb_raise(rb_eArgError, "unrecognized event");
90 static int io_to_pollfd_i(VALUE key, VALUE value, VALUE args)
92 struct poll_args *a = (struct poll_args *)args;
93 struct pollfd *pollfd = &a->fds[a->nfds++];
95 pollfd->fd = my_fileno(key);
96 pollfd->events = value2events(value);
97 st_insert(a->fd_to_io, (st_data_t)pollfd->fd, (st_data_t)key);
98 return ST_CONTINUE;
101 static void hash2pollfds(struct poll_args *a)
103 a->nfds = 0;
104 a->fds = xmalloc(sizeof(struct pollfd) * RHASH_SIZE(a->ios));
105 a->fd_to_io = st_init_numtable();
106 rb_hash_foreach(a->ios, io_to_pollfd_i, (VALUE)a);
109 static VALUE nogvl_poll(void *ptr)
111 struct poll_args *a = ptr;
113 if (a->timeout > 0)
114 clock_gettime(hopefully_CLOCK_MONOTONIC, &a->start);
116 return (VALUE)poll(a->fds, a->nfds, a->timeout);
119 static VALUE poll_result(int nr, struct poll_args *a)
121 struct pollfd *fds = a->fds;
122 VALUE io;
123 int rc;
125 if ((nfds_t)nr != a->nfds)
126 rb_funcall(a->ios, id_clear, 0);
127 for (; nr > 0; fds++) {
128 if (fds->revents == 0)
129 continue;
130 --nr;
131 rc = st_lookup(a->fd_to_io, (st_data_t)fds->fd, &io);
132 assert(rc == 1 && "fd => IO mapping failed");
133 rb_hash_aset(a->ios, io, INT2FIX((int)fds->revents));
135 return a->ios;
138 static VALUE do_poll(VALUE args)
140 struct poll_args *a = (struct poll_args *)args;
141 int nr;
143 Check_Type(a->ios, T_HASH);
145 retry:
146 hash2pollfds(a);
147 nr = (int)rb_thread_blocking_region(nogvl_poll, a, RUBY_UBF_IO, NULL);
148 if (nr < 0) {
149 if (interrupted()) {
150 if (retryable(a)) {
151 poll_free(args);
152 goto retry;
154 return Qnil;
156 rb_sys_fail("poll");
158 if (nr == 0) return Qnil;
160 return poll_result(nr, a);
164 * call-seq:
166 * Kgio.poll({ $stdin => :wait_readable }, 100) -> hash or nil
167 * Kgio.poll({ $stdin => Kgio::POLLIN }, 100) -> hash or nil
169 * Accepts an input hash with IO objects to wait for as the key and
170 * the events to wait for as its value. The events may either be
171 * +:wait_readable+ or +:wait_writable+ symbols or a Fixnum mask of
172 * Kgio::POLL* constants:
174 * Kgio::POLLIN - there is data to read
175 * Kgio::POLLPRI - there is urgent data to read
176 * Kgio::POLLOUT - writing will not block
177 * Kgio::POLLRDHUP - peer has shutdown writes (Linux 2.6.17+ only)
179 * Timeout is specified in Integer milliseconds just like the underlying
180 * poll(2), not in seconds like IO.select. A nil timeout means to wait
181 * forever. It must be an Integer or nil.
183 * Kgio.poll modifies and returns its input hash on success with the
184 * IO-like object as the key and an Integer mask of events as the hash
185 * value. It can return any of the events specified in the input
186 * above, along with the following events:
188 * Kgio::POLLERR - error condition occurred on the descriptor
189 * Kgio::POLLHUP - hang up
190 * Kgio::POLLNVAL - invalid request (bad file descriptor)
192 * This method is only available under Ruby 1.9 or any other
193 * implementations that uses native threads and rb_thread_blocking_region()
195 static VALUE s_poll(int argc, VALUE *argv, VALUE self)
197 VALUE timeout;
198 struct poll_args a;
200 rb_scan_args(argc, argv, "11", &a.ios, &timeout);
201 a.timeout = num2timeout(timeout);
202 a.fds = NULL;
203 a.fd_to_io = NULL;
205 return rb_ensure(do_poll, (VALUE)&a, poll_free, (VALUE)&a);
208 void init_kgio_poll(void)
210 VALUE mKgio = rb_define_module("Kgio");
212 if (check_clock() < 0)
213 return;
214 rb_define_singleton_method(mKgio, "poll", s_poll, -1);
216 sym_wait_readable = ID2SYM(rb_intern("wait_readable"));
217 sym_wait_writable = ID2SYM(rb_intern("wait_writable"));
218 id_clear = rb_intern("clear");
220 #define c(x) rb_define_const(mKgio,#x,INT2NUM((int)x))
222 /* standard types */
224 c(POLLIN);
225 c(POLLPRI);
226 c(POLLOUT);
228 #ifdef POLLRDHUP
229 c(POLLRDHUP);
230 #endif
232 /* outputs */
233 c(POLLERR);
234 c(POLLHUP);
235 c(POLLNVAL);
237 #else /* ! USE_KGIO_POLL */
238 void init_kgio_poll(void)
241 #endif /* ! USE_KGIO_POLL */