ruby-changes:32649
From: akr <ko1@a...>
Date: Tue, 28 Jan 2014 23:37:40 +0900 (JST)
Subject: [ruby-changes:32649] akr:r44728 (trunk): * ext/socket: Avoid redundant fcntl/fstat syscalls for cloexec
akr 2014-01-28 23:37:34 +0900 (Tue, 28 Jan 2014) New Revision: 44728 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=44728 Log: * ext/socket: Avoid redundant fcntl/fstat syscalls for cloexec sockets. Patch by Eric Wong. [ruby-core:59429] [Feature #9330] Modified files: trunk/ChangeLog trunk/ext/socket/ancdata.c trunk/ext/socket/init.c trunk/ext/socket/rubysocket.h trunk/ext/socket/socket.c trunk/ext/socket/unixsocket.c Index: ChangeLog =================================================================== --- ChangeLog (revision 44727) +++ ChangeLog (revision 44728) @@ -1,3 +1,9 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Tue Jan 28 23:36:01 2014 Tanaka Akira <akr@f...> + + * ext/socket: Avoid redundant fcntl/fstat syscalls for cloexec + sockets. + Patch by Eric Wong. [ruby-core:59429] [Feature #9330] + Tue Jan 28 20:51:07 2014 Tanaka Akira <akr@f...> * process.c (READ_FROM_CHILD): Apply the last hunk of Index: ext/socket/rubysocket.h =================================================================== --- ext/socket/rubysocket.h (revision 44727) +++ ext/socket/rubysocket.h (revision 44728) @@ -226,6 +226,7 @@ typedef union { https://github.com/ruby/ruby/blob/trunk/ext/socket/rubysocket.h#L226 #define INET_SOCKS 2 extern int rsock_do_not_reverse_lookup; +extern int rsock_cmsg_cloexec_state; #define FMODE_NOREVLOOKUP 0x100 extern VALUE rb_cBasicSocket; @@ -304,6 +305,7 @@ socklen_t rsock_unix_sockaddr_len(VALUE https://github.com/ruby/ruby/blob/trunk/ext/socket/rubysocket.h#L305 #endif int rsock_socket(int domain, int type, int proto); +int rsock_detect_cloexec(int fd); VALUE rsock_init_sock(VALUE sock, int fd); VALUE rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass); VALUE rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv, VALUE local_host, VALUE local_serv, int type); Index: ext/socket/init.c =================================================================== --- ext/socket/init.c (revision 44727) +++ ext/socket/init.c (revision 44728) @@ -249,24 +249,55 @@ rsock_s_recvfrom_nonblock(VALUE sock, in https://github.com/ruby/ruby/blob/trunk/ext/socket/init.c#L249 return rb_assoc_new(str, addr); } +/* returns true if SOCK_CLOEXEC is supported */ +int rsock_detect_cloexec(int fd) +{ +#ifdef SOCK_CLOEXEC + int flags = fcntl(fd, F_GETFD); + + if (flags == -1) + rb_bug("rsock_detect_cloexec: fcntl(%d, F_GETFD) failed: %s", fd, strerror(errno)); + + if (flags & FD_CLOEXEC) + return 1; +#endif + return 0; +} + static int rsock_socket0(int domain, int type, int proto) { int ret; #ifdef SOCK_CLOEXEC - static int try_sock_cloexec = 1; - if (try_sock_cloexec) { + static int cloexec_state = -1; /* <0: unknown, 0: ignored, >0: working */ + + if (cloexec_state > 0) { /* common path, if SOCK_CLOEXEC is defined */ + ret = socket(domain, type|SOCK_CLOEXEC, proto); + if (ret >= 0) { + if (ret <= 2) + goto fix_cloexec; + goto update_max_fd; + } + } + else if (cloexec_state < 0) { /* usually runs once only for detection */ ret = socket(domain, type|SOCK_CLOEXEC, proto); - if (ret == -1 && errno == EINVAL) { + if (ret >= 0) { + cloexec_state = rsock_detect_cloexec(ret); + if (cloexec_state == 0 || ret <= 2) + goto fix_cloexec; + goto update_max_fd; + } + else if (ret == -1 && errno == EINVAL) { /* SOCK_CLOEXEC is available since Linux 2.6.27. Linux 2.6.18 fails with EINVAL */ ret = socket(domain, type, proto); if (ret != -1) { - try_sock_cloexec = 0; + cloexec_state = 0; + /* fall through to fix_cloexec */ } } } - else { + else { /* cloexec_state == 0 */ ret = socket(domain, type, proto); } #else @@ -274,11 +305,12 @@ rsock_socket0(int domain, int type, int https://github.com/ruby/ruby/blob/trunk/ext/socket/init.c#L305 #endif if (ret == -1) return -1; - - rb_fd_fix_cloexec(ret); +fix_cloexec: + rb_maygvl_fd_fix_cloexec(ret); +update_max_fd: + rb_update_max_fd(ret); return ret; - } int Index: ext/socket/socket.c =================================================================== --- ext/socket/socket.c (revision 44727) +++ ext/socket/socket.c (revision 44728) @@ -174,10 +174,24 @@ rsock_socketpair0(int domain, int type, https://github.com/ruby/ruby/blob/trunk/ext/socket/socket.c#L174 int ret; #ifdef SOCK_CLOEXEC - static int try_sock_cloexec = 1; - if (try_sock_cloexec) { + static int cloexec_state = -1; /* <0: unknown, 0: ignored, >0: working */ + + if (cloexec_state > 0) { /* common path, if SOCK_CLOEXEC is defined */ ret = socketpair(domain, type|SOCK_CLOEXEC, protocol, sv); - if (ret == -1 && errno == EINVAL) { + if (ret == 0 && (sv[0] <= 2 || sv[1] <= 2)) { + goto fix_cloexec; /* highly unlikely */ + } + goto update_max_fd; + } + else if (cloexec_state < 0) { /* usually runs once only for detection */ + ret = socketpair(domain, type|SOCK_CLOEXEC, protocol, sv); + if (ret == 0) { + cloexec_state = rsock_detect_cloexec(sv[0]); + if ((cloexec_state == 0) || (sv[0] <= 2 || sv[1] <= 2)) + goto fix_cloexec; + goto update_max_fd; + } + else if (ret == -1 && errno == EINVAL) { /* SOCK_CLOEXEC is available since Linux 2.6.27. Linux 2.6.18 fails with EINVAL */ ret = socketpair(domain, type, protocol, sv); if (ret != -1) { @@ -185,11 +199,11 @@ rsock_socketpair0(int domain, int type, https://github.com/ruby/ruby/blob/trunk/ext/socket/socket.c#L199 * So disable SOCK_CLOEXEC only if socketpair() succeeds without SOCK_CLOEXEC. * Ex. Socket.pair(:UNIX, 0xff) fails with EINVAL. */ - try_sock_cloexec = 0; + cloexec_state = 0; } } } - else { + else { /* cloexec_state == 0 */ ret = socketpair(domain, type, protocol, sv); } #else @@ -200,8 +214,13 @@ rsock_socketpair0(int domain, int type, https://github.com/ruby/ruby/blob/trunk/ext/socket/socket.c#L214 return -1; } - rb_fd_fix_cloexec(sv[0]); - rb_fd_fix_cloexec(sv[1]); +fix_cloexec: + rb_maygvl_fd_fix_cloexec(sv[0]); + rb_maygvl_fd_fix_cloexec(sv[1]); + +update_max_fd: + rb_update_max_fd(sv[0]); + rb_update_max_fd(sv[1]); return ret; } @@ -267,8 +286,6 @@ rsock_sock_s_socketpair(int argc, VALUE https://github.com/ruby/ruby/blob/trunk/ext/socket/socket.c#L286 if (ret < 0) { rb_sys_fail("socketpair(2)"); } - rb_fd_fix_cloexec(sp[0]); - rb_fd_fix_cloexec(sp[1]); s1 = rsock_init_sock(rb_obj_alloc(klass), sp[0]); s2 = rsock_init_sock(rb_obj_alloc(klass), sp[1]); Index: ext/socket/ancdata.c =================================================================== --- ext/socket/ancdata.c (revision 44727) +++ ext/socket/ancdata.c (revision 44728) @@ -2,6 +2,8 @@ https://github.com/ruby/ruby/blob/trunk/ext/socket/ancdata.c#L2 #include <time.h> +int rsock_cmsg_cloexec_state = -1; /* <0: unknown, 0: ignored, >0: working */ + #if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) static VALUE rb_cAncillaryData; @@ -1416,7 +1418,7 @@ discard_cmsg(struct cmsghdr *cmh, char * https://github.com/ruby/ruby/blob/trunk/ext/socket/ancdata.c#L1418 int *end = (int *)((char *)cmh + cmh->cmsg_len); while ((char *)fdp + sizeof(int) <= (char *)end && (char *)fdp + sizeof(int) <= msg_end) { - rb_fd_fix_cloexec(*fdp); + rb_update_max_fd(*fdp); close(*fdp); fdp++; } @@ -1459,7 +1461,11 @@ make_io_for_unix_rights(VALUE ctl, struc https://github.com/ruby/ruby/blob/trunk/ext/socket/ancdata.c#L1461 VALUE io; if (fstat(fd, &stbuf) == -1) rb_raise(rb_eSocket, "invalid fd in SCM_RIGHTS"); - rb_fd_fix_cloexec(fd); + rb_update_max_fd(fd); + if (rsock_cmsg_cloexec_state < 0) + rsock_cmsg_cloexec_state = rsock_detect_cloexec(fd); + if (rsock_cmsg_cloexec_state == 0 || fd <= 2) + rb_maygvl_fd_fix_cloexec(fd); if (S_ISSOCK(stbuf.st_mode)) io = rsock_init_sock(rb_obj_alloc(rb_cSocket), fd); else Index: ext/socket/unixsocket.c =================================================================== --- ext/socket/unixsocket.c (revision 44727) +++ ext/socket/unixsocket.c (revision 44728) @@ -389,7 +389,13 @@ unix_recv_io(int argc, VALUE *argv, VALU https://github.com/ruby/ruby/blob/trunk/ext/socket/unixsocket.c#L389 #if FD_PASSING_BY_MSG_CONTROL memcpy(&fd, CMSG_DATA(&cmsg.hdr), sizeof(int)); #endif - rb_fd_fix_cloexec(fd); + + rb_update_max_fd(fd); + + if (rsock_cmsg_cloexec_state < 0) + rsock_cmsg_cloexec_state = rsock_detect_cloexec(fd); + if (rsock_cmsg_cloexec_state == 0 || fd <= 2) + rb_maygvl_fd_fix_cloexec(fd); if (klass == Qnil) return INT2FIX(fd); -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/