From 91416134cbbdafda36f808b824f77f5d54a6ae88 Mon Sep 17 00:00:00 2001 From: Nikolay Vashchenko Date: Wed, 19 Jul 2017 21:22:36 +0300 Subject: [PATCH] temporary workaround for https://bugs.ruby-lang.org/issues/13632 (#1345) * temporary workaround for https://bugs.ruby-lang.org/issues/13632 Purging interrupt queue if IOError was caught. * fixing only if mri * optimization to avoid redundant checks in empty queue * scoping fix to only affected versions * serving ruby version from mkmf * safe invoking for the workaround * switching to preprocessor vars * purging queue on runtime error * rubocop fix * covering workaround * improving names * styling * rubocop fixes * improved test reporting * wording * condition * improving comment * bugfix moved to separate gem: https://rubygems.org/gems/stopgap_13632 * using stopgap_13632 in gemfile to fix the builds * requiring stopgap for tests --- Gemfile | 4 ++++ lib/puma/client.rb | 1 + lib/puma/cluster.rb | 3 +++ lib/puma/minissl.rb | 1 + lib/puma/reactor.rb | 3 +++ lib/puma/server.rb | 16 ++++++++++++---- test/helper.rb | 2 ++ 7 files changed, 26 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 51c7ad49..3ac4e742 100644 --- a/Gemfile +++ b/Gemfile @@ -14,3 +14,7 @@ gem "minitest", "~> 5.9" gem "jruby-openssl", :platform => "jruby" gem "rubocop", "~> 0.49.1" + +if %w(2.2.7 2.3.4 2.4.1).include? RUBY_VERSION + gem "stopgap_13632", "~> 1.0", :platform => "mri" +end diff --git a/lib/puma/client.rb b/lib/puma/client.rb index fba31152..0e12c316 100644 --- a/lib/puma/client.rb +++ b/lib/puma/client.rb @@ -111,6 +111,7 @@ module Puma begin @io.close rescue IOError + Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue end end diff --git a/lib/puma/cluster.rb b/lib/puma/cluster.rb index 44cb8b4b..7bd3cc4c 100644 --- a/lib/puma/cluster.rb +++ b/lib/puma/cluster.rb @@ -224,6 +224,7 @@ module Puma begin @wakeup.write "!" unless @wakeup.closed? rescue SystemCallError, IOError + Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue end end @@ -267,6 +268,7 @@ module Puma begin @worker_write << "b#{Process.pid}\n" rescue SystemCallError, IOError + Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue STDERR.puts "Master seems to have exited, exiting." return end @@ -282,6 +284,7 @@ module Puma payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r} }\n! io << payload rescue IOError + Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue break end end diff --git a/lib/puma/minissl.rb b/lib/puma/minissl.rb index 7332926a..5f1216f2 100644 --- a/lib/puma/minissl.rb +++ b/lib/puma/minissl.rb @@ -118,6 +118,7 @@ module Puma return if read_and_drop(1) == :timeout end rescue IOError, SystemCallError + Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue # nothing ensure @socket.close diff --git a/lib/puma/reactor.rb b/lib/puma/reactor.rb index 5c1cb62f..12366edb 100644 --- a/lib/puma/reactor.rb +++ b/lib/puma/reactor.rb @@ -28,6 +28,7 @@ module Puma begin ready = IO.select sockets, nil, nil, @sleep_for rescue IOError => e + Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue if sockets.any? { |socket| socket.closed? } STDERR.puts "Error in select: #{e.message} (#{e.class})" STDERR.puts e.backtrace @@ -195,6 +196,7 @@ module Puma begin @trigger << "c" rescue IOError + Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue end end @@ -202,6 +204,7 @@ module Puma begin @trigger << "!" rescue IOError + Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue end @thread.join diff --git a/lib/puma/server.rb b/lib/puma/server.rb index 7ef517c1..51cd8108 100644 --- a/lib/puma/server.rb +++ b/lib/puma/server.rb @@ -110,6 +110,7 @@ module Puma begin socket.setsockopt(6, 3, 1) if socket.kind_of? TCPSocket rescue IOError, SystemCallError + Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue end end @@ -117,6 +118,7 @@ module Puma begin socket.setsockopt(6, 3, 0) if socket.kind_of? TCPSocket rescue IOError, SystemCallError + Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue end end @@ -127,6 +129,7 @@ module Puma begin tcp_info = socket.getsockopt(Socket::SOL_TCP, Socket::TCP_INFO) rescue IOError, SystemCallError + Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue @precheck_closing = false false else @@ -490,6 +493,7 @@ module Puma begin client.close if close_socket rescue IOError, SystemCallError + Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue # Already closed rescue StandardError => e @events.unknown_error self, e, "Client" @@ -896,11 +900,15 @@ module Puma begin @notify << message rescue IOError - # The server, in another thread, is shutting down + # The server, in another thread, is shutting down + Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue rescue RuntimeError => e - # The server, in another thread, has been shut down during the system call - # https://github.com/puma/puma/pull/1206 - raise e unless e.message.include?('IOError') + # Temporary workaround for https://bugs.ruby-lang.org/issues/13239 + if e.message.include?('IOError') + Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue + else + raise e + end end end private :notify_safely diff --git a/test/helper.rb b/test/helper.rb index ec271755..6f8d3be5 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -1,6 +1,8 @@ # Copyright (c) 2011 Evan Phoenix # Copyright (c) 2005 Zed A. Shaw +require 'stopgap_13632' if %w(2.2.7 2.3.4 2.4.1).include? RUBY_VERSION + begin require "bundler/setup" rescue LoadError