[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]