From 7727b22eb1dd2aab3127a0b4fdb6ee52aedf358e Mon Sep 17 00:00:00 2001 From: nobu Date: Mon, 30 Apr 2018 02:17:03 +0000 Subject: io.c: workaround for EPROTOTYPE * io.c (internal_write_func, internal_writev_func): retry at unexpected EPROTOTYPE on macOS, to get rid of a kernel bug. [ruby-core:86690] [Bug #14713] * ext/socket/init.c (rsock_{sendto,send,write}_blocking): ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@63304 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/socket/init.c | 26 ++++++++++++++++++++++---- io.c | 13 +++++++++++-- test/net/ftp/test_ftp.rb | 2 +- test/webrick/test_httpserver.rb | 2 +- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/ext/socket/init.c b/ext/socket/init.c index 3b22c1308b..c26ab135ab 100644 --- a/ext/socket/init.c +++ b/ext/socket/init.c @@ -68,6 +68,12 @@ is_socket(int fd) } #endif +#if defined __APPLE__ +# define do_write_retry(code) do {ret = code;} while (ret == -1 && errno == EPROTOTYPE) +#else +# define do_write_retry(code) ret = code +#endif + VALUE rsock_init_sock(VALUE sock, int fd) { @@ -95,8 +101,10 @@ rsock_sendto_blocking(void *data) { struct rsock_send_arg *arg = data; VALUE mesg = arg->mesg; - return (VALUE)sendto(arg->fd, RSTRING_PTR(mesg), RSTRING_LEN(mesg), - arg->flags, arg->to, arg->tolen); + ssize_t ret; + do_write_retry(sendto(arg->fd, RSTRING_PTR(mesg), RSTRING_LEN(mesg), + arg->flags, arg->to, arg->tolen)); + return (VALUE)ret; } VALUE @@ -104,8 +112,10 @@ rsock_send_blocking(void *data) { struct rsock_send_arg *arg = data; VALUE mesg = arg->mesg; - return (VALUE)send(arg->fd, RSTRING_PTR(mesg), RSTRING_LEN(mesg), - arg->flags); + ssize_t ret; + do_write_retry(send(arg->fd, RSTRING_PTR(mesg), RSTRING_LEN(mesg), + arg->flags)); + return (VALUE)ret; } struct recvfrom_arg { @@ -378,10 +388,18 @@ rsock_write_nonblock(VALUE sock, VALUE str, VALUE ex) rb_io_flush(sock); } +#ifdef __APPLE__ + again: +#endif n = (long)send(fptr->fd, RSTRING_PTR(str), RSTRING_LEN(str), MSG_DONTWAIT); if (n < 0) { int e = errno; +#ifdef __APPLE__ + if (e == EPROTOTYPE) { + goto again; + } +#endif if (e == EWOULDBLOCK || e == EAGAIN) { if (ex == Qfalse) return sym_wait_writable; rb_readwrite_syserr_fail(RB_IO_WAIT_WRITABLE, e, diff --git a/io.c b/io.c index 5e921a55bd..c0457731d0 100644 --- a/io.c +++ b/io.c @@ -945,11 +945,18 @@ internal_read_func(void *ptr) return read(iis->fd, iis->buf, iis->capa); } +#if defined __APPLE__ +# define do_write_retry(code) do {ret = code;} while (ret == -1 && errno == EPROTOTYPE) +#else +# define do_write_retry(code) ret = code +#endif static VALUE internal_write_func(void *ptr) { struct io_internal_write_struct *iis = ptr; - return write(iis->fd, iis->buf, iis->capa); + ssize_t ret; + do_write_retry(write(iis->fd, iis->buf, iis->capa)); + return (VALUE)ret; } static void* @@ -963,7 +970,9 @@ static VALUE internal_writev_func(void *ptr) { struct io_internal_writev_struct *iis = ptr; - return writev(iis->fd, iis->iov, iis->iovcnt); + ssize_t ret; + do_write_retry(writev(iis->fd, iis->iov, iis->iovcnt)); + return (VALUE)ret; } #endif diff --git a/test/net/ftp/test_ftp.rb b/test/net/ftp/test_ftp.rb index f1c13c287f..36292f181a 100644 --- a/test/net/ftp/test_ftp.rb +++ b/test/net/ftp/test_ftp.rb @@ -425,7 +425,7 @@ class FTPTest < Test::Unit::TestCase end conn.print(l, "\r\n") end - rescue Errno::EPIPE, Errno::EPROTOTYPE + rescue Errno::EPIPE ensure assert_nil($!) conn.close diff --git a/test/webrick/test_httpserver.rb b/test/webrick/test_httpserver.rb index 3f888110be..77da797282 100644 --- a/test/webrick/test_httpserver.rb +++ b/test/webrick/test_httpserver.rb @@ -452,7 +452,7 @@ class TestWEBrickHTTPServer < Test::Unit::TestCase TCPSocket.open(addr, port) do |c| c.write("GET / HTTP/1.0\r\n") junk = -"X-Junk: #{' ' * 1024}\r\n" - assert_raise(Errno::ECONNRESET, Errno::EPIPE, Errno::EPROTOTYPE) do + assert_raise(Errno::ECONNRESET, Errno::EPIPE) do loop { c.write(junk) } end end -- cgit v1.2.3