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/