From 7100ae4b9d8ac99db3663fdb70b6aa73dec9f7ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Fri, 1 Jul 2016 19:14:41 +0200 Subject: [PATCH] Configure threads to not abort on exceptions (#484) * Configure all threads to not abort on exceptions This solves long outstanding issue: https://github.com/rails/spring/issues/396. This happens, because some applications or gems do overwrite global setting of `Thread.abort_on_exception=` which is by default set to `false`. This leads to application process to exit and making the spring unable to recover from that. * Use rescue blocks instead of `abort_on_exception=` * Rescue PTY * Use `Spring.failsafe_thread` * Add CHANGELOG --- CHANGELOG.md | 4 ++++ lib/spring/application.rb | 4 ++-- lib/spring/application_manager.rb | 2 +- lib/spring/boot.rb | 2 ++ lib/spring/failsafe_thread.rb | 14 ++++++++++++++ lib/spring/process_title_updater.rb | 2 +- lib/spring/server.rb | 2 +- 7 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 lib/spring/failsafe_thread.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index c52f3da9..3c041690 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.7.2 + +* Use `Spring.failsafe_thread` to prevent threads from aborting process due to `Thread.abort_on_exception` when set to `true` + ## 1.7.1 * Specify absolute path to spring binfile when starting the server diff --git a/lib/spring/application.rb b/lib/spring/application.rb index e9735f82..c9fa4bab 100644 --- a/lib/spring/application.rb +++ b/lib/spring/application.rb @@ -306,7 +306,7 @@ def wait(pid, streams, client) @mutex.synchronize { @waiting << pid } # Wait in a separate thread so we can run multiple commands at once - Thread.new { + Spring.failsafe_thread { begin _, status = Process.wait2 pid log "#{pid} exited with #{status.exitstatus}" @@ -320,7 +320,7 @@ def wait(pid, streams, client) end } - Thread.new { + Spring.failsafe_thread { while signal = client.gets.chomp begin Process.kill(signal, -Process.getpgid(pid)) diff --git a/lib/spring/application_manager.rb b/lib/spring/application_manager.rb index bb5bdfad..ca4671fb 100644 --- a/lib/spring/application_manager.rb +++ b/lib/spring/application_manager.rb @@ -116,7 +116,7 @@ def start_child(preload = false) def start_wait_thread(pid, child) Process.detach(pid) - Thread.new { + Spring.failsafe_thread { # The recv can raise an ECONNRESET, killing the thread, but that's ok # as if it does we're no longer interested in the child loop do diff --git a/lib/spring/boot.rb b/lib/spring/boot.rb index e3b2421d..75498a18 100644 --- a/lib/spring/boot.rb +++ b/lib/spring/boot.rb @@ -6,3 +6,5 @@ require "spring/process_title_updater" require "spring/json" require "spring/watcher" +require "spring/failsafe_thread" + diff --git a/lib/spring/failsafe_thread.rb b/lib/spring/failsafe_thread.rb new file mode 100644 index 00000000..792dc7b8 --- /dev/null +++ b/lib/spring/failsafe_thread.rb @@ -0,0 +1,14 @@ +require 'thread' + +module Spring + class << self + def failsafe_thread + Thread.new { + begin + yield + rescue + end + } + end + end +end diff --git a/lib/spring/process_title_updater.rb b/lib/spring/process_title_updater.rb index e10140cb..dd701a07 100644 --- a/lib/spring/process_title_updater.rb +++ b/lib/spring/process_title_updater.rb @@ -10,7 +10,7 @@ class ProcessTitleUpdater def self.run(&block) updater = new(&block) - Thread.new { + Spring.failsafe_thread { $0 = updater.value loop { $0 = updater.next } } diff --git a/lib/spring/server.rb b/lib/spring/server.rb index be908532..91ea84ea 100644 --- a/lib/spring/server.rb +++ b/lib/spring/server.rb @@ -106,7 +106,7 @@ def shutdown end end - @applications.values.map { |a| Thread.new { a.stop } }.map(&:join) + @applications.values.map { |a| Spring.failsafe_thread { a.stop } }.map(&:join) end def write_pidfile