accept: prepare optional flags argument to accept() wrappers
[kgio.git] / ext / kgio / accept.c
blobb8efba5a2baddde885170e8d5c1e3d81c08812bd
1 #include "kgio.h"
2 #include "missing_accept4.h"
3 #include "sock_for_fd.h"
5 static VALUE localhost;
6 static VALUE cClientSocket;
7 static VALUE cKgio_Socket;
8 static VALUE mSocketMethods;
9 static VALUE iv_kgio_addr;
11 #if defined(__linux__)
12 static int accept4_flags = SOCK_CLOEXEC;
13 #else /* ! linux */
14 static int accept4_flags = SOCK_CLOEXEC | SOCK_NONBLOCK;
15 #endif /* ! linux */
17 struct accept_args {
18 int fd;
19 int flags;
20 struct sockaddr *addr;
21 socklen_t *addrlen;
25 * Sets the default class for newly accepted sockets. This is
26 * legacy behavior, kgio_accept and kgio_tryaccept now take optional
27 * class arguments to override this value.
29 static VALUE set_accepted(VALUE klass, VALUE aclass)
31 VALUE tmp;
33 if (NIL_P(aclass))
34 aclass = cKgio_Socket;
36 tmp = rb_funcall(aclass, rb_intern("included_modules"), 0, 0);
37 tmp = rb_funcall(tmp, rb_intern("include?"), 1, mSocketMethods);
39 if (tmp != Qtrue)
40 rb_raise(rb_eTypeError,
41 "class must include Kgio::SocketMethods");
43 cClientSocket = aclass;
45 return aclass;
49 * Returns the default class for newly accepted sockets when kgio_accept
50 * or kgio_tryaccept are not passed arguments
52 static VALUE get_accepted(VALUE klass)
54 return cClientSocket;
57 static VALUE xaccept(void *ptr)
59 struct accept_args *a = ptr;
60 int rv;
62 rv = accept_fn(a->fd, a->addr, a->addrlen, a->flags);
63 if (rv == -1 && errno == ENOSYS && accept_fn != my_accept4) {
64 accept_fn = my_accept4;
65 rv = accept_fn(a->fd, a->addr, a->addrlen, a->flags);
68 return (VALUE)rv;
71 #ifdef HAVE_RB_THREAD_BLOCKING_REGION
72 # include <time.h>
73 # include "blocking_io_region.h"
75 * Try to use a (real) blocking accept() since that can prevent
76 * thundering herds under Linux:
77 * http://www.citi.umich.edu/projects/linux-scalability/reports/accept.html
79 * So we periodically disable non-blocking, but not too frequently
80 * because other processes may set non-blocking (especially during
81 * a process upgrade) with Rainbows! concurrency model changes.
83 static int thread_accept(struct accept_args *a, int force_nonblock)
85 if (force_nonblock)
86 set_nonblocking(a->fd);
87 return (int)rb_thread_io_blocking_region(xaccept, a, a->fd);
90 static void set_blocking_or_block(int fd)
92 static time_t last_set_blocking;
93 time_t now = time(NULL);
95 if (last_set_blocking == 0) {
96 last_set_blocking = now;
97 (void)rb_io_wait_readable(fd);
98 } else if ((now - last_set_blocking) <= 5) {
99 (void)rb_io_wait_readable(fd);
100 } else {
101 int flags = fcntl(fd, F_GETFL);
102 if (flags == -1)
103 rb_sys_fail("fcntl(F_GETFL)");
104 if (flags & O_NONBLOCK) {
105 flags = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
106 if (flags == -1)
107 rb_sys_fail("fcntl(F_SETFL)");
109 last_set_blocking = now;
112 #else /* ! HAVE_RB_THREAD_BLOCKING_REGION */
113 # include <rubysig.h>
114 static int thread_accept(struct accept_args *a, int force_nonblock)
116 int rv;
118 /* always use non-blocking accept() under 1.8 for green threads */
119 set_nonblocking(a->fd);
120 TRAP_BEG;
121 rv = (int)xaccept(a);
122 TRAP_END;
123 return rv;
125 #define set_blocking_or_block(fd) (void)rb_io_wait_readable(fd)
126 #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
128 static VALUE acceptor(int argc, const VALUE *argv)
130 if (argc == 0)
131 return cClientSocket; /* default, legacy behavior */
132 else if (argc == 1)
133 return argv[0];
135 rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
138 #if defined(__linux__)
139 # define post_accept kgio_autopush_accept
140 #else
141 # define post_accept(a,b) for(;0;)
142 #endif
144 static VALUE
145 my_accept(VALUE accept_io, VALUE klass,
146 struct sockaddr *addr, socklen_t *addrlen, int nonblock)
148 int client;
149 VALUE client_io;
150 struct accept_args a;
152 a.fd = my_fileno(accept_io);
153 a.addr = addr;
154 a.addrlen = addrlen;
155 a.flags = accept4_flags;
156 retry:
157 client = thread_accept(&a, nonblock);
158 if (client == -1) {
159 switch (errno) {
160 case EAGAIN:
161 if (nonblock)
162 return Qnil;
163 set_blocking_or_block(a.fd);
164 #ifdef ECONNABORTED
165 case ECONNABORTED:
166 #endif /* ECONNABORTED */
167 #ifdef EPROTO
168 case EPROTO:
169 #endif /* EPROTO */
170 case EINTR:
171 goto retry;
172 case ENOMEM:
173 case EMFILE:
174 case ENFILE:
175 #ifdef ENOBUFS
176 case ENOBUFS:
177 #endif /* ENOBUFS */
178 errno = 0;
179 rb_gc();
180 client = thread_accept(&a, nonblock);
182 if (client == -1) {
183 if (errno == EINTR)
184 goto retry;
185 rb_sys_fail("accept");
188 client_io = sock_for_fd(klass, client);
189 post_accept(accept_io, client_io);
190 return client_io;
193 static VALUE in_addr_set(VALUE io, struct sockaddr_storage *addr, socklen_t len)
195 VALUE host;
196 int host_len, rc;
197 char *host_ptr;
199 switch (addr->ss_family) {
200 case AF_INET:
201 host_len = (long)INET_ADDRSTRLEN;
202 break;
203 case AF_INET6:
204 host_len = (long)INET6_ADDRSTRLEN;
205 break;
206 default:
207 rb_raise(rb_eRuntimeError, "unsupported address family");
209 host = rb_str_new(NULL, host_len);
210 host_ptr = RSTRING_PTR(host);
211 rc = getnameinfo((struct sockaddr *)addr, len,
212 host_ptr, host_len, NULL, 0, NI_NUMERICHOST);
213 if (rc != 0)
214 rb_raise(rb_eRuntimeError, "getnameinfo: %s", gai_strerror(rc));
215 rb_str_set_len(host, strlen(host_ptr));
216 return rb_ivar_set(io, iv_kgio_addr, host);
220 * call-seq:
222 * io.kgio_addr! => refreshes the given sock address
224 static VALUE addr_bang(VALUE io)
226 int fd = my_fileno(io);
227 struct sockaddr_storage addr;
228 socklen_t len = sizeof(struct sockaddr_storage);
230 if (getpeername(fd, (struct sockaddr *)&addr, &len) != 0)
231 rb_sys_fail("getpeername");
233 if (addr.ss_family == AF_UNIX)
234 return rb_ivar_set(io, iv_kgio_addr, localhost);
236 return in_addr_set(io, &addr, len);
240 * call-seq:
242 * server = Kgio::TCPServer.new('0.0.0.0', 80)
243 * server.kgio_tryaccept -> Kgio::Socket or nil
245 * Initiates a non-blocking accept and returns a generic Kgio::Socket
246 * object with the kgio_addr attribute set to the IP address of the
247 * connected client on success.
249 * Returns nil on EAGAIN, and raises on other errors.
251 * An optional class argument may be specified to override the
252 * Kgio::Socket-class return value:
254 * server.kgio_tryaccept(MySocket) -> MySocket
256 static VALUE tcp_tryaccept(int argc, VALUE *argv, VALUE io)
258 struct sockaddr_storage addr;
259 socklen_t addrlen = sizeof(struct sockaddr_storage);
260 VALUE klass = acceptor(argc, argv);
261 VALUE rv = my_accept(io, klass, (struct sockaddr *)&addr, &addrlen, 1);
263 if (!NIL_P(rv))
264 in_addr_set(rv, &addr, addrlen);
265 return rv;
269 * call-seq:
271 * server = Kgio::TCPServer.new('0.0.0.0', 80)
272 * server.kgio_accept -> Kgio::Socket or nil
274 * Initiates a blocking accept and returns a generic Kgio::Socket
275 * object with the kgio_addr attribute set to the IP address of
276 * the client on success.
278 * On Ruby implementations using native threads, this can use a blocking
279 * accept(2) (or accept4(2)) system call to avoid thundering herds.
281 * An optional class argument may be specified to override the
282 * Kgio::Socket-class return value:
284 * server.kgio_accept(MySocket) -> MySocket
286 static VALUE tcp_accept(int argc, VALUE *argv, VALUE io)
288 struct sockaddr_storage addr;
289 socklen_t addrlen = sizeof(struct sockaddr_storage);
290 VALUE klass = acceptor(argc, argv);
291 VALUE rv = my_accept(io, klass, (struct sockaddr *)&addr, &addrlen, 0);
293 in_addr_set(rv, &addr, addrlen);
294 return rv;
298 * call-seq:
300 * server = Kgio::UNIXServer.new("/path/to/unix/socket")
301 * server.kgio_tryaccept -> Kgio::Socket or nil
303 * Initiates a non-blocking accept and returns a generic Kgio::Socket
304 * object with the kgio_addr attribute set (to the value of
305 * Kgio::LOCALHOST) on success.
307 * Returns nil on EAGAIN, and raises on other errors.
309 * An optional class argument may be specified to override the
310 * Kgio::Socket-class return value:
312 * server.kgio_tryaccept(MySocket) -> MySocket
314 static VALUE unix_tryaccept(int argc, VALUE *argv, VALUE io)
316 VALUE klass = acceptor(argc, argv);
317 VALUE rv = my_accept(io, klass, NULL, NULL, 1);
319 if (!NIL_P(rv))
320 rb_ivar_set(rv, iv_kgio_addr, localhost);
321 return rv;
325 * call-seq:
327 * server = Kgio::UNIXServer.new("/path/to/unix/socket")
328 * server.kgio_accept -> Kgio::Socket or nil
330 * Initiates a blocking accept and returns a generic Kgio::Socket
331 * object with the kgio_addr attribute set (to the value of
332 * Kgio::LOCALHOST) on success.
334 * On Ruby implementations using native threads, this can use a blocking
335 * accept(2) (or accept4(2)) system call to avoid thundering herds.
337 * An optional class argument may be specified to override the
338 * Kgio::Socket-class return value:
340 * server.kgio_accept(MySocket) -> MySocket
342 static VALUE unix_accept(int argc, VALUE *argv, VALUE io)
344 VALUE klass = acceptor(argc, argv);
345 VALUE rv = my_accept(io, klass, NULL, NULL, 0);
347 rb_ivar_set(rv, iv_kgio_addr, localhost);
348 return rv;
352 * call-seq:
354 * Kgio.accept_cloexec? -> true or false
356 * Returns true if newly accepted Kgio::Sockets are created with the
357 * FD_CLOEXEC file descriptor flag, false if not.
359 static VALUE get_cloexec(VALUE mod)
361 return (accept4_flags & SOCK_CLOEXEC) == SOCK_CLOEXEC ? Qtrue : Qfalse;
366 * call-seq:
368 * Kgio.accept_nonblock? -> true or false
370 * Returns true if newly accepted Kgio::Sockets are created with the
371 * O_NONBLOCK file status flag, false if not.
373 static VALUE get_nonblock(VALUE mod)
375 return (accept4_flags & SOCK_NONBLOCK)==SOCK_NONBLOCK ? Qtrue : Qfalse;
379 * call-seq:
381 * Kgio.accept_cloexec = true
382 * Kgio.accept_cloexec = false
384 * Sets whether or not Kgio::Socket objects created by
385 * TCPServer#kgio_accept,
386 * TCPServer#kgio_tryaccept,
387 * UNIXServer#kgio_accept,
388 * and UNIXServer#kgio_tryaccept
389 * are created with the FD_CLOEXEC file descriptor flag.
391 * This is on by default, as there is little reason to deal to enable
392 * it for client sockets on a socket server.
394 static VALUE set_cloexec(VALUE mod, VALUE boolean)
396 switch (TYPE(boolean)) {
397 case T_TRUE:
398 accept4_flags |= SOCK_CLOEXEC;
399 return boolean;
400 case T_FALSE:
401 accept4_flags &= ~SOCK_CLOEXEC;
402 return boolean;
404 rb_raise(rb_eTypeError, "not true or false");
405 return Qnil;
409 * call-seq:
411 * Kgio.accept_nonblock = true
412 * Kgio.accept_nonblock = false
414 * Sets whether or not Kgio::Socket objects created by
415 * TCPServer#kgio_accept,
416 * TCPServer#kgio_tryaccept,
417 * UNIXServer#kgio_accept,
418 * and UNIXServer#kgio_tryaccept
419 * are created with the O_NONBLOCK file status flag.
421 * This defaults to +false+ for GNU/Linux where MSG_DONTWAIT is
422 * available (and on newer GNU/Linux, accept4() may also set
423 * the non-blocking flag. This defaults to +true+ on non-GNU/Linux
424 * systems.
426 static VALUE set_nonblock(VALUE mod, VALUE boolean)
428 switch (TYPE(boolean)) {
429 case T_TRUE:
430 accept4_flags |= SOCK_NONBLOCK;
431 return boolean;
432 case T_FALSE:
433 accept4_flags &= ~SOCK_NONBLOCK;
434 return boolean;
436 rb_raise(rb_eTypeError, "not true or false");
437 return Qnil;
440 void init_kgio_accept(void)
442 VALUE cUNIXServer, cTCPServer;
443 VALUE mKgio = rb_define_module("Kgio");
445 localhost = rb_const_get(mKgio, rb_intern("LOCALHOST"));
446 cKgio_Socket = rb_const_get(mKgio, rb_intern("Socket"));
447 cClientSocket = cKgio_Socket;
448 mSocketMethods = rb_const_get(mKgio, rb_intern("SocketMethods"));
450 rb_define_method(mSocketMethods, "kgio_addr!", addr_bang, 0);
452 rb_define_singleton_method(mKgio, "accept_cloexec?", get_cloexec, 0);
453 rb_define_singleton_method(mKgio, "accept_cloexec=", set_cloexec, 1);
454 rb_define_singleton_method(mKgio, "accept_nonblock?", get_nonblock, 0);
455 rb_define_singleton_method(mKgio, "accept_nonblock=", set_nonblock, 1);
456 rb_define_singleton_method(mKgio, "accept_class=", set_accepted, 1);
457 rb_define_singleton_method(mKgio, "accept_class", get_accepted, 0);
460 * Document-class: Kgio::UNIXServer
462 * Kgio::UNIXServer should be used in place of the plain UNIXServer
463 * when kgio_accept and kgio_tryaccept methods are needed.
465 cUNIXServer = rb_const_get(rb_cObject, rb_intern("UNIXServer"));
466 cUNIXServer = rb_define_class_under(mKgio, "UNIXServer", cUNIXServer);
467 rb_define_method(cUNIXServer, "kgio_tryaccept", unix_tryaccept, -1);
468 rb_define_method(cUNIXServer, "kgio_accept", unix_accept, -1);
471 * Document-class: Kgio::TCPServer
473 * Kgio::TCPServer should be used in place of the plain TCPServer
474 * when kgio_accept and kgio_tryaccept methods are needed.
476 cTCPServer = rb_const_get(rb_cObject, rb_intern("TCPServer"));
477 cTCPServer = rb_define_class_under(mKgio, "TCPServer", cTCPServer);
479 rb_define_method(cTCPServer, "kgio_tryaccept", tcp_tryaccept, -1);
480 rb_define_method(cTCPServer, "kgio_accept", tcp_accept, -1);
481 init_sock_for_fd();
482 iv_kgio_addr = rb_intern("@kgio_addr");