diff --git a/Gemfile b/Gemfile index 51c7ad4933..3ac4e7428b 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 fba31152cd..0e12c316b4 100644 --- a/lib/puma/client.rb +++ b/lib/puma/client.rb @@ -111,6 +111,7 @@ def close 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 44cb8b4bd5..7bd3cc4cdb 100644 --- a/lib/puma/cluster.rb +++ b/lib/puma/cluster.rb @@ -224,6 +224,7 @@ def wakeup! 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 @@ def worker(index, master) 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 @@ def worker(index, master) 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 7332926ac3..5f1216f22c 100644 --- a/lib/puma/minissl.rb +++ b/lib/puma/minissl.rb @@ -118,6 +118,7 @@ def close 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 5c1cb62f18..12366edb0e 100644 --- a/lib/puma/reactor.rb +++ b/lib/puma/reactor.rb @@ -28,6 +28,7 @@ def run_internal 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 @@ def clear! begin @trigger << "c" rescue IOError + Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue end end @@ -202,6 +204,7 @@ def shutdown 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 7ef517c1ba..51cd810811 100644 --- a/lib/puma/server.rb +++ b/lib/puma/server.rb @@ -110,6 +110,7 @@ def cork_socket(socket) 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 @@ def uncork_socket(socket) 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 @@ def closed_socket?(socket) 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 @@ def process_client(client, buffer) 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 @@ def notify_safely(message) 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 ec2717551c..6f8d3be5c0 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