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

ruby-changes:51532

From: normal <ko1@a...>
Date: Mon, 25 Jun 2018 07:08:20 +0900 (JST)
Subject: [ruby-changes:51532] normal:r63742 (trunk): UNIXSocket#recv_io: trigger GC when out of FDs

normal	2018-06-25 07:08:15 +0900 (Mon, 25 Jun 2018)

  New Revision: 63742

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=63742

  Log:
    UNIXSocket#recv_io: trigger GC when out of FDs
    
    Make this behavior is consistent with our other FD-allocating
    methods.
    
    EMFILE and ENFILE are not documented nor can I trigger them when
    using UNIXSocket#recv_io.  However, ENOMEM is documented, and
    I've triggered EMSGSIZE on FreeBSD and truncated messages when
    an EMFILE condition is hit on my system.

  Modified files:
    trunk/ext/socket/unixsocket.c
Index: ext/socket/unixsocket.c
===================================================================
--- ext/socket/unixsocket.c	(revision 63741)
+++ ext/socket/unixsocket.c	(revision 63742)
@@ -339,6 +339,12 @@ unix_recv_io(int argc, VALUE *argv, VALU https://github.com/ruby/ruby/blob/trunk/ext/socket/unixsocket.c#L339
     struct iomsg_arg arg;
     struct iovec vec[2];
     char buf[1];
+    unsigned int gc_reason = 0;
+    enum {
+        GC_REASON_EMSGSIZE = 0x1,
+        GC_REASON_TRUNCATE = 0x2,
+        GC_REASON_ENOMEM = 0x4,
+    };
 
     int fd;
 #if FD_PASSING_BY_MSG_CONTROL
@@ -354,6 +360,7 @@ unix_recv_io(int argc, VALUE *argv, VALU https://github.com/ruby/ruby/blob/trunk/ext/socket/unixsocket.c#L360
     if (argc <= 1)
 	mode = Qnil;
 
+retry:
     GetOpenFile(sock, fptr);
 
     arg.msg.msg_name = NULL;
@@ -381,12 +388,31 @@ unix_recv_io(int argc, VALUE *argv, VALU https://github.com/ruby/ruby/blob/trunk/ext/socket/unixsocket.c#L388
 
     arg.fd = fptr->fd;
     while ((int)BLOCKING_REGION_FD(recvmsg_blocking, &arg) == -1) {
+        int e = errno;
+        if (e == EMSGSIZE && !(gc_reason & GC_REASON_EMSGSIZE)) {
+            /* FreeBSD gets here when we're out of FDs */
+            gc_reason |= GC_REASON_EMSGSIZE;
+            rb_gc_for_fd(EMFILE);
+            goto retry;
+        }
+        else if (e == ENOMEM && !(gc_reason & GC_REASON_ENOMEM)) {
+            /* ENOMEM is documented in recvmsg manpages */
+            gc_reason |= GC_REASON_ENOMEM;
+            rb_gc_for_fd(e);
+            goto retry;
+        }
 	if (!rb_io_wait_readable(arg.fd))
-	    rsock_sys_fail_path("recvmsg(2)", fptr->pathv);
+	    rsock_syserr_fail_path(e, "recvmsg(2)", fptr->pathv);
     }
 
 #if FD_PASSING_BY_MSG_CONTROL
     if (arg.msg.msg_controllen < (socklen_t)sizeof(struct cmsghdr)) {
+        /* FreeBSD and Linux both get here when we're out of FDs */
+        if (!(gc_reason & GC_REASON_TRUNCATE)) {
+            gc_reason |= GC_REASON_TRUNCATE;
+            rb_gc_for_fd(EMFILE);
+            goto retry;
+        }
 	rb_raise(rb_eSocket,
 		 "file descriptor was not passed (msg_controllen=%d smaller than sizeof(struct cmsghdr)=%d)",
 		 (int)arg.msg.msg_controllen, (int)sizeof(struct cmsghdr));

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

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